From b6806eb1c23fc7f7ce99f1602fca1c282a2e8047 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 8 Dec 2017 04:34:50 +0800 Subject: [PATCH 01/17] Add more splitting rules for string equations (including rules based on length constraints) --- src/smt/smt_setup.cpp | 8 +- src/smt/theory_seq.cpp | 1121 +++++++++++++++++++++++++++++++++++++++- src/smt/theory_seq.h | 43 +- 3 files changed, 1146 insertions(+), 26 deletions(-) diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 631805b4d..80543bb6f 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -123,8 +123,8 @@ namespace smt { setup_QF_FP(); else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP") setup_QF_FPBV(); - else if (m_logic == "QF_S") - setup_QF_S(); +// else if (m_logic == "QF_S") +// setup_QF_S(); else if (m_logic == "QF_DT") setup_QF_DT(); else @@ -170,8 +170,8 @@ namespace smt { setup_QF_BVRE(); else if (m_logic == "QF_AUFLIA") setup_QF_AUFLIA(st); - else if (m_logic == "QF_S") - setup_QF_S(); +// else if (m_logic == "QF_S") +// setup_QF_S(); else if (m_logic == "AUFLIA") setup_AUFLIA(st); else if (m_logic == "AUFLIRA") diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 9830a16b4..1b8c27630 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -216,6 +216,7 @@ theory_seq::theory_seq(ast_manager& m): m_atoms_qhead(0), m_new_solution(false), m_new_propagation(false), + m_len_prop_lvl(-1), m_mk_aut(m) { m_prefix = "seq.p.suffix"; m_suffix = "seq.s.prefix"; @@ -266,6 +267,16 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>solve_nqs\n";); return FC_CONTINUE; } + if (fixed_length(true)) { + ++m_stats.m_fixed_length; + TRACE("seq", tout << ">>zero_length\n";); + return FC_CONTINUE; + } + if (len_based_split()) { + ++m_stats.m_branch_variable; + TRACE("seq", tout << ">>split_based_on_length\n";); + return FC_CONTINUE; + } if (fixed_length()) { ++m_stats.m_fixed_length; TRACE("seq", tout << ">>fixed_length\n";); @@ -286,12 +297,17 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>branch_unit_variable\n";); return FC_CONTINUE; } - if (branch_binary_variable() || branch_variable_mb()) { + if (branch_binary_variable()) { ++m_stats.m_branch_variable; - TRACE("seq", tout << ">>branch_variable\n";); + TRACE("seq", tout << ">>branch_binary_variable\n";); return FC_CONTINUE; } - if (branch_variable()) { + if (branch_ternary_variable1() || branch_ternary_variable2() || branch_quat_variable()) { + ++m_stats.m_branch_variable; + TRACE("seq", tout << ">>split_based_on_alignment\n";); + return FC_CONTINUE; + } + if (branch_variable_mb() || branch_variable()) { ++m_stats.m_branch_variable; TRACE("seq", tout << ">>branch_variable\n";); return FC_CONTINUE; @@ -484,6 +500,877 @@ void theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector } } +bool theory_seq::branch_ternary_variable1() { + unsigned sz = m_eqs.size(); + for (unsigned i = 0; i < sz; ++i) { + eq const& e = m_eqs[i]; + if (branch_ternary_variable(e) || branch_ternary_variable2(e)) { + return true; + } + } + return false; +} + +bool theory_seq::branch_ternary_variable2() { + unsigned sz = m_eqs.size(); + for (unsigned i = 0; i < sz; ++i) { + eq const& e = m_eqs[i]; + if (branch_ternary_variable(e, true)) { + return true; + } + } + return false; +} + +bool theory_seq::eq_unit(expr* const& l, expr* const &r) const { + return l == r || is_unit_nth(l) || is_unit_nth(r); +} + +// exists y s.t. ls and rs ++ y have the same suffix +unsigned_vector theory_seq::overlap(ptr_vector const& ls, ptr_vector const& rs) { + unsigned_vector res; + expr* l = m_util.str.mk_concat(ls.size(),ls.c_ptr()); + expr* r = m_util.str.mk_concat(rs.size(),rs.c_ptr()); + expr* pair = m.mk_eq(l,r); + if (m_overlap.find(pair, res)) { + return res; + } + unsigned_vector result; + for (unsigned i = 0; i < ls.size(); ++i) { + if (eq_unit(ls[i], rs.back())) { + bool same = true; + if (i >= 1) { + for (unsigned j = i-1; j>=0 && rs.size()+j-i>=1; --j) { + if (!eq_unit(ls[j], rs[rs.size()+j-i-1])) { + same = false; + break; + } + } + if (same) + result.push_back(i+1); + } + else + result.push_back(1); + } + } + m_overlap.insert(pair, result); + return result; +} + +// exists x s.t. x ++ ls and rs have the same prefix +unsigned_vector theory_seq::overlap2(ptr_vector const& ls, ptr_vector const& rs) { + unsigned_vector res; + expr* l = m_util.str.mk_concat(ls.size(),ls.c_ptr()); + expr* r = m_util.str.mk_concat(rs.size(),rs.c_ptr()); + expr* pair = m.mk_eq(l,r); + if (m_overlap2.find(pair, res)) { + return res; + } + unsigned_vector result; + for (unsigned i = 0; i < ls.size(); ++i) { + if (eq_unit(ls[i],rs[0])) { + bool same = true; + for (unsigned j = i; j xs, expr* y1, ptr_vector ys, expr* y2) { + context& ctx = get_context(); + bool change = false; + for (unsigned i = 0; i < indexes.size(); ++i) { + unsigned ind = indexes[i]; + TRACE("seq", tout << "ind = " << ind << "\n";); + expr_ref xs2E(m); + if (xs.size() > ind) { + xs2E = m_util.str.mk_concat(xs.size()-ind, xs.c_ptr()+ind); + } + else { + xs2E = m_util.str.mk_empty(m.get_sort(x)); + } + literal lit1 = mk_literal(m_autil.mk_le(m_util.str.mk_length(y2), m_autil.mk_int(xs.size()-ind))); + if (ctx.get_assignment(lit1) == l_undef) { + TRACE("seq", tout << "base case init\n";); + ctx.mark_as_relevant(lit1); + ctx.force_phase(lit1); + change = true; + continue; + } + else if (ctx.get_assignment(lit1) == l_true) { + TRACE("seq", tout << "base case: true branch\n";); + literal_vector lits; + lits.push_back(lit1); + propagate_eq(dep, lits, y2, xs2E, true); + if (ind > ys.size()) { + expr_ref xs1E(m_util.str.mk_concat(ind-ys.size(), xs.c_ptr()), m); + expr_ref xxs1E(m_util.str.mk_concat(x, xs1E), m); + propagate_eq(dep, lits, xxs1E, y1, true); + } + else if (ind == ys.size()) { + propagate_eq(dep, lits, x, y1, true); + } + else { + expr_ref ys1E(m_util.str.mk_concat(ys.size()-ind, ys.c_ptr()), m); + expr_ref y1ys1E(m_util.str.mk_concat(y1, ys1E), m); + propagate_eq(dep, lits, x, y1ys1E, true); + } + return true; + } + else { + TRACE("seq", tout << "base case: false branch\n";); + continue; + } + } + return change; +} + +// Equation is of the form x ++ xs = y1 ++ ys ++ y2 where xs, ys are units. +bool theory_seq::branch_ternary_variable(eq const& e, bool flag1) { + ptr_vector xs, ys; + expr* x, *y1, *y2; + bool is_ternary = is_ternary_eq(e.ls(), e.rs(), x, xs, y1, ys, y2, flag1); + if (!is_ternary) { + is_ternary = is_ternary_eq(e.rs(), e.ls(), x, xs, y1, ys, y2, flag1); + } + if (!is_ternary) { + return false; + } + + rational lenX, lenY1, lenY2; + context& ctx = get_context(); + if (!get_length(x, lenX)) { + enforce_length(ensure_enode(x)); + } + if (!get_length(y1, lenY1)) { + enforce_length(ensure_enode(y1)); + } + if (!get_length(y2, lenY2)) { + enforce_length(ensure_enode(y2)); + } + + unsigned_vector indexes = overlap(xs, ys); + if (branch_ternary_variable_base(e.dep(), indexes, x, xs, y1, ys, y2)) + return true; + + // x ++ xs = y1 ++ ys ++ y2 => x = y1 ++ ys ++ z, z ++ xs = y2 + expr_ref xsE(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); + expr_ref Z(mk_skolem(symbol("seq.left.1"), y2, xsE), m); + expr_ref ZxsE(m_util.str.mk_concat(Z, xsE), m); + ys.push_back(Z); + expr_ref ysZ(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); + expr_ref y1ysZ(m_util.str.mk_concat(y1, ysZ), m); + if (indexes.empty()) { + TRACE("seq", tout << "one case\n";); + TRACE("seq", display_equation(tout, e);); + literal_vector lits; + dependency* dep = e.dep(); + propagate_eq(dep, lits, x, y1ysZ, true); + propagate_eq(dep, lits, y2, ZxsE, true); + } + else { + expr_ref ge(m_autil.mk_ge(m_util.str.mk_length(y2), m_autil.mk_int(xs.size())), m); + literal lit2 = mk_literal(ge); + if (ctx.get_assignment(lit2) == l_undef) { + TRACE("seq", tout << "rec case init\n";); + ctx.mark_as_relevant(lit2); + ctx.force_phase(lit2); + } + else if (ctx.get_assignment(lit2) == l_true) { + TRACE("seq", tout << "true branch\n";); + TRACE("seq", display_equation(tout, e);); + literal_vector lits; + lits.push_back(lit2); + dependency* dep = e.dep(); + propagate_eq(dep, lits, x, y1ysZ, true); + propagate_eq(dep, lits, y2, ZxsE, true); + } + else { + return branch_ternary_variable_base(e.dep(), indexes, x, xs, y1, ys, y2); + } + } + return true; +} + +bool theory_seq::branch_ternary_variable_base2(dependency* dep, unsigned_vector indexes, +ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2) { + context& ctx = get_context(); + bool change = false; + for (unsigned i = 0; i < indexes.size(); ++i) { + unsigned ind = indexes[i]; + expr_ref xs1E(m); + if (ind > 0) { + xs1E = m_util.str.mk_concat(ind, xs.c_ptr()); + } + else { + xs1E = m_util.str.mk_empty(m.get_sort(x)); + } + literal lit1 = mk_literal(m_autil.mk_le(m_util.str.mk_length(y1), m_autil.mk_int(ind))); + if (ctx.get_assignment(lit1) == l_undef) { + TRACE("seq", tout << "base case init\n";); + ctx.mark_as_relevant(lit1); + ctx.force_phase(lit1); + change = true; + continue; + } + else if (ctx.get_assignment(lit1) == l_true) { + TRACE("seq", tout << "base case: true branch\n";); + literal_vector lits; + lits.push_back(lit1); + propagate_eq(dep, lits, y1, xs1E, true); + if (xs.size() - ind > ys.size()) { + expr_ref xs2E(m_util.str.mk_concat(xs.size()-ind-ys.size(), xs.c_ptr()+ind+ys.size()), m); + expr_ref xs2x(m_util.str.mk_concat(xs2E, x), m); + propagate_eq(dep, lits, xs2x, y2, true); + } + else if (xs.size() - ind == ys.size()) { + propagate_eq(dep, lits, x, y2, true); + } + else { + expr_ref ys1E(m_util.str.mk_concat(ys.size()-xs.size()+ind, ys.c_ptr()+xs.size()-ind), m); + expr_ref ys1y2(m_util.str.mk_concat(ys1E, y2), m); + propagate_eq(dep, lits, x, ys1y2, true); + } + return true; + } + else { + TRACE("seq", tout << "base case: false branch\n";); + continue; + } + } + return change; +} + +// Equation is of the form xs ++ x = y1 ++ ys ++ y2 where xs, ys are units. +bool theory_seq::branch_ternary_variable2(eq const& e, bool flag1) { + ptr_vector xs, ys; + expr* x, *y1, *y2; + bool is_ternary = is_ternary_eq2(e.ls(), e.rs(), xs, x, y1, ys, y2, flag1); + if (!is_ternary) { + is_ternary = is_ternary_eq2(e.rs(), e.ls(), xs, x, y1, ys, y2, flag1); + } + if (!is_ternary) { + return false; + } + + rational lenX, lenY1, lenY2; + context& ctx = get_context(); + if (!get_length(x, lenX)) { + enforce_length(ensure_enode(x)); + } + if (!get_length(y1, lenY1)) { + enforce_length(ensure_enode(y1)); + } + if (!get_length(y2, lenY2)) { + enforce_length(ensure_enode(y2)); + } + unsigned_vector indexes = overlap2(xs, ys); + if (branch_ternary_variable_base2(e.dep(), indexes, xs, x, y1, ys, y2)) + return true; + + // xs ++ x = y1 ++ ys ++ y2 => xs ++ z = y1, x = z ++ ys ++ y2 + expr_ref xsE(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); + expr_ref Z(mk_skolem(symbol("seq.right.1"), y1, xsE), m); + xs.push_back(Z); + expr_ref xsZ(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); + ys.push_back(y2); + expr_ref ysy2(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); + expr_ref Zysy2(m_util.str.mk_concat(Z, ysy2), m); + if (indexes.empty()) { + TRACE("seq", tout << "one case\n";); + TRACE("seq", display_equation(tout, e);); + literal_vector lits; + dependency* dep = e.dep(); + propagate_eq(dep, lits, x, Zysy2, true); + propagate_eq(dep, lits, y1, xsZ, true); + } + else { + expr_ref ge(m_autil.mk_ge(m_util.str.mk_length(y1), m_autil.mk_int(xs.size())), m); + literal lit2 = mk_literal(ge); + if (ctx.get_assignment(lit2) == l_undef) { + TRACE("seq", tout << "rec case init\n";); + ctx.mark_as_relevant(lit2); + ctx.force_phase(lit2); + } + else if (ctx.get_assignment(lit2) == l_true) { + TRACE("seq", tout << "true branch\n";); + TRACE("seq", display_equation(tout, e);); + literal_vector lits; + lits.push_back(lit2); + dependency* dep = e.dep(); + propagate_eq(dep, lits, x, Zysy2, true); + propagate_eq(dep, lits, y1, xsZ, true); + } + else { + return branch_ternary_variable_base2(e.dep(), indexes, xs, x, y1, ys, y2); + } + } + + return true; +} + +bool theory_seq::branch_quat_variable() { + unsigned sz = m_eqs.size(); + for (unsigned i = 0; i < sz; ++i) { + eq const& e = m_eqs[i]; + if (branch_quat_variable(e)) { + return true; + } + } + return false; +} + +// Equation is of the form x1 ++ xs ++ x2 = y1 ++ ys ++ y2 where xs, ys are units. +bool theory_seq::branch_quat_variable(eq const& e) { + ptr_vector xs, ys; + expr* x1, *x2, *y1, *y2; + bool is_quat = is_quat_eq(e.ls(), e.rs(), x1, xs, x2, y1, ys, y2); + if (!is_quat) { + return false; + } + + rational lenX1, lenX2, lenY1, lenY2; + context& ctx = get_context(); + if (!get_length(x1, lenX1)) { + enforce_length(ensure_enode(x1)); + } + if (!get_length(y1, lenY1)) { + enforce_length(ensure_enode(y1)); + } + if (!get_length(x2, lenX2)) { + enforce_length(ensure_enode(x2)); + } + if (!get_length(y2, lenY2)) { + enforce_length(ensure_enode(y2)); + } + + xs.push_back(x2); + expr_ref xsx2(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); + ys.push_back(y2); + expr_ref ysy2(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); + expr_ref x1_bk(x1, m); + expr_ref y1_bk(y1, m); + expr_ref sub(mk_sub(m_util.str.mk_length(x1), m_util.str.mk_length(y1)), m); + expr_ref le(m_autil.mk_le(sub, m_autil.mk_int(0)), m); + literal lit2 = mk_literal(le); + if (ctx.get_assignment(lit2) == l_undef) { + TRACE("seq", tout << "init branch\n";); + TRACE("seq", display_equation(tout, e);); + ctx.mark_as_relevant(lit2); + ctx.force_phase(lit2); + } + else if (ctx.get_assignment(lit2) == l_false) { + // |x1| > |y1| => x1 = y1 ++ z1, z1 ++ xs ++ x2 = ys ++ y2 + TRACE("seq", tout << "false branch\n";); + TRACE("seq", display_equation(tout, e);); + expr_ref Z1(mk_skolem(symbol("seq.right.1"), x1_bk, y1_bk), m); + expr_ref y1Z1(m_util.str.mk_concat(y1_bk, Z1), m); + expr_ref Z1xsx2(m_util.str.mk_concat(Z1, xsx2), m); + literal_vector lits; + lits.push_back(~lit2); + dependency* dep = e.dep(); + propagate_eq(dep, lits, x1_bk, y1Z1, true); + propagate_eq(dep, lits, Z1xsx2, ysy2, true); + } + else if (ctx.get_assignment(lit2) == l_true) { + // |x1| <= |y1| => x1 ++ z2 = y1, xs ++ x2 = z2 ++ ys ++ y2 + TRACE("seq", tout << "true branch\n";); + TRACE("seq", display_equation(tout, e);); + expr_ref Z2(mk_skolem(symbol("seq.right.1"), y1_bk, x1_bk), m); + expr_ref x1Z2(m_util.str.mk_concat(x1_bk, Z2), m); + expr_ref Z2ysy2(m_util.str.mk_concat(Z2, ysy2), m); + literal_vector lits; + lits.push_back(lit2); + dependency* dep = e.dep(); + propagate_eq(dep, lits, x1Z2, y1_bk, true); + propagate_eq(dep, lits, xsx2, Z2ysy2, true); + } + return true; +} + +void theory_seq::len_offset(expr* const& e, rational val) { + context & ctx = get_context(); + expr *l1, *l2, *l21, *l22; + rational fact; + if (m_autil.is_add(e, l1, l2) && m_autil.is_mul(l2, l21, l22) && + m_autil.is_numeral(l21, fact) && fact.is_minus_one()) { + if (ctx.e_internalized(l1) && ctx.e_internalized(l22)) { + enode* r1 = ctx.get_enode(l1)->get_root(), *n1 = r1; + enode* r2 = ctx.get_enode(l22)->get_root(), *n2 = r2; + expr *e1, *e2; + do { + if (!m_util.str.is_length(n1->get_owner(), e1)) + n1 = n1->get_next(); + else + break; + } + while (n1 != r1); + do { + if (!m_util.str.is_length(n2->get_owner(), e2)) + n2 = n2->get_next(); + else + break; + } + while (n2 != r2); + if (m_util.str.is_length(n1->get_owner(), e1) + && m_util.str.is_length(n2->get_owner(), e2)) { + obj_map tmp; + m_len_offset.find(r1, tmp); + tmp.insert(r2, val.get_int32()); + m_len_offset.insert(r1, tmp); + TRACE("seq", tout << "a length pair: " << mk_pp(e1, m) + << ", " << mk_pp(e2, m) << "\n";); + return; + } + } + } +} + +// Find the length offset from the congruence closure core +void theory_seq::prop_arith_to_len_offset() { + context & ctx = get_context(); + obj_hashtable const_set; + ptr_vector::const_iterator it = ctx.begin_enodes(); + ptr_vector::const_iterator end = ctx.end_enodes(); + for (; it != end; ++it) { + enode * root = (*it)->get_root(); + rational val; + if (m_autil.is_numeral(root->get_owner(), val) && val.is_neg()) { + if (const_set.contains(root)) continue; + const_set.insert(root); + TRACE("seq", tout << "offset: " << mk_pp(root->get_owner(), m) << "\n";); + enode *next = root->get_next(); + while (next != root) { + TRACE("seq", tout << "eqc: " << mk_pp(next->get_owner(), m) << "\n";); + len_offset(next->get_owner(), val); + next = next->get_next(); + } + } + } +} + +int theory_seq::find_fst_non_empty_idx(expr_ref_vector const& x) const { + context & ctx = get_context(); + int i = 0; + for (; i < x.size(); ++i) { + if (!is_var(x[i])) return -1; + expr_ref e(m_util.str.mk_length(x[i]), m); + if (ctx.e_internalized(e)) { + enode* root = ctx.get_enode(e)->get_root(); + rational val; + if (m_autil.is_numeral(root->get_owner(), val) && val.is_zero()) + continue; + } + break; + } + if (i == x.size()) + return -1; + else { + return i; + } +} + +expr* theory_seq::find_fst_non_empty_var(expr_ref_vector const& x) const { + int i = find_fst_non_empty_idx(x); + if (i >= 0) + return x[i]; + return NULL; +} + +void theory_seq::find_max_eq_len(expr_ref_vector const& ls, expr_ref_vector const& rs) { + context& ctx = get_context(); + if (ls.size() >= 2 && rs.size() >= 2) { + expr_ref len1(m_autil.mk_int(0), m), len2(m_autil.mk_int(0), m); + int l_fst = find_fst_non_empty_idx(ls); + int r_fst = find_fst_non_empty_idx(rs); + if (l_fst<0 || r_fst<0) + return; + unsigned j = 2 + l_fst; + rational lo1(-1), hi1(-1), lo2(-1), hi2(-1); + if (j >= ls.size()) { + lo1 = 0; + hi1 = 0; + } + while (j < ls.size()) { + rational lo(-1), hi(-1); + if (m_util.str.is_unit(ls.get(j))) { + lo = 1; + hi = 1; + } + else { + lower_bound(ls.get(j), lo); + upper_bound(ls.get(j), hi); + } + if (!lo.is_minus_one()) { + if (lo1.is_minus_one()) + lo1 = lo; + else + lo1 += lo; + } + if (!hi.is_minus_one()) { + if (hi1.is_minus_one()) + hi1 = hi; + else if (hi1.is_nonneg()) + hi1 += hi; + } + else { + hi1 = rational(-2); + } + len1 = mk_add(len1, m_util.str.mk_length(ls.get(j))); + j++; + } + j = 2 + r_fst; + if (j >= rs.size()) { + lo2 = 0; + hi2 = 0; + } + while (j < rs.size()) { + rational lo(-1), hi(-1); + if (m_util.str.is_unit(rs.get(j))) { + lo = 1; + hi = 1; + } + else { + lower_bound(rs.get(j), lo); + upper_bound(rs.get(j), hi); + } + if (!lo.is_minus_one()) { + if (lo2.is_minus_one()) + lo2 = lo; + else + lo2 += lo; + } + if (!hi.is_minus_one()) { + if (hi2.is_minus_one()) + hi2 = hi; + else if (hi1.is_nonneg()) + hi2 += hi; + } + else { + hi2 = rational(-2); + } + len2 = mk_add(len2, m_util.str.mk_length(rs.get(j))); + j++; + } + if (m_autil.is_numeral(len1) && m_autil.is_numeral(len2)) + return; + TRACE("seq", tout << lo1 << ", " << hi1 << ", " << lo2 << ", " << hi2 << "\n";); + if (!lo1.is_neg() && !hi2.is_neg() && lo1 > hi2) + return; + if (!lo2.is_neg() && !hi1.is_neg() && lo2 > hi1) + return; + + literal lit1 = null_literal; + if (hi1.is_zero()) { + if (!is_var(rs[1 + r_fst])) + return; + lit1 = mk_literal(m_autil.mk_le(len2, len1)); + TRACE("seq", tout << mk_pp(len1, m) << " >= " << mk_pp(len2, m) << "\n";); + } + else if (hi2.is_zero()) { + if (!is_var(ls[1 + l_fst])) + return; + lit1 = mk_literal(m_autil.mk_le(len1, len2)); + TRACE("seq", tout << mk_pp(len1, m) << " <= " << mk_pp(len2, m) << "\n";); + } + else { + lit1 = mk_eq(len1, len2, false); + TRACE("seq", tout << mk_pp(len1, m) << " = " << mk_pp(len2, m) << "\n";); + } + if (ctx.get_assignment(lit1) == l_undef) { + ctx.mark_as_relevant(lit1); + ctx.force_phase(lit1); + } + } +} + +// TODO: propagate length offsets for last vars +bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned const& idx, + dependency*& deps, expr_ref_vector & res) { + context& ctx = get_context(); + + if (ls.size() == 0 || rs.size() == 0) + return false; + expr* l_fst = find_fst_non_empty_var(ls); + expr* r_fst = find_fst_non_empty_var(rs); + if (!r_fst) return false; + expr_ref len_r_fst(m_util.str.mk_length(r_fst), m); + enode * root2; + if (!ctx.e_internalized(len_r_fst)) + return false; + else + root2 = ctx.get_enode(len_r_fst)->get_root(); + + // Offset = 0, No change + if (l_fst) { + expr_ref len_l_fst(m_util.str.mk_length(l_fst), m); + if (ctx.e_internalized(len_l_fst)) { + enode * root1 = ctx.get_enode(len_l_fst)->get_root(); + if (root1 == root2) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + return false; + } + } + } + + // Offset = 0, Changed + { + for (unsigned i = 0; i < idx; ++i) { + eq const& e = m_eqs[i]; + if (e.ls().size() == ls.size()) { + bool flag = true; + for (unsigned j = 0; j < ls.size(); ++j) + if (e.ls().get(j) != ls.get(j)) { + flag = false; + break; + } + if (flag) { + expr* nl_fst = 0; + if (e.rs().size()>1 && is_var(e.rs().get(0))) + nl_fst = e.rs().get(0); + if (nl_fst && nl_fst != r_fst) { + expr_ref len_nl_fst(m_util.str.mk_length(nl_fst), m); + if (ctx.e_internalized(len_nl_fst)) { + enode * root1 = ctx.get_enode(len_nl_fst)->get_root(); + if (root1 == root2) { + res.reset(); + res.append(e.rs().size(), e.rs().c_ptr()); + deps = m_dm.mk_join(e.dep(), deps); + return true; + } + } + } + } + } + } + } + // Offset != 0, No change + if (l_fst) { + expr_ref len_l_fst(m_util.str.mk_length(l_fst), m); + if (ctx.e_internalized(len_l_fst)) { + enode * root1 = ctx.get_enode(len_l_fst)->get_root(); + obj_map tmp; + int offset; + if (!m_autil.is_numeral(root1->get_owner()) && !m_autil.is_numeral(root2->get_owner())) { + if (m_len_offset.find(root1, tmp) && tmp.find(root2, offset)) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + find_max_eq_len(ls, rs); + return false; + } + else if (m_len_offset.find(root2, tmp) && tmp.find(root1, offset)) { + TRACE("seq", tout << "(" << mk_pp(r_fst, m) << ", " << mk_pp(l_fst,m) << ")\n";); + find_max_eq_len(ls ,rs); + return false; + } + } + } + } + // Offset != 0, Changed + obj_map tmp; + if (!m_autil.is_numeral(root2->get_owner()) && m_len_offset.find(root2, tmp)) { + for (unsigned i = 0; i < idx; ++i) { + eq const& e = m_eqs[i]; + if (e.ls().size() == ls.size()) { + bool flag = true; + for (unsigned j = 0; j < ls.size(); ++j) + if (e.ls().get(j) != ls.get(j)) { + flag = false; + break; + } + if (flag) { + expr* nl_fst = 0; + if (e.rs().size()>1 && is_var(e.rs().get(0))) + nl_fst = e.rs().get(0); + if (nl_fst && nl_fst != r_fst) { + int offset; + expr_ref len_nl_fst(m_util.str.mk_length(nl_fst), m); + if (ctx.e_internalized(len_nl_fst)) { + enode * root1 = ctx.get_enode(len_nl_fst)->get_root(); + if (!m_autil.is_numeral(root1->get_owner()) && tmp.find(root1, offset)) { + res.reset(); + res.append(e.rs().size(), e.rs().c_ptr()); + deps = m_dm.mk_join(e.dep(), deps); + find_max_eq_len(res, rs); + return true; + } + } + } + } + } + } + } + return false; +} + +bool theory_seq::has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & offset) { + context& ctx = get_context(); + + if (ls.size() == 0 || rs.size() == 0) + return false; + expr* l_fst = ls[0]; + expr* r_fst = rs[0]; + if (!is_var(l_fst) || !is_var(r_fst)) + return false; + + expr_ref len_r_fst(m_util.str.mk_length(r_fst), m); + enode * root2; + if (!ctx.e_internalized(len_r_fst)) + return false; + else + root2 = ctx.get_enode(len_r_fst)->get_root(); + + expr_ref len_l_fst(m_util.str.mk_length(l_fst), m); + if (ctx.e_internalized(len_l_fst)) { + enode * root1 = ctx.get_enode(len_l_fst)->get_root(); + if (root1 == root2) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + offset = 0; + return true; + } + obj_map tmp; + if (!m_autil.is_numeral(root1->get_owner()) && !m_autil.is_numeral(root2->get_owner())) { + if (m_len_offset.find(root1, tmp) && tmp.find(root2, offset)) { + TRACE("seq", tout << "(" << mk_pp(l_fst, m) << ", " << mk_pp(r_fst,m) << ")\n";); + return true; + } + else if (m_len_offset.find(root2, tmp) && tmp.find(root1, offset)) { + offset = -offset; + TRACE("seq", tout << "(" << mk_pp(r_fst, m) << ", " << mk_pp(l_fst,m) << ")\n";); + return true; + } + } + } + return false; +} + +bool theory_seq::len_based_split() { + unsigned sz = m_eqs.size(); + if (sz == 0) + return false; + + if ((int) get_context().get_scope_level() > m_len_prop_lvl) { + m_len_prop_lvl = get_context().get_scope_level(); + prop_arith_to_len_offset(); + if (!m_len_offset.empty()) { + for (unsigned i = sz-1; i > 0; --i) { + eq const& e = m_eqs[i]; + expr_ref_vector new_ls(m); + dependency *deps = e.dep(); + if (find_better_rep(e.ls(), e.rs(), i, deps, new_ls)) { + expr_ref_vector rs(m); + rs.append(e.rs().size(), e.rs().c_ptr()); + m_eqs.set(i, eq(m_eq_id++, new_ls, rs, deps)); + TRACE("seq", display_equation(tout, m_eqs[i]);); + } + } + } + } + + for (unsigned i = 0; i < sz; ++i) { + eq const& e = m_eqs[i]; + if (len_based_split(e)) { + return true; + } + } + return false; +} + +bool theory_seq::len_based_split(eq const& e) { + context& ctx = get_context(); + expr_ref_vector const& ls = e.ls(); + expr_ref_vector const& rs = e.rs(); + + int offset_orig = 0; + if (!has_len_offset(ls, rs, offset_orig)) + return false; + + TRACE("seq", tout << "split based on length\n";); + TRACE("seq", display_equation(tout, e);); + expr_ref x11(m_util.str.mk_concat(1, ls.c_ptr()), m); + expr_ref x12(m_util.str.mk_concat(ls.size()-1, ls.c_ptr()+1), m); + expr_ref y11(m_util.str.mk_concat(1, rs.c_ptr()), m); + expr_ref y12(m_util.str.mk_concat(rs.size()-1, rs.c_ptr()+1), m); + + expr_ref lenX11(m_util.str.mk_length(x11),m); + expr_ref lenY11(m); + expr_ref Z(m); + int offset = 0; + if (offset_orig != 0) { + lenY11 = m_autil.mk_add(m_util.str.mk_length(y11), m_autil.mk_int(offset_orig)); + if (offset_orig > 0) { + offset = offset_orig; + Z = mk_skolem(symbol("seq.right.1"), x11, y11); + y11 = m_util.str.mk_concat(y11, Z); + x12 = m_util.str.mk_concat(Z, x12); + } + else { + offset = -offset_orig; + Z = mk_skolem(symbol("seq.right.1"), y11, x11); + x11 = m_util.str.mk_concat(x11, Z); + y12 = m_util.str.mk_concat(Z, y12); + } + } + else + lenY11 = m_util.str.mk_length(y11); + + dependency* dep = e.dep(); + literal_vector lits; + literal lit1 = mk_eq(lenX11, lenY11, false); + lits.push_back(lit1); + + if (ls.size()>=2 && rs.size()>=2 && (ls.size()>2 || rs.size()>2)) { + expr_ref len1(m_autil.mk_int(0),m), len2(m_autil.mk_int(0),m); + for (unsigned i = 2; i < ls.size(); ++i) + len1 = mk_add(len1, m_util.str.mk_length(ls[i])); + for (unsigned i = 2; i < rs.size(); ++i) + len2 = mk_add(len2, m_util.str.mk_length(rs[i])); + bool flag = false; + if (!m_autil.is_numeral(len1) && !m_autil.is_numeral(len2)) { + literal lit2 = mk_eq(len1, len2, false); + flag = ctx.get_assignment(lit2) == l_true; + } + else { + expr_ref eq_len(m.mk_eq(len1, len2), m); + flag = ctx.find_assignment(eq_len) == l_true; + } + + if (flag) { + literal lit2 = mk_eq(len1, len2, false); + lits.push_back(lit2); + TRACE("seq", tout << mk_pp(len1, m) << " = " << mk_pp(len2, m) << "\n";); + expr_ref lhs(m), rhs(m); + if (ls.size() > 2) + lhs = m_util.str.mk_concat(ls.size()-2, ls.c_ptr()+2); + else + lhs = m_util.str.mk_empty(m.get_sort(x11)); + if (rs.size() > 2) + rhs = m_util.str.mk_concat(rs.size()-2, rs.c_ptr()+2); + else + rhs = m_util.str.mk_empty(m.get_sort(x11)); + propagate_eq(dep, lits, lhs, rhs, true); + lits.pop_back(); + } + } + + if (offset != 0) { + expr_ref lenZ(m_util.str.mk_length(Z), m); + propagate_eq(dep, lits, lenZ, m_autil.mk_int(offset), false); + } + propagate_eq(dep, lits, y11, x11, true); + propagate_eq(dep, lits, x12, y12, false); + + return true; +} + bool theory_seq::branch_variable_mb() { bool change = false; for (unsigned i = 0; i < m_eqs.size(); ++i) { @@ -862,11 +1749,11 @@ bool theory_seq::propagate_length_coherence(expr* e) { if (!is_var(e) || !m_rep.is_root(e)) { return false; } - if (!lower_bound(e, lo) || !lo.is_pos() || lo >= rational(2048)) { + if (!lower_bound2(e, lo) || !lo.is_pos() || lo >= rational(2048)) { return false; } TRACE("seq", tout << "Unsolved " << mk_pp(e, m); - if (!lower_bound(e, lo)) lo = -rational::one(); + if (!lower_bound2(e, lo)) lo = -rational::one(); if (!upper_bound(e, hi)) hi = -rational::one(); tout << " lo: " << lo << " hi: " << hi << "\n"; ); @@ -951,19 +1838,20 @@ bool theory_seq::check_length_coherence() { return false; } -bool theory_seq::fixed_length() { +bool theory_seq::fixed_length(bool is_zero) { bool found = false; for (expr* e : m_length) { - if (fixed_length(e)) { + if (fixed_length(e, is_zero)) { found = true; } } return found; } -bool theory_seq::fixed_length(expr* e) { +bool theory_seq::fixed_length(expr* e, bool is_zero) { rational lo, hi; - if (!(is_var(e) && lower_bound(e, lo) && upper_bound(e, hi) && lo == hi && lo.is_unsigned())) { + if (!(is_var(e) && lower_bound(e, lo) && upper_bound(e, hi) && lo == hi + && ((is_zero && lo.is_zero()) || (!is_zero && lo.is_unsigned())))) { return false; } if (is_skolem(m_tail, e) || is_skolem(m_seq_first, e) || @@ -983,7 +1871,7 @@ bool theory_seq::fixed_length(expr* e) { if (lo.is_zero()) { seq = m_util.str.mk_empty(m.get_sort(e)); } - else { + else if (!is_zero) { unsigned _lo = lo.get_unsigned(); expr_ref_vector elems(m); @@ -1026,6 +1914,11 @@ bool theory_seq::propagate_is_conc(expr* e, expr* conc) { } } +bool theory_seq::is_unit_nth(expr* e) const { + expr *s; + return m_util.str.is_unit(e, s) && is_nth(s); +} + bool theory_seq::is_nth(expr* e) const { return is_skolem(m_nth, e); } @@ -1387,6 +2280,10 @@ bool theory_seq::solve_unit_eq(expr_ref_vector const& l, expr_ref_vector const& if (r.size() == 1 && is_var(r[0]) && !occurs(r[0], l) && add_solution(r[0], mk_concat(l, m.get_sort(r[0])), deps)) { return true; } +// if (l.size() == 1 && r.size() == 1 && l[0] != r[0] && is_nth(l[0]) && add_solution(l[0], r[0], deps)) +// return true; +// if (l.size() == 1 && r.size() == 1 && l[0] != r[0] && is_nth(r[0]) && add_solution(r[0], l[0], deps)) +// return true; return false; } @@ -1415,6 +2312,10 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) { if (is_var(r) && !occurs(r, l) && add_solution(r, l, deps)) { return true; } + if (is_nth(l) && !occurs(l, r) && add_solution(l, r, deps)) + return true; + if (is_nth(r) && !occurs(r, l) && add_solution(r, l, deps)) + return true; return false; } @@ -1449,7 +2350,7 @@ bool theory_seq::occurs(expr* a, expr* b) { } -bool theory_seq::is_var(expr* a) { +bool theory_seq::is_var(expr* a) const { return m_util.is_seq(a) && !m_util.str.is_concat(a) && @@ -1482,7 +2383,7 @@ bool theory_seq::solve_eqs(unsigned i) { bool change = false; for (; !ctx.inconsistent() && i < m_eqs.size(); ++i) { eq const& e = m_eqs[i]; - if (solve_eq(e.ls(), e.rs(), e.dep())) { + if (solve_eq(e.ls(), e.rs(), e.dep(), i)) { if (i + 1 != m_eqs.size()) { eq e1 = m_eqs[m_eqs.size()-1]; m_eqs.set(i, e1); @@ -1496,7 +2397,7 @@ bool theory_seq::solve_eqs(unsigned i) { return change || m_new_propagation || ctx.inconsistent(); } -bool theory_seq::solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* deps) { +bool theory_seq::solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* deps, unsigned idx) { context& ctx = get_context(); expr_ref_vector& ls = m_ls; expr_ref_vector& rs = m_rs; @@ -1523,8 +2424,26 @@ bool theory_seq::solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, de return true; } if (!ctx.inconsistent() && change) { + // The propagation step from arithmetic state (e.g. length offset) to length constraints + if (get_context().get_scope_level() == 0) { + prop_arith_to_len_offset(); + } TRACE("seq", tout << "inserting equality\n";); - m_eqs.push_back(eq(m_eq_id++, ls, rs, deps)); + bool updated = false; + if (!m_len_offset.empty()) { + // Find a better equivalent term for lhs of an equation based on length constraints + expr_ref_vector new_ls(m); + if (find_better_rep(ls, rs, idx, deps, new_ls)) { + eq const & new_eq = eq(m_eq_id++, new_ls, rs, deps); + m_eqs.push_back(new_eq); + TRACE("seq", tout << "find_better_rep\n";); + TRACE("seq", display_equation(tout, new_eq);); + updated = true; + } + } + if (!updated) { + m_eqs.push_back(eq(m_eq_id++, ls, rs, deps)); + } return true; } return false; @@ -1564,6 +2483,124 @@ bool theory_seq::is_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& return false; } +bool theory_seq::is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, +expr*& x1, ptr_vector& xs, expr*& x2, expr*& y1, ptr_vector& ys, expr*& y2) { + if (ls.size() > 1 && is_var(ls[0]) && is_var(ls.back()) && + rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { + unsigned l_start = 1; + for (; l_start < ls.size()-1; ++l_start) { + if (m_util.str.is_unit(ls[l_start])) break; + } + if (l_start == ls.size()-1) return false; + unsigned l_end = l_start; + for (; l_end < ls.size()-1; ++l_end) { + if (!m_util.str.is_unit(ls[l_end])) break; + } + --l_end; + unsigned r_start = 1; + for (; r_start < rs.size()-1; ++r_start) { + if (m_util.str.is_unit(rs[r_start])) break; + } + if (r_start == rs.size()-1) return false; + unsigned r_end = r_start; + for (; r_end < rs.size()-1; ++r_end) { + if (!m_util.str.is_unit(rs[r_end])) break; + } + --r_end; + for (unsigned i = l_start; i < l_end+1; ++i) { + if (!m_util.str.is_unit(ls[i])) return false; + } + for (unsigned i = r_start; i < r_end+1; ++i) { + if (!m_util.str.is_unit(rs[i])) return false; + } + xs.reset(); + xs.append(l_end-l_start+1, ls.c_ptr()+l_start); + x1 = m_util.str.mk_concat(l_start, ls.c_ptr()); + x2 = m_util.str.mk_concat(ls.size()-l_end-1, ls.c_ptr()+l_end+1); + ys.reset(); + ys.append(r_end-r_start+1, rs.c_ptr()+r_start); + y1 = m_util.str.mk_concat(r_start, rs.c_ptr()); + y2 = m_util.str.mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1); + return true; + } + return false; +} + +bool theory_seq::is_ternary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, +expr*& x, ptr_vector& xs, expr*& y1, ptr_vector& ys, expr*& y2, bool flag1) { + if (ls.size() > 1 && (is_var(ls[0]) || flag1) && + rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { + unsigned l_start = ls.size()-1; + for (; l_start > 0; --l_start) { + if (!m_util.str.is_unit(ls[l_start])) break; + } + if (l_start == ls.size()-1) return false; + ++l_start; + unsigned r_end = rs.size()-2; + for (; r_end > 0; --r_end) { + if (m_util.str.is_unit(rs[r_end])) break; + } + if (r_end == 0) return false; + unsigned r_start = r_end; + for (; r_start > 0; --r_start) { + if (!m_util.str.is_unit(rs[r_start])) break; + } + ++r_start; + for (unsigned i = l_start; i < ls.size(); ++i) { + if (!m_util.str.is_unit(ls[i])) return false; + } + for (unsigned i = r_start; i < r_end+1; ++i) { + if (!m_util.str.is_unit(rs[i])) return false; + } + xs.reset(); + xs.append(ls.size()-l_start, ls.c_ptr()+l_start); + x = m_util.str.mk_concat(l_start, ls.c_ptr()); + ys.reset(); + ys.append(r_end-r_start+1, rs.c_ptr()+r_start); + y1 = m_util.str.mk_concat(r_start, rs.c_ptr()); + y2 = m_util.str.mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1); + return true; + } + return false; +} + +bool theory_seq::is_ternary_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, +ptr_vector& xs, expr*& x, expr*& y1, ptr_vector& ys, expr*& y2, bool flag1) { + if (ls.size() > 1 && (is_var(ls.back()) || flag1) && + rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { + unsigned l_start = 0; + for (; l_start < ls.size()-1; ++l_start) { + if (!m_util.str.is_unit(ls[l_start])) break; + } + if (l_start == 0) return false; + unsigned r_start = 1; + for (; r_start < rs.size()-1; ++r_start) { + if (m_util.str.is_unit(rs[r_start])) break; + } + if (r_start == rs.size()-1) return false; + unsigned r_end = r_start; + for (; r_end < rs.size()-1; ++r_end) { + if (!m_util.str.is_unit(rs[r_end])) break; + } + --r_end; + for (unsigned i = 0; i < l_start; ++i) { + if (!m_util.str.is_unit(ls[i])) return false; + } + for (unsigned i = r_start; i < r_end+1; ++i) { + if (!m_util.str.is_unit(rs[i])) return false; + } + xs.reset(); + xs.append(l_start, ls.c_ptr()); + x = m_util.str.mk_concat(ls.size()-l_start, ls.c_ptr()+l_start); + ys.reset(); + ys.append(r_end-r_start+1, rs.c_ptr()+r_start); + y1 = m_util.str.mk_concat(r_start, rs.c_ptr()); + y2 = m_util.str.mk_concat(rs.size()-r_end-1, rs.c_ptr()+r_end+1); + return true; + } + return false; +} + bool theory_seq::reduce_length_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, dependency* deps) { if (ls.empty() || rs.empty()) { return false; @@ -1666,8 +2703,9 @@ bool theory_seq::reduce_length(unsigned i, unsigned j, bool front, expr_ref_vect expr_ref r(m_util.str.mk_concat(r1, rs1), m); expr_ref lenl(m_util.str.mk_length(l), m); expr_ref lenr(m_util.str.mk_length(r), m); - literal lit = mk_eq(lenl, lenr, false); - if (ctx.get_assignment(lit) == l_true) { + expr_ref len_eq(m.mk_eq(lenl, lenr), m); + if (ctx.find_assignment(len_eq) == l_true) { + literal lit = mk_eq(lenl, lenr, false); literal_vector lits; expr_ref_vector lhs(m), rhs(m); lhs.append(l2, ls2); @@ -2313,7 +3351,7 @@ void theory_seq::add_length(expr* e) { /* - ensure that all elements in equivalence class occur under an applicatin of 'length' + ensure that all elements in equivalence class occur under an application of 'length' */ void theory_seq::enforce_length(enode* n) { enode* n1 = n; @@ -3480,6 +4518,12 @@ expr_ref theory_seq::mk_sub(expr* a, expr* b) { return result; } +expr_ref theory_seq::mk_add(expr* a, expr* b) { + expr_ref result(m_autil.mk_add(a, b), m); + m_rewrite(result); + return result; +} + enode* theory_seq::ensure_enode(expr* e) { context& ctx = get_context(); if (!ctx.e_internalized(e)) { @@ -3540,6 +4584,45 @@ bool theory_seq::lower_bound(expr* _e, rational& lo) const { return m_autil.is_numeral(_lo, lo) && lo.is_int(); } +// The difference with lower_bound function is that since in some cases, +// the lower bound is not updated for all the enodes in the same eqc, +// we have to traverse the eqc to query for the better lower bound. +bool theory_seq::lower_bound2(expr* _e, rational& lo) { + context& ctx = get_context(); + expr_ref e(m_util.str.mk_length(_e), m); + expr_ref _lo(m); + 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; + } + enode *ee = ctx.get_enode(e); + if (!tha->get_lower(ee, _lo) || m_autil.is_numeral(_lo, lo)) { + enode *next = ee->get_next(); + bool flag = false; + while (next != ee) { + if (!m_autil.is_numeral(next->get_owner()) && !m_util.str.is_length(next->get_owner())) { + expr *var = next->get_owner(); + TRACE("seq", tout << mk_pp(var, m) << "\n";); + expr_ref _lo2(m); + rational lo2; + if (tha->get_lower(next, _lo2) && m_autil.is_numeral(_lo2, lo2) && lo2>lo) { + flag = true; + lo = lo2; + literal low(mk_literal(m_autil.mk_ge(var, _lo2))); + add_axiom(~low, mk_literal(m_autil.mk_ge(e, _lo2))); + } + } + next = next->get_next(); + } + if (flag) + return true; + if (!tha->get_lower(ee, _lo)) + return false; + } + return true; +} + bool theory_seq::upper_bound(expr* _e, rational& hi) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); @@ -4194,6 +5277,10 @@ void theory_seq::pop_scope_eh(unsigned num_scopes) { if (ctx.get_base_level() > ctx.get_scope_level() - num_scopes) { m_replay.reset(); } + if (m_len_prop_lvl > (int) ctx.get_scope_level()) { + m_len_prop_lvl = ctx.get_scope_level(); + m_len_offset.reset(); + } } void theory_seq::restart_eh() { diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index d4686c835..3d786ddff 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -301,6 +301,12 @@ namespace smt { unsigned m_eq_id; th_union_find m_find; + obj_map m_overlap; + obj_map m_overlap2; + obj_map> m_len_offset; + int m_len_prop_lvl; + + seq_factory* m_factory; // value factory exclusion_table m_exclude; // set of asserted disequalities. expr_ref_vector m_axioms; // list of axioms to add. @@ -365,23 +371,44 @@ namespace smt { virtual void init_search_eh(); void init_model(expr_ref_vector const& es); + + void len_offset(expr* const& e, rational val); + void prop_arith_to_len_offset(); + int find_fst_non_empty_idx(expr_ref_vector const& x) const; + expr* find_fst_non_empty_var(expr_ref_vector const& x) const; + void find_max_eq_len(expr_ref_vector const& ls, expr_ref_vector const& rs); + bool has_len_offset(expr_ref_vector const& ls, expr_ref_vector const& rs, int & diff); + bool find_better_rep(expr_ref_vector const& ls, expr_ref_vector const& rs, unsigned const& idx, dependency*& deps, expr_ref_vector & res); + // final check bool simplify_and_solve_eqs(); // solve unitary equalities bool reduce_length_eq(); bool branch_unit_variable(); // branch on XYZ = abcdef bool branch_binary_variable(); // branch on abcX = Ydefg + bool branch_ternary_variable1(); // branch on XabcY = Zdefg or XabcY = defgZ + bool branch_ternary_variable2(); // branch on XabcY = defgZmnpq + bool branch_quat_variable(); // branch on XabcY = ZdefgT + bool len_based_split(); // split based on len offset bool branch_variable_mb(); // branch on a variable, model based on length bool branch_variable(); // branch on a variable - bool split_variable(); // split a variable bool is_solved(); bool check_length_coherence(); bool check_length_coherence0(expr* e); bool check_length_coherence(expr* e); - bool fixed_length(); - bool fixed_length(expr* e); + bool fixed_length(bool is_zero = false); + bool fixed_length(expr* e, bool is_zero); void branch_unit_variable(dependency* dep, expr* X, expr_ref_vector const& units); bool branch_variable(eq const& e); bool branch_binary_variable(eq const& e); + bool eq_unit(expr* const& l, expr* const &r) const; + unsigned_vector overlap(ptr_vector const& ls, ptr_vector const& rs); + unsigned_vector overlap2(ptr_vector const& ls, ptr_vector const& rs); + bool branch_ternary_variable_base(dependency* dep, unsigned_vector indexes, expr* x, ptr_vector xs, expr* y1, ptr_vector ys, expr* y2); + bool branch_ternary_variable_base2(dependency* dep, unsigned_vector indexes, ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2); + bool branch_ternary_variable(eq const& e, bool flag1 = false); + bool branch_ternary_variable2(eq const& e, bool flag1 = false); + bool branch_quat_variable(eq const& e); + bool len_based_split(eq const& e); bool is_unit_eq(expr_ref_vector const& ls, expr_ref_vector const& rs); bool propagate_length_coherence(expr* e); bool split_lengths(dependency* dep, @@ -394,11 +421,14 @@ namespace smt { bool check_extensionality(); bool check_contains(); bool solve_eqs(unsigned start); - bool solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); + bool solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep, unsigned idx); bool simplify_eq(expr_ref_vector& l, expr_ref_vector& r, dependency* dep); bool solve_unit_eq(expr* l, expr* r, dependency* dep); bool solve_unit_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); bool is_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, expr*& x, ptr_vector& xunits, ptr_vector& yunits, expr*& y); + bool is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr*& x1, ptr_vector& xs, expr*& x2, expr*& y1, ptr_vector& ys, expr*& y2); + bool is_ternary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr*& x, ptr_vector& xs, expr*& y1, ptr_vector& ys, expr*& y2, bool flag1); + bool is_ternary_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, ptr_vector& xs, expr*& x, expr*& y1, ptr_vector& ys, expr*& y2, bool flag1); bool solve_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); bool propagate_max_length(expr* l, expr* r, dependency* dep); @@ -449,8 +479,9 @@ namespace smt { // variable solving utilities bool occurs(expr* a, expr* b); bool occurs(expr* a, expr_ref_vector const& b); - bool is_var(expr* b); + bool is_var(expr* b) const; bool add_solution(expr* l, expr* r, dependency* dep); + bool is_unit_nth(expr* a) const; bool is_nth(expr* a) const; bool is_nth(expr* a, expr*& e1, expr*& e2) const; bool is_tail(expr* a, expr*& s, unsigned& idx) const; @@ -514,6 +545,7 @@ namespace smt { literal mk_seq_eq(expr* a, expr* b); void tightest_prefix(expr* s, expr* x); expr_ref mk_sub(expr* a, expr* b); + expr_ref mk_add(expr* a, expr* b); enode* ensure_enode(expr* a); dependency* mk_join(dependency* deps, literal lit); @@ -523,6 +555,7 @@ namespace smt { // arithmetic integration bool get_num_value(expr* s, rational& val) const; bool lower_bound(expr* s, rational& lo) const; + bool lower_bound2(expr* s, rational& lo); bool upper_bound(expr* s, rational& hi) const; bool get_length(expr* s, rational& val) const; From 2c48ffe7a72ed88cb4a76e24242acba67f3b3635 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 8 Dec 2017 13:41:18 +0800 Subject: [PATCH 02/17] Fixed setup_QF_S(): using configuration to choose the corresponding string solver --- src/smt/smt_setup.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 80543bb6f..fc7442008 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -123,8 +123,8 @@ namespace smt { setup_QF_FP(); else if (m_logic == "QF_FPBV" || m_logic == "QF_BVFP") setup_QF_FPBV(); -// else if (m_logic == "QF_S") -// setup_QF_S(); + else if (m_logic == "QF_S") + setup_QF_S(); else if (m_logic == "QF_DT") setup_QF_DT(); else @@ -170,8 +170,8 @@ namespace smt { setup_QF_BVRE(); else if (m_logic == "QF_AUFLIA") setup_QF_AUFLIA(st); -// else if (m_logic == "QF_S") -// setup_QF_S(); + else if (m_logic == "QF_S") + setup_QF_S(); else if (m_logic == "AUFLIA") setup_AUFLIA(st); else if (m_logic == "AUFLIRA") @@ -723,8 +723,18 @@ namespace smt { } void setup::setup_QF_S() { - m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); - m_context.register_plugin(alloc(smt::theory_str, m_manager, m_params)); + if (m_params.m_string_solver == "z3str3") { + setup_str(); + } + else if (m_params.m_string_solver == "seq") { + setup_seq(); + } + else if (m_params.m_string_solver == "auto") { + setup_seq(); + } + else { + throw default_exception("invalid parameter for smt.string_solver, valid options are 'z3str3', 'seq', 'auto'"); + } } bool is_arith(static_features const & st) { From ff567412c1d86df389ede184c328d9dcfb7f0846 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 8 Dec 2017 14:26:20 +0800 Subject: [PATCH 03/17] Simplify code --- src/smt/theory_seq.cpp | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 1b8c27630..b412870aa 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -454,8 +454,8 @@ bool theory_seq::is_unit_eq(expr_ref_vector const& ls, expr_ref_vector const& rs if (ls.empty() || !is_var(ls[0])) { return false; } - for (unsigned i = 0; i < rs.size(); ++i) { - if (!m_util.str.is_unit(rs[i])) { + for (expr* const& elem : rs) { + if (!m_util.str.is_unit(elem)) { return false; } } @@ -501,9 +501,7 @@ void theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector } bool theory_seq::branch_ternary_variable1() { - unsigned sz = m_eqs.size(); - for (unsigned i = 0; i < sz; ++i) { - eq const& e = m_eqs[i]; + for (eq const& e : m_eqs) { if (branch_ternary_variable(e) || branch_ternary_variable2(e)) { return true; } @@ -512,9 +510,7 @@ bool theory_seq::branch_ternary_variable1() { } bool theory_seq::branch_ternary_variable2() { - unsigned sz = m_eqs.size(); - for (unsigned i = 0; i < sz; ++i) { - eq const& e = m_eqs[i]; + for (eq const& e : m_eqs) { if (branch_ternary_variable(e, true)) { return true; } @@ -588,8 +584,7 @@ bool theory_seq::branch_ternary_variable_base(dependency* dep, unsigned_vector i expr* x, ptr_vector xs, expr* y1, ptr_vector ys, expr* y2) { context& ctx = get_context(); bool change = false; - for (unsigned i = 0; i < indexes.size(); ++i) { - unsigned ind = indexes[i]; + for (unsigned ind : indexes) { TRACE("seq", tout << "ind = " << ind << "\n";); expr_ref xs2E(m); if (xs.size() > ind) { @@ -705,8 +700,7 @@ bool theory_seq::branch_ternary_variable_base2(dependency* dep, unsigned_vector ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2) { context& ctx = get_context(); bool change = false; - for (unsigned i = 0; i < indexes.size(); ++i) { - unsigned ind = indexes[i]; + for (unsigned ind : indexes) { expr_ref xs1E(m); if (ind > 0) { xs1E = m_util.str.mk_concat(ind, xs.c_ptr()); @@ -819,9 +813,7 @@ bool theory_seq::branch_ternary_variable2(eq const& e, bool flag1) { } bool theory_seq::branch_quat_variable() { - unsigned sz = m_eqs.size(); - for (unsigned i = 0; i < sz; ++i) { - eq const& e = m_eqs[i]; + for (eq const& e : m_eqs) { if (branch_quat_variable(e)) { return true; } @@ -1373,8 +1365,7 @@ bool theory_seq::len_based_split(eq const& e) { bool theory_seq::branch_variable_mb() { bool change = false; - for (unsigned i = 0; i < m_eqs.size(); ++i) { - eq const& e = m_eqs[i]; + for (eq const& e : m_eqs) { vector len1, len2; if (!is_complex(e)) { continue; @@ -1389,8 +1380,8 @@ bool theory_seq::branch_variable_mb() { continue; } rational l1, l2; - for (unsigned j = 0; j < len1.size(); ++j) l1 += len1[j]; - for (unsigned j = 0; j < len2.size(); ++j) l2 += len2[j]; + for (rational elem : len1) l1 += elem; + for (rational elem : len2) l2 += elem; if (l1 != l2) { TRACE("seq", tout << "lengths are not compatible\n";); expr_ref l = mk_concat(e.ls().size(), e.ls().c_ptr()); @@ -1414,11 +1405,11 @@ bool theory_seq::branch_variable_mb() { bool theory_seq::is_complex(eq const& e) { unsigned num_vars1 = 0, num_vars2 = 0; - for (unsigned i = 0; i < e.ls().size(); ++i) { - if (is_var(e.ls()[i])) ++num_vars1; + for (expr* const& elem : e.ls()) { + if (is_var(elem)) ++num_vars1; } - for (unsigned i = 0; i < e.rs().size(); ++i) { - if (is_var(e.rs()[i])) ++num_vars2; + for (expr* const& elem : e.rs()) { + if (is_var(elem)) ++num_vars2; } return num_vars1 > 0 && num_vars2 > 0 && num_vars1 + num_vars2 > 2; } @@ -2322,8 +2313,8 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) { bool theory_seq::occurs(expr* a, expr_ref_vector const& b) { - for (unsigned i = 0; i < b.size(); ++i) { - if (a == b[i] || m.is_ite(b[i])) return true; + for (expr* const& elem : b) { + if (a == elem || m.is_ite(elem)) return true; } return false; } From 7ece37f9a1e13902df439e00644ea7cd79994b11 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 8 Dec 2017 17:10:28 +0800 Subject: [PATCH 04/17] added assertions --- src/smt/theory_seq.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index b412870aa..754e2b84c 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -522,8 +522,9 @@ bool theory_seq::eq_unit(expr* const& l, expr* const &r) const { return l == r || is_unit_nth(l) || is_unit_nth(r); } -// exists y s.t. ls and rs ++ y have the same suffix +// exists x, y, rs' s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = rs' ++ x && rs = y ++ rs') unsigned_vector theory_seq::overlap(ptr_vector const& ls, ptr_vector const& rs) { + SASSERT(!ls.empty() && !rs.empty()); unsigned_vector res; expr* l = m_util.str.mk_concat(ls.size(),ls.c_ptr()); expr* r = m_util.str.mk_concat(rs.size(),rs.c_ptr()); @@ -553,8 +554,9 @@ unsigned_vector theory_seq::overlap(ptr_vector const& ls, ptr_vector return result; } -// exists x s.t. x ++ ls and rs have the same prefix +// exists x, y, rs' s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = x ++ rs' && rs = rs' ++ y) unsigned_vector theory_seq::overlap2(ptr_vector const& ls, ptr_vector const& rs) { + SASSERT(!ls.empty() && !rs.empty()); unsigned_vector res; expr* l = m_util.str.mk_concat(ls.size(),ls.c_ptr()); expr* r = m_util.str.mk_concat(rs.size(),rs.c_ptr()); From 6253faece7a886c550e8ab436ae2b357b76af26e Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 8 Dec 2017 17:20:30 +0800 Subject: [PATCH 05/17] fixed redundant check --- src/smt/theory_seq.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 754e2b84c..9f5799a45 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -522,7 +522,7 @@ bool theory_seq::eq_unit(expr* const& l, expr* const &r) const { return l == r || is_unit_nth(l) || is_unit_nth(r); } -// exists x, y, rs' s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = rs' ++ x && rs = y ++ rs') +// exists x, y, rs' != empty s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = rs' ++ x && rs = y ++ rs') unsigned_vector theory_seq::overlap(ptr_vector const& ls, ptr_vector const& rs) { SASSERT(!ls.empty() && !rs.empty()); unsigned_vector res; @@ -554,7 +554,7 @@ unsigned_vector theory_seq::overlap(ptr_vector const& ls, ptr_vector return result; } -// exists x, y, rs' s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = x ++ rs' && rs = rs' ++ y) +// exists x, y, rs' != empty s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = x ++ rs' && rs = rs' ++ y) unsigned_vector theory_seq::overlap2(ptr_vector const& ls, ptr_vector const& rs) { SASSERT(!ls.empty() && !rs.empty()); unsigned_vector res; @@ -568,11 +568,13 @@ unsigned_vector theory_seq::overlap2(ptr_vector const& ls, ptr_vector Date: Fri, 8 Dec 2017 18:36:20 +0800 Subject: [PATCH 06/17] use obj_ref_map --- src/smt/theory_seq.cpp | 2 ++ src/smt/theory_seq.h | 5 +++-- src/util/obj_ref_hashtable.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 9f5799a45..85286056f 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -217,6 +217,8 @@ theory_seq::theory_seq(ast_manager& m): m_new_solution(false), m_new_propagation(false), m_len_prop_lvl(-1), + m_overlap(m), + m_overlap2(m), m_mk_aut(m) { m_prefix = "seq.p.suffix"; m_suffix = "seq.s.prefix"; diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 3d786ddff..85ec35557 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -29,6 +29,7 @@ Revision History: #include "math/automata/automaton.h" #include "ast/rewriter/seq_rewriter.h" #include "util/union_find.h" +#include "util/obj_ref_hashtable.h" namespace smt { @@ -301,8 +302,8 @@ namespace smt { unsigned m_eq_id; th_union_find m_find; - obj_map m_overlap; - obj_map m_overlap2; + obj_ref_map m_overlap; + obj_ref_map m_overlap2; obj_map> m_len_offset; int m_len_prop_lvl; diff --git a/src/util/obj_ref_hashtable.h b/src/util/obj_ref_hashtable.h index 3d6bbf883..23a2a1867 100644 --- a/src/util/obj_ref_hashtable.h +++ b/src/util/obj_ref_hashtable.h @@ -26,7 +26,7 @@ class obj_ref_map { M& m; obj_map m_table; public: - typedef typename obj_map iterator; + typedef typename obj_map::iterator iterator; typedef Key key; typedef Value value; typedef typename obj_map::key_data key_data; From b181d9d5fa8fb9e9f0ab33d749c9f0202cd2e74c Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 8 Dec 2017 18:45:56 +0800 Subject: [PATCH 07/17] fix set-up --- src/smt/smt_setup.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 263931be8..5245e8020 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -727,10 +727,10 @@ namespace smt { setup_str(); } else if (m_params.m_string_solver == "seq") { - setup_seq(); + setup_unknown(); } else if (m_params.m_string_solver == "auto") { - setup_seq(); + setup_unknown(); } else { throw default_exception("invalid parameter for smt.string_solver, valid options are 'z3str3', 'seq', 'auto'"); From b8ce5509b070544969024be264ab7f891018d427 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 8 Dec 2017 19:16:28 +0800 Subject: [PATCH 08/17] change to "auto" --- src/smt/theory_seq.cpp | 44 +++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 4c7a90772..4eda3dc39 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -352,7 +352,7 @@ bool theory_seq::reduce_length_eq() { } bool theory_seq::branch_binary_variable() { - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { if (branch_binary_variable(e)) { TRACE("seq", display_equation(tout, e);); return true; @@ -431,7 +431,7 @@ bool theory_seq::branch_binary_variable(eq const& e) { bool theory_seq::branch_unit_variable() { bool result = false; - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { if (is_unit_eq(e.ls(), e.rs())) { branch_unit_variable(e.dep(), e.ls()[0], e.rs()); result = true; @@ -454,7 +454,7 @@ bool theory_seq::is_unit_eq(expr_ref_vector const& ls, expr_ref_vector const& rs if (ls.empty() || !is_var(ls[0])) { return false; } - for (expr* const& elem : rs) { + for (auto const& elem : rs) { if (!m_util.str.is_unit(elem)) { return false; } @@ -499,7 +499,7 @@ void theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector } bool theory_seq::branch_ternary_variable1() { - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { if (branch_ternary_variable(e) || branch_ternary_variable2(e)) { return true; } @@ -508,7 +508,7 @@ bool theory_seq::branch_ternary_variable1() { } bool theory_seq::branch_ternary_variable2() { - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { if (branch_ternary_variable(e, true)) { return true; } @@ -586,7 +586,7 @@ bool theory_seq::branch_ternary_variable_base(dependency* dep, unsigned_vector i expr* x, ptr_vector xs, expr* y1, ptr_vector ys, expr* y2) { context& ctx = get_context(); bool change = false; - for (unsigned ind : indexes) { + for (auto ind : indexes) { TRACE("seq", tout << "ind = " << ind << "\n";); expr_ref xs2E(m); if (xs.size() > ind) { @@ -702,7 +702,7 @@ bool theory_seq::branch_ternary_variable_base2(dependency* dep, unsigned_vector ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2) { context& ctx = get_context(); bool change = false; - for (unsigned ind : indexes) { + for (auto ind : indexes) { expr_ref xs1E(m); if (ind > 0) { xs1E = m_util.str.mk_concat(ind, xs.c_ptr()); @@ -815,7 +815,7 @@ bool theory_seq::branch_ternary_variable2(eq const& e, bool flag1) { } bool theory_seq::branch_quat_variable() { - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { if (branch_quat_variable(e)) { return true; } @@ -1367,7 +1367,7 @@ bool theory_seq::len_based_split(eq const& e) { bool theory_seq::branch_variable_mb() { bool change = false; - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { vector len1, len2; if (!is_complex(e)) { continue; @@ -1382,8 +1382,8 @@ bool theory_seq::branch_variable_mb() { continue; } rational l1, l2; - for (rational elem : len1) l1 += elem; - for (rational elem : len2) l2 += elem; + for (auto elem : len1) l1 += elem; + for (auto elem : len2) l2 += elem; if (l1 != l2) { TRACE("seq", tout << "lengths are not compatible\n";); expr_ref l = mk_concat(e.ls()); @@ -1406,10 +1406,10 @@ bool theory_seq::branch_variable_mb() { bool theory_seq::is_complex(eq const& e) { unsigned num_vars1 = 0, num_vars2 = 0; - for (expr* const& elem : e.ls()) { + for (auto const& elem : e.ls()) { if (is_var(elem)) ++num_vars1; } - for (expr* const& elem : e.rs()) { + for (auto const& elem : e.rs()) { if (is_var(elem)) ++num_vars2; } return num_vars1 > 0 && num_vars2 > 0 && num_vars1 + num_vars2 > 2; @@ -1816,13 +1816,13 @@ bool theory_seq::check_length_coherence0(expr* e) { bool theory_seq::check_length_coherence() { #if 1 - for (expr* e : m_length) { + for (auto e : m_length) { if (check_length_coherence0(e)) { return true; } } #endif - for (expr* e : m_length) { + for (auto e : m_length) { if (check_length_coherence(e)) { return true; } @@ -1832,7 +1832,7 @@ bool theory_seq::check_length_coherence() { bool theory_seq::fixed_length(bool is_zero) { bool found = false; - for (expr* e : m_length) { + for (auto e : m_length) { if (fixed_length(e, is_zero)) { found = true; } @@ -2326,7 +2326,7 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) { bool theory_seq::occurs(expr* a, expr_ref_vector const& b) { - for (expr* const& elem : b) { + for (auto const& elem : b) { if (a == elem || m.is_ite(elem)) return true; } return false; @@ -3326,7 +3326,7 @@ bool theory_seq::internalize_term(app* term) { return true; } TRACE("seq_verbose", tout << mk_pp(term, m) << "\n";); - for (expr* arg : *term) { + for (auto arg : *term) { mk_var(ensure_enode(arg)); } if (m.is_bool(term)) { @@ -3569,7 +3569,7 @@ void theory_seq::display(std::ostream & out) const { } if (!m_length.empty()) { - for (expr* e : m_length) { + for (auto e : m_length) { rational lo(-1), hi(-1); lower_bound(e, lo); upper_bound(e, hi); @@ -3594,7 +3594,7 @@ void theory_seq::display_nc(std::ostream& out, nc const& nc) const { } void theory_seq::display_equations(std::ostream& out) const { - for (eq const& e : m_eqs) { + for (auto const& e : m_eqs) { display_equation(out, e); } } @@ -3690,7 +3690,7 @@ void theory_seq::init_search_eh() { void theory_seq::init_model(expr_ref_vector const& es) { expr_ref new_s(m); - for (expr* e : es) { + for (auto e : es) { dependency* eqs = 0; expr_ref s = canonize(e, eqs); if (is_var(s)) { @@ -4946,7 +4946,7 @@ literal theory_seq::mk_eq_empty(expr* _e, bool phase) { } expr_ref_vector concats(m); m_util.str.get_concat(e, concats); - for (expr* c : concats) { + for (auto c : concats) { if (m_util.str.is_unit(c)) { return false_literal; } From b819b368f1bf2345e150ee331a032e55ea0b592b Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 8 Dec 2017 19:29:07 +0800 Subject: [PATCH 09/17] minor --- src/smt/theory_seq.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 4eda3dc39..c1edca47b 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -1269,8 +1269,7 @@ bool theory_seq::len_based_split() { } } - for (unsigned i = 0; i < sz; ++i) { - eq const& e = m_eqs[i]; + for (auto const& e : m_eqs) { if (len_based_split(e)) { return true; } From a2641909d00ec889d1aa24f59a79f977776c674c Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 8 Dec 2017 19:40:20 +0800 Subject: [PATCH 10/17] initialize pointers --- src/smt/theory_seq.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index c1edca47b..2db9ab33f 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -634,7 +634,7 @@ expr* x, ptr_vector xs, expr* y1, ptr_vector ys, expr* y2) { // Equation is of the form x ++ xs = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_ternary_variable(eq const& e, bool flag1) { ptr_vector xs, ys; - expr* x, *y1, *y2; + expr* x = nullptr, *y1 = nullptr, *y2 = nullptr; bool is_ternary = is_ternary_eq(e.ls(), e.rs(), x, xs, y1, ys, y2, flag1); if (!is_ternary) { is_ternary = is_ternary_eq(e.rs(), e.ls(), x, xs, y1, ys, y2, flag1); @@ -749,7 +749,7 @@ ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2) { // Equation is of the form xs ++ x = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_ternary_variable2(eq const& e, bool flag1) { ptr_vector xs, ys; - expr* x, *y1, *y2; + expr* x = nullptr, *y1 = nullptr, *y2 = nullptr; bool is_ternary = is_ternary_eq2(e.ls(), e.rs(), xs, x, y1, ys, y2, flag1); if (!is_ternary) { is_ternary = is_ternary_eq2(e.rs(), e.ls(), xs, x, y1, ys, y2, flag1); @@ -826,7 +826,7 @@ bool theory_seq::branch_quat_variable() { // Equation is of the form x1 ++ xs ++ x2 = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_quat_variable(eq const& e) { ptr_vector xs, ys; - expr* x1, *x2, *y1, *y2; + expr* x1 = nullptr, *x2 = nullptr, *y1 = nullptr, *y2 = nullptr; bool is_quat = is_quat_eq(e.ls(), e.rs(), x1, xs, x2, y1, ys, y2); if (!is_quat) { return false; @@ -893,14 +893,14 @@ bool theory_seq::branch_quat_variable(eq const& e) { void theory_seq::len_offset(expr* const& e, rational val) { context & ctx = get_context(); - expr *l1, *l2, *l21, *l22; + expr *l1 = nullptr, *l2 = nullptr, *l21 = nullptr, *l22 = nullptr; rational fact; if (m_autil.is_add(e, l1, l2) && m_autil.is_mul(l2, l21, l22) && m_autil.is_numeral(l21, fact) && fact.is_minus_one()) { if (ctx.e_internalized(l1) && ctx.e_internalized(l22)) { enode* r1 = ctx.get_enode(l1)->get_root(), *n1 = r1; enode* r2 = ctx.get_enode(l22)->get_root(), *n2 = r2; - expr *e1, *e2; + expr *e1 = nullptr, *e2 = nullptr; do { if (!m_util.str.is_length(n1->get_owner(), e1)) n1 = n1->get_next(); @@ -1906,7 +1906,7 @@ bool theory_seq::propagate_is_conc(expr* e, expr* conc) { } bool theory_seq::is_unit_nth(expr* e) const { - expr *s; + expr *s = nullptr; return m_util.str.is_unit(e, s) && is_nth(s); } From 8bf4a15c27a5ddc785a4c07a464e43bf7ffeec9a Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Sat, 9 Dec 2017 00:47:48 +0800 Subject: [PATCH 11/17] update "seq.align" skolem function --- src/smt/theory_seq.cpp | 74 +++++++++++++++++++++--------------------- src/smt/theory_seq.h | 2 +- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 2db9ab33f..57be6c205 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -661,11 +661,11 @@ bool theory_seq::branch_ternary_variable(eq const& e, bool flag1) { // x ++ xs = y1 ++ ys ++ y2 => x = y1 ++ ys ++ z, z ++ xs = y2 expr_ref xsE(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); - expr_ref Z(mk_skolem(symbol("seq.left.1"), y2, xsE), m); + expr_ref ysE(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); + expr_ref y1ys(mk_concat(y1, ysE)); + expr_ref Z(mk_skolem(symbol("seq.align"), y2, xsE, x, y1ys), m); expr_ref ZxsE(m_util.str.mk_concat(Z, xsE), m); - ys.push_back(Z); - expr_ref ysZ(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); - expr_ref y1ysZ(m_util.str.mk_concat(y1, ysZ), m); + expr_ref y1ysZ(m_util.str.mk_concat(y1ys, Z), m); if (indexes.empty()) { TRACE("seq", tout << "one case\n";); TRACE("seq", display_equation(tout, e);); @@ -775,11 +775,11 @@ bool theory_seq::branch_ternary_variable2(eq const& e, bool flag1) { // xs ++ x = y1 ++ ys ++ y2 => xs ++ z = y1, x = z ++ ys ++ y2 expr_ref xsE(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); - expr_ref Z(mk_skolem(symbol("seq.right.1"), y1, xsE), m); + expr_ref ysE(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); + expr_ref ysy2(m_util.str.mk_concat(ysE, y2), m); + expr_ref Z(mk_skolem(symbol("seq.align"), x, ysy2, y1, xsE), m); xs.push_back(Z); expr_ref xsZ(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); - ys.push_back(y2); - expr_ref ysy2(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); expr_ref Zysy2(m_util.str.mk_concat(Z, ysy2), m); if (indexes.empty()) { TRACE("seq", tout << "one case\n";); @@ -826,19 +826,19 @@ bool theory_seq::branch_quat_variable() { // Equation is of the form x1 ++ xs ++ x2 = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_quat_variable(eq const& e) { ptr_vector xs, ys; - expr* x1 = nullptr, *x2 = nullptr, *y1 = nullptr, *y2 = nullptr; - bool is_quat = is_quat_eq(e.ls(), e.rs(), x1, xs, x2, y1, ys, y2); + expr* x1_l = nullptr, *x2 = nullptr, *y1_l = nullptr, *y2 = nullptr; + bool is_quat = is_quat_eq(e.ls(), e.rs(), x1_l, xs, x2, y1_l, ys, y2); if (!is_quat) { return false; } rational lenX1, lenX2, lenY1, lenY2; context& ctx = get_context(); - if (!get_length(x1, lenX1)) { - enforce_length(ensure_enode(x1)); + if (!get_length(x1_l, lenX1)) { + enforce_length(ensure_enode(x1_l)); } - if (!get_length(y1, lenY1)) { - enforce_length(ensure_enode(y1)); + if (!get_length(y1_l, lenY1)) { + enforce_length(ensure_enode(y1_l)); } if (!get_length(x2, lenX2)) { enforce_length(ensure_enode(x2)); @@ -851,9 +851,9 @@ bool theory_seq::branch_quat_variable(eq const& e) { expr_ref xsx2(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); ys.push_back(y2); expr_ref ysy2(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); - expr_ref x1_bk(x1, m); - expr_ref y1_bk(y1, m); - expr_ref sub(mk_sub(m_util.str.mk_length(x1), m_util.str.mk_length(y1)), m); + expr_ref x1(x1_l, m); + expr_ref y1(y1_l, m); + expr_ref sub(mk_sub(m_util.str.mk_length(x1_l), m_util.str.mk_length(y1_l)), m); expr_ref le(m_autil.mk_le(sub, m_autil.mk_int(0)), m); literal lit2 = mk_literal(le); if (ctx.get_assignment(lit2) == l_undef) { @@ -866,26 +866,26 @@ bool theory_seq::branch_quat_variable(eq const& e) { // |x1| > |y1| => x1 = y1 ++ z1, z1 ++ xs ++ x2 = ys ++ y2 TRACE("seq", tout << "false branch\n";); TRACE("seq", display_equation(tout, e);); - expr_ref Z1(mk_skolem(symbol("seq.right.1"), x1_bk, y1_bk), m); - expr_ref y1Z1(m_util.str.mk_concat(y1_bk, Z1), m); + expr_ref Z1(mk_skolem(symbol("seq.align"), ysy2, xsx2, x1, y1), m); + expr_ref y1Z1(m_util.str.mk_concat(y1, Z1), m); expr_ref Z1xsx2(m_util.str.mk_concat(Z1, xsx2), m); literal_vector lits; lits.push_back(~lit2); dependency* dep = e.dep(); - propagate_eq(dep, lits, x1_bk, y1Z1, true); + propagate_eq(dep, lits, x1, y1Z1, true); propagate_eq(dep, lits, Z1xsx2, ysy2, true); } else if (ctx.get_assignment(lit2) == l_true) { // |x1| <= |y1| => x1 ++ z2 = y1, xs ++ x2 = z2 ++ ys ++ y2 TRACE("seq", tout << "true branch\n";); TRACE("seq", display_equation(tout, e);); - expr_ref Z2(mk_skolem(symbol("seq.right.1"), y1_bk, x1_bk), m); - expr_ref x1Z2(m_util.str.mk_concat(x1_bk, Z2), m); + expr_ref Z2(mk_skolem(symbol("seq.align"), xsx2, ysy2, y1, x1), m); + expr_ref x1Z2(m_util.str.mk_concat(x1, Z2), m); expr_ref Z2ysy2(m_util.str.mk_concat(Z2, ysy2), m); literal_vector lits; lits.push_back(lit2); dependency* dep = e.dep(); - propagate_eq(dep, lits, x1Z2, y1_bk, true); + propagate_eq(dep, lits, x1Z2, y1, true); propagate_eq(dep, lits, xsx2, Z2ysy2, true); } return true; @@ -1301,13 +1301,13 @@ bool theory_seq::len_based_split(eq const& e) { lenY11 = m_autil.mk_add(m_util.str.mk_length(y11), m_autil.mk_int(offset_orig)); if (offset_orig > 0) { offset = offset_orig; - Z = mk_skolem(symbol("seq.right.1"), x11, y11); + Z = mk_skolem(symbol("seq.align"), y12, x12, x11, y11); y11 = m_util.str.mk_concat(y11, Z); x12 = m_util.str.mk_concat(Z, x12); } else { offset = -offset_orig; - Z = mk_skolem(symbol("seq.right.1"), y11, x11); + Z = mk_skolem(symbol("seq.align"), x12, y12, y11, x11); x11 = m_util.str.mk_concat(x11, Z); y12 = m_util.str.mk_concat(Z, y12); } @@ -1951,11 +1951,11 @@ bool theory_seq::is_post(expr* e, expr*& s, expr*& i) { expr_ref theory_seq::mk_nth(expr* s, expr* idx) { sort* char_sort = 0; VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); - return mk_skolem(m_nth, s, idx, 0, char_sort); + return mk_skolem(m_nth, s, idx, 0, 0, char_sort); } expr_ref theory_seq::mk_sk_ite(expr* c, expr* t, expr* e) { - return mk_skolem(symbol("seq.if"), c, t, e, m.get_sort(t)); + return mk_skolem(symbol("seq.if"), c, t, e, 0, m.get_sort(t)); } expr_ref theory_seq::mk_last(expr* s) { @@ -1965,7 +1965,7 @@ expr_ref theory_seq::mk_last(expr* s) { } sort* char_sort = 0; VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); - return mk_skolem(m_seq_last, s, 0, 0, char_sort); + return mk_skolem(m_seq_last, s, 0, 0, 0, char_sort); } expr_ref theory_seq::mk_first(expr* s) { @@ -3460,7 +3460,7 @@ bool theory_seq::add_stoi_axiom(expr* e) { literal theory_seq::is_digit(expr* ch) { bv_util bv(m); - literal isd = mk_literal(mk_skolem(symbol("seq.is_digit"), ch, 0, 0, m.mk_bool_sort())); + literal isd = mk_literal(mk_skolem(symbol("seq.is_digit"), ch, 0, 0, 0, m.mk_bool_sort())); expr_ref d2i = digit2int(ch); expr_ref _lo(bv.mk_ule(bv.mk_numeral(rational('0'), bv.mk_sort(8)), ch), m); expr_ref _hi(bv.mk_ule(ch, bv.mk_numeral(rational('9'), bv.mk_sort(8))), m); @@ -3476,7 +3476,7 @@ literal theory_seq::is_digit(expr* ch) { } expr_ref theory_seq::digit2int(expr* ch) { - return expr_ref(mk_skolem(symbol("seq.digit2int"), ch, 0, 0, m_autil.mk_int()), m); + return expr_ref(mk_skolem(symbol("seq.digit2int"), ch, 0, 0, 0, m_autil.mk_int()), m); } bool theory_seq::add_itos_axiom(expr* e) { @@ -4931,7 +4931,7 @@ literal theory_seq::mk_literal(expr* _e) { literal theory_seq::mk_seq_eq(expr* a, expr* b) { SASSERT(m_util.is_seq(a)); - return mk_literal(mk_skolem(m_eq, a, b, 0, m.mk_bool_sort())); + return mk_literal(mk_skolem(m_eq, a, b, 0, 0, m.mk_bool_sort())); } literal theory_seq::mk_eq_empty(expr* _e, bool phase) { @@ -4977,9 +4977,9 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4, liter } -expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2, expr* e3, sort* range) { - expr* es[3] = { e1, e2, e3 }; - unsigned len = e3?3:(e2?2:1); +expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2, expr* e3, expr*e4, sort* range) { + expr* es[4] = { e1, e2, e3, e4 }; + unsigned len = e4?4:(e3?3:(e2?2:1)); if (!range) { range = m.get_sort(e1); } @@ -5631,8 +5631,8 @@ void theory_seq::propagate_not_prefix(expr* e) { expr_ref x = mk_skolem(symbol("seq.prefix.x"), e1, e2); expr_ref y = mk_skolem(symbol("seq.prefix.y"), e1, e2); expr_ref z = mk_skolem(symbol("seq.prefix.z"), e1, e2); - expr_ref c = mk_skolem(symbol("seq.prefix.c"), e1, e2, 0, char_sort); - expr_ref d = mk_skolem(symbol("seq.prefix.d"), e1, e2, 0, char_sort); + expr_ref c = mk_skolem(symbol("seq.prefix.c"), e1, e2, 0, 0, char_sort); + expr_ref d = mk_skolem(symbol("seq.prefix.d"), e1, e2, 0, 0, char_sort); add_axiom(lit, e2_is_emp, mk_seq_eq(e1, mk_concat(x, m_util.str.mk_unit(c), y))); add_axiom(lit, e2_is_emp, mk_seq_eq(e2, mk_concat(x, m_util.str.mk_unit(d), z)), mk_seq_eq(e2, x)); add_axiom(lit, e2_is_emp, ~mk_eq(c, d, false), mk_seq_eq(e2, x)); @@ -5688,8 +5688,8 @@ void theory_seq::propagate_not_suffix(expr* e) { expr_ref x = mk_skolem(symbol("seq.suffix.x"), e1, e2); expr_ref y = mk_skolem(symbol("seq.suffix.y"), e1, e2); expr_ref z = mk_skolem(symbol("seq.suffix.z"), e1, e2); - expr_ref c = mk_skolem(symbol("seq.suffix.c"), e1, e2, 0, char_sort); - expr_ref d = mk_skolem(symbol("seq.suffix.d"), e1, e2, 0, char_sort); + expr_ref c = mk_skolem(symbol("seq.suffix.c"), e1, e2, 0, 0, char_sort); + expr_ref d = mk_skolem(symbol("seq.suffix.d"), e1, e2, 0, 0, char_sort); add_axiom(lit, e2_is_emp, mk_seq_eq(e1, mk_concat(y, m_util.str.mk_unit(c), x))); add_axiom(lit, e2_is_emp, mk_seq_eq(e2, mk_concat(z, m_util.str.mk_unit(d), x)), mk_seq_eq(e2, x)); add_axiom(lit, e2_is_emp, ~mk_eq(c, d, false), mk_seq_eq(e2, x)); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index beb7e6833..9424ed227 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -565,7 +565,7 @@ namespace smt { bool get_length(expr* s, rational& val) const; void mk_decompose(expr* e, expr_ref& head, expr_ref& tail); - expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, sort* range = 0); + expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, expr* e4 = 0, sort* range = 0); bool is_skolem(symbol const& s, expr* e) const; void set_incomplete(app* term); From c07a63cf8e29fa4995c96409d0286341403d01f9 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Tue, 12 Dec 2017 05:00:34 +0800 Subject: [PATCH 12/17] coalesce seq.unit into string in mk_skolem --- src/ast/rewriter/seq_rewriter.cpp | 2 +- src/smt/theory_seq.cpp | 57 +++++++++++++++++++++++++++++++ src/smt/theory_seq.h | 1 + 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index fa95278b4..2f9f3f9ce 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -453,7 +453,7 @@ br_status seq_rewriter::mk_seq_unit(expr* e, expr_ref& result) { if (bvu.is_bv(e) && bvu.is_numeral(e, n_val, n_size) && n_size == 8) { // convert to string constant zstring str(n_val.get_unsigned()); - TRACE("seq", tout << "rewrite seq.unit of 8-bit value " << n_val.to_string() << " to string constant \"" << str<< "\"" << std::endl;); + TRACE("seq_verbose", tout << "rewrite seq.unit of 8-bit value " << n_val.to_string() << " to string constant \"" << str<< "\"" << std::endl;); result = m_util.str.mk_string(str); return BR_DONE; } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 57be6c205..6626c05ac 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -4976,13 +4976,70 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4, liter ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } +expr* theory_seq::coalesce_chars(expr* const& e) { + expr* s; + if (m_util.str.is_concat(e)) { + expr_ref_vector concats(m); + m_util.str.get_concat(e, concats); + expr_ref_vector result(m); + for (unsigned i = 0; i < concats.size(); ++i) { + expr_ref tmp(coalesce_chars(concats[i].get()), m); + if (m_util.str.is_empty(tmp)) continue; + zstring zs, a; + bool flag = false; + while (m_util.str.is_string(tmp, a)) { + if (flag) + zs = zs + a; + else + zs = a; + flag = true; + if (i < concats.size()-1) + tmp = coalesce_chars(concats[++i].get()); + else { + ++i; + break; + } + } + if (flag) { + result.push_back(m_util.str.mk_string(zs)); + if (i < concats.size()) + result.push_back(tmp); + } + else + result.push_back(tmp); + } + SASSERT(result.size() > 0); + if (result.size() > 1) + return m_util.str.mk_concat(result.size(), result.c_ptr()); + else + return e; + } + else if (m_util.str.is_unit(e, s)) { + bv_util bvu(m); + if (bvu.is_bv(s)) { + expr_ref result(m); + expr * args[1] = {s}; + if (m_seq_rewrite.mk_app_core(to_app(s)->get_decl(), 1, args, result)) { + return result; + } + } + } + return e; +} expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2, expr* e3, expr*e4, sort* range) { expr* es[4] = { e1, e2, e3, e4 }; unsigned len = e4?4:(e3?3:(e2?2:1)); + if (!range) { range = m.get_sort(e1); } + if (name == symbol("seq.align")) { + for (unsigned i = 0; i < len; ++i) { + es[i] = coalesce_chars(es[i]); + TRACE("seq", tout << mk_pp(es[i], m) << "\n";); + } + } return expr_ref(m_util.mk_skolem(name, len, es, range), m); } diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 9424ed227..08eb205d6 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -565,6 +565,7 @@ namespace smt { bool get_length(expr* s, rational& val) const; void mk_decompose(expr* e, expr_ref& head, expr_ref& tail); + expr* coalesce_chars(expr* const& str); expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, expr* e4 = 0, sort* range = 0); bool is_skolem(symbol const& s, expr* e) const; From fe503d95ec47e17a0e75c4d60454e0d09ba0c8c7 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Fri, 15 Dec 2017 20:01:03 +0800 Subject: [PATCH 13/17] simplify code --- src/smt/theory_seq.cpp | 73 ++++++++++++++++++++++-------------------- src/smt/theory_seq.h | 2 +- 2 files changed, 39 insertions(+), 36 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 6626c05ac..ddfadaa8c 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -234,6 +234,7 @@ theory_seq::theory_seq(ast_manager& m): m_pre = "seq.pre"; // (seq.pre s l): prefix of string s of length l m_post = "seq.post"; // (seq.post s l): suffix of string s of length l m_eq = "seq.eq"; + m_seq_align = "seq.align"; } @@ -524,8 +525,8 @@ bool theory_seq::eq_unit(expr* const& l, expr* const &r) const { unsigned_vector theory_seq::overlap(ptr_vector const& ls, ptr_vector const& rs) { SASSERT(!ls.empty() && !rs.empty()); unsigned_vector res; - expr* l = m_util.str.mk_concat(ls.size(),ls.c_ptr()); - expr* r = m_util.str.mk_concat(rs.size(),rs.c_ptr()); + expr* l = mk_concat(ls); + expr* r = mk_concat(rs); expr* pair = m.mk_eq(l,r); if (m_overlap.find(pair, res)) { return res; @@ -556,8 +557,8 @@ unsigned_vector theory_seq::overlap(ptr_vector const& ls, ptr_vector unsigned_vector theory_seq::overlap2(ptr_vector const& ls, ptr_vector const& rs) { SASSERT(!ls.empty() && !rs.empty()); unsigned_vector res; - expr* l = m_util.str.mk_concat(ls.size(),ls.c_ptr()); - expr* r = m_util.str.mk_concat(rs.size(),rs.c_ptr()); + expr* l = mk_concat(ls); + expr* r = mk_concat(rs); expr* pair = m.mk_eq(l,r); if (m_overlap2.find(pair, res)) { return res; @@ -610,7 +611,7 @@ expr* x, ptr_vector xs, expr* y1, ptr_vector ys, expr* y2) { propagate_eq(dep, lits, y2, xs2E, true); if (ind > ys.size()) { expr_ref xs1E(m_util.str.mk_concat(ind-ys.size(), xs.c_ptr()), m); - expr_ref xxs1E(m_util.str.mk_concat(x, xs1E), m); + expr_ref xxs1E(mk_concat(x, xs1E), m); propagate_eq(dep, lits, xxs1E, y1, true); } else if (ind == ys.size()) { @@ -618,7 +619,7 @@ expr* x, ptr_vector xs, expr* y1, ptr_vector ys, expr* y2) { } else { expr_ref ys1E(m_util.str.mk_concat(ys.size()-ind, ys.c_ptr()), m); - expr_ref y1ys1E(m_util.str.mk_concat(y1, ys1E), m); + expr_ref y1ys1E(mk_concat(y1, ys1E), m); propagate_eq(dep, lits, x, y1ys1E, true); } return true; @@ -655,17 +656,18 @@ bool theory_seq::branch_ternary_variable(eq const& e, bool flag1) { enforce_length(ensure_enode(y2)); } + SASSERT(!xs.empty() && !ys.empty()); unsigned_vector indexes = overlap(xs, ys); if (branch_ternary_variable_base(e.dep(), indexes, x, xs, y1, ys, y2)) return true; // x ++ xs = y1 ++ ys ++ y2 => x = y1 ++ ys ++ z, z ++ xs = y2 - expr_ref xsE(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); - expr_ref ysE(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); + expr_ref xsE(mk_concat(xs), m); + expr_ref ysE(mk_concat(ys), m); expr_ref y1ys(mk_concat(y1, ysE)); - expr_ref Z(mk_skolem(symbol("seq.align"), y2, xsE, x, y1ys), m); - expr_ref ZxsE(m_util.str.mk_concat(Z, xsE), m); - expr_ref y1ysZ(m_util.str.mk_concat(y1ys, Z), m); + expr_ref Z(mk_skolem(m_seq_align, y2, xsE, x, y1ys), m); + expr_ref ZxsE(mk_concat(Z, xsE), m); + expr_ref y1ysZ(mk_concat(y1ys, Z), m); if (indexes.empty()) { TRACE("seq", tout << "one case\n";); TRACE("seq", display_equation(tout, e);); @@ -725,7 +727,7 @@ ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2) { propagate_eq(dep, lits, y1, xs1E, true); if (xs.size() - ind > ys.size()) { expr_ref xs2E(m_util.str.mk_concat(xs.size()-ind-ys.size(), xs.c_ptr()+ind+ys.size()), m); - expr_ref xs2x(m_util.str.mk_concat(xs2E, x), m); + expr_ref xs2x(mk_concat(xs2E, x), m); propagate_eq(dep, lits, xs2x, y2, true); } else if (xs.size() - ind == ys.size()) { @@ -733,7 +735,7 @@ ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2) { } else { expr_ref ys1E(m_util.str.mk_concat(ys.size()-xs.size()+ind, ys.c_ptr()+xs.size()-ind), m); - expr_ref ys1y2(m_util.str.mk_concat(ys1E, y2), m); + expr_ref ys1y2(mk_concat(ys1E, y2), m); propagate_eq(dep, lits, x, ys1y2, true); } return true; @@ -769,18 +771,18 @@ bool theory_seq::branch_ternary_variable2(eq const& e, bool flag1) { if (!get_length(y2, lenY2)) { enforce_length(ensure_enode(y2)); } + SASSERT(!xs.empty() && !ys.empty()); unsigned_vector indexes = overlap2(xs, ys); if (branch_ternary_variable_base2(e.dep(), indexes, xs, x, y1, ys, y2)) return true; // xs ++ x = y1 ++ ys ++ y2 => xs ++ z = y1, x = z ++ ys ++ y2 - expr_ref xsE(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); - expr_ref ysE(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); - expr_ref ysy2(m_util.str.mk_concat(ysE, y2), m); - expr_ref Z(mk_skolem(symbol("seq.align"), x, ysy2, y1, xsE), m); - xs.push_back(Z); - expr_ref xsZ(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); - expr_ref Zysy2(m_util.str.mk_concat(Z, ysy2), m); + expr_ref xsE(mk_concat(xs), m); + expr_ref ysE(mk_concat(ys), m); + expr_ref ysy2(mk_concat(ysE, y2), m); + expr_ref Z(mk_skolem(m_seq_align, x, ysy2, y1, xsE), m); + expr_ref xsZ(mk_concat(xsE, Z), m); + expr_ref Zysy2(mk_concat(Z, ysy2), m); if (indexes.empty()) { TRACE("seq", tout << "one case\n";); TRACE("seq", display_equation(tout, e);); @@ -846,11 +848,12 @@ bool theory_seq::branch_quat_variable(eq const& e) { if (!get_length(y2, lenY2)) { enforce_length(ensure_enode(y2)); } + SASSERT(!xs.empty() && !ys.empty()); xs.push_back(x2); - expr_ref xsx2(m_util.str.mk_concat(xs.size(), xs.c_ptr()), m); + expr_ref xsx2(mk_concat(xs), m); ys.push_back(y2); - expr_ref ysy2(m_util.str.mk_concat(ys.size(), ys.c_ptr()), m); + expr_ref ysy2(mk_concat(ys), m); expr_ref x1(x1_l, m); expr_ref y1(y1_l, m); expr_ref sub(mk_sub(m_util.str.mk_length(x1_l), m_util.str.mk_length(y1_l)), m); @@ -866,9 +869,9 @@ bool theory_seq::branch_quat_variable(eq const& e) { // |x1| > |y1| => x1 = y1 ++ z1, z1 ++ xs ++ x2 = ys ++ y2 TRACE("seq", tout << "false branch\n";); TRACE("seq", display_equation(tout, e);); - expr_ref Z1(mk_skolem(symbol("seq.align"), ysy2, xsx2, x1, y1), m); - expr_ref y1Z1(m_util.str.mk_concat(y1, Z1), m); - expr_ref Z1xsx2(m_util.str.mk_concat(Z1, xsx2), m); + expr_ref Z1(mk_skolem(m_seq_align, ysy2, xsx2, x1, y1), m); + expr_ref y1Z1(mk_concat(y1, Z1), m); + expr_ref Z1xsx2(mk_concat(Z1, xsx2), m); literal_vector lits; lits.push_back(~lit2); dependency* dep = e.dep(); @@ -879,9 +882,9 @@ bool theory_seq::branch_quat_variable(eq const& e) { // |x1| <= |y1| => x1 ++ z2 = y1, xs ++ x2 = z2 ++ ys ++ y2 TRACE("seq", tout << "true branch\n";); TRACE("seq", display_equation(tout, e);); - expr_ref Z2(mk_skolem(symbol("seq.align"), xsx2, ysy2, y1, x1), m); - expr_ref x1Z2(m_util.str.mk_concat(x1, Z2), m); - expr_ref Z2ysy2(m_util.str.mk_concat(Z2, ysy2), m); + expr_ref Z2(mk_skolem(m_seq_align, xsx2, ysy2, y1, x1), m); + expr_ref x1Z2(mk_concat(x1, Z2), m); + expr_ref Z2ysy2(mk_concat(Z2, ysy2), m); literal_vector lits; lits.push_back(lit2); dependency* dep = e.dep(); @@ -1301,15 +1304,15 @@ bool theory_seq::len_based_split(eq const& e) { lenY11 = m_autil.mk_add(m_util.str.mk_length(y11), m_autil.mk_int(offset_orig)); if (offset_orig > 0) { offset = offset_orig; - Z = mk_skolem(symbol("seq.align"), y12, x12, x11, y11); - y11 = m_util.str.mk_concat(y11, Z); - x12 = m_util.str.mk_concat(Z, x12); + Z = mk_skolem(m_seq_align, y12, x12, x11, y11); + y11 = mk_concat(y11, Z); + x12 = mk_concat(Z, x12); } else { offset = -offset_orig; - Z = mk_skolem(symbol("seq.align"), x12, y12, y11, x11); - x11 = m_util.str.mk_concat(x11, Z); - y12 = m_util.str.mk_concat(Z, y12); + Z = mk_skolem(m_seq_align, x12, y12, y11, x11); + x11 = mk_concat(x11, Z); + y12 = mk_concat(Z, y12); } } else @@ -5034,7 +5037,7 @@ expr_ref theory_seq::mk_skolem(symbol const& name, expr* e1, expr* e2, expr* e3, if (!range) { range = m.get_sort(e1); } - if (name == symbol("seq.align")) { + if (name == m_seq_align) { for (unsigned i = 0; i < len; ++i) { es[i] = coalesce_chars(es[i]); TRACE("seq", tout << mk_pp(es[i], m) << "\n";); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 08eb205d6..6be9b894f 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -328,7 +328,7 @@ namespace smt { stats m_stats; symbol m_prefix, m_suffix, m_accept, m_reject; symbol m_tail, m_nth, m_seq_first, m_seq_last, m_indexof_left, m_indexof_right, m_aut_step; - symbol m_pre, m_post, m_eq; + symbol m_pre, m_post, m_eq, m_seq_align; ptr_vector m_todo; expr_ref_vector m_ls, m_rs, m_lhs, m_rhs; From 07afce6a647b392d3413878ee3f82e0237645cf9 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Sat, 16 Dec 2017 20:44:07 +0800 Subject: [PATCH 14/17] pass vectors by reference --- src/smt/theory_seq.cpp | 24 ++++++++++++------------ src/smt/theory_seq.h | 16 ++++++++-------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index ddfadaa8c..a775b268d 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -522,7 +522,7 @@ bool theory_seq::eq_unit(expr* const& l, expr* const &r) const { } // exists x, y, rs' != empty s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = rs' ++ x && rs = y ++ rs') -unsigned_vector theory_seq::overlap(ptr_vector const& ls, ptr_vector const& rs) { +unsigned_vector theory_seq::overlap(expr_ref_vector const& ls, expr_ref_vector const& rs) { SASSERT(!ls.empty() && !rs.empty()); unsigned_vector res; expr* l = mk_concat(ls); @@ -554,7 +554,7 @@ unsigned_vector theory_seq::overlap(ptr_vector const& ls, ptr_vector } // exists x, y, rs' != empty s.t. (ls = x ++ rs' ++ y & rs = rs') || (ls = x ++ rs' && rs = rs' ++ y) -unsigned_vector theory_seq::overlap2(ptr_vector const& ls, ptr_vector const& rs) { +unsigned_vector theory_seq::overlap2(expr_ref_vector const& ls, expr_ref_vector const& rs) { SASSERT(!ls.empty() && !rs.empty()); unsigned_vector res; expr* l = mk_concat(ls); @@ -584,7 +584,7 @@ unsigned_vector theory_seq::overlap2(ptr_vector const& ls, ptr_vector xs, expr* y1, ptr_vector ys, expr* y2) { + expr* const& x, expr_ref_vector const& xs, expr* const& y1, expr_ref_vector const& ys, expr* const& y2) { context& ctx = get_context(); bool change = false; for (auto ind : indexes) { @@ -634,7 +634,7 @@ expr* x, ptr_vector xs, expr* y1, ptr_vector ys, expr* y2) { // Equation is of the form x ++ xs = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_ternary_variable(eq const& e, bool flag1) { - ptr_vector xs, ys; + expr_ref_vector xs(m), ys(m); expr* x = nullptr, *y1 = nullptr, *y2 = nullptr; bool is_ternary = is_ternary_eq(e.ls(), e.rs(), x, xs, y1, ys, y2, flag1); if (!is_ternary) { @@ -701,7 +701,7 @@ bool theory_seq::branch_ternary_variable(eq const& e, bool flag1) { } bool theory_seq::branch_ternary_variable_base2(dependency* dep, unsigned_vector indexes, -ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2) { + expr_ref_vector const& xs, expr* const& x, expr* const& y1, expr_ref_vector const& ys, expr* const& y2) { context& ctx = get_context(); bool change = false; for (auto ind : indexes) { @@ -750,7 +750,7 @@ ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2) { // Equation is of the form xs ++ x = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_ternary_variable2(eq const& e, bool flag1) { - ptr_vector xs, ys; + expr_ref_vector xs(m), ys(m); expr* x = nullptr, *y1 = nullptr, *y2 = nullptr; bool is_ternary = is_ternary_eq2(e.ls(), e.rs(), xs, x, y1, ys, y2, flag1); if (!is_ternary) { @@ -827,7 +827,7 @@ bool theory_seq::branch_quat_variable() { // Equation is of the form x1 ++ xs ++ x2 = y1 ++ ys ++ y2 where xs, ys are units. bool theory_seq::branch_quat_variable(eq const& e) { - ptr_vector xs, ys; + expr_ref_vector xs(m), ys(m); expr* x1_l = nullptr, *x2 = nullptr, *y1_l = nullptr, *y2 = nullptr; bool is_quat = is_quat_eq(e.ls(), e.rs(), x1_l, xs, x2, y1_l, ys, y2); if (!is_quat) { @@ -1388,8 +1388,8 @@ bool theory_seq::branch_variable_mb() { for (auto elem : len2) l2 += elem; if (l1 != l2) { TRACE("seq", tout << "lengths are not compatible\n";); - expr_ref l = mk_concat(e.ls()); - expr_ref r = mk_concat(e.rs()); + expr_ref l(mk_concat(e.ls()), m); + expr_ref r(mk_concat(e.rs()), m); expr_ref lnl(m_util.str.mk_length(l), m), lnr(m_util.str.mk_length(r), m); propagate_eq(e.dep(), lnl, lnr, false); change = true; @@ -2490,7 +2490,7 @@ bool theory_seq::is_binary_eq(expr_ref_vector const& ls, expr_ref_vector const& } bool theory_seq::is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, -expr*& x1, ptr_vector& xs, expr*& x2, expr*& y1, ptr_vector& ys, expr*& y2) { +expr*& x1, expr_ref_vector& xs, expr*& x2, expr*& y1, expr_ref_vector& ys, expr*& y2) { if (ls.size() > 1 && is_var(ls[0]) && is_var(ls.back()) && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { unsigned l_start = 1; @@ -2533,7 +2533,7 @@ expr*& x1, ptr_vector& xs, expr*& x2, expr*& y1, ptr_vector& ys, exp } bool theory_seq::is_ternary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, -expr*& x, ptr_vector& xs, expr*& y1, ptr_vector& ys, expr*& y2, bool flag1) { +expr*& x, expr_ref_vector& xs, expr*& y1, expr_ref_vector& ys, expr*& y2, bool flag1) { if (ls.size() > 1 && (is_var(ls[0]) || flag1) && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { unsigned l_start = ls.size()-1; @@ -2571,7 +2571,7 @@ expr*& x, ptr_vector& xs, expr*& y1, ptr_vector& ys, expr*& y2, bool } bool theory_seq::is_ternary_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, -ptr_vector& xs, expr*& x, expr*& y1, ptr_vector& ys, expr*& y2, bool flag1) { + expr_ref_vector& xs, expr*& x, expr*& y1, expr_ref_vector& ys, expr*& y2, bool flag1) { if (ls.size() > 1 && (is_var(ls.back()) || flag1) && rs.size() > 1 && is_var(rs[0]) && is_var(rs.back())) { unsigned l_start = 0; diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 6be9b894f..577454621 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -402,10 +402,10 @@ namespace smt { bool branch_variable(eq const& e); bool branch_binary_variable(eq const& e); bool eq_unit(expr* const& l, expr* const &r) const; - unsigned_vector overlap(ptr_vector const& ls, ptr_vector const& rs); - unsigned_vector overlap2(ptr_vector const& ls, ptr_vector const& rs); - bool branch_ternary_variable_base(dependency* dep, unsigned_vector indexes, expr* x, ptr_vector xs, expr* y1, ptr_vector ys, expr* y2); - bool branch_ternary_variable_base2(dependency* dep, unsigned_vector indexes, ptr_vector xs, expr* x, expr* y1, ptr_vector ys, expr* y2); + unsigned_vector overlap(expr_ref_vector const& ls, expr_ref_vector const& rs); + unsigned_vector overlap2(expr_ref_vector const& ls, expr_ref_vector const& rs); + bool branch_ternary_variable_base(dependency* dep, unsigned_vector indexes, expr* const& x, expr_ref_vector const& xs, expr* const& y1, expr_ref_vector const& ys, expr* const& y2); + bool branch_ternary_variable_base2(dependency* dep, unsigned_vector indexes, expr_ref_vector const& xs, expr* const& x, expr* const& y1, expr_ref_vector const& ys, expr* const& y2); bool branch_ternary_variable(eq const& e, bool flag1 = false); bool branch_ternary_variable2(eq const& e, bool flag1 = false); bool branch_quat_variable(eq const& e); @@ -427,9 +427,9 @@ namespace smt { bool solve_unit_eq(expr* l, expr* r, dependency* dep); bool solve_unit_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); bool is_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, expr*& x, ptr_vector& xunits, ptr_vector& yunits, expr*& y); - bool is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr*& x1, ptr_vector& xs, expr*& x2, expr*& y1, ptr_vector& ys, expr*& y2); - bool is_ternary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr*& x, ptr_vector& xs, expr*& y1, ptr_vector& ys, expr*& y2, bool flag1); - bool is_ternary_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, ptr_vector& xs, expr*& x, expr*& y1, ptr_vector& ys, expr*& y2, bool flag1); + bool is_quat_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr*& x1, expr_ref_vector& xs, expr*& x2, expr*& y1, expr_ref_vector& ys, expr*& y2); + bool is_ternary_eq(expr_ref_vector const& ls, expr_ref_vector const& rs, expr*& x, expr_ref_vector& xs, expr*& y1, expr_ref_vector& ys, expr*& y2, bool flag1); + bool is_ternary_eq2(expr_ref_vector const& ls, expr_ref_vector const& rs, expr_ref_vector& xs, expr*& x, expr*& y1, expr_ref_vector& ys, expr*& y2, bool flag1); bool solve_binary_eq(expr_ref_vector const& l, expr_ref_vector const& r, dependency* dep); bool propagate_max_length(expr* l, expr* r, dependency* dep); @@ -441,7 +441,7 @@ namespace smt { expr_ref mk_empty(sort* s) { return expr_ref(m_util.str.mk_empty(s), m); } expr_ref mk_concat(unsigned n, expr*const* es) { return expr_ref(m_util.str.mk_concat(n, es), m); } expr_ref mk_concat(expr_ref_vector const& es, sort* s) { if (es.empty()) return mk_empty(s); return mk_concat(es.size(), es.c_ptr()); } - expr_ref mk_concat(expr_ref_vector const& es) { SASSERT(!es.empty()); return mk_concat(es.size(), es.c_ptr()); } + expr* mk_concat(expr_ref_vector const& es) { SASSERT(!es.empty()); return m_util.str.mk_concat(es.size(), es.c_ptr()); } expr_ref mk_concat(ptr_vector const& es) { SASSERT(!es.empty()); return mk_concat(es.size(), es.c_ptr()); } expr_ref mk_concat(expr* e1, expr* e2) { return expr_ref(m_util.str.mk_concat(e1, e2), m); } expr_ref mk_concat(expr* e1, expr* e2, expr* e3) { return expr_ref(m_util.str.mk_concat(e1, e2, e3), m); } From aacb7289bee17446663f0b5d2d0156399a1d7217 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Mon, 25 Jun 2018 19:44:46 +0800 Subject: [PATCH 15/17] merge with Z3Prover/master --- .travis.yml | 2 +- CMakeLists.txt | 78 +- README-CMake.md | 4 +- README.md | 9 +- RELEASE_NOTES | 78 +- cmake/Z3Config.cmake.in | 2 +- cmake/modules/FindDotNetToolchain.cmake | 7 +- configure | 2 +- contrib/cmake/src/test/lp/CMakeLists.txt | 6 + contrib/suppressions/sanitizers/README.md | 2 +- doc/mk_api_doc.py | 8 +- examples/c++/example.cpp | 20 +- examples/c/test_capi.c | 80 +- examples/dotnet/Program.cs | 21 +- examples/java/JavaExample.java | 16 +- examples/maxsat/README | 2 +- examples/python/all_interval_series.py | 2 + examples/python/parallel.py | 36 + examples/tptp/tptp5.cpp | 16 +- scripts/mk_consts_files.py | 2 +- scripts/mk_project.py | 27 +- scripts/mk_util.py | 38 +- scripts/update_api.py | 20 +- scripts/vsts-mac.sh | 18 + scripts/vsts-vs2013.cmd | 49 + scripts/vsts-vs2017.cmd | 51 + src/CMakeLists.txt | 15 +- .../ackermannize_bv_tactic.cpp | 30 +- src/ackermannization/ackr_bound_probe.cpp | 4 +- src/ackermannization/ackr_info.h | 2 +- src/ackermannization/ackr_model_converter.cpp | 20 +- src/ackermannization/lackr.cpp | 8 +- .../lackr_model_constructor.cpp | 14 +- .../lackr_model_converter_lazy.cpp | 16 +- src/api/CMakeLists.txt | 2 - src/api/api_algebraic.cpp | 52 +- src/api/api_arith.cpp | 36 +- src/api/api_array.cpp | 50 +- src/api/api_ast.cpp | 118 +- src/api/api_ast_map.cpp | 14 +- src/api/api_ast_map.h | 2 +- src/api/api_ast_vector.cpp | 12 +- src/api/api_ast_vector.h | 2 +- src/api/api_bv.cpp | 30 +- src/api/api_config_params.cpp | 2 +- src/api/api_context.cpp | 29 +- src/api/api_context.h | 16 +- src/api/api_datalog.cpp | 76 +- src/api/api_datalog.h | 4 +- src/api/api_datalog_spacer.inc | 8 +- src/api/api_datatype.cpp | 84 +- src/api/api_fpa.cpp | 258 +- src/api/api_goal.cpp | 39 +- src/api/api_goal.h | 4 +- src/api/api_interp.cpp | 728 --- src/api/api_log.cpp | 16 +- src/api/api_model.cpp | 106 +- src/api/api_model.h | 10 +- src/api/api_numeral.cpp | 46 +- src/api/api_opt.cpp | 42 +- src/api/api_params.cpp | 12 +- src/api/api_parsers.cpp | 66 +- src/api/api_pb.cpp | 10 +- src/api/api_polynomial.cpp | 4 +- src/api/api_qe.cpp | 23 +- src/api/api_quant.cpp | 54 +- src/api/api_rcf.cpp | 24 +- src/api/api_seq.cpp | 12 +- src/api/api_solver.cpp | 156 +- src/api/api_solver.h | 4 +- src/api/api_stats.cpp | 2 +- src/api/api_stats.h | 2 +- src/api/api_tactic.cpp | 115 +- src/api/api_tactic.h | 11 +- src/api/api_util.h | 8 +- src/api/c++/z3++.h | 314 +- src/api/dotnet/AlgebraicNum.cs | 4 +- src/api/dotnet/ApplyResult.cs | 13 - src/api/dotnet/BitVecNum.cs | 4 +- src/api/dotnet/CMakeLists.txt | 1 - src/api/dotnet/Context.cs | 32 +- src/api/dotnet/Expr.cs | 60 +- src/api/dotnet/Goal.cs | 24 + src/api/dotnet/InterpolationContext.cs | 164 - src/api/dotnet/Model.cs | 2 +- src/api/dotnet/Params.cs | 4 +- src/api/dotnet/Solver.cs | 68 +- src/api/dotnet/Statistics.cs | 2 +- src/api/java/ApplyResult.java | 13 - src/api/java/CMakeLists.txt | 1 - src/api/java/Context.java | 36 +- src/api/java/Expr.java | 65 +- src/api/java/Goal.java | 15 + src/api/java/InterpolationContext.java | 216 - src/api/java/Model.java | 2 +- src/api/java/Solver.java | 33 +- src/api/java/Statistics.java | 2 +- src/api/ml/z3.ml | 52 +- src/api/ml/z3.mli | 112 +- src/api/python/z3/z3.py | 565 +- src/api/python/z3/z3printer.py | 29 +- src/api/z3.h | 5 +- src/api/z3_algebraic.h | 2 +- src/api/z3_api.h | 240 +- src/api/z3_fixedpoint.h | 14 +- src/api/z3_fpa.h | 16 +- src/api/z3_interp.h | 284 - src/api/z3_logger.h | 4 +- src/api/z3_replayer.cpp | 50 +- src/api/z3_replayer.h | 9 +- src/ast/CMakeLists.txt | 1 - src/ast/act_cache.cpp | 4 +- src/ast/arith_decl_plugin.cpp | 145 +- src/ast/arith_decl_plugin.h | 45 +- src/ast/array_decl_plugin.cpp | 82 +- src/ast/array_decl_plugin.h | 24 +- src/ast/ast.cpp | 392 +- src/ast/ast.h | 284 +- src/ast/ast_ll_pp.cpp | 4 +- src/ast/ast_pp.h | 4 +- src/ast/ast_pp_dot.cpp | 3 +- src/ast/ast_pp_dot.h | 11 +- src/ast/ast_pp_util.cpp | 24 +- src/ast/ast_pp_util.h | 10 +- src/ast/ast_printer.cpp | 22 +- src/ast/ast_printer.h | 2 +- src/ast/ast_smt2_pp.cpp | 135 +- src/ast/ast_smt2_pp.h | 34 +- src/ast/ast_smt_pp.cpp | 84 +- src/ast/ast_smt_pp.h | 14 +- src/ast/ast_trail.h | 2 +- src/ast/ast_translation.cpp | 56 +- src/ast/ast_translation.h | 38 +- src/ast/ast_util.cpp | 36 +- src/ast/ast_util.h | 5 +- src/ast/bv_decl_plugin.cpp | 52 +- src/ast/bv_decl_plugin.h | 36 +- src/ast/datatype_decl_plugin.cpp | 67 +- src/ast/datatype_decl_plugin.h | 67 +- src/ast/decl_collector.cpp | 106 +- src/ast/decl_collector.h | 14 +- src/ast/dl_decl_plugin.cpp | 164 +- src/ast/dl_decl_plugin.h | 34 +- src/ast/expr2polynomial.cpp | 10 +- src/ast/expr2polynomial.h | 6 +- src/ast/expr2var.cpp | 10 +- src/ast/expr_abstract.cpp | 2 +- src/ast/expr_delta_pair.h | 2 +- src/ast/expr_functors.cpp | 10 +- src/ast/expr_functors.h | 4 +- src/ast/expr_map.cpp | 6 +- src/ast/expr_substitution.cpp | 6 +- src/ast/expr_substitution.h | 6 +- src/ast/for_each_expr.h | 14 + src/ast/format.cpp | 28 +- src/ast/fpa/bv2fpa_converter.cpp | 33 +- src/ast/fpa/bv2fpa_converter.h | 2 +- src/ast/fpa/fpa2bv_converter.cpp | 124 +- src/ast/fpa/fpa2bv_converter.h | 3 +- src/ast/fpa/fpa2bv_rewriter.cpp | 4 +- src/ast/fpa_decl_plugin.cpp | 26 +- src/ast/fpa_decl_plugin.h | 32 +- src/ast/func_decl_dependencies.cpp | 4 +- src/ast/func_decl_dependencies.h | 2 +- src/ast/macro_substitution.cpp | 10 +- src/ast/macro_substitution.h | 2 +- src/ast/macros/macro_finder.cpp | 18 +- src/ast/macros/macro_manager.cpp | 21 +- src/ast/macros/macro_manager.h | 4 +- src/ast/macros/macro_util.cpp | 41 +- src/ast/macros/macro_util.h | 2 +- src/ast/macros/quasi_macros.cpp | 16 +- src/ast/normal_forms/defined_names.cpp | 4 +- src/ast/normal_forms/name_exprs.cpp | 22 +- src/ast/normal_forms/nnf.cpp | 31 +- src/ast/normal_forms/pull_quant.cpp | 12 +- src/ast/pattern/expr_pattern_match.cpp | 4 +- src/ast/pattern/expr_pattern_match.h | 4 +- src/ast/pattern/pattern_inference.cpp | 24 +- src/ast/pattern/pattern_inference.h | 2 +- src/ast/pb_decl_plugin.cpp | 24 +- src/ast/pb_decl_plugin.h | 17 +- src/ast/proofs/proof_checker.cpp | 267 +- src/ast/proofs/proof_checker.h | 72 +- src/ast/proofs/proof_utils.cpp | 20 +- src/ast/proofs/proof_utils.h | 6 +- src/ast/recurse_expr_def.h | 2 +- src/ast/rewriter/CMakeLists.txt | 2 +- src/ast/rewriter/arith_rewriter.cpp | 108 +- src/ast/rewriter/arith_rewriter.h | 4 + src/ast/rewriter/bit2int.cpp | 2 +- src/ast/rewriter/bit2int.h | 2 +- .../bit_blaster/bit_blaster_rewriter.cpp | 44 +- .../bit_blaster/bit_blaster_rewriter.h | 6 +- .../bit_blaster/bit_blaster_tpl_def.h | 1 - src/ast/rewriter/bool_rewriter.cpp | 7 +- src/ast/rewriter/bool_rewriter.h | 4 +- src/ast/rewriter/bv_bounds.cpp | 22 +- src/ast/rewriter/bv_bounds.h | 18 +- src/ast/rewriter/bv_elim.cpp | 2 +- src/ast/rewriter/bv_rewriter.cpp | 44 +- src/ast/rewriter/bv_trailing.cpp | 22 +- src/ast/rewriter/datatype_rewriter.cpp | 3 + src/ast/rewriter/der.cpp | 25 +- src/ast/rewriter/distribute_forall.h | 2 +- src/ast/rewriter/dl_rewriter.cpp | 2 +- src/ast/rewriter/elim_bounds.cpp | 22 +- src/ast/rewriter/elim_bounds.h | 2 +- src/ast/rewriter/enum2bv_rewriter.cpp | 4 +- src/ast/rewriter/expr_replacer.cpp | 34 +- src/ast/rewriter/expr_safe_replace.cpp | 2 +- src/ast/{ => rewriter}/factor_equivs.cpp | 60 +- src/ast/{ => rewriter}/factor_equivs.h | 7 + src/ast/rewriter/factor_rewriter.h | 2 +- src/ast/rewriter/fpa_rewriter.cpp | 8 +- src/ast/rewriter/inj_axiom.cpp | 8 +- src/ast/rewriter/maximize_ac_sharing.cpp | 4 +- src/ast/rewriter/maximize_ac_sharing.h | 6 +- src/ast/rewriter/mk_extract_proc.cpp | 4 +- src/ast/rewriter/mk_simplified_app.cpp | 2 +- src/ast/rewriter/pb2bv_rewriter.cpp | 640 ++- src/ast/rewriter/pb2bv_rewriter.h | 3 +- src/ast/rewriter/pb_rewriter.cpp | 66 +- src/ast/rewriter/pb_rewriter_def.h | 16 +- src/ast/rewriter/poly_rewriter.h | 2 +- src/ast/rewriter/poly_rewriter_def.h | 25 +- src/ast/rewriter/pull_ite_tree.cpp | 221 - src/ast/rewriter/pull_ite_tree.h | 113 - src/ast/rewriter/push_app_ite.cpp | 2 +- src/ast/rewriter/push_app_ite.h | 2 +- src/ast/rewriter/quant_hoist.cpp | 2 +- src/ast/rewriter/rewriter.cpp | 10 +- src/ast/rewriter/rewriter.h | 8 +- src/ast/rewriter/rewriter_def.h | 107 +- src/ast/rewriter/seq_rewriter.cpp | 93 +- src/ast/rewriter/seq_rewriter.h | 6 +- src/ast/rewriter/th_rewriter.cpp | 30 +- src/ast/rewriter/var_subst.cpp | 8 +- src/ast/seq_decl_plugin.cpp | 61 +- src/ast/seq_decl_plugin.h | 43 +- src/ast/static_features.cpp | 14 +- src/ast/static_features.h | 1 + src/ast/substitution/expr_offset.h | 2 +- src/ast/substitution/substitution.cpp | 12 +- src/ast/substitution/substitution.h | 2 +- src/ast/substitution/substitution_tree.cpp | 18 +- src/ast/substitution/substitution_tree.h | 2 +- src/cmd_context/CMakeLists.txt | 2 - src/cmd_context/basic_cmds.cpp | 167 +- src/cmd_context/check_logic.cpp | 10 +- src/cmd_context/cmd_context.cpp | 252 +- src/cmd_context/cmd_context.h | 80 +- src/cmd_context/cmd_context_to_goal.cpp | 4 +- src/cmd_context/context_params.cpp | 6 +- src/cmd_context/context_params.h | 5 +- src/cmd_context/echo_tactic.cpp | 20 +- src/cmd_context/eval_cmd.cpp | 26 +- src/cmd_context/extra_cmds/dbg_cmds.cpp | 252 +- .../extra_cmds/polynomial_cmds.cpp | 48 +- src/cmd_context/interpolant_cmds.cpp | 263 - src/cmd_context/interpolant_cmds.h | 24 - src/cmd_context/parametric_cmd.cpp | 2 +- src/cmd_context/parametric_cmd.h | 30 +- src/cmd_context/pdecl.cpp | 129 +- src/cmd_context/pdecl.h | 84 +- src/cmd_context/simplify_cmd.cpp | 26 +- src/cmd_context/tactic_cmds.cpp | 73 +- src/cmd_context/tactic_manager.cpp | 4 +- src/duality/CMakeLists.txt | 11 - src/duality/duality.h | 1364 ----- src/duality/duality_profiling.cpp | 144 - src/duality/duality_profiling.h | 38 - src/duality/duality_rpfp.cpp | 4233 --------------- src/duality/duality_solver.cpp | 3600 ------------- src/duality/duality_wrapper.cpp | 744 --- src/duality/duality_wrapper.h | 1489 ------ src/interp/CMakeLists.txt | 18 - src/interp/interp_params.pyg | 6 - src/interp/iz3base.cpp | 372 -- src/interp/iz3base.h | 195 - src/interp/iz3checker.cpp | 227 - src/interp/iz3checker.h | 49 - src/interp/iz3exception.h | 28 - src/interp/iz3hash.h | 479 -- src/interp/iz3interp.cpp | 573 -- src/interp/iz3interp.h | 123 - src/interp/iz3mgr.cpp | 969 ---- src/interp/iz3mgr.h | 738 --- src/interp/iz3pp.cpp | 185 - src/interp/iz3pp.h | 36 - src/interp/iz3profiling.cpp | 153 - src/interp/iz3profiling.h | 37 - src/interp/iz3proof.cpp | 628 --- src/interp/iz3proof.h | 274 - src/interp/iz3proof_itp.cpp | 3117 ----------- src/interp/iz3proof_itp.h | 143 - src/interp/iz3scopes.cpp | 321 -- src/interp/iz3scopes.h | 222 - src/interp/iz3secondary.h | 40 - src/interp/iz3translate.cpp | 2194 -------- src/interp/iz3translate.h | 62 - src/interp/iz3translate_direct.cpp | 1717 ------ src/math/automata/automaton.h | 8 +- src/math/automata/boolean_algebra.h | 2 +- src/math/automata/symbolic_automata_def.h | 2 +- src/math/euclid/euclidean_solver.cpp | 8 +- src/math/grobner/grobner.cpp | 22 +- src/math/grobner/grobner.h | 10 +- src/math/hilbert/heap_trie.h | 24 +- src/math/hilbert/hilbert_basis.cpp | 6 +- src/math/hilbert/hilbert_basis.h | 2 +- src/math/polynomial/algebraic_numbers.cpp | 52 +- src/math/polynomial/algebraic_numbers.h | 4 +- src/math/polynomial/polynomial.cpp | 177 +- src/math/polynomial/polynomial.h | 49 +- src/math/polynomial/polynomial_cache.cpp | 4 +- src/math/polynomial/polynomial_var2value.h | 6 +- src/math/polynomial/rpolynomial.cpp | 54 +- src/math/polynomial/rpolynomial.h | 2 +- src/math/polynomial/upolynomial.cpp | 20 +- src/math/polynomial/upolynomial.h | 6 +- .../polynomial/upolynomial_factorization.cpp | 34 +- .../polynomial/upolynomial_factorization.h | 12 +- .../upolynomial_factorization_int.h | 2 +- src/math/realclosure/mpz_matrix.cpp | 8 +- src/math/realclosure/mpz_matrix.h | 2 +- src/math/realclosure/realclosure.cpp | 138 +- src/math/realclosure/realclosure.h | 4 +- src/math/simplex/model_based_opt.cpp | 471 +- src/math/simplex/model_based_opt.h | 45 +- src/math/simplex/simplex_def.h | 4 +- src/math/simplex/sparse_matrix_def.h | 2 +- src/math/subpaving/subpaving.cpp | 80 +- src/math/subpaving/subpaving.h | 10 +- src/math/subpaving/subpaving_t.h | 36 +- src/math/subpaving/subpaving_t_def.h | 152 +- src/math/subpaving/tactic/expr2subpaving.cpp | 6 +- src/math/subpaving/tactic/expr2subpaving.h | 2 +- .../subpaving/tactic/subpaving_tactic.cpp | 32 +- src/model/func_interp.cpp | 29 +- src/model/func_interp.h | 2 +- src/model/model.cpp | 105 +- src/model/model.h | 60 +- src/model/model_core.cpp | 10 +- src/model/model_core.h | 5 +- src/model/model_evaluator.cpp | 110 +- src/model/model_evaluator.h | 17 +- src/model/model_evaluator_params.pyg | 3 +- src/model/model_implicant.cpp | 29 +- src/model/model_pp.cpp | 6 +- src/model/model_smt2_pp.cpp | 5 + src/muz/base/CMakeLists.txt | 2 +- src/muz/base/dl_boogie_proof.h | 2 +- src/muz/base/dl_context.cpp | 245 +- src/muz/base/dl_context.h | 98 +- src/muz/base/dl_costs.cpp | 6 +- src/muz/base/dl_costs.h | 6 +- src/muz/base/dl_engine_base.h | 16 +- src/muz/base/dl_rule.cpp | 22 +- src/muz/base/dl_rule.h | 6 +- src/muz/base/dl_rule_set.cpp | 10 +- src/muz/base/dl_rule_transformer.h | 2 +- src/muz/base/dl_util.cpp | 67 +- src/muz/base/dl_util.h | 18 +- .../{fixedpoint_params.pyg => fp_params.pyg} | 230 +- src/muz/base/hnf.cpp | 14 +- src/muz/base/rule_properties.cpp | 6 +- src/muz/bmc/dl_bmc_engine.cpp | 51 +- src/muz/bmc/dl_bmc_engine.h | 14 +- src/muz/clp/clp_context.cpp | 2 +- src/muz/clp/clp_context.h | 12 +- src/muz/ddnf/ddnf.cpp | 16 +- src/muz/ddnf/ddnf.h | 12 +- src/muz/duality/CMakeLists.txt | 8 - src/muz/duality/duality_dl_interface.cpp | 622 --- src/muz/duality/duality_dl_interface.h | 80 - src/muz/fp/CMakeLists.txt | 2 - src/muz/fp/datalog_parser.cpp | 92 +- src/muz/fp/dl_cmds.cpp | 172 +- src/muz/fp/dl_register_engine.cpp | 13 +- src/muz/fp/dl_register_engine.h | 4 +- src/muz/fp/horn_tactic.cpp | 104 +- src/muz/pdr/CMakeLists.txt | 20 - src/muz/pdr/pdr_closure.cpp | 177 - src/muz/pdr/pdr_closure.h | 67 - src/muz/pdr/pdr_context.cpp | 2421 --------- src/muz/pdr/pdr_context.h | 448 -- src/muz/pdr/pdr_dl_interface.cpp | 225 - src/muz/pdr/pdr_dl_interface.h | 78 - src/muz/pdr/pdr_farkas_learner.cpp | 1015 ---- src/muz/pdr/pdr_farkas_learner.h | 128 - src/muz/pdr/pdr_generalizers.cpp | 777 --- src/muz/pdr/pdr_generalizers.h | 110 - src/muz/pdr/pdr_manager.cpp | 321 -- src/muz/pdr/pdr_manager.h | 304 -- src/muz/pdr/pdr_prop_solver.cpp | 459 -- src/muz/pdr/pdr_prop_solver.h | 139 - src/muz/pdr/pdr_reachable_cache.cpp | 132 - src/muz/pdr/pdr_reachable_cache.h | 66 - src/muz/pdr/pdr_smt_context_manager.cpp | 167 - src/muz/pdr/pdr_smt_context_manager.h | 92 - src/muz/pdr/pdr_sym_mux.cpp | 601 --- src/muz/pdr/pdr_sym_mux.h | 247 - src/muz/pdr/pdr_util.cpp | 508 -- src/muz/pdr/pdr_util.h | 81 - src/muz/rel/aig_exporter.cpp | 6 +- src/muz/rel/aig_exporter.h | 4 +- src/muz/rel/check_relation.cpp | 70 +- src/muz/rel/check_relation.h | 76 +- src/muz/rel/dl_base.cpp | 12 +- src/muz/rel/dl_base.h | 80 +- src/muz/rel/dl_bound_relation.cpp | 48 +- src/muz/rel/dl_bound_relation.h | 76 +- src/muz/rel/dl_check_table.cpp | 52 +- src/muz/rel/dl_check_table.h | 74 +- src/muz/rel/dl_compiler.cpp | 6 +- src/muz/rel/dl_compiler.h | 8 +- src/muz/rel/dl_external_relation.cpp | 44 +- src/muz/rel/dl_external_relation.h | 58 +- src/muz/rel/dl_finite_product_relation.cpp | 88 +- src/muz/rel/dl_finite_product_relation.h | 76 +- src/muz/rel/dl_instruction.cpp | 134 +- src/muz/rel/dl_instruction.h | 14 +- src/muz/rel/dl_interval_relation.cpp | 62 +- src/muz/rel/dl_interval_relation.h | 64 +- src/muz/rel/dl_lazy_table.cpp | 44 +- src/muz/rel/dl_lazy_table.h | 122 +- src/muz/rel/dl_mk_explanations.cpp | 112 +- src/muz/rel/dl_mk_explanations.h | 2 +- src/muz/rel/dl_mk_similarity_compressor.cpp | 2 +- src/muz/rel/dl_mk_similarity_compressor.h | 2 +- src/muz/rel/dl_mk_simple_joins.cpp | 16 +- src/muz/rel/dl_mk_simple_joins.h | 2 +- src/muz/rel/dl_product_relation.cpp | 68 +- src/muz/rel/dl_product_relation.h | 62 +- src/muz/rel/dl_relation_manager.cpp | 228 +- src/muz/rel/dl_relation_manager.h | 18 +- src/muz/rel/dl_sieve_relation.cpp | 82 +- src/muz/rel/dl_sieve_relation.h | 66 +- src/muz/rel/dl_sparse_table.cpp | 64 +- src/muz/rel/dl_sparse_table.h | 126 +- src/muz/rel/dl_table.cpp | 22 +- src/muz/rel/dl_table.h | 36 +- src/muz/rel/dl_table_relation.cpp | 54 +- src/muz/rel/dl_table_relation.h | 80 +- src/muz/rel/dl_vector_relation.h | 8 +- src/muz/rel/doc.cpp | 6 +- src/muz/rel/doc.h | 10 +- src/muz/rel/karr_relation.cpp | 58 +- src/muz/rel/karr_relation.h | 34 +- src/muz/rel/rel_context.cpp | 6 +- src/muz/rel/rel_context.h | 58 +- src/muz/rel/tbv.cpp | 6 +- src/muz/rel/tbv.h | 10 +- src/muz/rel/udoc_relation.cpp | 58 +- src/muz/rel/udoc_relation.h | 84 +- src/muz/spacer/CMakeLists.txt | 13 +- src/muz/spacer/spacer_antiunify.cpp | 311 +- src/muz/spacer/spacer_antiunify.h | 45 +- src/muz/spacer/spacer_callback.cpp | 38 + src/muz/spacer/spacer_callback.h | 65 + src/muz/spacer/spacer_context.cpp | 4621 +++++++++-------- src/muz/spacer/spacer_context.h | 698 ++- src/muz/spacer/spacer_dl_interface.cpp | 31 +- src/muz/spacer/spacer_dl_interface.h | 41 +- src/muz/spacer/spacer_farkas_learner.h | 6 - src/muz/spacer/spacer_generalizers.cpp | 189 +- src/muz/spacer/spacer_generalizers.h | 76 +- src/muz/spacer/spacer_itp_solver.cpp | 355 -- src/muz/spacer/spacer_itp_solver.h | 177 - src/muz/spacer/spacer_iuc_proof.cpp | 280 + src/muz/spacer/spacer_iuc_proof.h | 67 + src/muz/spacer/spacer_iuc_solver.cpp | 469 ++ src/muz/spacer/spacer_iuc_solver.h | 181 + src/muz/spacer/spacer_json.cpp | 191 + src/muz/spacer/spacer_json.h | 61 + src/muz/spacer/spacer_legacy_frames.cpp | 7 +- src/muz/spacer/spacer_legacy_mbp.cpp | 7 +- src/muz/spacer/spacer_legacy_mev.cpp | 24 +- src/muz/spacer/spacer_manager.cpp | 209 +- src/muz/spacer/spacer_manager.h | 277 +- src/muz/spacer/spacer_matrix.cpp | 4 +- src/muz/spacer/spacer_matrix.h | 6 +- src/muz/spacer/spacer_mbc.cpp | 102 + src/muz/spacer/spacer_mbc.h | 45 + src/muz/spacer/spacer_pdr.cpp | 375 ++ src/muz/spacer/spacer_pdr.h | 107 + src/muz/spacer/spacer_proof_utils.cpp | 834 +++ src/muz/spacer/spacer_proof_utils.h | 105 + src/muz/spacer/spacer_prop_solver.cpp | 185 +- src/muz/spacer/spacer_prop_solver.h | 53 +- src/muz/spacer/spacer_qe_project.cpp | 80 +- src/muz/spacer/spacer_qe_project.h | 2 +- src/muz/spacer/spacer_quant_generalizer.cpp | 671 +++ src/muz/spacer/spacer_sat_answer.cpp | 181 + src/muz/spacer/spacer_sat_answer.h | 55 + src/muz/spacer/spacer_sem_matcher.cpp | 147 + src/muz/spacer/spacer_sem_matcher.h | 69 + src/muz/spacer/spacer_smt_context_manager.cpp | 79 - src/muz/spacer/spacer_smt_context_manager.h | 68 - src/muz/spacer/spacer_sym_mux.cpp | 596 +-- src/muz/spacer/spacer_sym_mux.h | 238 +- src/muz/spacer/spacer_unsat_core_learner.cpp | 331 +- src/muz/spacer/spacer_unsat_core_learner.h | 69 +- src/muz/spacer/spacer_unsat_core_plugin.cpp | 782 ++- src/muz/spacer/spacer_unsat_core_plugin.h | 110 +- src/muz/spacer/spacer_util.cpp | 1531 ++---- src/muz/spacer/spacer_util.h | 197 +- src/muz/spacer/spacer_virtual_solver.cpp | 360 -- src/muz/spacer/spacer_virtual_solver.h | 154 - src/muz/tab/tab_context.cpp | 9 +- src/muz/tab/tab_context.h | 14 +- src/muz/transforms/dl_mk_array_blast.cpp | 8 +- src/muz/transforms/dl_mk_array_blast.h | 4 +- src/muz/transforms/dl_mk_array_eq_rewrite.cpp | 4 +- src/muz/transforms/dl_mk_array_eq_rewrite.h | 4 +- .../transforms/dl_mk_array_instantiation.cpp | 2 +- .../transforms/dl_mk_array_instantiation.h | 8 +- src/muz/transforms/dl_mk_backwards.h | 4 +- src/muz/transforms/dl_mk_bit_blast.cpp | 76 +- src/muz/transforms/dl_mk_bit_blast.h | 4 +- src/muz/transforms/dl_mk_coalesce.h | 2 +- src/muz/transforms/dl_mk_coi_filter.cpp | 23 +- src/muz/transforms/dl_mk_coi_filter.h | 2 +- src/muz/transforms/dl_mk_filter_rules.cpp | 6 +- src/muz/transforms/dl_mk_filter_rules.h | 4 +- .../dl_mk_interp_tail_simplifier.cpp | 55 +- .../transforms/dl_mk_interp_tail_simplifier.h | 8 +- src/muz/transforms/dl_mk_karr_invariants.cpp | 26 +- src/muz/transforms/dl_mk_karr_invariants.h | 4 +- src/muz/transforms/dl_mk_loop_counter.cpp | 2 +- src/muz/transforms/dl_mk_loop_counter.h | 4 +- src/muz/transforms/dl_mk_magic_sets.cpp | 16 +- src/muz/transforms/dl_mk_magic_sets.h | 2 +- src/muz/transforms/dl_mk_magic_symbolic.cpp | 4 +- src/muz/transforms/dl_mk_magic_symbolic.h | 4 +- .../dl_mk_quantifier_abstraction.cpp | 81 +- .../transforms/dl_mk_quantifier_abstraction.h | 4 +- .../dl_mk_quantifier_instantiation.cpp | 14 +- .../dl_mk_quantifier_instantiation.h | 4 +- src/muz/transforms/dl_mk_rule_inliner.cpp | 216 +- src/muz/transforms/dl_mk_rule_inliner.h | 10 +- src/muz/transforms/dl_mk_scale.cpp | 43 +- src/muz/transforms/dl_mk_scale.h | 6 +- .../dl_mk_separate_negated_tails.cpp | 2 +- .../transforms/dl_mk_separate_negated_tails.h | 2 +- src/muz/transforms/dl_mk_slice.cpp | 39 +- src/muz/transforms/dl_mk_slice.h | 4 +- .../transforms/dl_mk_subsumption_checker.cpp | 37 +- .../transforms/dl_mk_subsumption_checker.h | 4 +- .../transforms/dl_mk_unbound_compressor.cpp | 4 +- src/muz/transforms/dl_mk_unbound_compressor.h | 2 +- src/muz/transforms/dl_mk_unfold.h | 2 +- src/muz/transforms/dl_transforms.cpp | 20 +- src/nlsat/nlsat_assignment.h | 12 +- src/nlsat/nlsat_clause.h | 2 + src/nlsat/nlsat_explain.cpp | 105 +- src/nlsat/nlsat_interval_set.cpp | 22 +- src/nlsat/nlsat_interval_set.h | 4 +- src/nlsat/nlsat_justification.h | 4 +- src/nlsat/nlsat_params.pyg | 1 + src/nlsat/nlsat_scoped_literal_vector.h | 7 +- src/nlsat/nlsat_solver.cpp | 783 ++- src/nlsat/nlsat_solver.h | 14 +- src/nlsat/nlsat_types.h | 7 +- src/nlsat/tactic/goal2nlsat.cpp | 9 +- src/nlsat/tactic/nlsat_tactic.cpp | 60 +- src/nlsat/tactic/qfnra_nlsat_tactic.cpp | 4 + src/opt/CMakeLists.txt | 1 - src/opt/maxres.cpp | 278 +- src/opt/maxsmt.cpp | 138 +- src/opt/maxsmt.h | 75 +- src/opt/mss.cpp | 285 - src/opt/mss.h | 57 - src/opt/opt_cmds.cpp | 8 +- src/opt/opt_cmds.h | 2 +- src/opt/opt_context.cpp | 314 +- src/opt/opt_context.h | 113 +- src/opt/opt_params.pyg | 3 +- src/opt/opt_pareto.cpp | 15 +- src/opt/opt_pareto.h | 8 +- src/opt/opt_parse.cpp | 589 ++- src/opt/opt_parse.h | 2 + src/opt/opt_solver.cpp | 24 +- src/opt/opt_solver.h | 53 +- src/opt/optsmt.cpp | 22 +- src/opt/optsmt.h | 2 +- src/opt/pb_sls.cpp | 10 +- src/opt/sortmax.cpp | 44 +- src/opt/wmax.cpp | 25 +- src/parsers/smt2/marshal.cpp | 2 +- src/parsers/smt2/smt2parser.cpp | 173 +- src/parsers/smt2/smt2parser.h | 2 +- src/parsers/util/cost_parser.cpp | 2 +- src/parsers/util/cost_parser.h | 6 +- src/parsers/util/pattern_validation.cpp | 2 +- src/parsers/util/simple_parser.cpp | 6 +- src/qe/CMakeLists.txt | 3 + src/qe/nlarith_util.cpp | 48 +- src/qe/nlqsat.cpp | 85 +- src/qe/qe.cpp | 179 +- src/qe/qe.h | 8 +- src/qe/qe_arith.cpp | 163 +- src/qe/qe_arith.h | 18 +- src/qe/qe_arith_plugin.cpp | 101 +- src/qe/qe_array_plugin.cpp | 12 +- src/qe/qe_arrays.cpp | 1406 ++++- src/qe/qe_arrays.h | 10 +- src/qe/qe_bool_plugin.cpp | 40 +- src/qe/qe_bv_plugin.cpp | 16 +- src/qe/qe_cmd.cpp | 20 +- src/qe/qe_datatype_plugin.cpp | 48 +- src/qe/qe_datatypes.cpp | 10 +- src/qe/qe_datatypes.h | 9 +- src/qe/qe_dl_plugin.cpp | 24 +- src/qe/qe_lite.cpp | 319 +- src/qe/qe_lite.h | 1 - src/qe/qe_mbi.cpp | 545 ++ src/qe/qe_mbi.h | 135 + src/qe/qe_mbp.cpp | 341 +- src/qe/qe_mbp.h | 49 +- src/qe/qe_sat_tactic.cpp | 70 +- src/qe/qe_solve_plugin.cpp | 401 ++ src/qe/qe_solve_plugin.h | 54 + src/qe/qe_tactic.cpp | 32 +- src/qe/qe_term_graph.cpp | 873 ++++ src/qe/qe_term_graph.h | 119 + src/qe/qe_vartest.h | 7 +- src/qe/qsat.cpp | 70 +- src/qe/qsat.h | 6 +- src/sat/CMakeLists.txt | 10 +- src/sat/ba_solver.cpp | 4218 +++++++++++++++ src/sat/ba_solver.h | 531 ++ src/sat/dimacs.cpp | 9 +- src/sat/sat_allocator.h | 103 + src/sat/sat_asymm_branch.cpp | 492 +- src/sat/sat_asymm_branch.h | 70 +- src/sat/sat_asymm_branch_params.pyg | 7 +- src/sat/sat_bdd.cpp | 894 ++++ src/sat/sat_bdd.h | 260 + src/sat/sat_big.cpp | 289 ++ src/sat/sat_big.h | 88 + src/sat/sat_clause.cpp | 150 +- src/sat/sat_clause.h | 52 +- src/sat/sat_clause_use_list.cpp | 16 +- src/sat/sat_clause_use_list.h | 66 +- src/sat/sat_cleaner.cpp | 27 +- src/sat/sat_config.cpp | 179 +- src/sat/sat_config.h | 106 +- src/sat/sat_drat.cpp | 502 ++ src/sat/sat_drat.h | 101 + src/sat/sat_elim_eqs.cpp | 133 +- src/sat/sat_elim_eqs.h | 7 + src/sat/sat_elim_vars.cpp | 336 ++ src/sat/sat_elim_vars.h | 74 + src/sat/sat_extension.h | 43 +- src/sat/sat_iff3_finder.cpp | 4 +- src/sat/sat_integrity_checker.cpp | 165 +- src/sat/sat_integrity_checker.h | 3 + src/sat/sat_justification.h | 9 +- src/sat/sat_local_search.cpp | 1092 ++++ src/sat/sat_local_search.h | 299 ++ src/sat/sat_lookahead.cpp | 2494 +++++++++ src/sat/sat_lookahead.h | 619 +++ src/sat/sat_model_converter.cpp | 306 +- src/sat/sat_model_converter.h | 92 +- src/sat/sat_parallel.cpp | 315 ++ src/sat/sat_parallel.h | 116 + src/sat/sat_params.pyg | 51 +- src/sat/sat_probing.cpp | 35 +- src/sat/sat_probing.h | 8 +- src/sat/sat_scc.cpp | 100 +- src/sat/sat_scc.h | 22 +- src/sat/sat_scc_params.pyg | 3 +- src/sat/sat_simplifier.cpp | 1478 ++++-- src/sat/sat_simplifier.h | 54 +- src/sat/sat_simplifier_params.pyg | 22 +- src/sat/sat_solver.cpp | 1942 ++++--- src/sat/sat_solver.h | 219 +- src/sat/sat_solver/inc_sat_solver.cpp | 466 +- src/sat/sat_solver/inc_sat_solver.h | 6 +- src/sat/sat_types.h | 86 +- src/sat/sat_unit_walk.cpp | 370 ++ src/sat/sat_unit_walk.h | 79 + src/sat/sat_var_queue.h | 13 + src/sat/sat_watched.cpp | 83 +- src/sat/sat_watched.h | 36 +- src/sat/tactic/CMakeLists.txt | 1 + src/sat/tactic/atom2bool_var.cpp | 23 +- src/sat/tactic/atom2bool_var.h | 1 + src/sat/tactic/goal2sat.cpp | 889 +++- src/sat/tactic/goal2sat.h | 33 +- src/sat/tactic/sat_tactic.cpp | 50 +- src/shell/datalog_frontend.cpp | 8 +- src/shell/dimacs_frontend.cpp | 44 +- src/shell/lp_frontend.cpp | 12 +- src/shell/main.cpp | 32 +- src/shell/opt_frontend.cpp | 96 +- src/shell/opt_frontend.h | 4 +- src/shell/smtlib_frontend.cpp | 4 +- src/smt/CMakeLists.txt | 1 + src/smt/arith_eq_adapter.cpp | 10 +- src/smt/arith_eq_adapter.h | 2 +- src/smt/asserted_formulas.cpp | 76 +- src/smt/asserted_formulas.h | 76 +- src/smt/cached_var_subst.cpp | 4 +- src/smt/cost_evaluator.cpp | 3 +- src/smt/dyn_ack.cpp | 18 +- src/smt/expr_context_simplifier.cpp | 28 +- src/smt/fingerprints.cpp | 6 +- src/smt/mam.cpp | 226 +- src/smt/old_interval.cpp | 58 +- src/smt/old_interval.h | 2 +- src/smt/params/preprocessor_params.cpp | 3 +- src/smt/params/preprocessor_params.h | 6 +- src/smt/params/qi_params.cpp | 2 +- src/smt/params/qi_params.h | 3 +- src/smt/params/smt_params.cpp | 1 + src/smt/params/smt_params.h | 1 + src/smt/params/smt_params_helper.pyg | 14 +- src/smt/params/theory_arith_params.cpp | 3 + src/smt/params/theory_arith_params.h | 13 +- src/smt/params/theory_str_params.cpp | 6 + src/smt/params/theory_str_params.h | 45 +- src/smt/proto_model/array_factory.cpp | 12 +- src/smt/proto_model/array_factory.h | 8 +- src/smt/proto_model/datatype_factory.cpp | 14 +- src/smt/proto_model/datatype_factory.h | 6 +- src/smt/proto_model/numeral_factory.h | 10 +- src/smt/proto_model/proto_model.cpp | 26 +- src/smt/proto_model/proto_model.h | 14 +- src/smt/proto_model/struct_factory.cpp | 2 +- src/smt/proto_model/struct_factory.h | 6 +- src/smt/proto_model/value_factory.cpp | 14 +- src/smt/proto_model/value_factory.h | 44 +- src/smt/qi_queue.cpp | 2 +- src/smt/smt2_extra_cmds.cpp | 22 +- src/smt/smt_almost_cg_table.cpp | 6 +- src/smt/smt_almost_cg_table.h | 2 +- src/smt/smt_b_justification.h | 2 +- src/smt/smt_case_split_queue.cpp | 168 +- src/smt/smt_cg_table.h | 10 +- src/smt/smt_checker.cpp | 46 +- src/smt/smt_checker.h | 4 +- src/smt/smt_clause.cpp | 13 +- src/smt/smt_clause.h | 18 +- src/smt/smt_conflict_resolution.cpp | 63 +- src/smt/smt_conflict_resolution.h | 2 +- src/smt/smt_consequences.cpp | 9 +- src/smt/smt_context.cpp | 526 +- src/smt/smt_context.h | 74 +- src/smt/smt_context_inv.cpp | 83 +- src/smt/smt_context_pp.cpp | 113 +- src/smt/smt_enode.cpp | 12 +- src/smt/smt_enode.h | 50 +- src/smt/smt_eq_justification.h | 2 +- src/smt/smt_for_each_relevant_expr.h | 8 +- src/smt/smt_implied_equalities.cpp | 18 +- src/smt/smt_internalizer.cpp | 64 +- src/smt/smt_justification.cpp | 34 +- src/smt/smt_justification.h | 126 +- src/smt/smt_kernel.cpp | 31 +- src/smt/smt_kernel.h | 9 +- src/smt/smt_model_checker.cpp | 30 +- src/smt/smt_model_finder.cpp | 358 +- src/smt/smt_model_generator.cpp | 25 +- src/smt/smt_model_generator.h | 12 +- src/smt/smt_quantifier.cpp | 52 +- src/smt/smt_quantifier.h | 2 +- src/smt/smt_quantifier_stat.cpp | 12 +- src/smt/smt_quantifier_stat.h | 2 +- src/smt/smt_quick_checker.cpp | 18 +- src/smt/smt_quick_checker.h | 2 +- src/smt/smt_relevancy.cpp | 64 +- src/smt/smt_relevancy.h | 8 +- src/smt/smt_setup.cpp | 32 +- src/smt/smt_solver.cpp | 154 +- src/smt/smt_theory.cpp | 4 +- src/smt/smt_theory.h | 6 +- src/smt/smt_theory_var_list.h | 4 +- src/smt/smt_types.h | 1 + src/smt/tactic/ctx_solver_simplify_tactic.cpp | 29 +- src/smt/tactic/smt_tactic.cpp | 79 +- src/smt/tactic/smt_tactic.h | 5 + src/smt/tactic/unit_subsumption_tactic.cpp | 17 +- src/smt/theory_arith.h | 127 +- src/smt/theory_arith_aux.h | 24 +- src/smt/theory_arith_core.h | 563 +- src/smt/theory_arith_eq.h | 2 +- src/smt/theory_arith_int.h | 30 +- src/smt/theory_arith_nl.h | 76 +- src/smt/theory_arith_pp.h | 4 +- src/smt/theory_array.h | 36 +- src/smt/theory_array_base.cpp | 38 +- src/smt/theory_array_base.h | 20 +- src/smt/theory_array_full.cpp | 2 +- src/smt/theory_array_full.h | 36 +- src/smt/theory_bv.cpp | 26 +- src/smt/theory_bv.h | 54 +- src/smt/theory_datatype.cpp | 231 +- src/smt/theory_datatype.h | 82 +- src/smt/theory_dense_diff_logic.h | 64 +- src/smt/theory_dense_diff_logic_def.h | 4 +- src/smt/theory_diff_logic.h | 70 +- src/smt/theory_diff_logic_def.h | 16 +- src/smt/theory_dl.cpp | 42 +- src/smt/theory_dummy.h | 24 +- src/smt/theory_fpa.cpp | 14 +- src/smt/theory_fpa.h | 64 +- src/smt/theory_lra.cpp | 50 +- src/smt/theory_lra.h | 66 +- src/smt/theory_opt.h | 2 +- src/smt/theory_pb.cpp | 1914 ++++--- src/smt/theory_pb.h | 246 +- src/smt/theory_seq.cpp | 593 +-- src/smt/theory_seq.h | 93 +- src/smt/theory_seq_empty.h | 34 +- src/smt/theory_str.cpp | 2631 ++++++++-- src/smt/theory_str.h | 247 +- src/smt/theory_utvpi.h | 60 +- src/smt/theory_utvpi_def.h | 6 +- src/smt/theory_wmaxsat.cpp | 8 +- src/smt/theory_wmaxsat.h | 44 +- src/smt/watch_list.cpp | 2 +- src/smt/watch_list.h | 10 +- src/solver/CMakeLists.txt | 3 + src/solver/check_sat_result.cpp | 12 +- src/solver/check_sat_result.h | 35 +- src/solver/combined_solver.cpp | 73 +- src/solver/mus.cpp | 41 +- src/solver/mus.h | 2 - src/solver/parallel_params.pyg | 15 + src/solver/parallel_tactic.cpp | 753 +++ src/solver/parallel_tactic.h | 27 + src/solver/smt_logics.cpp | 11 +- src/solver/solver.cpp | 105 +- src/solver/solver.h | 77 +- src/solver/solver2tactic.cpp | 71 +- src/solver/solver2tactic.h | 4 +- src/solver/solver_na2as.cpp | 12 +- src/solver/solver_na2as.h | 28 +- src/solver/solver_pool.cpp | 242 +- src/solver/solver_pool.h | 17 +- src/solver/tactic2solver.cpp | 89 +- src/solver/tactic2solver.h | 2 +- src/tactic/CMakeLists.txt | 4 +- src/tactic/aig/aig.cpp | 46 +- src/tactic/aig/aig_tactic.cpp | 24 +- src/tactic/arith/CMakeLists.txt | 2 - src/tactic/arith/add_bounds_tactic.cpp | 29 +- src/tactic/arith/arith_bounds_tactic.cpp | 13 +- src/tactic/arith/bound_manager.cpp | 23 +- src/tactic/arith/bound_manager.h | 8 +- src/tactic/arith/bound_propagator.cpp | 14 +- src/tactic/arith/bv2int_rewriter.cpp | 6 +- src/tactic/arith/bv2int_rewriter.h | 2 +- src/tactic/arith/bv2real_rewriter.h | 4 +- src/tactic/arith/card2bv_tactic.cpp | 32 +- src/tactic/arith/card2bv_tactic.h | 26 +- src/tactic/arith/degree_shift_tactic.cpp | 102 +- src/tactic/arith/diff_neq_tactic.cpp | 35 +- src/tactic/arith/elim01_tactic.cpp | 267 - src/tactic/arith/elim01_tactic.h | 33 - src/tactic/arith/eq2bv_tactic.cpp | 42 +- src/tactic/arith/factor_tactic.cpp | 25 +- src/tactic/arith/fix_dl_var_tactic.cpp | 43 +- src/tactic/arith/fm_tactic.cpp | 82 +- src/tactic/arith/lia2card_tactic.cpp | 188 +- src/tactic/arith/lia2pb_tactic.cpp | 69 +- src/tactic/arith/linear_equation.cpp | 4 +- src/tactic/arith/nla2bv_tactic.cpp | 50 +- src/tactic/arith/normalize_bounds_tactic.cpp | 48 +- src/tactic/arith/pb2bv_model_converter.cpp | 69 +- src/tactic/arith/pb2bv_model_converter.h | 10 +- src/tactic/arith/pb2bv_tactic.cpp | 56 +- src/tactic/arith/probe_arith.cpp | 34 +- src/tactic/arith/propagate_ineqs_tactic.cpp | 29 +- src/tactic/arith/purify_arith_tactic.cpp | 55 +- src/tactic/arith/purify_arith_tactic.h | 2 +- src/tactic/arith/recover_01_tactic.cpp | 70 +- src/tactic/bv/bit_blaster_model_converter.cpp | 108 +- src/tactic/bv/bit_blaster_model_converter.h | 4 +- src/tactic/bv/bit_blaster_tactic.cpp | 48 +- src/tactic/bv/bv1_blaster_tactic.cpp | 35 +- src/tactic/bv/bv_bound_chk_tactic.cpp | 30 +- src/tactic/bv/bv_bounds_tactic.cpp | 48 +- src/tactic/bv/bv_size_reduction_tactic.cpp | 56 +- src/tactic/bv/bvarray2uf_rewriter.cpp | 21 +- src/tactic/bv/bvarray2uf_rewriter.h | 10 +- src/tactic/bv/bvarray2uf_tactic.cpp | 38 +- src/tactic/bv/dt2bv_tactic.cpp | 55 +- src/tactic/bv/elim_small_bv_tactic.cpp | 40 +- src/tactic/bv/max_bv_sharing_tactic.cpp | 37 +- src/tactic/converter.h | 56 +- src/tactic/core/blast_term_ite_tactic.cpp | 105 +- src/tactic/core/blast_term_ite_tactic.h | 2 +- src/tactic/core/cofactor_elim_term_ite.cpp | 29 +- src/tactic/core/cofactor_term_ite_tactic.cpp | 19 +- src/tactic/core/collect_statistics_tactic.cpp | 27 +- src/tactic/core/ctx_simplify_tactic.cpp | 40 +- src/tactic/core/ctx_simplify_tactic.h | 16 +- src/tactic/core/der_tactic.cpp | 14 +- src/tactic/core/distribute_forall_tactic.cpp | 17 +- src/tactic/core/dom_simplify_tactic.cpp | 32 +- src/tactic/core/dom_simplify_tactic.h | 33 +- src/tactic/core/elim_term_ite_tactic.cpp | 47 +- src/tactic/core/elim_uncnstr_tactic.cpp | 156 +- src/tactic/core/injectivity_tactic.cpp | 25 +- src/tactic/core/nnf_tactic.cpp | 33 +- src/tactic/core/occf_tactic.cpp | 66 +- src/tactic/core/occf_tactic.h | 2 +- src/tactic/core/pb_preprocess_tactic.cpp | 117 +- src/tactic/core/propagate_values_tactic.cpp | 50 +- src/tactic/core/reduce_args_tactic.cpp | 53 +- src/tactic/core/simplify_tactic.cpp | 6 +- src/tactic/core/simplify_tactic.h | 18 +- src/tactic/core/solve_eqs_tactic.cpp | 265 +- src/tactic/core/solve_eqs_tactic.h | 2 +- src/tactic/core/split_clause_tactic.cpp | 61 +- src/tactic/core/symmetry_reduce_tactic.cpp | 32 +- src/tactic/core/tseitin_cnf_tactic.cpp | 87 +- src/tactic/dependency_converter.cpp | 107 + src/tactic/dependency_converter.h | 47 + src/tactic/equiv_proof_converter.h | 9 +- src/tactic/extension_model_converter.cpp | 97 - src/tactic/extension_model_converter.h | 8 +- src/tactic/filter_model_converter.cpp | 70 - src/tactic/filter_model_converter.h | 12 +- src/tactic/fpa/CMakeLists.txt | 2 + src/tactic/fpa/fpa2bv_model_converter.h | 15 +- src/tactic/fpa/fpa2bv_tactic.cpp | 32 +- src/tactic/fpa/qffp_tactic.cpp | 8 +- src/tactic/fpa/qffplra_tactic.cpp | 72 + src/tactic/fpa/qffplra_tactic.h | 38 + src/tactic/generic_model_converter.cpp | 269 + src/tactic/generic_model_converter.h | 80 + src/tactic/goal.cpp | 58 +- src/tactic/goal.h | 28 +- src/tactic/horn_subsume_model_converter.cpp | 11 +- src/tactic/horn_subsume_model_converter.h | 15 +- src/tactic/model_converter.cpp | 202 +- src/tactic/model_converter.h | 97 +- src/tactic/nlsat_smt/CMakeLists.txt | 9 - src/tactic/nlsat_smt/nl_purify_tactic.cpp | 40 +- src/tactic/nlsat_smt/nl_purify_tactic.h | 35 - src/tactic/portfolio/CMakeLists.txt | 6 +- .../portfolio/bounded_int2bv_solver.cpp | 132 +- src/tactic/portfolio/default_tactic.cpp | 15 +- src/tactic/portfolio/enum2bv_solver.cpp | 129 +- src/tactic/portfolio/fd_solver.cpp | 16 +- src/tactic/portfolio/fd_solver.h | 10 +- src/tactic/portfolio/pb2bv_solver.cpp | 100 +- src/tactic/portfolio/smt_strategic_solver.cpp | 11 +- src/tactic/portfolio/solver2lookahead.cpp | 24 + src/tactic/portfolio/solver2lookahead.h | 26 + src/tactic/probe.cpp | 58 +- src/tactic/proof_converter.cpp | 111 +- src/tactic/proof_converter.h | 25 +- src/tactic/replace_proof_converter.cpp | 11 +- src/tactic/replace_proof_converter.h | 8 +- src/tactic/sine_filter.cpp | 143 +- src/tactic/sls/bvsls_opt_engine.cpp | 2 +- src/tactic/sls/bvsls_opt_engine.h | 2 +- src/tactic/sls/sls_engine.cpp | 39 +- src/tactic/sls/sls_engine.h | 4 +- src/tactic/sls/sls_tactic.cpp | 26 +- src/tactic/sls/sls_tracker.h | 12 +- src/tactic/smtlogics/CMakeLists.txt | 3 - src/tactic/smtlogics/nra_tactic.cpp | 6 +- src/tactic/smtlogics/qfbv_tactic.cpp | 6 +- src/tactic/smtlogics/qflia_tactic.cpp | 7 +- src/tactic/smtlogics/qfnia_tactic.cpp | 18 +- src/tactic/smtlogics/qfnra_tactic.cpp | 6 +- src/tactic/smtlogics/qfufbv_tactic.cpp | 39 +- src/tactic/smtlogics/qfufnra_tactic.cpp | 50 - src/tactic/smtlogics/qfufnra_tactic.h | 31 - src/tactic/tactic.cpp | 84 +- src/tactic/tactic.h | 30 +- src/tactic/tactic_exception.h | 6 +- src/tactic/tactical.cpp | 625 +-- src/tactic/tactical.h | 2 +- src/tactic/ufbv/macro_finder_tactic.cpp | 38 +- src/tactic/ufbv/quasi_macros_tactic.cpp | 38 +- src/tactic/ufbv/ufbv_rewriter.cpp | 32 +- src/tactic/ufbv/ufbv_rewriter.h | 4 +- src/tactic/ufbv/ufbv_rewriter_tactic.cpp | 29 +- src/test/CMakeLists.txt | 7 +- src/test/algebraic.cpp | 2 +- src/test/arith_rewriter.cpp | 5 - src/test/bdd.cpp | 83 + src/test/check_assumptions.cpp | 12 +- src/test/cnf_backbones.cpp | 6 +- src/test/cube_clause.cpp | 66 + src/test/ddnf.cpp | 2 +- src/test/dl_query.cpp | 6 +- src/test/escaped.cpp | 4 +- src/test/ex.cpp | 4 +- src/test/expr_rand.cpp | 4 +- src/test/fuzzing/expr_rand.cpp | 90 +- src/test/get_consequences.cpp | 12 +- src/test/get_implied_equalities.cpp | 6 +- src/test/heap.cpp | 22 +- src/test/hilbert_basis.cpp | 2 +- src/test/horn_subsume_model_converter.cpp | 10 +- src/test/list.cpp | 8 +- src/test/main.cpp | 76 +- src/test/model_based_opt.cpp | 57 +- src/test/mpff.cpp | 36 +- src/test/mpfx.cpp | 2 +- src/test/mpq.cpp | 2 +- src/test/mpz.cpp | 10 +- src/test/nlsat.cpp | 14 +- src/test/old_interval.cpp | 6 +- src/test/pb2bv.cpp | 17 +- src/test/pdr.cpp | 128 - src/test/polynorm.cpp | 2 +- src/test/prime_generator.cpp | 6 +- src/test/qe_arith.cpp | 3 +- src/test/quant_elim.cpp | 2 + src/test/rational.cpp | 8 +- src/test/sat_local_search.cpp | 132 + src/test/sat_lookahead.cpp | 53 + src/test/sat_user_scope.cpp | 4 +- src/test/smt2print_parse.cpp | 39 +- src/test/solver_pool.cpp | 41 + src/test/sorting_network.cpp | 159 +- src/test/theory_pb.cpp | 4 +- src/test/udoc_relation.cpp | 10 +- src/test/var_subst.cpp | 2 +- src/util/array.h | 6 +- src/util/bit_vector.h | 4 +- src/util/cancel_eh.h | 4 +- src/util/chashtable.h | 54 +- src/util/checked_int64.h | 24 +- src/util/cmd_context_types.h | 4 +- src/util/container_util.h | 2 +- src/util/cooperate.cpp | 2 +- src/util/debug.cpp | 8 +- src/util/dependency.h | 6 +- src/util/double_manager.h | 8 +- src/util/ema.h | 56 + src/util/env_params.cpp | 2 +- src/util/file_path.h | 8 +- src/util/gparams.cpp | 80 +- src/util/gparams.h | 7 +- src/util/hash.h | 4 +- src/util/hashtable.h | 79 +- src/util/heap.h | 8 +- src/util/hwf.cpp | 40 +- src/util/hwf.h | 8 +- src/util/inf_eps_rational.h | 4 +- src/util/inf_int_rational.h | 4 +- src/util/inf_rational.h | 4 +- src/util/inf_s_integer.h | 4 +- src/util/list.h | 4 +- src/util/lp/core_solver_pretty_printer.h | 2 +- src/util/lp/dense_matrix.h | 13 +- src/util/lp/disjoint_intervals.h | 334 ++ src/util/lp/eta_matrix.h | 20 +- src/util/lp/iterator_on_column.h | 10 +- src/util/lp/iterator_on_indexed_vector.h | 10 +- src/util/lp/iterator_on_pivot_row.h | 10 +- src/util/lp/iterator_on_row.h | 10 +- src/util/lp/iterator_on_term_with_basis_var.h | 10 +- src/util/lp/lar_constraints.h | 15 +- src/util/lp/lar_core_solver.h | 2 +- src/util/lp/lar_solver.h | 2 +- src/util/lp/lar_solver.hpp | 2089 ++++++++ src/util/lp/lp_dual_core_solver.h | 2 +- src/util/lp/lp_dual_simplex.h | 8 +- src/util/lp/lp_primal_core_solver.h | 2 +- src/util/lp/lp_primal_simplex.h | 8 +- src/util/lp/lp_settings.h | 2 +- src/util/lp/lp_solver_instances.cpp | 2 + src/util/lp/lu.h | 20 +- src/util/lp/mps_reader.h | 18 +- src/util/lp/numeric_pair.h | 8 +- src/util/lp/permutation_matrix.h | 22 +- src/util/lp/row_eta_matrix.h | 20 +- src/util/lp/sparse_matrix.h | 10 +- src/util/lp/square_dense_submatrix.h | 20 +- src/util/map.h | 4 +- src/util/max_cliques.h | 7 +- src/util/memory_manager.cpp | 23 +- src/util/memory_manager.h | 7 +- src/util/mpbq.cpp | 2 +- src/util/mpbq.h | 2 +- src/util/mpf.cpp | 22 +- src/util/mpf.h | 8 +- src/util/mpff.cpp | 76 +- src/util/mpff.h | 28 +- src/util/mpfx.cpp | 34 +- src/util/mpfx.h | 20 +- src/util/mpn.cpp | 2 +- src/util/mpq.h | 27 +- src/util/mpz.cpp | 204 +- src/util/mpz.h | 46 +- src/util/mpzzp.h | 16 +- src/util/obj_hashtable.h | 24 +- src/util/obj_pair_hashtable.h | 27 +- src/util/obj_ref.h | 10 +- src/util/obj_triple_hashtable.h | 12 +- src/util/optional.h | 8 +- src/util/page.cpp | 2 +- src/util/params.cpp | 47 +- src/util/params.h | 6 +- src/util/parray.h | 22 +- src/util/plugin_manager.h | 7 +- src/util/prime_generator.cpp | 22 +- src/util/prime_generator.h | 10 +- src/util/queue.h | 73 + src/util/rational.cpp | 4 +- src/util/rational.h | 10 +- src/util/ref.h | 8 +- src/util/ref_buffer.h | 3 + src/util/region.cpp | 20 +- src/util/rlimit.cpp | 4 +- src/util/rlimit.h | 8 +- src/util/s_integer.cpp | 2 +- src/util/s_integer.h | 8 +- src/util/scoped_ctrl_c.cpp | 2 +- src/util/scoped_ptr_vector.h | 4 +- src/util/scoped_timer.cpp | 48 +- src/util/small_object_allocator.cpp | 28 +- src/util/smt2_util.cpp | 12 +- src/util/sorting_network.h | 254 +- src/util/sstream.h | 20 +- src/util/stack.cpp | 8 +- src/util/stats.h | 2 +- src/util/stopwatch.h | 5 + src/util/symbol.cpp | 12 +- src/util/symbol.h | 8 +- src/util/symbol_table.h | 4 +- src/util/timeit.cpp | 2 +- src/util/timeout.cpp | 8 +- src/util/top_sort.h | 101 + src/util/total_order.h | 30 +- src/util/trace.cpp | 4 +- src/util/trail.h | 44 +- src/util/uint_set.h | 127 +- src/util/union_find.h | 8 +- src/util/util.cpp | 29 +- src/util/util.h | 61 +- src/util/vector.h | 37 +- src/util/warning.cpp | 4 +- src/util/z3_exception.h | 8 +- todo.txt | 9 + 1147 files changed, 59004 insertions(+), 63575 deletions(-) create mode 100644 contrib/cmake/src/test/lp/CMakeLists.txt create mode 100644 examples/python/parallel.py create mode 100644 scripts/vsts-mac.sh create mode 100644 scripts/vsts-vs2013.cmd create mode 100644 scripts/vsts-vs2017.cmd delete mode 100644 src/api/api_interp.cpp delete mode 100644 src/api/dotnet/InterpolationContext.cs delete mode 100644 src/api/java/InterpolationContext.java delete mode 100644 src/api/z3_interp.h rename src/ast/{ => rewriter}/factor_equivs.cpp (66%) rename src/ast/{ => rewriter}/factor_equivs.h (96%) delete mode 100644 src/ast/rewriter/pull_ite_tree.cpp delete mode 100644 src/ast/rewriter/pull_ite_tree.h delete mode 100644 src/cmd_context/interpolant_cmds.cpp delete mode 100644 src/cmd_context/interpolant_cmds.h delete mode 100644 src/duality/CMakeLists.txt delete mode 100644 src/duality/duality.h delete mode 100755 src/duality/duality_profiling.cpp delete mode 100755 src/duality/duality_profiling.h delete mode 100755 src/duality/duality_rpfp.cpp delete mode 100644 src/duality/duality_solver.cpp delete mode 100755 src/duality/duality_wrapper.cpp delete mode 100644 src/duality/duality_wrapper.h delete mode 100644 src/interp/CMakeLists.txt delete mode 100644 src/interp/interp_params.pyg delete mode 100755 src/interp/iz3base.cpp delete mode 100755 src/interp/iz3base.h delete mode 100755 src/interp/iz3checker.cpp delete mode 100644 src/interp/iz3checker.h delete mode 100644 src/interp/iz3exception.h delete mode 100644 src/interp/iz3hash.h delete mode 100755 src/interp/iz3interp.cpp delete mode 100644 src/interp/iz3interp.h delete mode 100755 src/interp/iz3mgr.cpp delete mode 100755 src/interp/iz3mgr.h delete mode 100644 src/interp/iz3pp.cpp delete mode 100644 src/interp/iz3pp.h delete mode 100755 src/interp/iz3profiling.cpp delete mode 100755 src/interp/iz3profiling.h delete mode 100755 src/interp/iz3proof.cpp delete mode 100755 src/interp/iz3proof.h delete mode 100755 src/interp/iz3proof_itp.cpp delete mode 100644 src/interp/iz3proof_itp.h delete mode 100755 src/interp/iz3scopes.cpp delete mode 100755 src/interp/iz3scopes.h delete mode 100755 src/interp/iz3secondary.h delete mode 100755 src/interp/iz3translate.cpp delete mode 100755 src/interp/iz3translate.h delete mode 100755 src/interp/iz3translate_direct.cpp rename src/muz/base/{fixedpoint_params.pyg => fp_params.pyg} (53%) delete mode 100644 src/muz/duality/CMakeLists.txt delete mode 100755 src/muz/duality/duality_dl_interface.cpp delete mode 100644 src/muz/duality/duality_dl_interface.h delete mode 100644 src/muz/pdr/CMakeLists.txt delete mode 100644 src/muz/pdr/pdr_closure.cpp delete mode 100644 src/muz/pdr/pdr_closure.h delete mode 100644 src/muz/pdr/pdr_context.cpp delete mode 100644 src/muz/pdr/pdr_context.h delete mode 100644 src/muz/pdr/pdr_dl_interface.cpp delete mode 100644 src/muz/pdr/pdr_dl_interface.h delete mode 100644 src/muz/pdr/pdr_farkas_learner.cpp delete mode 100644 src/muz/pdr/pdr_farkas_learner.h delete mode 100644 src/muz/pdr/pdr_generalizers.cpp delete mode 100644 src/muz/pdr/pdr_generalizers.h delete mode 100644 src/muz/pdr/pdr_manager.cpp delete mode 100644 src/muz/pdr/pdr_manager.h delete mode 100644 src/muz/pdr/pdr_prop_solver.cpp delete mode 100644 src/muz/pdr/pdr_prop_solver.h delete mode 100644 src/muz/pdr/pdr_reachable_cache.cpp delete mode 100644 src/muz/pdr/pdr_reachable_cache.h delete mode 100644 src/muz/pdr/pdr_smt_context_manager.cpp delete mode 100644 src/muz/pdr/pdr_smt_context_manager.h delete mode 100644 src/muz/pdr/pdr_sym_mux.cpp delete mode 100644 src/muz/pdr/pdr_sym_mux.h delete mode 100644 src/muz/pdr/pdr_util.cpp delete mode 100644 src/muz/pdr/pdr_util.h create mode 100644 src/muz/spacer/spacer_callback.cpp create mode 100644 src/muz/spacer/spacer_callback.h delete mode 100644 src/muz/spacer/spacer_itp_solver.cpp delete mode 100644 src/muz/spacer/spacer_itp_solver.h create mode 100644 src/muz/spacer/spacer_iuc_proof.cpp create mode 100644 src/muz/spacer/spacer_iuc_proof.h create mode 100644 src/muz/spacer/spacer_iuc_solver.cpp create mode 100644 src/muz/spacer/spacer_iuc_solver.h create mode 100644 src/muz/spacer/spacer_json.cpp create mode 100644 src/muz/spacer/spacer_json.h create mode 100644 src/muz/spacer/spacer_mbc.cpp create mode 100644 src/muz/spacer/spacer_mbc.h create mode 100644 src/muz/spacer/spacer_pdr.cpp create mode 100644 src/muz/spacer/spacer_pdr.h create mode 100644 src/muz/spacer/spacer_proof_utils.cpp create mode 100644 src/muz/spacer/spacer_proof_utils.h create mode 100644 src/muz/spacer/spacer_quant_generalizer.cpp create mode 100644 src/muz/spacer/spacer_sat_answer.cpp create mode 100644 src/muz/spacer/spacer_sat_answer.h create mode 100644 src/muz/spacer/spacer_sem_matcher.cpp create mode 100644 src/muz/spacer/spacer_sem_matcher.h delete mode 100644 src/muz/spacer/spacer_smt_context_manager.cpp delete mode 100644 src/muz/spacer/spacer_smt_context_manager.h delete mode 100644 src/muz/spacer/spacer_virtual_solver.cpp delete mode 100644 src/muz/spacer/spacer_virtual_solver.h delete mode 100644 src/opt/mss.cpp delete mode 100644 src/opt/mss.h create mode 100644 src/qe/qe_mbi.cpp create mode 100644 src/qe/qe_mbi.h create mode 100644 src/qe/qe_solve_plugin.cpp create mode 100644 src/qe/qe_solve_plugin.h create mode 100644 src/qe/qe_term_graph.cpp create mode 100644 src/qe/qe_term_graph.h create mode 100644 src/sat/ba_solver.cpp create mode 100644 src/sat/ba_solver.h create mode 100644 src/sat/sat_allocator.h create mode 100644 src/sat/sat_bdd.cpp create mode 100644 src/sat/sat_bdd.h create mode 100644 src/sat/sat_big.cpp create mode 100644 src/sat/sat_big.h create mode 100644 src/sat/sat_drat.cpp create mode 100644 src/sat/sat_drat.h create mode 100644 src/sat/sat_elim_vars.cpp create mode 100644 src/sat/sat_elim_vars.h create mode 100644 src/sat/sat_local_search.cpp create mode 100644 src/sat/sat_local_search.h create mode 100644 src/sat/sat_lookahead.cpp create mode 100644 src/sat/sat_lookahead.h create mode 100644 src/sat/sat_parallel.cpp create mode 100644 src/sat/sat_parallel.h create mode 100644 src/sat/sat_unit_walk.cpp create mode 100644 src/sat/sat_unit_walk.h create mode 100644 src/solver/parallel_params.pyg create mode 100644 src/solver/parallel_tactic.cpp create mode 100644 src/solver/parallel_tactic.h delete mode 100644 src/tactic/arith/elim01_tactic.cpp delete mode 100644 src/tactic/arith/elim01_tactic.h create mode 100644 src/tactic/dependency_converter.cpp create mode 100644 src/tactic/dependency_converter.h delete mode 100644 src/tactic/extension_model_converter.cpp delete mode 100644 src/tactic/filter_model_converter.cpp create mode 100644 src/tactic/fpa/qffplra_tactic.cpp create mode 100644 src/tactic/fpa/qffplra_tactic.h create mode 100644 src/tactic/generic_model_converter.cpp create mode 100644 src/tactic/generic_model_converter.h delete mode 100644 src/tactic/nlsat_smt/CMakeLists.txt delete mode 100644 src/tactic/nlsat_smt/nl_purify_tactic.h create mode 100644 src/tactic/portfolio/solver2lookahead.cpp create mode 100644 src/tactic/portfolio/solver2lookahead.h delete mode 100644 src/tactic/smtlogics/qfufnra_tactic.cpp delete mode 100644 src/tactic/smtlogics/qfufnra_tactic.h create mode 100644 src/test/bdd.cpp create mode 100644 src/test/cube_clause.cpp delete mode 100644 src/test/pdr.cpp create mode 100644 src/test/sat_local_search.cpp create mode 100644 src/test/sat_lookahead.cpp create mode 100644 src/test/solver_pool.cpp create mode 100644 src/util/ema.h create mode 100644 src/util/lp/disjoint_intervals.h create mode 100644 src/util/lp/lar_solver.hpp create mode 100644 src/util/queue.h create mode 100644 src/util/top_sort.h create mode 100644 todo.txt diff --git a/.travis.yml b/.travis.yml index a3ffb221e..9ec6132b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,7 +84,7 @@ matrix: - os: osx osx_image: xcode8.3 # Note: Apple Clang does not support OpenMP - env: Z3_BUILD_TYPE=RelWithDebInfo USE_OPENMP=0 + env: Z3_BUILD_TYPE=RelWithDebInfo USE_OPENMP=0 DOTNET_BINDINGS=0 script: # Use `travis_wait` when doing LTO builds because this configuration will # have long link times during which it will not show any output which diff --git a/CMakeLists.txt b/CMakeLists.txt index 871852c04..a086afd71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ endif() # Project version ################################################################################ set(Z3_VERSION_MAJOR 4) -set(Z3_VERSION_MINOR 6) +set(Z3_VERSION_MINOR 8) set(Z3_VERSION_PATCH 0) set(Z3_VERSION_TWEAK 0) set(Z3_VERSION "${Z3_VERSION_MAJOR}.${Z3_VERSION_MINOR}.${Z3_VERSION_PATCH}.${Z3_VERSION_TWEAK}") @@ -218,12 +218,17 @@ include(${CMAKE_SOURCE_DIR}/cmake/z3_add_cxx_flag.cmake) ################################################################################ # C++ language version ################################################################################ -# FIXME: Use CMake's own mechanism for selecting language version -if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) - z3_add_cxx_flag("-std=c++11" REQUIRED) -else() - message(AUTHOR_WARNING "Not setting C++ language version for compiler") -endif() +if ("${CMAKE_VERSION}" VERSION_LESS "3.1") + # FIXME: Drop this when we upgrade to newer CMake versions. + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + z3_add_cxx_flag("-std=c++11" REQUIRED) + else() + message(AUTHOR_WARNING "Not setting C++ language version for compiler") + endif() +else () + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif () ################################################################################ # Platform detection @@ -240,6 +245,9 @@ elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") message(STATUS "Platform: FreeBSD") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_FREEBSD_") +elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "NetBSD") + message(STATUS "Platform: NetBSD") + list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_NetBSD_") elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "OpenBSD") message(STATUS "Platform: OpenBSD") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_OPENBSD_") @@ -257,6 +265,46 @@ list(APPEND Z3_COMPONENT_EXTRA_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/src" "${CMAKE_SOURCE_DIR}/src" ) + +################################################################################ +# Linux specific configuration +################################################################################ +if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") + # Try to detect if it is necessary to link against librt. + # Note that glibc < 2.17 required librt to be linked to use clock_gettime() + # and friends. + set(CLOCK_GETTIME_REQUIRES_LIBRT_TEST_CODE + " + #include + int main() { + timespec res; + int result = clock_gettime(CLOCK_REALTIME, &res); + return result == 0; + } + " + ) + check_cxx_source_compiles( + "${CLOCK_GETTIME_REQUIRES_LIBRT_TEST_CODE}" + CLOCK_GETTIME_NO_REQUIRE_LIBRT + ) + if (NOT CLOCK_GETTIME_NO_REQUIRE_LIBRT) + # Try again with librt + message(STATUS "Failed to link against clock_gettime(), trying with librt") + set(CMAKE_REQUIRED_LIBRARIES_OLD "${CMAKE_REQUIRED_LIBRARIES}") + set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES} rt") + check_cxx_source_compiles( + "${CLOCK_GETTIME_REQUIRES_LIBRT_TEST_CODE}" + CLOCK_GETTIME_REQUIRES_LIBRT + ) + set(CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES_OLD}") + if (CLOCK_GETTIME_REQUIRES_LIBRT) + list(APPEND Z3_DEPENDENT_LIBS "rt") + else() + message(FATAL_ERROR "Failed to link against clock_gettime()") + endif() + endif() +endif() + ################################################################################ # GNU multiple precision library support ################################################################################ @@ -369,6 +417,20 @@ list(APPEND Z3_DEPENDENT_LIBS ${CMAKE_THREAD_LIBS_INIT}) ################################################################################ include(${CMAKE_SOURCE_DIR}/cmake/compiler_warnings.cmake) +################################################################################ +# If using Ninja, force color output for Clang (and gcc, disabled to check build). +################################################################################ +if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja") + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics") + endif() +# if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdiagnostics-color") +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdiagnostics-color") +# endif() +endif() + ################################################################################ # Option to control what type of library we build ################################################################################ @@ -394,7 +456,7 @@ else() endif() ################################################################################ -# Postion independent code +# Position independent code ################################################################################ # This is required because code built in the components will end up in a shared # library. If not building a shared library ``-fPIC`` isn't needed and would add diff --git a/README-CMake.md b/README-CMake.md index 0d323e08f..1b1f72729 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -5,7 +5,7 @@ of the project written in the ``CMakeLists.txt`` files and emits a build system for that project of your choice using one of CMake's "generators". This allows CMake to support many different platforms and build tools. You can run ``cmake --help`` to see the list of supported "generators" -on your platform. Example generators include "UNIX Makfiles" and "Visual Studio +on your platform. Example generators include "UNIX Makefiles" and "Visual Studio 12 2013". ## Getting started @@ -44,7 +44,7 @@ cmake -G "Unix Makefiles" ../ make -j4 # Replace 4 with an appropriate number ``` -Note that on some platforms "Unix Makesfiles" is the default generator so on those +Note that on some platforms "Unix Makefiles" is the default generator so on those platforms you don't need to pass ``-G "Unix Makefiles"`` command line option to ``cmake``. diff --git a/README.md b/README.md index 70956d439..447034a84 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,9 @@ See the [release notes](RELEASE_NOTES) for notes on various stable releases of Z ## Build status -| Windows x86 | Windows x64 | Ubuntu x64 | Debian x64 | OSX | TravisCI | -| ----------- | ----------- | ---------- | ---------- | --- | -------- | -[![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) +| Windows x64 | Windows x86 | Windows x64 | Ubuntu x64 | Debian x64 | OSX | 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 [2]: #building-z3-using-make-and-gccclang @@ -189,3 +189,6 @@ python -c 'import z3; print(z3.get_version_string())' See [``examples/python``](examples/python) for examples. +### ``Web Assembly`` + +[WebAssembly](https://github.com/cpitclaudel/z3.wasm) bindings are provided by Clément Pit-Claudel. diff --git a/RELEASE_NOTES b/RELEASE_NOTES index e85e3d8d6..e55b329db 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -1,7 +1,62 @@ RELEASE NOTES +Version 4.8.0 +============= + +- New requirements: + - A breaking change to the API is that parsers for SMT-LIB2 formulas return a vector of + formulas as opposed to a conjunction of formulas. The vector of formulas correspond to + the set of "assert" instructions in the SMT-LIB input. + +- New features + - A parallel mode is available for select theories, including QF_BV. + By setting parallel.enable=true Z3 will spawn a number of worker threads proportional to the + number of available CPU cores to apply cube and conquer solving on the goal. + - The SAT solver by default handle cardinality and PB constraints using a custom plugin + that operates directly on cardinality and PB constraints. + - A "cube" interface is exposed over the solver API. + - Model conversion is first class over the textual API, such that subgoals created from running a + solver can be passed in text files and a model for the original formula can be recreated from the result. + - This has also led to changes in how models are tracked over tactic subgoals. The API for + 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. + 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 + as lemmas (redundant) and are garbage collected if their glue level is high. + - Substantial overhaul of the spacer horn clause engine. + +- Removed features: + - interpolation API + - duality engine for constrained Horn clauses. + - pdr engine for constrained Horn clauses. The engine's functionality has been + folded into spacer as one of optional strategies. + - long deprecated API functions have been removed from z3_api.h + + + +Version 4.7.1 +============= + +- New requirements: + - uses stdbool and stdint as part of z3. + +- New features: + - none + +- Removed features: + - none + +- Notes: + This is a minor release prior to a set of planned major updates. + It uses minor version 7 to indicate that the use of stdbool and + stdint are breaking changes to consumers of the C-based API. + Version 4.6.0 ============= + - New requirements: - C++11 capable compiler to build Z3. - C++ API now requires C++11 or newer. @@ -14,6 +69,10 @@ Version 4.6.0 issuing the command (get-objectives). Pareto front objectives are accessed by issuing multiple (check-sat) calls until it returns unsat. +- Removed features: + - Removed support for SMT-LIB 1.x + + Version 4.5.0 ============= @@ -49,10 +108,9 @@ Version 4.5.0 over compound formulas, introduce a fresh predicate whose arguments are the relevant free variables in the formula and add a rule that uses the fresh predicate in the head and formula in the body. - - minimization of unsat cores is avaialble as an option for the SAT and SMT cores. + - Minimization of unsat cores is available as an option for the SAT and SMT cores. By setting smt.core.minimize=true resp. sat.core.minimize=true - cores produced by these modules are minimized. - + cores produced by these modules are minimized. - A multitude of bugs has been fixed. @@ -423,11 +481,11 @@ Version 3.0 - New Bitvector (QF_BV) solver. The new solver is only available when using the new SMT2 front-end. -- Major performace improvements. +- Major performance improvements. - New preprocessing stack. -- Performance improvements for linear and nonlinear arithmetic. The improvements are only available when using the the SMT2 front-end. +- Performance improvements for linear and nonlinear arithmetic. The improvements are only available when using the SMT2 front-end. - Added API for parsing SMT2 files. @@ -708,7 +766,7 @@ The following bugs are fixed in this release: bvshl when using a shift amount that evaluates to the length of the bit-vector. Thanks to Trevor Hansen and Robert Brummayer. -- Incorrect NNF conversion in linear quantifier elimniation routines. +- Incorrect NNF conversion in linear quantifier elimination routines. Thanks to Josh Berdine. - Missing constant folding of extraction for large bit-vectors. @@ -768,7 +826,7 @@ This release also introduces some new preprocessing features: - More efficient destructive equality resolution DER=true. -- DISTRIBUTE_FORALL=true (distributes universal quatifiers over conjunctions, this transformation may affect pattern inference). +- DISTRIBUTE_FORALL=true (distributes universal quantifiers over conjunctions, this transformation may affect pattern inference). - Rewriter that uses universally quantified equations PRE_DEMODULATOR=true (yes, the option name is not good, we will change it in a future release). @@ -838,7 +896,7 @@ This release introduces the following features: It fixes the following bugs: -- Incorrect simplification of map over store in the extendted array theory. Reported by Catalin Hritcu. +- Incorrect simplification of map over store in the extended array theory. Reported by Catalin Hritcu. - Incomplete handling of equality propagation with constant arrays. Reported by Catalin Hritcu. @@ -882,7 +940,7 @@ Version 2.0 proof object. - Proof Objects. - The #Z3_check_assumptions retuns a proof object if + The #Z3_check_assumptions returns a proof object if the configuration flag PROOF_MODE is set to 1 or 2. - Partial support for non-linear arithmetic. @@ -895,4 +953,4 @@ Version 2.0 The theory of well-founded recursive data-types is supported over the binary APIs. It supports ground satisfiability checking for tuples, enumeration types (scalars), - lists and mututally recursive data-types. + lists and mutually recursive data-types. diff --git a/cmake/Z3Config.cmake.in b/cmake/Z3Config.cmake.in index e7f604591..dbd63b103 100644 --- a/cmake/Z3Config.cmake.in +++ b/cmake/Z3Config.cmake.in @@ -2,7 +2,7 @@ # @AUTO_GEN_MSG@ # # This file is intended to be consumed by clients who wish to use Z3 from CMake. -# It can be use by doing `find_package(Z3 config)` from within a +# It can be used by doing `find_package(Z3 config)` from within a # `CMakeLists.txt` file. If CMake doesn't find this package automatically you # can give it a hint by passing `-DZ3_DIR=` to the CMake invocation where # `` is the path to the directory containing this file. diff --git a/cmake/modules/FindDotNetToolchain.cmake b/cmake/modules/FindDotNetToolchain.cmake index abc4ff21c..6e8cc7610 100644 --- a/cmake/modules/FindDotNetToolchain.cmake +++ b/cmake/modules/FindDotNetToolchain.cmake @@ -33,13 +33,18 @@ if (DOTNET_CSC_EXECUTABLE) set(DOTNET_TOOLCHAIN_IS_MONO TRUE) set(DOTNET_TOOLCHAIN_IS_WINDOWS FALSE) message(STATUS ".NET toolchain is Mono") + elseif ("${CSC_STD_OUT}" MATCHES "^Turbo[ ]+C#") + set(DOTNET_DETERMINED_VENDOR TRUE) + set(DOTNET_TOOLCHAIN_IS_MONO TRUE) + set(DOTNET_TOOLCHAIN_IS_WINDOWS FALSE) + message(STATUS ".NET toolchain is Mono") elseif ("${CSC_STD_OUT}" MATCHES "^Microsoft.+Visual[ ]+C#") set(DOTNET_DETERMINED_VENDOR TRUE) set(DOTNET_TOOLCHAIN_IS_MONO FALSE) set(DOTNET_TOOLCHAIN_IS_WINDOWS TRUE) message(STATUS ".NET toolchain is Windows native") else() - message(STATUS ".NET toolchain is unknown") + message(STATUS ".NET toolchain is unknown: ${CSC_STD_OUT}") endif() endif() endif() diff --git a/configure b/configure index 29408d3e7..807fba392 100755 --- a/configure +++ b/configure @@ -14,4 +14,4 @@ if ! $PYTHON -c "print('testing')" > /dev/null ; then exit 1 fi -$PYTHON scripts/mk_make.py $* +$PYTHON scripts/mk_make.py "$@" diff --git a/contrib/cmake/src/test/lp/CMakeLists.txt b/contrib/cmake/src/test/lp/CMakeLists.txt new file mode 100644 index 000000000..6683a1758 --- /dev/null +++ b/contrib/cmake/src/test/lp/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable(lp_tst lp_main.cpp lp.cpp $ $ $ $ ) +target_compile_definitions(lp_tst PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) +target_compile_options(lp_tst PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) +target_include_directories(lp_tst PRIVATE ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) +target_link_libraries(lp_tst PRIVATE ${Z3_DEPENDENT_LIBS}) +z3_append_linker_flag_list_to_target(lp_tst ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) diff --git a/contrib/suppressions/sanitizers/README.md b/contrib/suppressions/sanitizers/README.md index f76f920b2..1d7cd0ac8 100644 --- a/contrib/suppressions/sanitizers/README.md +++ b/contrib/suppressions/sanitizers/README.md @@ -1,4 +1,4 @@ -# Sanitizer supression files +# Sanitizer suppression files This directory contains files used to suppress ASan/LSan/UBSan warnings/errors. diff --git a/doc/mk_api_doc.py b/doc/mk_api_doc.py index a944f2c65..bfe865e06 100644 --- a/doc/mk_api_doc.py +++ b/doc/mk_api_doc.py @@ -226,12 +226,14 @@ try: website_dox_substitutions = {} bullet_point_prefix='\n - ' if Z3PY_ENABLED: + print("Python documentation enabled") website_dox_substitutions['PYTHON_API'] = ( '{prefix}Python API ' '(also available in pydoc format)' ).format( prefix=bullet_point_prefix) else: + print("Python documentation disabled") website_dox_substitutions['PYTHON_API'] = '' if DOTNET_ENABLED: website_dox_substitutions['DOTNET_API'] = ( @@ -250,7 +252,7 @@ try: website_dox_substitutions['JAVA_API'] = '' if ML_ENABLED: website_dox_substitutions['OCAML_API'] = ( - 'ML/OCaml API' + '{prefix}ML/OCaml API' ).format( prefix=bullet_point_prefix) else: @@ -270,7 +272,6 @@ try: cleanup_API(doc_path('../src/api/z3_rcf.h'), temp_path('z3_rcf.h')) cleanup_API(doc_path('../src/api/z3_fixedpoint.h'), temp_path('z3_fixedpoint.h')) cleanup_API(doc_path('../src/api/z3_optimization.h'), temp_path('z3_optimization.h')) - cleanup_API(doc_path('../src/api/z3_interp.h'), temp_path('z3_interp.h')) cleanup_API(doc_path('../src/api/z3_fpa.h'), temp_path('z3_fpa.h')) print("Removed annotations from z3_api.h.") @@ -316,7 +317,7 @@ try: if ML_ENABLED: ml_output_dir = os.path.join(OUTPUT_DIRECTORY, 'html', 'ml') mk_dir(ml_output_dir) - if subprocess.call(['ocamldoc', '-html', '-d', ml_output_dir, '-sort', '-hide', 'Z3', '-I', '%s/api/ml' % BUILD_DIR, doc_path('../src/api/ml/z3enums.mli'), doc_path('../src/api/ml/z3.mli')]) != 0: + if subprocess.call(['ocamldoc', '-html', '-d', ml_output_dir, '-sort', '-hide', 'Z3', '-I', '%s/api/ml' % BUILD_DIR, '%s/api/ml/z3enums.mli' % BUILD_DIR, '%s/api/ml/z3.mli' % BUILD_DIR]) != 0: print("ERROR: ocamldoc failed.") exit(1) print("Generated ML/OCaml documentation.") @@ -326,3 +327,4 @@ except Exception: exctype, value = sys.exc_info()[:2] print("ERROR: failed to generate documentation: %s" % value) exit(1) + diff --git a/examples/c++/example.cpp b/examples/c++/example.cpp index df977268a..640e3a10b 100644 --- a/examples/c++/example.cpp +++ b/examples/c++/example.cpp @@ -470,7 +470,7 @@ void unsat_core_example2() { // The solver s already contains p1 => F // To disable F, we add (not p1) as an additional assumption qs.push_back(!p1); - std::cout << s.check((unsigned)qs.size(), &qs[0]) << "\n"; + std::cout << s.check(static_cast(qs.size()), &qs[0]) << "\n"; expr_vector core2 = s.unsat_core(); std::cout << core2 << "\n"; std::cout << "size: " << core2.size() << "\n"; @@ -707,7 +707,7 @@ void tactic_example7() { std::cout << s.check() << "\n"; model m = s.get_model(); std::cout << "model for subgoal:\n" << m << "\n"; - std::cout << "model for original goal:\n" << r.convert_model(m) << "\n"; + std::cout << "model for original goal:\n" << subgoal.convert_model(m) << "\n"; } void tactic_example8() { @@ -920,6 +920,19 @@ void enum_sort_example() { std::cout << "2: " << result_goal.as_expr() << std::endl; } +void tuple_example() { + std::cout << "tuple example\n"; + context ctx; + const char * names[] = { "first", "second" }; + sort sorts[2] = { ctx.int_sort(), ctx.bool_sort() }; + func_decl_vector projs(ctx); + func_decl pair = ctx.tuple_sort("pair", 2, names, sorts, projs); + sorts[1] = pair.range(); + func_decl pair2 = ctx.tuple_sort("pair2", 2, names, sorts, projs); + + std::cout << pair2 << "\n"; +} + void expr_vector_example() { std::cout << "expr_vector example\n"; context c; @@ -1136,7 +1149,7 @@ static void parse_example() { func_decl_vector decls(c); sort B = c.bool_sort(); decls.push_back(c.function("a", 0, 0, B)); - expr a = c.parse_string("(assert a)", sorts, decls); + expr_vector a = c.parse_string("(assert a)", sorts, decls); std::cout << a << "\n"; // expr b = c.parse_string("(benchmark tst :extrafuns ((x Int) (y Int)) :formula (> x y) :formula (> x 0))"); @@ -1179,6 +1192,7 @@ int main() { incremental_example2(); std::cout << "\n"; incremental_example3(); std::cout << "\n"; enum_sort_example(); std::cout << "\n"; + tuple_example(); std::cout << "\n"; expr_vector_example(); std::cout << "\n"; exists_expr_vector_example(); std::cout << "\n"; substitute_example(); std::cout << "\n"; diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index d71771f98..a6937f293 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -378,7 +378,7 @@ void assert_comm_axiom(Z3_context ctx, Z3_solver s, Z3_func_decl f) { Z3_sort t; Z3_symbol f_name, t_name; - Z3_ast q; + Z3_ast_vector q; t = Z3_get_range(ctx, f); @@ -394,13 +394,14 @@ void assert_comm_axiom(Z3_context ctx, Z3_solver s, Z3_func_decl f) /* Inside the parser, type t will be referenced using the symbol 'T'. */ t_name = Z3_mk_string_symbol(ctx, "T"); - q = Z3_parse_smtlib2_string(ctx, "(assert (forall ((x T) (y T)) (= (f x y) (f y x))))", 1, &t_name, &t, 1, &f_name, &f); - printf("assert axiom:\n%s\n", Z3_ast_to_string(ctx, q)); - Z3_solver_assert(ctx, s, q); + printf("assert axiom:\n%s\n", Z3_ast_vector_to_string(ctx, q)); + for (unsigned i = 0; i < Z3_ast_vector_size(ctx, q); ++i) { + Z3_solver_assert(ctx, s, Z3_ast_vector_get(ctx, q, i)); + } } /** @@ -1546,7 +1547,7 @@ void two_contexts_example1() } /** - \brief Demonstrates how error codes can be read insted of registering an error handler. + \brief Demonstrates how error codes can be read instead of registering an error handler. */ void error_code_example1() { @@ -1642,7 +1643,7 @@ void parser_example2() Z3_ast x, y; Z3_symbol names[2]; Z3_func_decl decls[2]; - Z3_ast f; + Z3_ast_vector f; printf("\nparser_example2\n"); LOG_MSG("parser_example2"); @@ -1665,8 +1666,11 @@ void parser_example2() 0, 0, 0, /* 'x' and 'y' declarations are inserted as 'a' and 'b' into the parser symbol table. */ 2, names, decls); - printf("formula: %s\n", Z3_ast_to_string(ctx, f)); - Z3_solver_assert(ctx, s, f); + printf("formula: %s\n", Z3_ast_vector_to_string(ctx, f)); + printf("assert axiom:\n%s\n", Z3_ast_vector_to_string(ctx, f)); + for (unsigned i = 0; i < Z3_ast_vector_size(ctx, f); ++i) { + Z3_solver_assert(ctx, s, Z3_ast_vector_get(ctx, f, i)); + } check(ctx, s, Z3_L_TRUE); del_solver(ctx, s); @@ -1685,7 +1689,7 @@ void parser_example3() Z3_symbol g_name; Z3_sort g_domain[2]; Z3_func_decl g; - Z3_ast thm; + Z3_ast_vector thm; printf("\nparser_example3\n"); LOG_MSG("parser_example3"); @@ -1710,8 +1714,8 @@ void parser_example3() "(assert (forall ((x Int) (y Int)) (=> (= x y) (= (g x 0) (g 0 y)))))", 0, 0, 0, 1, &g_name, &g); - printf("formula: %s\n", Z3_ast_to_string(ctx, thm)); - prove(ctx, s, thm, Z3_TRUE); + printf("formula: %s\n", Z3_ast_vector_to_string(ctx, thm)); + prove(ctx, s, Z3_ast_vector_get(ctx, thm, 0), Z3_TRUE); del_solver(ctx, s); Z3_del_context(ctx); @@ -2286,46 +2290,6 @@ void unsat_core_and_proof_example() { Z3_del_context(ctx); } -void interpolation_example() { - Z3_context ctx = mk_context(); - Z3_ast pa = mk_bool_var(ctx, "PredA"); - Z3_ast pb = mk_bool_var(ctx, "PredB"); - Z3_ast pc = mk_bool_var(ctx, "PredC"); - Z3_ast args1[2] = {pa,pb}, args2[2] = {Z3_mk_not(ctx,pb),pc}; - Z3_ast args3[2] = {Z3_mk_interpolant(ctx,Z3_mk_and(ctx,2,args1)),Z3_mk_and(ctx,2,args2)}; - Z3_ast f = Z3_mk_and(ctx,2,args3); - Z3_ast_vector interpolant = 0; - Z3_model m = 0; - Z3_lbool result = Z3_L_UNDEF; - - printf("\ninterpolation_example\n"); - LOG_MSG("interpolation_example"); - - result = Z3_compute_interpolant(ctx,f,0,&interpolant,&m); - - switch (result) { - case Z3_L_FALSE: - printf("unsat\n"); - printf("interpolant: %s\n", Z3_ast_to_string(ctx, Z3_ast_vector_get(ctx, interpolant, 0))); - printf("\n"); - break; - case Z3_L_UNDEF: - printf("unknown\n"); - printf("potential model:\n"); - if (m) Z3_model_inc_ref(ctx, m); - display_model(ctx, stdout, m); - break; - case Z3_L_TRUE: - printf("sat\n"); - if (m) Z3_model_inc_ref(ctx, m); - display_model(ctx, stdout, m); - break; - } - - /* delete logical context */ - if (m) Z3_model_dec_ref(ctx, m); - Z3_del_context(ctx); -} #define MAX_RETRACTABLE_ASSERTIONS 1024 @@ -2452,7 +2416,6 @@ Z3_lbool ext_check(Z3_ext_context ctx) { } printf("\n"); } - return result; } @@ -2464,7 +2427,7 @@ void incremental_example1() { Z3_context ctx = ext_ctx->m_context; Z3_ast x, y, z, two, one; unsigned c1, c2, c3, c4; - Z3_bool result; + Z3_lbool result; printf("\nincremental_example1\n"); LOG_MSG("incremental_example1"); @@ -2485,7 +2448,7 @@ void incremental_example1() { c4 = assert_retractable_cnstr(ext_ctx, Z3_mk_lt(ctx, y, one)); result = ext_check(ext_ctx); - if (result != Z3_L_FALSE) + if (result != Z3_L_FALSE) exitf("bug in Z3"); printf("unsat\n"); @@ -2533,7 +2496,7 @@ void reference_counter_example() { cfg = Z3_mk_config(); Z3_set_param_value(cfg, "model", "true"); - // Create a Z3 context where the user is reponsible for managing + // Create a Z3 context where the user is responsible for managing // Z3_ast reference counters. ctx = Z3_mk_context_rc(cfg); Z3_del_config(cfg); @@ -2577,13 +2540,15 @@ void reference_counter_example() { */ void smt2parser_example() { Z3_context ctx; - Z3_ast fs; + Z3_ast_vector fs; printf("\nsmt2parser_example\n"); LOG_MSG("smt2parser_example"); ctx = mk_context(); fs = Z3_parse_smtlib2_string(ctx, "(declare-fun a () (_ BitVec 8)) (assert (bvuge a #x10)) (assert (bvule a #xf0))", 0, 0, 0, 0, 0, 0); - printf("formulas: %s\n", Z3_ast_to_string(ctx, fs)); + Z3_ast_vector_inc_ref(ctx, fs); + printf("formulas: %s\n", Z3_ast_vector_to_string(ctx, fs)); + Z3_ast_vector_dec_ref(ctx, fs); Z3_del_context(ctx); } @@ -3009,7 +2974,6 @@ int main() { binary_tree_example(); enum_example(); unsat_core_and_proof_example(); - interpolation_example(); incremental_example1(); reference_counter_example(); smt2parser_example(); diff --git a/examples/dotnet/Program.cs b/examples/dotnet/Program.cs index 71364013b..06cc66150 100644 --- a/examples/dotnet/Program.cs +++ b/examples/dotnet/Program.cs @@ -175,7 +175,7 @@ namespace test_mapi string bench = string.Format("(assert (forall ((x {0}) (y {1})) (= ({2} x y) ({3} y x))))", t.Name, t.Name, f.Name, f.Name); - return ctx.ParseSMTLIB2String(bench, new Symbol[] { t.Name }, new Sort[] { t }, new Symbol[] { f.Name }, new FuncDecl[] { f }); + return ctx.ParseSMTLIB2String(bench, new Symbol[] { t.Name }, new Sort[] { t }, new Symbol[] { f.Name }, new FuncDecl[] { f })[0]; } /// @@ -322,7 +322,6 @@ namespace test_mapi Status q = s.Check(); Console.WriteLine("Solver says: " + q); Console.WriteLine("Model: \n" + s.Model); - Console.WriteLine("Converted Model: \n" + ar.ConvertModel(0, s.Model)); if (q != Status.SATISFIABLE) throw new TestFailedException(); } @@ -612,7 +611,6 @@ namespace test_mapi Expr f_x = ctx.MkApp(f, x); Expr f_y = ctx.MkApp(f, y); Expr g_y = ctx.MkApp(g, y); - Pattern[] pats = new Pattern[] { ctx.MkPattern(new Expr[] { f_x, g_y }) }; Expr[] no_pats = new Expr[] { f_y }; Expr[] bound = new Expr[2] { x, y }; Expr body = ctx.MkAnd(ctx.MkEq(f_x, f_y), ctx.MkEq(f_y, g_y)); @@ -622,14 +620,13 @@ namespace test_mapi Console.WriteLine("{0}", q1); } - // Quantifier with de-Brujin indices. + // Quantifier with de-Bruijn indices. { Expr x = ctx.MkBound(1, ctx.IntSort); Expr y = ctx.MkBound(0, ctx.IntSort); Expr f_x = ctx.MkApp(f, x); Expr f_y = ctx.MkApp(f, y); Expr g_y = ctx.MkApp(g, y); - Pattern[] pats = new Pattern[] { ctx.MkPattern(new Expr[] { f_x, g_y }) }; Expr[] no_pats = new Expr[] { f_y }; Symbol[] names = new Symbol[] { ctx.MkSymbol("x"), ctx.MkSymbol("y") }; Sort[] sorts = new Sort[] { ctx.IntSort, ctx.IntSort }; @@ -730,7 +727,6 @@ namespace test_mapi { Console.WriteLine("BasicTests"); - Symbol qi = ctx.MkSymbol(1); Symbol fname = ctx.MkSymbol("f"); Symbol x = ctx.MkSymbol("x"); Symbol y = ctx.MkSymbol("y"); @@ -977,7 +973,8 @@ namespace test_mapi using (Context ctx = new Context(new Dictionary() { { "MODEL", "true" } })) { - Expr a = ctx.ParseSMTLIB2File(filename); + BoolExpr[] fmls = ctx.ParseSMTLIB2File(filename); + BoolExpr a = ctx.MkAnd(fmls); Console.WriteLine("SMT2 file read time: " + (System.DateTime.Now - before).TotalSeconds + " sec"); @@ -1319,7 +1316,7 @@ namespace test_mapi new Sort[] { int_type, int_type } // types of projection operators ); FuncDecl first = tuple.FieldDecls[0]; // declarations are for projections - FuncDecl second = tuple.FieldDecls[1]; + // FuncDecl second = tuple.FieldDecls[1]; Expr x = ctx.MkConst("x", int_type); Expr y = ctx.MkConst("y", int_type); Expr n1 = tuple.MkDecl[x, y]; @@ -1383,7 +1380,9 @@ namespace test_mapi { Console.WriteLine("ParserExample1"); - var fml = ctx.ParseSMTLIB2String("(declare-const x Int) (declare-const y Int) (assert (> x y)) (assert (> x 0))"); + var fmls = ctx.ParseSMTLIB2String("(declare-const x Int) (declare-const y Int) (assert (> x y)) (assert (> x 0))"); + var fml = ctx.MkAnd(fmls); + Console.WriteLine("formula {0}", fml); Model m = Check(ctx, fml, Status.SATISFIABLE); @@ -1399,7 +1398,7 @@ namespace test_mapi FuncDecl a = ctx.MkConstDecl(declNames[0], ctx.MkIntSort()); FuncDecl b = ctx.MkConstDecl(declNames[1], ctx.MkIntSort()); FuncDecl[] decls = new FuncDecl[] { a, b }; - BoolExpr f = ctx.ParseSMTLIB2String("(assert (> a b))", null, null, declNames, decls); + BoolExpr f = ctx.ParseSMTLIB2String("(assert (> a b))", null, null, declNames, decls)[0]; Console.WriteLine("formula: {0}", f); Check(ctx, f, Status.SATISFIABLE); } @@ -1420,7 +1419,7 @@ namespace test_mapi BoolExpr thm = ctx.ParseSMTLIB2String("(assert (forall ((x Int) (y Int)) (=> (= x y) (= (gg x 0) (gg 0 y)))))", null, null, new Symbol[] { ctx.MkSymbol("gg") }, - new FuncDecl[] { g }); + new FuncDecl[] { g })[0]; Console.WriteLine("formula: {0}", thm); Prove(ctx, thm, false, ca); diff --git a/examples/java/JavaExample.java b/examples/java/JavaExample.java index 40fb25a92..0ad9a8d10 100644 --- a/examples/java/JavaExample.java +++ b/examples/java/JavaExample.java @@ -167,12 +167,12 @@ class JavaExample "function must be binary, and argument types must be equal to return type"); } - String bench = "(benchmark comm :formula (forall (x " + t.getName() + String bench = "(assert (forall (x " + t.getName() + ") (y " + t.getName() + ") (= (" + f.getName() + " x y) (" + f.getName() + " y x))))"; return ctx.parseSMTLIB2String(bench, new Symbol[] { t.getName() }, new Sort[] { t }, new Symbol[] { f.getName() }, - new FuncDecl[] { f }); + new FuncDecl[] { f })[0]; } // / "Hello world" example: create a Z3 logical context, and delete it. @@ -344,8 +344,6 @@ class JavaExample Status q = s.check(); System.out.println("Solver says: " + q); System.out.println("Model: \n" + s.getModel()); - System.out.println("Converted Model: \n" - + ar.convertModel(0, s.getModel())); if (q != Status.SATISFIABLE) throw new TestFailedException(); } @@ -660,7 +658,7 @@ class JavaExample System.out.println(q1); } - // Quantifier with de-Brujin indices. + // Quantifier with de-Bruijn indices. { Expr x = ctx.mkBound(1, ctx.getIntSort()); Expr y = ctx.mkBound(0, ctx.getIntSort()); @@ -1041,7 +1039,7 @@ class JavaExample HashMap cfg = new HashMap(); cfg.put("model", "true"); Context ctx = new Context(cfg); - Expr a = ctx.parseSMTLIB2File(filename, null, null, null, null); + BoolExpr a = ctx.mkAnd(ctx.parseSMTLIB2File(filename, null, null, null, null)); long t_diff = ((new Date()).getTime() - before.getTime()) / 1000; @@ -1445,7 +1443,7 @@ class JavaExample BoolExpr f = ctx.parseSMTLIB2String( "(declare-const x Int) (declare-const y Int) (assert (and (> x y) (> x 0)))", - null, null, null, null); + null, null, null, null)[0]; System.out.println("formula " + f); @SuppressWarnings("unused") @@ -1465,7 +1463,7 @@ class JavaExample FuncDecl[] decls = new FuncDecl[] { a, b }; BoolExpr f = ctx.parseSMTLIB2String("(assert (> a b))", null, null, - declNames, decls); + declNames, decls)[0]; System.out.println("formula: " + f); check(ctx, f, Status.SATISFIABLE); } @@ -1486,7 +1484,7 @@ class JavaExample BoolExpr thm = ctx.parseSMTLIB2String( "(assert (forall ((x Int) (y Int)) (=> (= x y) (= (gg x 0) (gg 0 y)))))", null, null, new Symbol[] { ctx.mkSymbol("gg") }, - new FuncDecl[] { g }); + new FuncDecl[] { g })[0]; System.out.println("formula: " + thm); prove(ctx, thm, false, ca); } diff --git a/examples/maxsat/README b/examples/maxsat/README index e5e4ba610..6c24da66b 100644 --- a/examples/maxsat/README +++ b/examples/maxsat/README @@ -9,4 +9,4 @@ On OSX 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. -This directory contains a test file (ex.smt) that can be use as input for the maxsat test application. +This directory contains a test file (ex.smt) that can be used as input for the maxsat test application. diff --git a/examples/python/all_interval_series.py b/examples/python/all_interval_series.py index 216941451..8269303d3 100644 --- a/examples/python/all_interval_series.py +++ b/examples/python/all_interval_series.py @@ -8,6 +8,8 @@ from __future__ import print_function from z3 import * import time +set_option("sat.gc.burst", False) # disable GC at every search. It is wasteful for these small queries. + def diff_at_j_is_i(xs, j, i): assert(0 <= j and j + 1 < len(xs)) assert(1 <= i and i < len(xs)) diff --git a/examples/python/parallel.py b/examples/python/parallel.py new file mode 100644 index 000000000..42ff50927 --- /dev/null +++ b/examples/python/parallel.py @@ -0,0 +1,36 @@ +from z3 import * +from multiprocessing.pool import ThreadPool +from copy import deepcopy + +pool = ThreadPool(8) +x = Int('x') + +assert x.ctx == main_ctx() + + +def calculate(x, n, ctx): + """ Do a simple computation with a context""" + assert x.ctx == ctx + assert x.ctx != main_ctx() + + # Parallel creation of z3 object + condition = And(x < 2, x > n, ctx) + + # Parallel solving + solver = Solver(ctx=ctx) + solver.add(condition) + solver.check() + + +for i in range(100): + # Create new context for the computation + # Note that we need to do this sequentially, as parallel access to the current context or its objects + # will result in a segfault + i_context = Context() + x_i = deepcopy(x).translate(i_context) + + # Kick off parallel computation + pool.apply_async(calculate, [x_i, i, i_context]) + +pool.close() +pool.join() diff --git a/examples/tptp/tptp5.cpp b/examples/tptp/tptp5.cpp index b2736df4c..facbf6c0a 100644 --- a/examples/tptp/tptp5.cpp +++ b/examples/tptp/tptp5.cpp @@ -1609,7 +1609,6 @@ public: display_inference(out, "rewrite", "thm", p); break; case Z3_OP_PR_PULL_QUANT: - case Z3_OP_PR_PULL_QUANT_STAR: display_inference(out, "pull_quant", "thm", p); break; case Z3_OP_PR_PUSH_QUANT: @@ -1669,12 +1668,6 @@ public: case Z3_OP_PR_NNF_NEG: display_inference(out, "nnf_neg", "sab", p); break; - case Z3_OP_PR_NNF_STAR: - display_inference(out, "nnf", "sab", p); - break; - case Z3_OP_PR_CNF_STAR: - display_inference(out, "cnf", "sab", p); - break; case Z3_OP_PR_SKOLEMIZE: display_inference(out, "skolemize", "sab", p); break; @@ -1706,10 +1699,6 @@ public: return display_hyp_inference(out, "modus_ponens", "thm", conclusion, hyp, hyp2); } case Z3_OP_PR_NNF_POS: - case Z3_OP_PR_NNF_STAR: - return display_hyp_inference(out, "nnf", "sab", conclusion, hyp); - case Z3_OP_PR_CNF_STAR: - return display_hyp_inference(out, "cnf", "sab", conclusion, hyp); case Z3_OP_PR_SKOLEMIZE: return display_hyp_inference(out, "skolemize", "sab", conclusion, hyp); case Z3_OP_PR_TRANSITIVITY: @@ -2214,9 +2203,8 @@ static void check_error(z3::context& ctx) { static void display_tptp(std::ostream& out) { // run SMT2 parser, pretty print TFA format. z3::context ctx; - Z3_ast _fml = Z3_parse_smtlib2_file(ctx, g_input_file, 0, 0, 0, 0, 0, 0); - check_error(ctx); - z3::expr fml(ctx, _fml); + z3::expr_vector fmls = ctx.parse_file(g_input_file); + z3::expr fml = z3::mk_and(fmls); pp_tptp pp(ctx); pp.collect_decls(fml); diff --git a/scripts/mk_consts_files.py b/scripts/mk_consts_files.py index d0502c19d..39d4e9439 100755 --- a/scripts/mk_consts_files.py +++ b/scripts/mk_consts_files.py @@ -72,7 +72,7 @@ def main(args): if count == 0: logging.info('No files generated. You need to specific an output directory' - ' for the relevant langauge bindings') + ' for the relevant language bindings') # TODO: Add support for other bindings return 0 diff --git a/scripts/mk_project.py b/scripts/mk_project.py index 60b3b7756..dada93069 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -9,12 +9,12 @@ from mk_util import * # Z3 Project definition def init_project_def(): - set_version(4, 6, 0, 0) + set_version(4, 8, 0, 0) add_lib('util', []) - add_lib('lp', ['util'], 'util/lp') add_lib('polynomial', ['util'], 'math/polynomial') add_lib('sat', ['util']) add_lib('nlsat', ['polynomial', 'sat']) + add_lib('lp', ['util','nlsat'], 'util/lp') add_lib('hilbert', ['util'], 'math/hilbert') add_lib('simplex', ['util'], 'math/simplex') add_lib('automata', ['util'], 'math/automata') @@ -32,17 +32,15 @@ def init_project_def(): add_lib('grobner', ['ast'], 'math/grobner') add_lib('euclid', ['util'], 'math/euclid') add_lib('core_tactics', ['tactic', 'macros', 'normal_forms', 'rewriter'], 'tactic/core') - add_lib('sat_tactic', ['tactic', 'sat'], 'sat/tactic') + add_lib('proofs', ['rewriter', 'util'], 'ast/proofs') + add_lib('solver', ['model', 'tactic', 'proofs']) + add_lib('sat_tactic', ['tactic', 'sat', 'solver'], 'sat/tactic') add_lib('arith_tactics', ['core_tactics', 'sat'], 'tactic/arith') add_lib('nlsat_tactic', ['nlsat', 'sat_tactic', 'arith_tactics'], 'nlsat/tactic') add_lib('subpaving_tactic', ['core_tactics', 'subpaving'], 'math/subpaving/tactic') add_lib('aig_tactic', ['tactic'], 'tactic/aig') - add_lib('proofs', ['rewriter', 'util'], 'ast/proofs') - add_lib('solver', ['model', 'tactic', 'proofs']) add_lib('ackermannization', ['model', 'rewriter', 'ast', 'solver', 'tactic'], 'ackermannization') - add_lib('interp', ['solver']) - add_lib('cmd_context', ['solver', 'rewriter', 'interp']) - add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'arith_tactics'], 'cmd_context/extra_cmds') + add_lib('cmd_context', ['solver', 'rewriter']) add_lib('smt2parser', ['cmd_context', 'parser_util'], 'parsers/smt2') add_lib('fpa', ['ast', 'util', 'rewriter', 'model'], 'ast/fpa') add_lib('pattern', ['normal_forms', 'smt2parser', 'rewriter'], 'ast/pattern') @@ -56,29 +54,26 @@ def init_project_def(): add_lib('smt_tactic', ['smt'], 'smt/tactic') add_lib('sls_tactic', ['tactic', 'normal_forms', 'core_tactics', 'bv_tactics'], 'tactic/sls') add_lib('qe', ['smt','sat','nlsat','tactic','nlsat_tactic'], 'qe') - add_lib('duality', ['smt', 'interp', 'qe']) add_lib('muz', ['smt', 'sat', 'smt2parser', 'aig_tactic', 'qe'], 'muz/base') add_lib('dataflow', ['muz'], 'muz/dataflow') add_lib('transforms', ['muz', 'hilbert', 'dataflow'], 'muz/transforms') add_lib('rel', ['muz', 'transforms'], 'muz/rel') - add_lib('pdr', ['muz', 'transforms', 'arith_tactics', 'core_tactics', 'smt_tactic'], 'muz/pdr') add_lib('spacer', ['muz', 'transforms', 'arith_tactics', 'smt_tactic'], 'muz/spacer') add_lib('clp', ['muz', 'transforms'], 'muz/clp') add_lib('tab', ['muz', 'transforms'], 'muz/tab') add_lib('bmc', ['muz', 'transforms'], 'muz/bmc') add_lib('ddnf', ['muz', 'transforms', 'rel'], 'muz/ddnf') - add_lib('duality_intf', ['muz', 'transforms', 'duality'], 'muz/duality') - add_lib('fp', ['muz', 'pdr', 'clp', 'tab', 'rel', 'bmc', 'duality_intf', 'ddnf', 'spacer'], 'muz/fp') - add_lib('nlsat_smt_tactic', ['nlsat_tactic', 'smt_tactic'], 'tactic/nlsat_smt') + add_lib('fp', ['muz', 'clp', 'tab', 'rel', 'bmc', 'ddnf', 'spacer'], 'muz/fp') add_lib('ufbv_tactic', ['normal_forms', 'core_tactics', 'macros', 'smt_tactic', 'rewriter'], 'tactic/ufbv') add_lib('sat_solver', ['solver', 'core_tactics', 'aig_tactic', 'bv_tactics', 'arith_tactics', 'sat_tactic'], 'sat/sat_solver') - add_lib('smtlogic_tactics', ['ackermannization', 'sat_solver', 'arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe','nlsat_smt_tactic'], 'tactic/smtlogics') + add_lib('smtlogic_tactics', ['ackermannization', 'sat_solver', 'arith_tactics', 'bv_tactics', 'nlsat_tactic', 'smt_tactic', 'aig_tactic', 'fp', 'muz','qe'], 'tactic/smtlogics') add_lib('fpa_tactics', ['fpa', 'core_tactics', 'bv_tactics', 'sat_tactic', 'smt_tactic', 'arith_tactics', 'smtlogic_tactics'], 'tactic/fpa') add_lib('portfolio', ['smtlogic_tactics', 'sat_solver', 'ufbv_tactic', 'fpa_tactics', 'aig_tactic', 'fp', 'qe','sls_tactic', 'subpaving_tactic'], 'tactic/portfolio') add_lib('opt', ['smt', 'smtlogic_tactics', 'sls_tactic', 'sat_solver'], 'opt') - API_files = ['z3_api.h', 'z3_ast_containers.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_fixedpoint.h', 'z3_optimization.h', 'z3_interp.h', 'z3_fpa.h', 'z3_spacer.h'] - add_lib('api', ['portfolio', 'realclosure', 'interp', 'opt'], + API_files = ['z3_api.h', 'z3_ast_containers.h', 'z3_algebraic.h', 'z3_polynomial.h', 'z3_rcf.h', 'z3_fixedpoint.h', 'z3_optimization.h', 'z3_fpa.h', 'z3_spacer.h'] + add_lib('api', ['portfolio', 'realclosure', 'opt'], includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files) + add_lib('extra_cmds', ['cmd_context', 'subpaving_tactic', 'qe', 'arith_tactics'], 'cmd_context/extra_cmds') add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3') add_exe('test', ['api', 'fuzzing', 'simplex'], exe_name='test-z3', install=False) _libz3Component = add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll', diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 03256125c..d51735255 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -69,6 +69,7 @@ IS_WINDOWS=False IS_LINUX=False IS_OSX=False IS_FREEBSD=False +IS_NETBSD=False IS_OPENBSD=False IS_CYGWIN=False IS_CYGWIN_MINGW=False @@ -141,6 +142,9 @@ def is_linux(): def is_freebsd(): return IS_FREEBSD +def is_netbsd(): + return IS_NETBSD + def is_openbsd(): return IS_OPENBSD @@ -604,6 +608,8 @@ elif os.name == 'posix': IS_LINUX=True elif os.uname()[0] == 'FreeBSD': IS_FREEBSD=True + elif os.uname()[0] == 'NetBSD': + IS_NETBSD=True elif os.uname()[0] == 'OpenBSD': IS_OPENBSD=True elif os.uname()[0][:6] == 'CYGWIN': @@ -889,8 +895,13 @@ def is_CXX_gpp(): return is_compiler(CXX, 'g++') def is_clang_in_gpp_form(cc): - version_string = check_output([cc, '--version']).encode('utf-8').decode('utf-8') - return version_string.find('clang') != -1 + str = check_output([cc, '--version']) + try: + version_string = str.encode('utf-8') + except: + version_string = str + clang = 'clang'.encode('utf-8') + return version_string.find(clang) != -1 def is_CXX_clangpp(): if is_compiler(CXX, 'g++'): @@ -1240,7 +1251,7 @@ def get_so_ext(): sysname = os.uname()[0] if sysname == 'Darwin': return 'dylib' - elif sysname == 'Linux' or sysname == 'FreeBSD' or sysname == 'OpenBSD': + elif sysname == 'Linux' or sysname == 'FreeBSD' or sysname == 'NetBSD' or sysname == 'OpenBSD': return 'so' elif sysname == 'CYGWIN' or sysname.startswith('MSYS_NT') or sysname.startswith('MINGW'): return 'dll' @@ -1790,6 +1801,8 @@ class JavaDLLComponent(Component): t = t.replace('PLATFORM', 'linux') elif IS_FREEBSD: t = t.replace('PLATFORM', 'freebsd') + elif IS_NETBSD: + t = t.replace('PLATFORM', 'netbsd') elif IS_OPENBSD: t = t.replace('PLATFORM', 'openbsd') elif IS_CYGWIN: @@ -1919,8 +1932,14 @@ class MLComponent(Component): OCAML_FLAGS = '' if DEBUG_MODE: OCAML_FLAGS += '-g' - OCAMLCF = OCAMLC + ' ' + OCAML_FLAGS - OCAMLOPTF = OCAMLOPT + ' ' + OCAML_FLAGS + + if OCAMLFIND: + # Load Big_int, which is no longer part of the standard library, via the num package: https://github.com/ocaml/num + OCAMLCF = OCAMLFIND + ' ' + 'ocamlc -package num' + ' ' + OCAML_FLAGS + OCAMLOPTF = OCAMLFIND + ' ' + 'ocamlopt -package num' + ' ' + OCAML_FLAGS + else: + OCAMLCF = OCAMLC + ' ' + OCAML_FLAGS + OCAMLOPTF = OCAMLOPT + ' ' + OCAML_FLAGS src_dir = self.to_src_dir mk_dir(os.path.join(BUILD_DIR, self.sub_dir)) @@ -2492,6 +2511,7 @@ def mk_config(): LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS + SLIBEXTRAFLAGS = '%s -Wl,-soname,libz3.so' % SLIBEXTRAFLAGS elif sysname == 'FreeBSD': CXXFLAGS = '%s -D_FREEBSD_' % CXXFLAGS OS_DEFINES = '-D_FREEBSD_' @@ -2499,6 +2519,13 @@ def mk_config(): LDFLAGS = '%s -lrt' % LDFLAGS SLIBFLAGS = '-shared' SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS + elif sysname == 'NetBSD': + CXXFLAGS = '%s -D_NETBSD_' % CXXFLAGS + OS_DEFINES = '-D_NETBSD_' + SO_EXT = '.so' + LDFLAGS = '%s -lrt' % LDFLAGS + SLIBFLAGS = '-shared' + SLIBEXTRAFLAGS = '%s -lrt' % SLIBEXTRAFLAGS elif sysname == 'OpenBSD': CXXFLAGS = '%s -D_OPENBSD_' % CXXFLAGS OS_DEFINES = '-D_OPENBSD_' @@ -2783,6 +2810,7 @@ def get_header_files_for_components(component_src_dirs): def mk_install_tactic_cpp(cnames, path): component_src_dirs = [] for cname in cnames: + print("Component %s" % cname) c = get_component(cname) component_src_dirs.append(c.src_dir) h_files_full_path = get_header_files_for_components(component_src_dirs) diff --git a/scripts/update_api.py b/scripts/update_api.py index 45ea9be23..78fad45be 100755 --- a/scripts/update_api.py +++ b/scripts/update_api.py @@ -60,7 +60,7 @@ FIRST_OBJ_ID = 100 def is_obj(ty): return ty >= FIRST_OBJ_ID -Type2Str = { VOID : 'void', VOID_PTR : 'void*', INT : 'int', UINT : 'unsigned', INT64 : '__int64', UINT64 : '__uint64', DOUBLE : 'double', +Type2Str = { VOID : 'void', VOID_PTR : 'void*', INT : 'int', UINT : 'unsigned', INT64 : 'int64_t', UINT64 : 'uint64_t', DOUBLE : 'double', FLOAT : 'float', STRING : 'Z3_string', STRING_PTR : 'Z3_string_ptr', BOOL : 'Z3_bool', SYMBOL : 'Z3_symbol', PRINT_MODE : 'Z3_ast_print_mode', ERROR_CODE : 'Z3_error_code' } @@ -577,9 +577,6 @@ def mk_java(java_dir, package_name): java_wrapper = open(java_wrapperf, 'w') pkg_str = package_name.replace('.', '_') java_wrapper.write('// Automatically generated file\n') - java_wrapper.write('#ifdef _CYGWIN\n') - java_wrapper.write('typedef long long __int64;\n') - java_wrapper.write('#endif\n') java_wrapper.write('#include\n') java_wrapper.write('#include\n') java_wrapper.write('#include"z3.h"\n') @@ -957,11 +954,16 @@ def def_API(name, result, params): log_c.write(" }\n") log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_uint_array(%s)" % i) - elif ty == INT or ty == BOOL: + elif ty == INT: log_c.write("U(a%s[i]);" % i) log_c.write(" }\n") log_c.write(" Au(a%s);\n" % sz) exe_c.write("in.get_int_array(%s)" % i) + elif ty == BOOL: + log_c.write("U(a%s[i]);" % i) + log_c.write(" }\n") + log_c.write(" Au(a%s);\n" % sz) + exe_c.write("in.get_bool_array(%s)" % i) else: error ("unsupported parameter for %s, %s, %s" % (ty, name, p)) elif kind == OUT_ARRAY: @@ -1655,7 +1657,7 @@ else: if hasattr(builtins, "Z3_LIB_DIRS"): _all_dirs = builtins.Z3_LIB_DIRS -for v in ('Z3_LIBRARY_PATH', 'PATH'): +for v in ('Z3_LIBRARY_PATH', 'PATH', 'PYTHONPATH'): if v in os.environ: lp = os.environ[v]; lds = lp.split(';') if sys.platform in ('win32') else lp.split(':') @@ -1696,7 +1698,11 @@ if _lib is None: def _to_ascii(s): if isinstance(s, str): - return s.encode('ascii') + try: + return s.encode('ascii') + except: + # kick the bucket down the road. :-J + return s else: return s diff --git a/scripts/vsts-mac.sh b/scripts/vsts-mac.sh new file mode 100644 index 000000000..959dccbbd --- /dev/null +++ b/scripts/vsts-mac.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +cd .. +mkdir build +CSC=/usr/bin/csc GACUTIL=/usr/bin/gacutil CXX=clang++ CC=clang python scripts/mk_make.py --java --python +cd build +make +make test-z3 +make cpp_example +make c_example +# make java_example +# make python_example +./cpp_example +./test_capi + +git clone https://github.com/z3prover/z3test.git z3test +ls +python z3test/scripts/test_benchmarks.py ./z3 ./z3test/regressions/smt2 \ No newline at end of file diff --git a/scripts/vsts-vs2013.cmd b/scripts/vsts-vs2013.cmd new file mode 100644 index 000000000..1676f3e2d --- /dev/null +++ b/scripts/vsts-vs2013.cmd @@ -0,0 +1,49 @@ + +set +echo "Build" +md build +cd build +call "C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat" amd64 +cmake -DBUILD_DOTNET_BINDINGS=True -DBUILD_JAVA_BINDINGS=True -DBUILD_PYTHON_BINDINGS=True -G "NMake Makefiles" ../ +nmake +if ERRORLEVEL 1 exit 1 + +rem echo "Test python bindings" +rem pushd python +rem python z3test.py z3 +rem if ERRORLEVEL 1 exit 1 +rem python z3test.py z3num +rem if ERRORLEVEL 1 exit 1 +rem popd + +echo "Build and run examples" +nmake cpp_example +examples\cpp_example_build_dir\cpp_example.exe +if ERRORLEVEL 1 exit 1 + +nmake c_example +examples\c_example_build_dir\c_example.exe +if ERRORLEVEL 1 exit 1 + +rem nmake java_example +rem java_example.exe +if ERRORLEVEL 1 exit 1 + +rem nmake dotnet_example +rem dotnet_example.exe +if ERRORLEVEL 1 exit 1 + +echo "Build and run unit tests" +nmake test-z3 +rem TBD: test error level +rem test-z3.exe -a + + +cd .. +echo "Run regression tests" +git clone https://github.com/z3prover/z3test z3test +echo "test-benchmarks" +python z3test\scripts\test_benchmarks.py build\z3.exe z3test\regressions\smt2 +if ERRORLEVEL 1 exit 1 +echo "benchmarks tested" + diff --git a/scripts/vsts-vs2017.cmd b/scripts/vsts-vs2017.cmd new file mode 100644 index 000000000..4ec4deca9 --- /dev/null +++ b/scripts/vsts-vs2017.cmd @@ -0,0 +1,51 @@ +rem Supply argument x64 or x86 + +echo "Build" +md build +cd build +call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" %1 +cmake -DBUILD_DOTNET_BINDINGS=True -DBUILD_JAVA_BINDINGS=True -DBUILD_PYTHON_BINDINGS=True -G "NMake Makefiles" ../ +nmake +if ERRORLEVEL 1 exit 1 + +if %1 == "x86" goto :BUILD_EXAMPLES +echo "Test python bindings" +pushd python +python z3test.py z3 +if ERRORLEVEL 1 exit 1 +python z3test.py z3num +if ERRORLEVEL 1 exit 1 +popd + +:BUILD_EXAMPLES +echo "Build and run examples" +nmake cpp_example +examples\cpp_example_build_dir\cpp_example.exe +if ERRORLEVEL 1 exit 1 + +nmake c_example +examples\c_example_build_dir\c_example.exe +if ERRORLEVEL 1 exit 1 + +rem nmake java_example +rem java_example.exe +if ERRORLEVEL 1 exit 1 + +rem nmake dotnet_example +rem dotnet_example.exe +if ERRORLEVEL 1 exit 1 + +echo "Build and run unit tests" +nmake test-z3 +rem TBD: test error level +rem test-z3.exe -a + + +cd .. +echo "Run regression tests" +git clone https://github.com/z3prover/z3test z3test +echo "test-benchmarks" +python z3test\scripts\test_benchmarks.py build\z3.exe z3test\regressions\smt2 +if ERRORLEVEL 1 exit 1 +echo "benchmarks tested" + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7cde89ae2..7dee4039a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,7 +12,6 @@ set(Z3_API_HEADER_FILES_TO_SCAN z3_rcf.h z3_fixedpoint.h z3_optimization.h - z3_interp.h z3_fpa.h z3_spacer.h ) @@ -36,10 +35,10 @@ endforeach() # raised if you try to declare a component is dependent on another component # that has not yet been declared. add_subdirectory(util) -add_subdirectory(util/lp) add_subdirectory(math/polynomial) add_subdirectory(sat) add_subdirectory(nlsat) +add_subdirectory(util/lp) add_subdirectory(math/hilbert) add_subdirectory(math/simplex) add_subdirectory(math/automata) @@ -56,14 +55,13 @@ add_subdirectory(parsers/util) add_subdirectory(math/grobner) add_subdirectory(math/euclid) add_subdirectory(tactic/core) -add_subdirectory(sat/tactic) -add_subdirectory(tactic/arith) -add_subdirectory(nlsat/tactic) add_subdirectory(math/subpaving/tactic) add_subdirectory(tactic/aig) add_subdirectory(solver) +add_subdirectory(sat/tactic) +add_subdirectory(tactic/arith) +add_subdirectory(nlsat/tactic) add_subdirectory(ackermannization) -add_subdirectory(interp) add_subdirectory(cmd_context) add_subdirectory(cmd_context/extra_cmds) add_subdirectory(parsers/smt2) @@ -79,20 +77,16 @@ add_subdirectory(tactic/bv) add_subdirectory(smt/tactic) add_subdirectory(tactic/sls) add_subdirectory(qe) -add_subdirectory(duality) add_subdirectory(muz/base) add_subdirectory(muz/dataflow) add_subdirectory(muz/transforms) add_subdirectory(muz/rel) -add_subdirectory(muz/pdr) add_subdirectory(muz/clp) add_subdirectory(muz/tab) add_subdirectory(muz/bmc) add_subdirectory(muz/ddnf) -add_subdirectory(muz/duality) add_subdirectory(muz/spacer) add_subdirectory(muz/fp) -add_subdirectory(tactic/nlsat_smt) add_subdirectory(tactic/ufbv) add_subdirectory(sat/sat_solver) add_subdirectory(tactic/smtlogics) @@ -160,7 +154,6 @@ set (libz3_public_headers z3_fpa.h z3.h c++/z3++.h - z3_interp.h z3_macros.h z3_optimization.h z3_polynomial.h diff --git a/src/ackermannization/ackermannize_bv_tactic.cpp b/src/ackermannization/ackermannize_bv_tactic.cpp index 82ef19274..3ffee518e 100644 --- a/src/ackermannization/ackermannize_bv_tactic.cpp +++ b/src/ackermannization/ackermannize_bv_tactic.cpp @@ -27,14 +27,9 @@ public: : m(m), m_p(p) {} - virtual ~ackermannize_bv_tactic() { } + ~ackermannize_bv_tactic() override { } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; + void operator()(goal_ref const & g, goal_ref_buffer & result) override { tactic_report report("ackermannize", *g); fail_if_unsat_core_generation("ackermannize", g); fail_if_proof_generation("ackermannize", g); @@ -43,7 +38,7 @@ public: expr_ref_vector flas(m); const unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) flas.push_back(g->form(i)); - lackr lackr(m, m_p, m_st, flas, NULL); + lackr lackr(m, m_p, m_st, flas, nullptr); // mk result goal_ref resg(alloc(goal, *g, true)); @@ -52,41 +47,38 @@ public: TRACE("ackermannize", tout << "ackermannize not run due to limit" << std::endl;); result.reset(); result.push_back(g.get()); - mc = 0; - pc = 0; - core = 0; return; } result.push_back(resg.get()); // report model if (g->models_enabled()) { - mc = mk_ackermannize_bv_model_converter(m, lackr.get_info()); + resg->add(mk_ackermannize_bv_model_converter(m, lackr.get_info())); } - + resg->inc_depth(); TRACE("ackermannize", resg->display(tout << "out\n");); SASSERT(resg->is_well_sorted()); } - void updt_params(params_ref const & _p) { + void updt_params(params_ref const & _p) override { ackermannize_bv_tactic_params p(_p); m_lemma_limit = p.div0_ackermann_limit(); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { ackermannize_bv_tactic_params::collect_param_descrs(r); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.update("ackr-constraints", m_st.m_ackrs_sz); } - virtual void reset_statistics() { m_st.reset(); } + void reset_statistics() override { m_st.reset(); } - virtual void cleanup() { } + void cleanup() override { } - virtual tactic* translate(ast_manager& m) { + tactic* translate(ast_manager& m) override { return alloc(ackermannize_bv_tactic, m, m_p); } private: diff --git a/src/ackermannization/ackr_bound_probe.cpp b/src/ackermannization/ackr_bound_probe.cpp index 50b9cc092..c6cdaf268 100644 --- a/src/ackermannization/ackr_bound_probe.cpp +++ b/src/ackermannization/ackr_bound_probe.cpp @@ -45,7 +45,7 @@ class ackr_bound_probe : public probe { if (a->get_num_args() == 0) return; if (!m_ackr_helper.should_ackermannize(a)) return; func_decl* const fd = a->get_decl(); - app_set* ts = 0; + app_set* ts = nullptr; if (!m_fun2terms.find(fd, ts)) { ts = alloc(app_set); m_fun2terms.insert(fd, ts); @@ -57,7 +57,7 @@ class ackr_bound_probe : public probe { public: ackr_bound_probe() {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { proc p(g.m()); unsigned sz = g.size(); expr_fast_mark1 visited; diff --git a/src/ackermannization/ackr_info.h b/src/ackermannization/ackr_info.h index 0b67e144f..db5f64a70 100644 --- a/src/ackermannization/ackr_info.h +++ b/src/ackermannization/ackr_info.h @@ -65,7 +65,7 @@ class ackr_info { } inline app* find_term(func_decl* c) const { - app * rv = 0; + app * rv = nullptr; m_c2t.find(c,rv); return rv; } diff --git a/src/ackermannization/ackr_model_converter.cpp b/src/ackermannization/ackr_model_converter.cpp index a7c021913..9e87a1a69 100644 --- a/src/ackermannization/ackr_model_converter.cpp +++ b/src/ackermannization/ackr_model_converter.cpp @@ -37,10 +37,11 @@ public: , fixed_model(false) { } - virtual ~ackr_model_converter() { } + ~ackr_model_converter() override { } - virtual void operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); + void get_units(obj_map& units) override { units.reset(); } + + void operator()(model_ref & md) override { SASSERT(!fixed_model || md.get() == 0 || (!md->get_num_constants() && !md->get_num_functions())); model_ref& old_model = fixed_model ? abstr_model : md; SASSERT(old_model.get()); @@ -49,9 +50,7 @@ public: md = new_model; } - virtual void operator()(model_ref & md) { operator()(md, 0); } - - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { ackr_info_ref retv_info = info->translate(translator); if (fixed_model) { model_ref retv_mod_ref = abstr_model->translate(translator); @@ -62,6 +61,10 @@ public: } } + void display(std::ostream & out) override { + out << "(ackr-model-converter)\n"; + } + protected: ast_manager & m; const ackr_info_ref info; @@ -116,7 +119,7 @@ void ackr_model_converter::add_entry(model_evaluator & evaluator, << mk_ismt2_pp(value, m, 2) << "\n"; ); - func_interp * fi = 0; + func_interp * fi = nullptr; func_decl * const declaration = term->get_decl(); const unsigned sz = declaration->get_arity(); SASSERT(sz == term->get_num_args()); @@ -133,7 +136,7 @@ void ackr_model_converter::add_entry(model_evaluator & evaluator, evaluator(aarg, arg_value); args.push_back(arg_value); } - if (fi->get_entry(args.c_ptr()) == 0) { + if (fi->get_entry(args.c_ptr()) == nullptr) { TRACE("ackr_model", tout << mk_ismt2_pp(declaration, m) << " args: " << std::endl; for (unsigned i = 0; i < args.size(); i++) @@ -144,6 +147,7 @@ void ackr_model_converter::add_entry(model_evaluator & evaluator, else { TRACE("ackr_model", tout << "entry already present\n";); } + } model_converter * mk_ackr_model_converter(ast_manager & m, const ackr_info_ref& info) { diff --git a/src/ackermannization/lackr.cpp b/src/ackermannization/lackr.cpp index aac98d7dc..b739af9d3 100644 --- a/src/ackermannization/lackr.cpp +++ b/src/ackermannization/lackr.cpp @@ -185,7 +185,7 @@ void lackr::add_term(app* a) { if (a->get_num_args() == 0) return; if (!m_ackr_helper.should_ackermannize(a)) return; func_decl* const fd = a->get_decl(); - app_set* ts = 0; + app_set* ts = nullptr; if (!m_fun2terms.find(fd, ts)) { ts = alloc(app_set); m_fun2terms.insert(fd, ts); @@ -205,7 +205,7 @@ lbool lackr::eager() { SASSERT(m_is_init); push_abstraction(); TRACE("lackr", tout << "run sat 0\n"; ); - const lbool rv0 = m_sat->check_sat(0, 0); + const lbool rv0 = m_sat->check_sat(0, nullptr); if (rv0 == l_false) return l_false; eager_enc(); expr_ref all(m_m); @@ -213,7 +213,7 @@ lbool lackr::eager() { m_simp(all); m_sat->assert_expr(all); TRACE("lackr", tout << "run sat all\n"; ); - return m_sat->check_sat(0, 0); + return m_sat->check_sat(0, nullptr); } lbool lackr::lazy() { @@ -225,7 +225,7 @@ lbool lackr::lazy() { m_st.m_it++; checkpoint(); TRACE("lackr", tout << "lazy check: " << m_st.m_it << "\n";); - const lbool r = m_sat->check_sat(0, 0); + const lbool r = m_sat->check_sat(0, nullptr); if (r == l_undef) return l_undef; // give up if (r == l_false) return l_false; // abstraction unsat // reconstruct model diff --git a/src/ackermannization/lackr_model_constructor.cpp b/src/ackermannization/lackr_model_constructor.cpp index 641c70cbc..420fbda10 100644 --- a/src/ackermannization/lackr_model_constructor.cpp +++ b/src/ackermannization/lackr_model_constructor.cpp @@ -34,7 +34,7 @@ struct lackr_model_constructor::imp { , m_conflicts(conflicts) , m_b_rw(m) , m_bv_rw(m) - , m_evaluator(NULL) + , m_evaluator(nullptr) , m_empty_model(m) , m_ackr_helper(m) {} @@ -121,7 +121,7 @@ struct lackr_model_constructor::imp { void add_entry(app* term, expr* value, obj_map& interpretations) { - func_interp* fi = 0; + func_interp* fi = nullptr; func_decl * const declaration = term->get_decl(); const unsigned sz = declaration->get_arity(); SASSERT(sz == term->get_num_args()); @@ -169,7 +169,7 @@ struct lackr_model_constructor::imp { // Stops upon the first failure. // Returns true if and only if all congruence checks succeeded. bool _check_stack() { - if (m_evaluator == NULL) m_evaluator = alloc(model_evaluator, m_empty_model); + if (m_evaluator == nullptr) m_evaluator = alloc(model_evaluator, m_empty_model); expr * curr; while (!m_stack.empty()) { curr = m_stack.back(); @@ -276,7 +276,7 @@ struct lackr_model_constructor::imp { SASSERT(a->get_num_args() == 0); func_decl * const fd = a->get_decl(); expr * val = m_abstr_model->get_const_interp(fd); - if (val == 0) { // TODO: avoid model completetion? + if (val == nullptr) { // TODO: avoid model completetion? sort * s = fd->get_range(); val = m_abstr_model->get_some_value(s); } @@ -295,7 +295,7 @@ struct lackr_model_constructor::imp { expr_ref value(m_m); value = m_abstr_model->get_const_interp(ac->get_decl()); // get ackermann constant's interpretation - if (value.get() == 0) { // TODO: avoid model completion? + if (value.get() == nullptr) { // TODO: avoid model completion? sort * s = a_fd->get_range(); value = m_abstr_model->get_some_value(s); } @@ -362,7 +362,7 @@ struct lackr_model_constructor::imp { }; lackr_model_constructor::lackr_model_constructor(ast_manager& m, ackr_info_ref info) - : m_imp(0) + : m_imp(nullptr) , m_m(m) , m_state(UNKNOWN) , m_info(info) @@ -377,7 +377,7 @@ bool lackr_model_constructor::check(model_ref& abstr_model) { m_conflicts.reset(); if (m_imp) { dealloc(m_imp); - m_imp = 0; + m_imp = nullptr; } m_imp = alloc(lackr_model_constructor::imp, m_m, m_info, abstr_model, m_conflicts); const bool rv = m_imp->check(); diff --git a/src/ackermannization/lackr_model_converter_lazy.cpp b/src/ackermannization/lackr_model_converter_lazy.cpp index 2a7adb839..a37373aab 100644 --- a/src/ackermannization/lackr_model_converter_lazy.cpp +++ b/src/ackermannization/lackr_model_converter_lazy.cpp @@ -28,10 +28,9 @@ public: , model_constructor(lmc) { } - virtual ~lackr_model_converter_lazy() { } + ~lackr_model_converter_lazy() override { } - virtual void operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); + void operator()(model_ref & md) override { SASSERT(md.get() == 0 || (!md->get_num_constants() && !md->get_num_functions())); SASSERT(model_constructor.get()); model * new_model = alloc(model, m); @@ -39,15 +38,18 @@ public: model_constructor->make_model(md); } - virtual void operator()(model_ref & md) { - operator()(md, 0); - } + void get_units(obj_map& units) override { units.reset(); } //void display(std::ostream & out); - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { NOT_IMPLEMENTED_YET(); } + + void display(std::ostream & out) override { + out << "(lackr-model-converter)\n"; + } + protected: ast_manager& m; const lackr_model_constructor_ref model_constructor; diff --git a/src/api/CMakeLists.txt b/src/api/CMakeLists.txt index fcdbb1651..4a5514a7b 100644 --- a/src/api/CMakeLists.txt +++ b/src/api/CMakeLists.txt @@ -48,7 +48,6 @@ z3_add_component(api api_datatype.cpp api_fpa.cpp api_goal.cpp - api_interp.cpp api_log.cpp api_model.cpp api_numeral.cpp @@ -67,7 +66,6 @@ z3_add_component(api z3_replayer.cpp ${full_path_generated_files} COMPONENT_DEPENDENCIES - interp opt portfolio realclosure diff --git a/src/api/api_algebraic.cpp b/src/api/api_algebraic.cpp index 96d8392ba..47d91209e 100644 --- a/src/api/api_algebraic.cpp +++ b/src/api/api_algebraic.cpp @@ -162,57 +162,57 @@ extern "C" { Z3_TRY; LOG_Z3_algebraic_add(c, a, b); RESET_ERROR_CODE(); - CHECK_IS_ALGEBRAIC_X(a, 0); - CHECK_IS_ALGEBRAIC_X(b, 0); + CHECK_IS_ALGEBRAIC_X(a, nullptr); + CHECK_IS_ALGEBRAIC_X(b, nullptr); BIN_OP(+,add); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_sub(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_sub(c, a, b); RESET_ERROR_CODE(); - CHECK_IS_ALGEBRAIC_X(a, 0); - CHECK_IS_ALGEBRAIC_X(b, 0); + CHECK_IS_ALGEBRAIC_X(a, nullptr); + CHECK_IS_ALGEBRAIC_X(b, nullptr); BIN_OP(-,sub); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_mul(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_mul(c, a, b); RESET_ERROR_CODE(); - CHECK_IS_ALGEBRAIC_X(a, 0); - CHECK_IS_ALGEBRAIC_X(b, 0); + CHECK_IS_ALGEBRAIC_X(a, nullptr); + CHECK_IS_ALGEBRAIC_X(b, nullptr); BIN_OP(*,mul); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_div(Z3_context c, Z3_ast a, Z3_ast b) { Z3_TRY; LOG_Z3_algebraic_div(c, a, b); RESET_ERROR_CODE(); - CHECK_IS_ALGEBRAIC_X(a, 0); - CHECK_IS_ALGEBRAIC_X(b, 0); + CHECK_IS_ALGEBRAIC_X(a, nullptr); + CHECK_IS_ALGEBRAIC_X(b, nullptr); if ((is_rational(c, b) && get_rational(c, b).is_zero()) || (!is_rational(c, b) && am(c).is_zero(get_irrational(c, b)))) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } BIN_OP(/,div); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_root(Z3_context c, Z3_ast a, unsigned k) { Z3_TRY; LOG_Z3_algebraic_root(c, a, k); RESET_ERROR_CODE(); - CHECK_IS_ALGEBRAIC_X(a, 0); + CHECK_IS_ALGEBRAIC_X(a, nullptr); if (k % 2 == 0) { if ((is_rational(c, a) && get_rational(c, a).is_neg()) || (!is_rational(c, a) && am(c).is_neg(get_irrational(c, a)))) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } } algebraic_numbers::manager & _am = am(c); @@ -229,14 +229,14 @@ extern "C" { expr * r = au(c).mk_numeral(_r, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_algebraic_power(Z3_context c, Z3_ast a, unsigned k) { Z3_TRY; LOG_Z3_algebraic_power(c, a, k); RESET_ERROR_CODE(); - CHECK_IS_ALGEBRAIC_X(a, 0); + CHECK_IS_ALGEBRAIC_X(a, nullptr); algebraic_numbers::manager & _am = am(c); scoped_anum _r(_am); if (is_rational(c, a)) { @@ -251,7 +251,7 @@ extern "C" { expr * r = au(c).mk_numeral(_r, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } #define BIN_PRED(RAT_PRED, IRAT_PRED) \ @@ -345,9 +345,9 @@ extern "C" { public: vector_var2anum(scoped_anum_vector & as):m_as(as) {} virtual ~vector_var2anum() {} - virtual algebraic_numbers::manager & m() const { return m_as.m(); } - virtual bool contains(polynomial::var x) const { return static_cast(x) < m_as.size(); } - virtual algebraic_numbers::anum const & operator()(polynomial::var x) const { return m_as.get(x); } + algebraic_numbers::manager & m() const override { return m_as.m(); } + bool contains(polynomial::var x) const override { return static_cast(x) < m_as.size(); } + algebraic_numbers::anum const & operator()(polynomial::var x) const override { return m_as.get(x); } }; Z3_ast_vector Z3_API Z3_algebraic_roots(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) { @@ -357,17 +357,17 @@ extern "C" { polynomial::manager & pm = mk_c(c)->pm(); polynomial_ref _p(pm); polynomial::scoped_numeral d(pm.m()); - expr2polynomial converter(mk_c(c)->m(), pm, 0, true); + expr2polynomial converter(mk_c(c)->m(), pm, nullptr, true); if (!converter.to_polynomial(to_expr(p), _p, d) || static_cast(max_var(_p)) >= n + 1) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } algebraic_numbers::manager & _am = am(c); scoped_anum_vector as(_am); if (!to_anum_vector(c, n, a, as)) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } scoped_anum_vector roots(_am); { @@ -383,7 +383,7 @@ extern "C" { result->m_ast_vector.push_back(au(c).mk_numeral(roots.get(i), false)); } RETURN_Z3(of_ast_vector(result)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } int Z3_API Z3_algebraic_eval(Z3_context c, Z3_ast p, unsigned n, Z3_ast a[]) { @@ -393,7 +393,7 @@ extern "C" { polynomial::manager & pm = mk_c(c)->pm(); polynomial_ref _p(pm); polynomial::scoped_numeral d(pm.m()); - expr2polynomial converter(mk_c(c)->m(), pm, 0, true); + expr2polynomial converter(mk_c(c)->m(), pm, nullptr, true); if (!converter.to_polynomial(to_expr(p), _p, d) || static_cast(max_var(_p)) >= n) { SET_ERROR_CODE(Z3_INVALID_ARG); diff --git a/src/api/api_arith.cpp b/src/api/api_arith.cpp index 8fbed6f46..9bb236fe2 100644 --- a/src/api/api_arith.cpp +++ b/src/api/api_arith.cpp @@ -34,7 +34,7 @@ extern "C" { RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), INT_SORT)); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_real_sort(Z3_context c) { @@ -43,7 +43,7 @@ extern "C" { RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT)); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_real(Z3_context c, int num, int den) { @@ -52,12 +52,12 @@ extern "C" { RESET_ERROR_CODE(); if (den == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } sort* s = mk_c(c)->m().mk_sort(mk_c(c)->get_arith_fid(), REAL_SORT); ast* a = mk_c(c)->mk_numeral_core(rational(num, den), s); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } MK_ARITH_OP(Z3_mk_add, OP_ADD); @@ -77,11 +77,11 @@ extern "C" { k = OP_DIV; } expr * args[2] = { to_expr(n1), to_expr(n2) }; - ast* a = mk_c(c)->m().mk_app(mk_c(c)->get_arith_fid(), k, 0, 0, 2, args); + ast* a = mk_c(c)->m().mk_app(mk_c(c)->get_arith_fid(), k, 0, nullptr, 2, args); mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } MK_ARITH_PRED(Z3_mk_lt, OP_LT); @@ -98,17 +98,17 @@ extern "C" { RESET_ERROR_CODE(); if (num_args == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } expr* r = to_expr(args[0]); for (unsigned i = 1; i < num_args; ++i) { expr* args1[2] = { r, to_expr(args[i]) }; - r = mk_c(c)->m().mk_app(mk_c(c)->get_arith_fid(), OP_SUB, 0, 0, 2, args1); + r = mk_c(c)->m().mk_app(mk_c(c)->get_arith_fid(), OP_SUB, 0, nullptr, 2, args1); check_sorts(c, r); } mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_unary_minus(Z3_context c, Z3_ast n) { @@ -116,7 +116,7 @@ extern "C" { LOG_Z3_mk_unary_minus(c, n); RESET_ERROR_CODE(); MK_UNARY_BODY(Z3_mk_unary_minus, mk_c(c)->get_arith_fid(), OP_UMINUS, SKIP); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_is_algebraic_number(Z3_context c, Z3_ast a) { @@ -134,7 +134,7 @@ extern "C" { RESET_ERROR_CODE(); if (!Z3_is_algebraic_number(c, a)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } expr * e = to_expr(a); algebraic_numbers::anum const & val = mk_c(c)->autil().to_irrational_algebraic_numeral(e); @@ -143,7 +143,7 @@ extern "C" { expr * r = mk_c(c)->autil().mk_numeral(l, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_algebraic_number_upper(Z3_context c, Z3_ast a, unsigned precision) { @@ -152,7 +152,7 @@ extern "C" { RESET_ERROR_CODE(); if (!Z3_is_algebraic_number(c, a)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } expr * e = to_expr(a); algebraic_numbers::anum const & val = mk_c(c)->autil().to_irrational_algebraic_numeral(e); @@ -161,7 +161,7 @@ extern "C" { expr * r = mk_c(c)->autil().mk_numeral(l, false); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_numerator(Z3_context c, Z3_ast a) { @@ -172,12 +172,12 @@ extern "C" { ast * _a = to_ast(a); if (!is_expr(_a) || !mk_c(c)->autil().is_numeral(to_expr(_a), val)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } expr * r = mk_c(c)->autil().mk_numeral(numerator(val), true); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_denominator(Z3_context c, Z3_ast a) { @@ -188,12 +188,12 @@ extern "C" { ast * _a = to_ast(a); if (!is_expr(_a) || !mk_c(c)->autil().is_numeral(to_expr(_a), val)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } expr * r = mk_c(c)->autil().mk_numeral(denominator(val), true); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } }; diff --git a/src/api/api_array.cpp b/src/api/api_array.cpp index 5e6764dff..61c37ec3e 100644 --- a/src/api/api_array.cpp +++ b/src/api/api_array.cpp @@ -31,7 +31,7 @@ extern "C" { sort * ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, 2, params); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_array_sort_n(Z3_context c, unsigned n, Z3_sort const* domain, Z3_sort range) { @@ -44,7 +44,7 @@ extern "C" { sort * ty = mk_c(c)->m().mk_sort(mk_c(c)->get_array_fid(), ARRAY_SORT, params.size(), params.c_ptr()); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_select(Z3_context c, Z3_ast a, Z3_ast i) { @@ -58,7 +58,7 @@ extern "C" { sort * i_ty = m.get_sort(_i); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } sort * domain[2] = {a_ty, i_ty}; func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_SELECT, 2, a_ty->get_parameters(), 2, domain); @@ -67,7 +67,7 @@ extern "C" { mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_select_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs) { @@ -81,7 +81,7 @@ extern "C" { // sort * i_ty = m.get_sort(_i); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ptr_vector domain; ptr_vector args; @@ -96,7 +96,7 @@ extern "C" { mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_store(Z3_context c, Z3_ast a, Z3_ast i, Z3_ast v) { @@ -112,7 +112,7 @@ extern "C" { sort * v_ty = m.get_sort(_v); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } sort * domain[3] = {a_ty, i_ty, v_ty}; func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_STORE, 2, a_ty->get_parameters(), 3, domain); @@ -121,7 +121,7 @@ extern "C" { mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_store_n(Z3_context c, Z3_ast a, unsigned n, Z3_ast const* idxs, Z3_ast v) { @@ -135,7 +135,7 @@ extern "C" { sort * v_ty = m.get_sort(_v); if (a_ty->get_family_id() != mk_c(c)->get_array_fid()) { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ptr_vector domain; ptr_vector args; @@ -152,7 +152,7 @@ extern "C" { mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_map(Z3_context c, Z3_func_decl f, unsigned n, Z3_ast const* args) { @@ -161,7 +161,7 @@ extern "C" { RESET_ERROR_CODE(); if (n == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ast_manager & m = mk_c(c)->m(); func_decl* _f = to_func_decl(f); @@ -177,7 +177,7 @@ extern "C" { mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_const_array(Z3_context c, Z3_sort domain, Z3_ast v) { @@ -196,7 +196,7 @@ extern "C" { mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_array_default(Z3_context c, Z3_ast array) { @@ -206,12 +206,12 @@ extern "C" { ast_manager & m = mk_c(c)->m(); expr * _a = to_expr(array); - func_decl * f = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_ARRAY_DEFAULT, 0, 0, 1, &_a); + func_decl * f = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_ARRAY_DEFAULT, 0, nullptr, 1, &_a); app * r = m.mk_app(f, 1, &_a); mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast mk_app_array_core(Z3_context c, Z3_sort domain, Z3_ast v) { @@ -233,7 +233,7 @@ extern "C" { Z3_sort Z3_API Z3_mk_set_sort(Z3_context c, Z3_sort ty) { Z3_TRY; return Z3_mk_array_sort(c, ty, Z3_mk_bool_sort(c)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_empty_set(Z3_context c, Z3_sort domain) { @@ -242,7 +242,7 @@ extern "C" { RESET_ERROR_CODE(); Z3_ast r = mk_app_array_core(c, domain, Z3_mk_false(c)); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_full_set(Z3_context c, Z3_sort domain) { @@ -251,7 +251,7 @@ extern "C" { RESET_ERROR_CODE(); Z3_ast r = mk_app_array_core(c, domain, Z3_mk_true(c)); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } MK_NARY(Z3_mk_set_union, mk_c(c)->get_array_fid(), OP_SET_UNION, SKIP); @@ -270,7 +270,7 @@ extern "C" { app * r = a.mk_as_array(to_func_decl(f)); mk_c(c)->save_ast_trail(r); return of_ast(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_mk_set_member(Z3_context c, Z3_ast elem, Z3_ast set) { @@ -289,22 +289,22 @@ extern "C" { Z3_TRY; LOG_Z3_get_array_sort_domain(c, t); RESET_ERROR_CODE(); - CHECK_VALID_AST(t, 0); + CHECK_VALID_AST(t, nullptr); if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { Z3_sort r = reinterpret_cast(to_sort(t)->get_parameter(0).get_ast()); RETURN_Z3(r); } SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); - Z3_CATCH_RETURN(0); + RETURN_Z3(nullptr); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_array_sort_range(Z3_context c, Z3_sort t) { Z3_TRY; LOG_Z3_get_array_sort_range(c, t); RESET_ERROR_CODE(); - CHECK_VALID_AST(t, 0); + CHECK_VALID_AST(t, nullptr); if (to_sort(t)->get_family_id() == mk_c(c)->get_array_fid() && to_sort(t)->get_decl_kind() == ARRAY_SORT) { unsigned n = to_sort(t)->get_num_parameters(); @@ -312,8 +312,8 @@ extern "C" { RETURN_Z3(r); } SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); - Z3_CATCH_RETURN(0); + RETURN_Z3(nullptr); + Z3_CATCH_RETURN(nullptr); } }; diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index ae23ca100..34168f1f4 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -49,11 +49,11 @@ extern "C" { RESET_ERROR_CODE(); if (i < 0 || (size_t)i >= (SIZE_MAX >> PTR_ALIGNMENT)) { SET_ERROR_CODE(Z3_IOB); - return 0; + return nullptr; } Z3_symbol result = of_symbol(symbol(i)); return result; - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_symbol Z3_API Z3_mk_string_symbol(Z3_context c, char const * str) { @@ -61,13 +61,13 @@ extern "C" { LOG_Z3_mk_string_symbol(c, str); RESET_ERROR_CODE(); symbol s; - if (str == 0 || *str == 0) + if (str == nullptr || *str == 0) s = symbol::null; else s = symbol(str); Z3_symbol result = of_symbol(s); return result; - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_is_eq_sort(Z3_context c, Z3_sort s1, Z3_sort s2) { @@ -82,7 +82,7 @@ extern "C" { sort* ty = mk_c(c)->m().mk_uninterpreted_sort(to_symbol(name)); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_is_eq_ast(Z3_context c, Z3_ast s1, Z3_ast s2) { @@ -107,7 +107,7 @@ extern "C" { mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_app(Z3_context c, Z3_func_decl d, unsigned num_args, Z3_ast const * args) { @@ -123,7 +123,7 @@ extern "C" { mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_const(Z3_context c, Z3_symbol s, Z3_sort ty) { @@ -133,7 +133,7 @@ extern "C" { app* a = mk_c(c)->m().mk_const(mk_c(c)->m().mk_const_decl(to_symbol(s), to_sort(ty))); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } @@ -142,7 +142,7 @@ extern "C" { Z3_TRY; LOG_Z3_mk_fresh_func_decl(c, prefix, domain_size, domain, range); RESET_ERROR_CODE(); - if (prefix == 0) { + if (prefix == nullptr) { prefix = ""; } @@ -153,20 +153,20 @@ extern "C" { mk_c(c)->save_ast_trail(d); RETURN_Z3(of_func_decl(d)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fresh_const(Z3_context c, const char * prefix, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fresh_const(c, prefix, ty); RESET_ERROR_CODE(); - if (prefix == 0) { + if (prefix == nullptr) { prefix = ""; } app* a = mk_c(c)->m().mk_fresh_const(prefix, to_sort(ty)); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_true(Z3_context c) { @@ -175,7 +175,7 @@ extern "C" { RESET_ERROR_CODE(); Z3_ast r = of_ast(mk_c(c)->m().mk_true()); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_false(Z3_context c) { @@ -184,18 +184,17 @@ extern "C" { RESET_ERROR_CODE(); Z3_ast r = of_ast(mk_c(c)->m().mk_false()); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } MK_UNARY(Z3_mk_not, mk_c(c)->get_basic_fid(), OP_NOT, SKIP); MK_BINARY(Z3_mk_eq, mk_c(c)->get_basic_fid(), OP_EQ, SKIP); MK_NARY(Z3_mk_distinct, mk_c(c)->get_basic_fid(), OP_DISTINCT, SKIP); - MK_BINARY(Z3_mk_iff, mk_c(c)->get_basic_fid(), OP_IFF, SKIP); + MK_BINARY(Z3_mk_iff, mk_c(c)->get_basic_fid(), OP_EQ, SKIP); MK_BINARY(Z3_mk_implies, mk_c(c)->get_basic_fid(), OP_IMPLIES, SKIP); MK_BINARY(Z3_mk_xor, mk_c(c)->get_basic_fid(), OP_XOR, SKIP); MK_NARY(Z3_mk_and, mk_c(c)->get_basic_fid(), OP_AND, SKIP); MK_NARY(Z3_mk_or, mk_c(c)->get_basic_fid(), OP_OR, SKIP); - MK_UNARY(Z3_mk_interpolant, mk_c(c)->get_basic_fid(), OP_INTERP, SKIP); Z3_ast mk_ite_core(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_ast t3) { expr * result = mk_c(c)->m().mk_ite(to_expr(t1), to_expr(t2), to_expr(t3)); @@ -210,7 +209,7 @@ extern "C" { RESET_ERROR_CODE(); Z3_ast r = mk_ite_core(c, t1, t2, t3); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_bool_sort(Z3_context c) { @@ -219,7 +218,7 @@ extern "C" { RESET_ERROR_CODE(); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->m().get_basic_family_id(), BOOL_SORT)); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_app_to_ast(Z3_context c, Z3_app a) { @@ -335,7 +334,7 @@ extern "C" { Z3_bool Z3_API Z3_is_app(Z3_context c, Z3_ast a) { LOG_Z3_is_app(c, a); RESET_ERROR_CODE(); - return a != 0 && is_app(reinterpret_cast(a)); + return a != nullptr && is_app(reinterpret_cast(a)); } Z3_app Z3_API Z3_to_app(Z3_context c, Z3_ast a) { @@ -357,7 +356,7 @@ extern "C" { RESET_ERROR_CODE(); if (!is_app(reinterpret_cast(a))) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } RETURN_Z3(of_func_decl(to_app(a)->get_decl())); } @@ -373,11 +372,11 @@ extern "C" { RESET_ERROR_CODE(); if (!is_app(reinterpret_cast(a))) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } if (i >= to_app(a)->get_num_args()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } RETURN_Z3(of_ast(to_app(a)->get_arg(i))); } @@ -466,15 +465,15 @@ extern "C" { RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); - return 0; + return nullptr; } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_symbol()) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } return of_symbol(p.get_symbol()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_decl_sort_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { @@ -483,15 +482,15 @@ extern "C" { RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast() || !is_sort(p.get_ast())) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } RETURN_Z3(of_sort(to_sort(p.get_ast()))); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_decl_ast_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { @@ -500,15 +499,15 @@ extern "C" { RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast()) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } RETURN_Z3(of_ast(p.get_ast())); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_get_decl_func_decl_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { @@ -517,15 +516,15 @@ extern "C" { RESET_ERROR_CODE(); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } parameter const& p = to_func_decl(d)->get_parameters()[idx]; if (!p.is_ast() || !is_func_decl(p.get_ast())) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } RETURN_Z3(of_func_decl(to_func_decl(p.get_ast()))); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_get_decl_rational_parameter(Z3_context c, Z3_func_decl d, unsigned idx) { @@ -551,17 +550,17 @@ extern "C" { LOG_Z3_get_sort_name(c, t); RESET_ERROR_CODE(); return of_symbol(to_sort(t)->get_name()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_sort(Z3_context c, Z3_ast a) { Z3_TRY; LOG_Z3_get_sort(c, a); RESET_ERROR_CODE(); - CHECK_IS_EXPR(a, 0); + CHECK_IS_EXPR(a, nullptr); Z3_sort r = of_sort(mk_c(c)->m().get_sort(to_expr(a))); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_arity(Z3_context c, Z3_func_decl d) { @@ -586,21 +585,21 @@ extern "C" { RESET_ERROR_CODE(); if (i >= to_func_decl(d)->get_arity()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } Z3_sort r = of_sort(to_func_decl(d)->get_domain(i)); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_range(Z3_context c, Z3_func_decl d) { Z3_TRY; LOG_Z3_get_range(c, d); RESET_ERROR_CODE(); - CHECK_VALID_AST(d, 0); + CHECK_VALID_AST(d, nullptr); Z3_sort r = of_sort(to_func_decl(d)->get_range()); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort_kind Z3_get_sort_kind(Z3_context c, Z3_sort t) { @@ -688,17 +687,17 @@ extern "C" { } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); - return 0; + return nullptr; } } mk_c(c)->save_ast_trail(result); return of_ast(result.get()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_simplify(Z3_context c, Z3_ast _a) { LOG_Z3_simplify(c, _a); - RETURN_Z3(simplify(c, _a, 0)); + RETURN_Z3(simplify(c, _a, nullptr)); } Z3_ast Z3_API Z3_simplify_ex(Z3_context c, Z3_ast _a, Z3_params p) { @@ -727,7 +726,7 @@ extern "C" { th_rewriter::get_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_update_term(Z3_context c, Z3_ast _a, unsigned num_args, Z3_ast const _args[]) { @@ -762,7 +761,7 @@ extern "C" { } mk_c(c)->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_substitute(Z3_context c, @@ -777,11 +776,11 @@ extern "C" { expr * a = to_expr(_a); expr * const * from = to_exprs(_from); expr * const * to = to_exprs(_to); - expr * r = 0; + expr * r = nullptr; for (unsigned i = 0; i < num_exprs; i++) { if (m.get_sort(from[i]) != m.get_sort(to[i])) { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(of_expr(0)); + RETURN_Z3(of_expr(nullptr)); } SASSERT(from[i]->get_ref_count() > 0); SASSERT(to[i]->get_ref_count() > 0); @@ -795,7 +794,7 @@ extern "C" { mk_c(c)->save_ast_trail(new_a); r = new_a.get(); RETURN_Z3(of_expr(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_substitute_vars(Z3_context c, @@ -813,7 +812,7 @@ extern "C" { subst(a, num_exprs, to, new_a); mk_c(c)->save_ast_trail(new_a); RETURN_Z3(of_expr(new_a.get())); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_API char const * Z3_ast_to_string(Z3_context c, Z3_ast a) { @@ -839,7 +838,7 @@ extern "C" { UNREACHABLE(); } return mk_c(c)->mk_external_string(buffer.str()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_API char const * Z3_sort_to_string(Z3_context c, Z3_sort s) { @@ -895,12 +894,10 @@ extern "C" { case OP_ITE: return Z3_OP_ITE; case OP_AND: return Z3_OP_AND; case OP_OR: return Z3_OP_OR; - case OP_IFF: return Z3_OP_IFF; case OP_XOR: return Z3_OP_XOR; case OP_NOT: return Z3_OP_NOT; case OP_IMPLIES: return Z3_OP_IMPLIES; case OP_OEQ: return Z3_OP_OEQ; - case OP_INTERP: return Z3_OP_INTERP; case PR_UNDEF: return Z3_OP_PR_UNDEF; case PR_TRUE: return Z3_OP_PR_TRUE; @@ -919,7 +916,6 @@ extern "C" { case PR_REWRITE: return Z3_OP_PR_REWRITE; case PR_REWRITE_STAR: return Z3_OP_PR_REWRITE_STAR; case PR_PULL_QUANT: return Z3_OP_PR_PULL_QUANT; - case PR_PULL_QUANT_STAR: return Z3_OP_PR_PULL_QUANT_STAR; case PR_PUSH_QUANT: return Z3_OP_PR_PUSH_QUANT; case PR_ELIM_UNUSED_VARS: return Z3_OP_PR_ELIM_UNUSED_VARS; case PR_DER: return Z3_OP_PR_DER; @@ -936,9 +932,7 @@ extern "C" { case PR_IFF_OEQ: return Z3_OP_PR_IFF_OEQ; case PR_NNF_POS: return Z3_OP_PR_NNF_POS; case PR_NNF_NEG: return Z3_OP_PR_NNF_NEG; - case PR_NNF_STAR: return Z3_OP_PR_NNF_STAR; case PR_SKOLEMIZE: return Z3_OP_PR_SKOLEMIZE; - case PR_CNF_STAR: return Z3_OP_PR_CNF_STAR; case PR_MODUS_PONENS_OEQ: return Z3_OP_PR_MODUS_PONENS_OEQ; case PR_TH_LEMMA: return Z3_OP_PR_TH_LEMMA; case PR_HYPER_RESOLVE: return Z3_OP_PR_HYPER_RESOLVE; @@ -1059,6 +1053,7 @@ extern "C" { switch(_d->get_decl_kind()) { case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR; case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER; + case OP_DT_IS: return Z3_OP_DT_IS; case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR; case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD; default: @@ -1115,7 +1110,7 @@ extern "C" { case _OP_STRING_SUBSTR: return Z3_OP_SEQ_EXTRACT; case _OP_STRING_STRIDOF: return Z3_OP_SEQ_INDEX; case _OP_REGEXP_EMPTY: return Z3_OP_RE_EMPTY_SET; - case _OP_REGEXP_FULL: return Z3_OP_RE_FULL_SET; + case _OP_REGEXP_FULL_CHAR: return Z3_OP_RE_FULL_SET; case OP_STRING_STOI: return Z3_OP_STR_TO_INT; case OP_STRING_ITOS: return Z3_OP_INT_TO_STR; @@ -1127,7 +1122,8 @@ extern "C" { case OP_RE_UNION: return Z3_OP_RE_UNION; case OP_RE_INTERSECT: return Z3_OP_RE_INTERSECT; case OP_RE_LOOP: return Z3_OP_RE_LOOP; - case OP_RE_FULL_SET: return Z3_OP_RE_FULL_SET; + // case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET; + case OP_RE_FULL_CHAR_SET: return Z3_OP_RE_FULL_SET; case OP_RE_EMPTY_SET: return Z3_OP_RE_EMPTY_SET; default: return Z3_OP_INTERNAL; @@ -1232,17 +1228,17 @@ extern "C" { Z3_TRY; LOG_Z3_translate(c, a, target); RESET_ERROR_CODE(); - CHECK_VALID_AST(a, 0); + CHECK_VALID_AST(a, nullptr); if (c == target) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } SASSERT(mk_c(c)->m().contains(to_ast(a))); ast_translation translator(mk_c(c)->m(), mk_c(target)->m()); ast * _result = translator(to_ast(a)); mk_c(target)->save_ast_trail(_result); RETURN_Z3(of_ast(_result)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } }; diff --git a/src/api/api_ast_map.cpp b/src/api/api_ast_map.cpp index d0388b97a..17dd086b5 100644 --- a/src/api/api_ast_map.cpp +++ b/src/api/api_ast_map.cpp @@ -38,7 +38,7 @@ extern "C" { mk_c(c)->save_object(m); Z3_ast_map r = of_ast_map(m); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_ast_map_inc_ref(Z3_context c, Z3_ast_map m) { @@ -70,15 +70,15 @@ extern "C" { LOG_Z3_ast_map_find(c, m, k); RESET_ERROR_CODE(); obj_map::obj_map_entry * entry = to_ast_map_ref(m).find_core(to_ast(k)); - if (entry == 0) { + if (entry == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } else { ast * r = entry->get_data().m_value; RETURN_Z3(of_ast(r)); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_ast_map_insert(Z3_context c, Z3_ast_map m, Z3_ast k, Z3_ast v) { @@ -115,7 +115,7 @@ extern "C" { Z3_TRY; LOG_Z3_ast_map_erase(c, m, k); RESET_ERROR_CODE(); - ast * v = 0; + ast * v = nullptr; if (to_ast_map_ref(m).find(to_ast(k), v)) { to_ast_map_ref(m).erase(to_ast(k)); ast_manager & mng = to_ast_map(m)->m; @@ -146,7 +146,7 @@ extern "C" { } Z3_ast_vector r = of_ast_vector(v); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_ast_map_to_string(Z3_context c, Z3_ast_map m) { @@ -163,7 +163,7 @@ extern "C" { } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } }; diff --git a/src/api/api_ast_map.h b/src/api/api_ast_map.h index 3dacc54f1..27ad1eb9d 100644 --- a/src/api/api_ast_map.h +++ b/src/api/api_ast_map.h @@ -25,7 +25,7 @@ struct Z3_ast_map_ref : public api::object { ast_manager & m; obj_map m_map; Z3_ast_map_ref(api::context& c, ast_manager & _m): api::object(c), m(_m) {} - virtual ~Z3_ast_map_ref(); + ~Z3_ast_map_ref() override; }; inline Z3_ast_map_ref * to_ast_map(Z3_ast_map v) { return reinterpret_cast(v); } diff --git a/src/api/api_ast_vector.cpp b/src/api/api_ast_vector.cpp index 471a308b6..ae5adecea 100644 --- a/src/api/api_ast_vector.cpp +++ b/src/api/api_ast_vector.cpp @@ -33,7 +33,7 @@ extern "C" { mk_c(c)->save_object(v); Z3_ast_vector r = of_ast_vector(v); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_ast_vector_inc_ref(Z3_context c, Z3_ast_vector v) { @@ -66,12 +66,12 @@ extern "C" { RESET_ERROR_CODE(); if (i >= to_ast_vector_ref(v).size()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } // Remark: Don't need to invoke save_object. ast * r = to_ast_vector_ref(v).get(i); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_ast_vector_set(Z3_context c, Z3_ast_vector v, unsigned i, Z3_ast a) { @@ -108,7 +108,7 @@ extern "C" { RESET_ERROR_CODE(); if (c == t) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ast_translation translator(mk_c(c)->m(), mk_c(t)->m()); Z3_ast_vector_ref * new_v = alloc(Z3_ast_vector_ref, *mk_c(t), mk_c(t)->m()); @@ -119,7 +119,7 @@ extern "C" { new_v->m_ast_vector.push_back(new_ast); } RETURN_Z3(of_ast_vector(new_v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_ast_vector_to_string(Z3_context c, Z3_ast_vector v) { @@ -134,7 +134,7 @@ extern "C" { } buffer << ")"; return mk_c(c)->mk_external_string(buffer.str()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } }; diff --git a/src/api/api_ast_vector.h b/src/api/api_ast_vector.h index ced20481e..d9d3913e7 100644 --- a/src/api/api_ast_vector.h +++ b/src/api/api_ast_vector.h @@ -27,7 +27,7 @@ namespace api { struct Z3_ast_vector_ref : public api::object { ast_ref_vector m_ast_vector; Z3_ast_vector_ref(api::context& c, ast_manager & m): api::object(c), m_ast_vector(m) {} - virtual ~Z3_ast_vector_ref() {} + ~Z3_ast_vector_ref() override {} }; inline Z3_ast_vector_ref * to_ast_vector(Z3_ast_vector v) { return reinterpret_cast(v); } diff --git a/src/api/api_bv.cpp b/src/api/api_bv.cpp index 3b5d60be3..1876d930d 100644 --- a/src/api/api_bv.cpp +++ b/src/api/api_bv.cpp @@ -33,7 +33,7 @@ extern "C" { parameter p(sz); Z3_sort r = of_sort(mk_c(c)->m().mk_sort(mk_c(c)->get_bv_fid(), BV_SORT, 1, &p)); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } #define MK_BV_UNARY(NAME, OP) MK_UNARY(NAME, mk_c(c)->get_bv_fid(), OP, SKIP) @@ -85,7 +85,7 @@ extern "C" { RESET_ERROR_CODE(); Z3_ast r = mk_extract_core(c, high, low, n); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } #define MK_BV_PUNARY(NAME, OP) \ @@ -146,7 +146,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ check_sorts(c, a); RETURN_Z3(of_ast(a)); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } /** @@ -164,7 +164,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ unsigned sz = Z3_get_bv_sort_size(c, s); if (sz == 0) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } Z3_ast x = Z3_mk_int64(c, 1, s); Z3_inc_ref(c, x); @@ -174,7 +174,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_dec_ref(c, x); Z3_dec_ref(c, y); return result; - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_mk_bvsmin(Z3_context c, Z3_sort s) { @@ -229,7 +229,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_dec_ref(c, r); return result; } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } // only for signed machine integers @@ -257,7 +257,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_dec_ref(c, args_neg); Z3_dec_ref(c, zero); return result; - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } // only for signed machine integers @@ -286,7 +286,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_dec_ref(c, z); Z3_dec_ref(c, zero); return result; - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bvsub_no_underflow(Z3_context c, Z3_ast t1, Z3_ast t2, Z3_bool is_signed) { @@ -311,7 +311,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ else { return Z3_mk_bvule(c, t2, t1); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bvmul_no_overflow(Z3_context c, Z3_ast n1, Z3_ast n2, Z3_bool is_signed) { @@ -336,11 +336,11 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_TRY; RESET_ERROR_CODE(); Z3_ast min = Z3_mk_bvsmin(c, Z3_get_sort(c, t)); - if (Z3_get_error_code(c) != Z3_OK) return 0; + if (Z3_get_error_code(c) != Z3_OK) return nullptr; Z3_ast eq = Z3_mk_eq(c, t, min); - if (Z3_get_error_code(c) != Z3_OK) return 0; + if (Z3_get_error_code(c) != Z3_OK) return nullptr; return Z3_mk_not(c, eq); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } // only for signed machine integers @@ -366,7 +366,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ Z3_dec_ref(c, z); Z3_dec_ref(c, u); return result; - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bvsub(Z3_context c, Z3_ast n1, Z3_ast n2) { @@ -374,7 +374,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ LOG_Z3_mk_bvsub(c, n1, n2); RESET_ERROR_CODE(); MK_BINARY_BODY(Z3_mk_bvsub, mk_c(c)->get_bv_fid(), OP_BSUB, SKIP); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bvneg(Z3_context c, Z3_ast n) { @@ -382,7 +382,7 @@ Z3_ast Z3_API NAME(Z3_context c, unsigned i, Z3_ast n) { \ LOG_Z3_mk_bvneg(c, n); RESET_ERROR_CODE(); MK_UNARY_BODY(Z3_mk_bvneg, mk_c(c)->get_bv_fid(), OP_BNEG, SKIP); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_bv_sort_size(Z3_context c, Z3_sort t) { diff --git a/src/api/api_config_params.cpp b/src/api/api_config_params.cpp index 460101d34..604177561 100644 --- a/src/api/api_config_params.cpp +++ b/src/api/api_config_params.cpp @@ -53,7 +53,7 @@ extern "C" { Z3_bool_opt Z3_API Z3_global_param_get(Z3_string param_id, Z3_string_ptr param_value) { memory::initialize(UINT_MAX); LOG_Z3_global_param_get(param_id, param_value); - *param_value = 0; + *param_value = nullptr; try { g_Z3_global_param_get_buffer = gparams::get_value(param_id); *param_value = g_Z3_global_param_get_buffer.c_str(); diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 23ac62a66..5993e9fdd 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -70,7 +70,7 @@ namespace api { // ------------------------ context::context(context_params * p, bool user_ref_count): - m_params(p != 0 ? *p : context_params()), + m_params(p != nullptr ? *p : context_params()), m_user_ref_count(user_ref_count), m_manager(m_params.mk_ast_manager()), m_plugins(m()), @@ -78,7 +78,6 @@ namespace api { m_bv_util(m()), m_datalog_util(m()), m_fpa_util(m()), - m_dtutil(m()), m_sutil(m()), m_last_result(m()), m_ast_trail(m()), @@ -89,7 +88,7 @@ namespace api { m_searching = false; - m_interruptable = 0; + m_interruptable = nullptr; m_error_handler = &default_error_handler; m_basic_fid = m().get_basic_family_id(); @@ -108,7 +107,7 @@ namespace api { context::~context() { - m_last_obj = 0; + m_last_obj = nullptr; u_map::iterator it = m_allocated_objects.begin(); while (it != m_allocated_objects.end()) { api::object* val = it->m_value; @@ -131,7 +130,7 @@ namespace api { context::set_interruptable::~set_interruptable() { #pragma omp critical (set_interruptable) { - m_ctx.m_interruptable = 0; + m_ctx.m_interruptable = nullptr; } } @@ -152,6 +151,12 @@ namespace api { } } + void context::reset_error_code() { + m_error_code = Z3_OK; + } + + + void context::check_searching() { if (m_searching) { set_error_code(Z3_INVALID_USAGE); // TBD: error code could be fixed. @@ -169,7 +174,7 @@ namespace api { } expr * context::mk_numeral_core(rational const & n, sort * s) { - expr* e = 0; + expr* e = nullptr; family_id fid = s->get_family_id(); if (fid == m_arith_fid) { e = m_arith_util.mk_numeral(n, s); @@ -178,7 +183,7 @@ namespace api { e = m_bv_util.mk_numeral(n, s); } else if (fid == get_datalog_fid() && n.is_uint64()) { - uint64 sz; + uint64_t sz; if (m_datalog_util.try_get_size(s, sz) && sz <= n.get_uint64()) { invoke_error_handler(Z3_INVALID_ARG); @@ -233,7 +238,7 @@ namespace api { void context::reset_last_result() { if (m_user_ref_count) m_last_result.reset(); - m_last_obj = 0; + m_last_obj = nullptr; } void context::save_object(object * r) { @@ -307,7 +312,7 @@ namespace api { // // ----------------------- realclosure::manager & context::rcfm() { - if (m_rcf_manager.get() == 0) { + if (m_rcf_manager.get() == nullptr) { m_rcf_manager = alloc(realclosure::manager, m_limit, m_rcf_qm); } return *(m_rcf_manager.get()); @@ -330,7 +335,7 @@ extern "C" { memory::initialize(UINT_MAX); Z3_context r = reinterpret_cast(alloc(api::context, reinterpret_cast(c), false)); RETURN_Z3(r); - Z3_CATCH_RETURN_NO_HANDLE(0); + Z3_CATCH_RETURN_NO_HANDLE(nullptr); } Z3_context Z3_API Z3_mk_context_rc(Z3_config c) { @@ -339,7 +344,7 @@ extern "C" { memory::initialize(UINT_MAX); Z3_context r = reinterpret_cast(alloc(api::context, reinterpret_cast(c), true)); RETURN_Z3(r); - Z3_CATCH_RETURN_NO_HANDLE(0); + Z3_CATCH_RETURN_NO_HANDLE(nullptr); } void Z3_API Z3_del_context(Z3_context c) { @@ -453,7 +458,7 @@ extern "C" { case Z3_INTERNAL_FATAL: return "internal error"; case Z3_INVALID_USAGE: return "invalid usage"; case Z3_DEC_REF_ERROR: return "invalid dec_ref command"; - case Z3_EXCEPTION: return c == 0 ? "Z3 exception" : mk_c(c)->get_exception_msg(); + case Z3_EXCEPTION: return c == nullptr ? "Z3 exception" : mk_c(c)->get_exception_msg(); default: return "unknown"; } } diff --git a/src/api/api_context.h b/src/api/api_context.h index 58893d1c1..50e89113d 100644 --- a/src/api/api_context.h +++ b/src/api/api_context.h @@ -34,6 +34,7 @@ Revision History: #include "util/event_handler.h" #include "cmd_context/tactic_manager.h" #include "cmd_context/context_params.h" +#include "cmd_context/cmd_context.h" #include "api/api_polynomial.h" #include "util/hashtable.h" @@ -51,15 +52,15 @@ namespace api { class context : public tactic_manager { struct add_plugins { add_plugins(ast_manager & m); }; context_params m_params; - bool m_user_ref_count; //!< if true, the user is responsible for managing referenc counters. + bool m_user_ref_count; //!< if true, the user is responsible for managing reference counters. scoped_ptr m_manager; + scoped_ptr m_cmd; add_plugins m_plugins; arith_util m_arith_util; bv_util m_bv_util; datalog::dl_decl_util m_datalog_util; fpa_util m_fpa_util; - datatype_util m_dtutil; seq_util m_sutil; // Support for old solver API @@ -113,18 +114,19 @@ namespace api { ~context(); ast_manager & m() const { return *(m_manager.get()); } - context_params & params() { return m_params; } + context_params & params() { m_params.updt_params(); return m_params; } + scoped_ptr& cmd() { return m_cmd; } bool produce_proofs() const { return m().proofs_enabled(); } bool produce_models() const { return m_params.m_model; } bool produce_unsat_cores() const { return m_params.m_unsat_core; } bool use_auto_config() const { return m_params.m_auto_config; } unsigned get_timeout() const { return m_params.m_timeout; } - unsigned get_rlimit() const { return m_params.m_rlimit; } + unsigned get_rlimit() const { return m_params.rlimit(); } arith_util & autil() { return m_arith_util; } bv_util & bvutil() { return m_bv_util; } datalog::dl_decl_util & datalog_util() { return m_datalog_util; } fpa_util & fpautil() { return m_fpa_util; } - datatype_util& dtutil() { return m_dtutil; } + datatype_util& dtutil() { return m_dt_plugin->u(); } seq_util& sutil() { return m_sutil; } family_id get_basic_fid() const { return m_basic_fid; } family_id get_array_fid() const { return m_array_fid; } @@ -138,7 +140,7 @@ namespace api { datatype_decl_plugin * get_dt_plugin() const { return m_dt_plugin; } Z3_error_code get_error_code() const { return m_error_code; } - void reset_error_code() { m_error_code = Z3_OK; } + void reset_error_code(); void set_error_code(Z3_error_code err); void set_error_handler(Z3_error_handler h) { m_error_handler = h; } // Sign an error if solver is searching @@ -158,7 +160,7 @@ namespace api { // Create a numeral of the given sort expr * mk_numeral_core(rational const & n, sort * s); - // Return a conjuction that will be exposed to the "external" world. + // Return a conjunction that will be exposed to the "external" world. expr * mk_and(unsigned num_exprs, expr * const * exprs); // Hack for preventing an AST for being GC when ref-count is not used diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index 397e2a8af..fd31a65f8 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -45,14 +45,14 @@ namespace api { ast_ref_vector m_trail; public: fixedpoint_context(ast_manager& m, smt_params& p): - m_state(0), - m_reduce_app(0), - m_reduce_assign(0), + m_state(nullptr), + m_reduce_app(nullptr), + m_reduce_assign(nullptr), m_context(m, m_register_engine, p), m_trail(m) {} - virtual ~fixedpoint_context() {} - family_id get_family_id() const { return const_cast(m_context).get_decl_util().get_family_id(); } + ~fixedpoint_context() override {} + family_id get_family_id() const override { return const_cast(m_context).get_decl_util().get_family_id(); } void set_state(void* state) { SASSERT(!m_state); m_state = state; @@ -73,8 +73,8 @@ namespace api { void set_reduce_assign(reduce_assign_callback_fptr f) { m_reduce_assign = f; } - virtual void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) { - expr* r = 0; + void reduce(func_decl* f, unsigned num_args, expr * const* args, expr_ref& result) override { + expr* r = nullptr; if (m_reduce_app) { m_reduce_app(m_state, f, num_args, args, &r); result = r; @@ -85,12 +85,12 @@ namespace api { m_trail.push_back(r); } // allow fallthrough. - if (r == 0) { + if (r == nullptr) { ast_manager& m = m_context.get_manager(); result = m.mk_app(f, num_args, args); } } - virtual void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) { + void reduce_assign(func_decl* f, unsigned num_args, expr * const* args, unsigned num_out, expr* const* outs) override { if (m_reduce_assign) { m_trail.push_back(f); for (unsigned i = 0; i < num_args; ++i) { @@ -171,35 +171,35 @@ extern "C" { sort * r = to_sort(s); if (Z3_get_sort_kind(c, s) != Z3_RELATION_SORT) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } if (col >= r->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } parameter const& p = r->get_parameter(col); if (!p.is_ast() || !is_sort(p.get_ast())) { UNREACHABLE(); warning_msg("Sort parameter expected at %d", col); SET_ERROR_CODE(Z3_INTERNAL_FATAL); - RETURN_Z3(0); + RETURN_Z3(nullptr); } Z3_sort res = of_sort(to_sort(p.get_ast())); RETURN_Z3(res); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } - Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, __uint64 size) { + Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, uint64_t size) { Z3_TRY; LOG_Z3_mk_finite_domain_sort(c, name, size); RESET_ERROR_CODE(); sort* s = mk_c(c)->datalog_util().mk_sort(to_symbol(name), size); mk_c(c)->save_ast_trail(s); RETURN_Z3(of_sort(s)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } - Z3_bool Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, __uint64 * out) { + Z3_bool Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, uint64_t * out) { Z3_TRY; if (out) { *out = 0; @@ -215,7 +215,6 @@ extern "C" { RESET_ERROR_CODE(); VERIFY(mk_c(c)->datalog_util().try_get_size(to_sort(s), *out)); return Z3_TRUE; - Z3_CATCH_RETURN(Z3_FALSE); } @@ -228,7 +227,7 @@ extern "C" { mk_c(c)->save_object(d); Z3_fixedpoint r = of_datalog(d); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_inc_ref(Z3_context c, Z3_fixedpoint s) { @@ -331,7 +330,7 @@ extern "C" { expr* e = to_fixedpoint_ref(d)->ctx().get_answer_as_formula(); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_fixedpoint_get_reason_unknown(Z3_context c,Z3_fixedpoint d) { @@ -366,7 +365,7 @@ extern "C" { ctx.set_ignore_check(true); if (!parse_smt2_commands(ctx, s)) { SET_ERROR_CODE(Z3_PARSER_ERROR); - return 0; + return nullptr; } Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); @@ -398,7 +397,7 @@ extern "C" { std::string str(s); std::istringstream is(str); RETURN_Z3(Z3_fixedpoint_from_stream(c, d, is)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_fixedpoint_from_file( @@ -410,10 +409,10 @@ extern "C" { std::ifstream is(s); if (!is) { SET_ERROR_CODE(Z3_PARSER_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } RETURN_Z3(Z3_fixedpoint_from_stream(c, d, is)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } @@ -426,7 +425,7 @@ extern "C" { mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_register_relation(Z3_context c,Z3_fixedpoint d, Z3_func_decl f) { @@ -473,7 +472,7 @@ extern "C" { v->m_ast_vector.push_back(m.mk_not(queries[i].get())); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_fixedpoint_get_assertions( @@ -490,7 +489,7 @@ extern "C" { v->m_ast_vector.push_back(to_fixedpoint_ref(d)->ctx().get_assertion(i)); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_set_reduce_assign_callback( @@ -541,7 +540,7 @@ extern "C" { expr_ref r = to_fixedpoint_ref(d)->get_cover_delta(level, to_func_decl(pred)); mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r.get())); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_add_cover(Z3_context c, Z3_fixedpoint d, int level, Z3_func_decl pred, Z3_ast property) { @@ -573,7 +572,7 @@ extern "C" { to_fixedpoint_ref(f)->collect_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_set_params(Z3_context c, Z3_fixedpoint d, Z3_params p) { @@ -604,7 +603,26 @@ extern "C" { Z3_CATCH; } - + + void Z3_API Z3_fixedpoint_add_callback(Z3_context c, Z3_fixedpoint d, + void *state, + Z3_fixedpoint_new_lemma_eh new_lemma_eh, + Z3_fixedpoint_predecessor_eh predecessor_eh, + Z3_fixedpoint_unfold_eh unfold_eh){ + Z3_TRY; + // not logged + to_fixedpoint_ref(d)->ctx().add_callback(state, + reinterpret_cast(new_lemma_eh), + reinterpret_cast(predecessor_eh), + reinterpret_cast(unfold_eh)); + + Z3_CATCH; + } + + void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl){ + to_fixedpoint_ref(d)->ctx().add_constraint(to_expr(e), lvl); + } + #include "api_datalog_spacer.inc" }; diff --git a/src/api/api_datalog.h b/src/api/api_datalog.h index c8dbc4180..35458f902 100644 --- a/src/api/api_datalog.h +++ b/src/api/api_datalog.h @@ -37,8 +37,8 @@ namespace api { struct Z3_fixedpoint_ref : public api::object { api::fixedpoint_context * m_datalog; params_ref m_params; - Z3_fixedpoint_ref(api::context& c): api::object(c), m_datalog(0) {} - virtual ~Z3_fixedpoint_ref() { dealloc(m_datalog); } + Z3_fixedpoint_ref(api::context& c): api::object(c), m_datalog(nullptr) {} + ~Z3_fixedpoint_ref() override { dealloc(m_datalog); } }; inline Z3_fixedpoint_ref * to_fixedpoint(Z3_fixedpoint s) { return reinterpret_cast(s); } diff --git a/src/api/api_datalog_spacer.inc b/src/api/api_datalog_spacer.inc index 871d2be63..1888d4b96 100644 --- a/src/api/api_datalog_spacer.inc +++ b/src/api/api_datalog_spacer.inc @@ -49,7 +49,7 @@ Notes: 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(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_fixedpoint_get_rules_along_trace( @@ -69,7 +69,7 @@ Notes: v->m_ast_vector.push_back(rules[i].get()); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_symbol Z3_API Z3_fixedpoint_get_rule_names_along_trace( @@ -90,7 +90,7 @@ Notes: ss << ";" << names[i].str(); } RETURN_Z3(of_symbol(symbol(ss.str().substr(1).c_str()))); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_fixedpoint_add_invariant(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred, Z3_ast property) { @@ -108,6 +108,6 @@ Notes: 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(0); + Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/api_datatype.cpp b/src/api/api_datatype.cpp index d667a7428..799e537ea 100644 --- a/src/api/api_datatype.cpp +++ b/src/api/api_datatype.cpp @@ -52,12 +52,12 @@ extern "C" { { datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, 1, constrs); - bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, 0, tuples); + bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, nullptr, tuples); del_datatype_decl(dt); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } } @@ -82,7 +82,7 @@ extern "C" { proj_decls[i] = of_func_decl(_accs[i]); } RETURN_Z3_mk_tuple_sort(of_sort(tuple)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_enumeration_sort(Z3_context c, @@ -108,18 +108,18 @@ extern "C" { recognizer_s += e_name.str(); symbol recognizer(recognizer_s.c_str()); - constrs.push_back(mk_constructor_decl(e_name, recognizer, 0, 0)); + constrs.push_back(mk_constructor_decl(e_name, recognizer, 0, nullptr)); } { - datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 0, 0, n, constrs.c_ptr()); - bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, 0, sorts); + datatype_decl * dt = mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, n, constrs.c_ptr()); + bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &dt, 0, nullptr, sorts); del_datatype_decl(dt); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } } @@ -137,13 +137,13 @@ extern "C" { func_decl* decl = (decls)[i]; mk_c(c)->save_multiple_ast_trail(decl); enum_consts[i] = of_func_decl(decl); - decl = dt_util.get_constructor_recognizer(decl); + decl = dt_util.get_constructor_is(decl); mk_c(c)->save_multiple_ast_trail(decl); enum_testers[i] = of_func_decl(decl); } RETURN_Z3_mk_enumeration_sort(of_sort(e)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_list_sort(Z3_context c, @@ -168,7 +168,7 @@ extern "C" { mk_accessor_decl(m, symbol("tail"), type_ref(0)) }; constructor_decl* constrs[2] = { - mk_constructor_decl(symbol("nil"), symbol("is_nil"), 0, 0), + mk_constructor_decl(symbol("nil"), symbol("is_nil"), 0, nullptr), // Leo: SMT 2.0 document uses 'insert' instead of cons mk_constructor_decl(symbol("cons"), symbol("is_cons"), 2, head_tail) }; @@ -176,12 +176,12 @@ extern "C" { sort_ref_vector sorts(m); { datatype_decl * decl = mk_datatype_decl(dt_util, to_symbol(name), 0, nullptr, 2, constrs); - bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &decl, 0, 0, sorts); + bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &decl, 0, nullptr, sorts); del_datatype_decl(decl); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } } sort * s = sorts.get(0); @@ -196,7 +196,7 @@ extern "C" { *nil_decl = of_func_decl(f); } if (is_nil_decl) { - f = data_util.get_constructor_recognizer(cnstrs[0]); + f = data_util.get_constructor_is(cnstrs[0]); mk_c(c)->save_multiple_ast_trail(f); *is_nil_decl = of_func_decl(f); } @@ -206,7 +206,7 @@ extern "C" { *cons_decl = of_func_decl(f); } if (is_cons_decl) { - f = data_util.get_constructor_recognizer(cnstrs[1]); + f = data_util.get_constructor_is(cnstrs[1]); mk_c(c)->save_multiple_ast_trail(f); *is_cons_decl = of_func_decl(f); } @@ -225,7 +225,7 @@ extern "C" { *tail_decl = of_func_decl(f); } RETURN_Z3_mk_list_sort(of_sort(s)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } struct constructor { @@ -259,7 +259,7 @@ extern "C" { cnstr->m_sort_refs.push_back(sort_refs[i]); } RETURN_Z3(reinterpret_cast(cnstr)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } @@ -290,7 +290,7 @@ extern "C" { *constructor_decl = of_func_decl(f); } if (tester) { - func_decl* f2 = data_util.get_constructor_recognizer(f); + func_decl* f2 = data_util.get_constructor_is(f); mk_c(c)->save_multiple_ast_trail(f2); *tester = of_func_decl(f2); } @@ -349,12 +349,12 @@ extern "C" { sort_ref_vector sorts(m); { datatype_decl * data = mk_datatype_decl(c, name, num_constructors, constructors); - bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &data, 0, 0, sorts); + bool is_ok = mk_c(c)->get_dt_plugin()->mk_datatypes(1, &data, 0, nullptr, sorts); del_datatype_decl(data); if (!is_ok) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } } sort * s = sorts.get(0); @@ -367,7 +367,7 @@ extern "C" { cn->m_constructor = cnstrs[i]; } RETURN_Z3_mk_datatype(of_sort(s)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } typedef ptr_vector constructor_list; @@ -383,7 +383,7 @@ extern "C" { result->push_back(reinterpret_cast(constructors[i])); } RETURN_Z3(reinterpret_cast(result)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_del_constructor_list(Z3_context c, Z3_constructor_list clist) { @@ -412,7 +412,7 @@ extern "C" { datas.push_back(mk_datatype_decl(c, sort_names[i], cl->size(), reinterpret_cast(cl->c_ptr()))); } sort_ref_vector _sorts(m); - bool ok = mk_c(c)->get_dt_plugin()->mk_datatypes(datas.size(), datas.c_ptr(), 0, 0, _sorts); + bool ok = mk_c(c)->get_dt_plugin()->mk_datatypes(datas.size(), datas.c_ptr(), 0, nullptr, _sorts); del_datatype_decls(datas.size(), datas.c_ptr()); if (!ok) { @@ -454,17 +454,17 @@ extern "C" { Z3_func_decl get_datatype_sort_constructor_core(Z3_context c, Z3_sort t, unsigned idx) { RESET_ERROR_CODE(); - CHECK_VALID_AST(t, 0); + CHECK_VALID_AST(t, nullptr); sort * _t = to_sort(t); datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } ptr_vector const & decls = *dt_util.get_datatype_constructors(_t); if (idx >= decls.size()) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } func_decl* decl = (decls)[idx]; mk_c(c)->save_ast_trail(decl); @@ -477,7 +477,7 @@ extern "C" { RESET_ERROR_CODE(); Z3_func_decl r = get_datatype_sort_constructor_core(c, t, idx); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_get_datatype_sort_recognizer(Z3_context c, Z3_sort t, unsigned idx) { @@ -489,18 +489,18 @@ extern "C" { if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ptr_vector const & decls = *dt_util.get_datatype_constructors(_t); if (idx >= decls.size()) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } func_decl* decl = (decls)[idx]; - decl = dt_util.get_constructor_recognizer(decl); + decl = dt_util.get_constructor_is(decl); mk_c(c)->save_ast_trail(decl); RETURN_Z3(of_func_decl(decl)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_get_datatype_sort_constructor_accessor(Z3_context c, Z3_sort t, unsigned idx_c, unsigned idx_a) { @@ -512,28 +512,28 @@ extern "C" { if (!dt_util.is_datatype(_t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ptr_vector const & decls = *dt_util.get_datatype_constructors(_t); if (idx_c >= decls.size()) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } func_decl* decl = (decls)[idx_c]; if (decl->get_arity() <= idx_a) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ptr_vector const & accs = *dt_util.get_constructor_accessors(decl); SASSERT(accs.size() == decl->get_arity()); if (accs.size() <= idx_a) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } decl = (accs)[idx_a]; mk_c(c)->save_ast_trail(decl); RETURN_Z3(of_func_decl(decl)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_func_decl Z3_API Z3_get_tuple_sort_mk_decl(Z3_context c, Z3_sort t) { @@ -544,11 +544,11 @@ extern "C" { datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } Z3_func_decl r = get_datatype_sort_constructor_core(c, t, 0); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_tuple_sort_num_fields(Z3_context c, Z3_sort t) { @@ -579,22 +579,22 @@ extern "C" { datatype_util& dt_util = mk_c(c)->dtutil(); if (!dt_util.is_datatype(tuple) || dt_util.is_recursive(tuple) || dt_util.get_datatype_num_constructors(tuple) != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ptr_vector const & decls = *dt_util.get_datatype_constructors(tuple); if (decls.size() != 1) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ptr_vector const & accs = *dt_util.get_constructor_accessors((decls)[0]); if (accs.size() <= i) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } func_decl* acc = (accs)[i]; mk_c(c)->save_ast_trail(acc); RETURN_Z3(of_func_decl(acc)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_datatype_update_field( @@ -614,7 +614,7 @@ extern "C" { mk_c(c)->save_ast_trail(r); check_sorts(c, r); RETURN_Z3(of_ast(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/api_fpa.cpp b/src/api/api_fpa.cpp index 484cb3d76..261198354 100644 --- a/src/api/api_fpa.cpp +++ b/src/api/api_fpa.cpp @@ -56,7 +56,7 @@ extern "C" { sort * s = ctx->fpautil().mk_rm_sort(); mk_c(c)->save_ast_trail(s); RETURN_Z3(of_sort(s)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_even(Z3_context c) { @@ -67,7 +67,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_nearest_ties_to_even(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rne(Z3_context c) { @@ -78,7 +78,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_nearest_ties_to_even(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_nearest_ties_to_away(Z3_context c) { @@ -89,7 +89,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_nearest_ties_to_away(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rna(Z3_context c) { @@ -100,7 +100,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_nearest_ties_to_away(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_toward_positive(Z3_context c) { @@ -111,7 +111,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_toward_positive(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rtp(Z3_context c) { @@ -122,7 +122,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_toward_positive(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_toward_negative(Z3_context c) { @@ -133,7 +133,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_toward_negative(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rtn(Z3_context c) { @@ -144,7 +144,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_toward_negative(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_toward_zero(Z3_context c) { @@ -155,7 +155,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_toward_zero(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rtz(Z3_context c) { @@ -166,7 +166,7 @@ extern "C" { expr * a = ctx->fpautil().mk_round_toward_zero(); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } @@ -181,7 +181,7 @@ extern "C" { sort * s = ctx->fpautil().mk_float_sort(ebits, sbits); ctx->save_ast_trail(s); RETURN_Z3(of_sort(s)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_fpa_sort_half(Z3_context c) { @@ -220,50 +220,50 @@ extern "C" { Z3_TRY; LOG_Z3_mk_fpa_nan(c, s); RESET_ERROR_CODE(); - CHECK_VALID_AST(s, 0); + CHECK_VALID_AST(s, nullptr); if (!is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_nan(to_sort(s)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_inf(Z3_context c, Z3_sort s, Z3_bool negative) { Z3_TRY; LOG_Z3_mk_fpa_inf(c, s, negative); RESET_ERROR_CODE(); - CHECK_VALID_AST(s, 0); + CHECK_VALID_AST(s, nullptr); if (!is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = negative != 0 ? ctx->fpautil().mk_ninf(to_sort(s)) : ctx->fpautil().mk_pinf(to_sort(s)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_zero(Z3_context c, Z3_sort s, Z3_bool negative) { Z3_TRY; LOG_Z3_mk_fpa_inf(c, s, negative); RESET_ERROR_CODE(); - CHECK_VALID_AST(s, 0); + CHECK_VALID_AST(s, nullptr); if (!is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = negative != 0 ? ctx->fpautil().mk_nzero(to_sort(s)) : ctx->fpautil().mk_pzero(to_sort(s)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_fp(Z3_context c, Z3_ast sgn, Z3_ast exp, Z3_ast sig) { @@ -272,13 +272,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_bv(c, sgn) || !is_bv(c, exp) || !is_bv(c, sig)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_fp(to_expr(sgn), to_expr(exp), to_expr(sig)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_numeral_float(Z3_context c, float v, Z3_sort ty) { @@ -287,7 +287,7 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); @@ -298,7 +298,7 @@ extern "C" { expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_numeral_double(Z3_context c, double v, Z3_sort ty) { @@ -307,7 +307,7 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); @@ -315,7 +315,7 @@ extern "C" { expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_numeral_int(Z3_context c, signed v, Z3_sort ty) { @@ -324,7 +324,7 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); @@ -335,7 +335,7 @@ extern "C" { expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_numeral_int_uint(Z3_context c, Z3_bool sgn, signed exp, unsigned sig, Z3_sort ty) { @@ -344,7 +344,7 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); @@ -355,16 +355,16 @@ extern "C" { expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } - Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(Z3_context c, Z3_bool sgn, __int64 exp, __uint64 sig, Z3_sort ty) { + Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(Z3_context c, Z3_bool sgn, int64_t exp, uint64_t sig, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_fpa_numeral_int64_uint64(c, sgn, exp, sig, ty); RESET_ERROR_CODE(); if (!is_fp_sort(c, ty)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); scoped_mpf tmp(ctx->fpautil().fm()); @@ -375,7 +375,7 @@ extern "C" { expr * a = ctx->fpautil().mk_value(tmp); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_abs(Z3_context c, Z3_ast t) { @@ -384,13 +384,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_abs(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_neg(Z3_context c, Z3_ast t) { @@ -399,13 +399,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_neg(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_add(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { @@ -414,13 +414,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_add(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_sub(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { @@ -429,13 +429,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_sub(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_mul(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { @@ -444,13 +444,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_mul(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_div(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2) { @@ -459,13 +459,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_div(to_expr(rm), to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_fma(Z3_context c, Z3_ast rm, Z3_ast t1, Z3_ast t2, Z3_ast t3) { @@ -474,13 +474,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t1) || !is_fp(c, t2) || !is_fp(c, t3)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_fma(to_expr(rm), to_expr(t1), to_expr(t2), to_expr(t3)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_sqrt(Z3_context c, Z3_ast rm, Z3_ast t) { @@ -489,13 +489,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_sqrt(to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_rem(Z3_context c, Z3_ast t1, Z3_ast t2) { @@ -504,13 +504,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_rem(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_round_to_integral(Z3_context c, Z3_ast rm, Z3_ast t) { @@ -519,13 +519,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_round_to_integral(to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_min(Z3_context c, Z3_ast t1, Z3_ast t2) { @@ -534,13 +534,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_min(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_max(Z3_context c, Z3_ast t1, Z3_ast t2) { @@ -549,13 +549,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_max(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_leq(Z3_context c, Z3_ast t1, Z3_ast t2) { @@ -564,13 +564,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_le(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_lt(Z3_context c, Z3_ast t1, Z3_ast t2) { @@ -579,13 +579,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_lt(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_geq(Z3_context c, Z3_ast t1, Z3_ast t2) { @@ -594,13 +594,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_ge(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_gt(Z3_context c, Z3_ast t1, Z3_ast t2) { @@ -609,13 +609,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_gt(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_eq(Z3_context c, Z3_ast t1, Z3_ast t2) { @@ -624,13 +624,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t1) || !is_fp(c, t2)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_float_eq(to_expr(t1), to_expr(t2)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_normal(Z3_context c, Z3_ast t) { @@ -639,13 +639,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_normal(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_subnormal(Z3_context c, Z3_ast t) { @@ -654,13 +654,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_subnormal(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_zero(Z3_context c, Z3_ast t) { @@ -669,13 +669,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_zero(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_infinite(Z3_context c, Z3_ast t) { @@ -684,13 +684,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_inf(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_nan(Z3_context c, Z3_ast t) { @@ -699,13 +699,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_nan(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_negative(Z3_context c, Z3_ast t) { @@ -714,13 +714,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_negative(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_is_positive(Z3_context c, Z3_ast t) { @@ -729,13 +729,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_is_positive(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } @@ -745,19 +745,19 @@ extern "C" { RESET_ERROR_CODE(); if (!is_bv(c, bv) || !is_fp_sort(c, s)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); fpa_util & fu = ctx->fpautil(); if (!ctx->bvutil().is_bv(to_expr(bv)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(bv)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_float(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { @@ -770,12 +770,12 @@ extern "C" { !fu.is_float(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_real(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { @@ -788,12 +788,12 @@ extern "C" { !ctx->autil().is_real(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_signed(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { @@ -806,12 +806,12 @@ extern "C" { !ctx->bvutil().is_bv(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_unsigned(Z3_context c, Z3_ast rm, Z3_ast t, Z3_sort s) { @@ -824,12 +824,12 @@ extern "C" { !ctx->bvutil().is_bv(to_expr(t)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } expr * a = fu.mk_to_fp_unsigned(to_sort(s), to_expr(rm), to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_ubv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz) { @@ -838,13 +838,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_to_ubv(to_expr(rm), to_expr(t), sz); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_sbv(Z3_context c, Z3_ast rm, Z3_ast t, unsigned sz) { @@ -853,13 +853,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_rm(c, rm) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_to_sbv(to_expr(rm), to_expr(t), sz); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_real(Z3_context c, Z3_ast t) { @@ -868,13 +868,13 @@ extern "C" { RESET_ERROR_CODE(); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); expr * a = ctx->fpautil().mk_to_real(to_expr(t)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_fpa_get_ebits(Z3_context c, Z3_sort s) { @@ -911,7 +911,7 @@ extern "C" { RESET_ERROR_CODE(); CHECK_NON_NULL(t, 0); CHECK_VALID_AST(t, 0); - if (sgn == 0) { + if (sgn == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } @@ -939,8 +939,8 @@ extern "C" { Z3_TRY; LOG_Z3_fpa_get_numeral_sign_bv(c, t); RESET_ERROR_CODE(); - CHECK_NON_NULL(t, 0); - CHECK_VALID_AST(t, 0); + CHECK_NON_NULL(t, nullptr); + CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); family_id fid = mk_c(c)->get_fpa_fid(); @@ -949,13 +949,13 @@ extern "C" { expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } scoped_mpf val(mpfm); bool r = plugin->is_numeral(to_expr(t), val); if (!r || mpfm.is_nan(val)) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } app * a; if (mpfm.is_pos(val)) @@ -964,15 +964,15 @@ extern "C" { a = ctx->bvutil().mk_numeral(1, 1); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_fpa_get_numeral_significand_bv(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_get_numeral_significand_bv(c, t); RESET_ERROR_CODE(); - CHECK_NON_NULL(t, 0); - CHECK_VALID_AST(t, 0); + CHECK_NON_NULL(t, nullptr); + CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); unsynch_mpq_manager & mpqm = mpfm.mpq_manager(); @@ -982,13 +982,13 @@ extern "C" { expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } unsigned sbits = val.get().get_sbits(); scoped_mpq q(mpqm); @@ -997,15 +997,15 @@ extern "C" { app * a = mk_c(c)->bvutil().mk_numeral(q.get(), sbits-1); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_fpa_get_numeral_significand_string(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_fpa_get_numeral_significand_string(c, t); RESET_ERROR_CODE(); - CHECK_NON_NULL(t, 0); - CHECK_VALID_AST(t, 0); + CHECK_NON_NULL(t, nullptr); + CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); unsynch_mpq_manager & mpqm = mpfm.mpq_manager(); @@ -1035,13 +1035,13 @@ extern "C" { Z3_CATCH_RETURN(""); } - Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, __uint64 * n) { + Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, uint64_t * n) { Z3_TRY; LOG_Z3_fpa_get_numeral_significand_uint64(c, t, n); RESET_ERROR_CODE(); CHECK_NON_NULL(t, 0); CHECK_VALID_AST(t, 0); - if (n == 0) { + if (n == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } @@ -1076,8 +1076,8 @@ extern "C" { Z3_TRY; LOG_Z3_fpa_get_numeral_exponent_string(c, t, biased); RESET_ERROR_CODE(); - CHECK_NON_NULL(t, 0); - CHECK_VALID_AST(t, 0); + CHECK_NON_NULL(t, nullptr); + CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); family_id fid = mk_c(c)->get_fpa_fid(); @@ -1113,13 +1113,13 @@ extern "C" { Z3_CATCH_RETURN(""); } - Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, __int64 * n, Z3_bool biased) { + Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, int64_t * n, Z3_bool biased) { Z3_TRY; LOG_Z3_fpa_get_numeral_exponent_int64(c, t, n, biased); RESET_ERROR_CODE(); CHECK_NON_NULL(t, 0); CHECK_VALID_AST(t, 0); - if (n == 0) { + if (n == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG); return 0; } @@ -1161,8 +1161,8 @@ extern "C" { Z3_TRY; LOG_Z3_fpa_get_numeral_exponent_bv(c, t, biased); RESET_ERROR_CODE(); - CHECK_NON_NULL(t, 0); - CHECK_VALID_AST(t, 0); + CHECK_NON_NULL(t, nullptr); + CHECK_VALID_AST(t, nullptr); ast_manager & m = mk_c(c)->m(); mpf_manager & mpfm = mk_c(c)->fpautil().fm(); family_id fid = mk_c(c)->get_fpa_fid(); @@ -1170,13 +1170,13 @@ extern "C" { expr * e = to_expr(t); if (!is_app(e) || is_app_of(e, fid, OP_FPA_NAN) || !is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } scoped_mpf val(mpfm); bool r = plugin->is_numeral(e, val); if (!r || !(mpfm.is_normal(val) || mpfm.is_denormal(val) || mpfm.is_zero(val) || mpfm.is_inf(val))) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } unsigned ebits = val.get().get_ebits(); mpf_exp_t exp; @@ -1194,23 +1194,23 @@ extern "C" { app * a = mk_c(c)->bvutil().mk_numeral(exp, ebits); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_ieee_bv(Z3_context c, Z3_ast t) { Z3_TRY; LOG_Z3_mk_fpa_to_ieee_bv(c, t); RESET_ERROR_CODE(); - CHECK_NON_NULL(t, 0); - CHECK_VALID_AST(t, 0); + CHECK_NON_NULL(t, nullptr); + CHECK_VALID_AST(t, nullptr); if (!is_fp(c, t)) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } api::context * ctx = mk_c(c); Z3_ast r = of_ast(ctx->fpautil().mk_to_ieee_bv(to_expr(t))); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_fpa_to_fp_int_real(Z3_context c, Z3_ast rm, Z3_ast exp, Z3_ast sig, Z3_sort s) { @@ -1224,12 +1224,12 @@ extern "C" { !ctx->autil().is_real(to_expr(sig)) || !fu.is_float(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } expr * a = fu.mk_to_fp(to_sort(s), to_expr(rm), to_expr(exp), to_expr(sig)); ctx->save_ast_trail(a); RETURN_Z3(of_expr(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_fpa_is_numeral_nan(Z3_context c, Z3_ast t) { diff --git a/src/api/api_goal.cpp b/src/api/api_goal.cpp index 0f1b9056b..ae48a3f6f 100644 --- a/src/api/api_goal.cpp +++ b/src/api/api_goal.cpp @@ -21,6 +21,7 @@ Revision History: #include "api/api_context.h" #include "api/api_goal.h" #include "ast/ast_translation.h" +#include "api/api_model.h" extern "C" { @@ -30,14 +31,14 @@ extern "C" { RESET_ERROR_CODE(); if (proofs != 0 && !mk_c(c)->m().proofs_enabled()) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } Z3_goal_ref * g = alloc(Z3_goal_ref, *mk_c(c)); g->m_goal = alloc(goal, mk_c(c)->m(), proofs != 0, models != 0, unsat_cores != 0); mk_c(c)->save_object(g); Z3_goal r = of_goal(g); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_goal_inc_ref(Z3_context c, Z3_goal g) { @@ -119,12 +120,12 @@ extern "C" { RESET_ERROR_CODE(); if (idx >= to_goal_ref(g)->size()) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } expr * result = to_goal_ref(g)->form(idx); mk_c(c)->save_ast_trail(result); RETURN_Z3(of_ast(result)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_goal_num_exprs(Z3_context c, Z3_goal g) { @@ -151,6 +152,20 @@ extern "C" { Z3_CATCH_RETURN(Z3_FALSE); } + Z3_model Z3_API Z3_goal_convert_model(Z3_context c, Z3_goal g, Z3_model m) { + Z3_TRY; + LOG_Z3_goal_convert_model(c, g, m); + RESET_ERROR_CODE(); + model_ref new_m; + Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); + mk_c(c)->save_object(m_ref); + if (m) m_ref->m_model = to_model_ref(m)->copy(); + 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_goal Z3_API Z3_goal_translate(Z3_context c, Z3_goal g, Z3_context target) { Z3_TRY; LOG_Z3_goal_translate(c, g, target); @@ -161,7 +176,7 @@ extern "C" { mk_c(target)->save_object(_r); Z3_goal r = of_goal(_r); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_goal_to_string(Z3_context c, Z3_goal g) { @@ -178,4 +193,18 @@ extern "C" { Z3_CATCH_RETURN(""); } + Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g) { + Z3_TRY; + LOG_Z3_goal_to_dimacs_string(c, g); + RESET_ERROR_CODE(); + std::ostringstream buffer; + to_goal_ref(g)->display_dimacs(buffer); + // Hack for removing the trailing '\n' + std::string result = buffer.str(); + SASSERT(result.size() > 0); + result.resize(result.size()-1); + return mk_c(c)->mk_external_string(result); + Z3_CATCH_RETURN(""); + } + }; diff --git a/src/api/api_goal.h b/src/api/api_goal.h index 4e9729df5..fa2ea61cf 100644 --- a/src/api/api_goal.h +++ b/src/api/api_goal.h @@ -24,11 +24,11 @@ Revision History: struct Z3_goal_ref : public api::object { goal_ref m_goal; Z3_goal_ref(api::context& c) : api::object(c) {} - virtual ~Z3_goal_ref() {} + ~Z3_goal_ref() override {} }; inline Z3_goal_ref * to_goal(Z3_goal g) { return reinterpret_cast(g); } inline Z3_goal of_goal(Z3_goal_ref * g) { return reinterpret_cast(g); } -inline goal_ref to_goal_ref(Z3_goal g) { return g == 0 ? goal_ref() : to_goal(g)->m_goal; } +inline goal_ref to_goal_ref(Z3_goal g) { return g == nullptr ? goal_ref() : to_goal(g)->m_goal; } #endif diff --git a/src/api/api_interp.cpp b/src/api/api_interp.cpp deleted file mode 100644 index d6f4e1128..000000000 --- a/src/api/api_interp.cpp +++ /dev/null @@ -1,728 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - api_interp.cpp - - Abstract: - API for interpolation - - Author: - - Ken McMillan - - Revision History: - - --*/ -#include -#include -#include "api/z3.h" -#include "api/api_log_macros.h" -#include "api/api_context.h" -#include "api/api_tactic.h" -#include "api/api_solver.h" -#include "api/api_model.h" -#include "api/api_stats.h" -#include "api/api_ast_vector.h" -#include "solver/tactic2solver.h" -#include "util/scoped_ctrl_c.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" -#include "tactic/portfolio/smt_strategic_solver.h" -#include "smt/smt_solver.h" -#include "smt/smt_implied_equalities.h" -#include "interp/iz3interp.h" -#include "interp/iz3profiling.h" -#include "interp/iz3hash.h" -#include "interp/iz3pp.h" -#include "interp/iz3checker.h" -#include "ast/scoped_proof.h" - -using namespace stl_ext; - -// WARNING: don't make a hash_map with this if the range type -// has a destructor: you'll get an address dependency!!! -namespace stl_ext { - template <> - class hash < Z3_ast > { - public: - size_t operator()(const Z3_ast p) const { - return (size_t)p; - } - }; -} - -typedef interpolation_options_struct *Z3_interpolation_options; - -extern "C" { - - Z3_context Z3_mk_interpolation_context(Z3_config cfg){ - if (!cfg) cfg = Z3_mk_config(); - Z3_set_param_value(cfg, "PROOF", "true"); - Z3_set_param_value(cfg, "MODEL", "true"); - // Z3_set_param_value(cfg, "PRE_SIMPLIFIER","false"); - // Z3_set_param_value(cfg, "SIMPLIFY_CLAUSES","false"); - - Z3_context ctx = Z3_mk_context(cfg); - return ctx; - } - - void Z3_interpolate_proof(Z3_context ctx, - Z3_ast proof, - int num, - Z3_ast *cnsts, - unsigned *parents, - Z3_params options, - Z3_ast *interps, - int num_theory, - Z3_ast *theory) - { - - if (num > 1){ // if we have interpolants to compute - - ptr_vector pre_cnsts_vec(num); // get constraints in a vector - for (int i = 0; i < num; i++){ - ast *a = to_ast(cnsts[i]); - pre_cnsts_vec[i] = a; - } - - ::vector pre_parents_vec; // get parents in a vector - if (parents){ - pre_parents_vec.resize(num); - for (int i = 0; i < num; i++) - pre_parents_vec[i] = parents[i]; - } - - ptr_vector theory_vec; // get background theory in a vector - if (theory){ - theory_vec.resize(num_theory); - for (int i = 0; i < num_theory; i++) - theory_vec[i] = to_ast(theory[i]); - } - - ptr_vector interpolants(num - 1); // make space for result - - ast_manager &_m = mk_c(ctx)->m(); - iz3interpolate(_m, - to_ast(proof), - pre_cnsts_vec, - pre_parents_vec, - interpolants, - theory_vec, - 0); // ignore params for now FIXME - - // copy result back - for (unsigned i = 0; i < interpolants.size(); i++){ - mk_c(ctx)->save_ast_trail(interpolants[i]); - interps[i] = of_ast(interpolants[i]); - _m.dec_ref(interpolants[i]); - } - } - } - - static std::ostringstream itp_err; - - int Z3_check_interpolant(Z3_context ctx, - unsigned num, - Z3_ast *cnsts, - unsigned *parents, - Z3_ast *itp, - Z3_string *error, - unsigned num_theory, - Z3_ast *theory){ - - ast_manager &_m = mk_c(ctx)->m(); - itp_err.clear(); - - // need a solver -- make one here, but how? - params_ref p = params_ref::get_empty(); //FIXME - scoped_ptr sf(mk_smt_solver_factory()); - scoped_ptr sp((*(sf))(_m, p, false, true, false, symbol("AUFLIA"))); - - ptr_vector cnsts_vec(num); // get constraints in a vector - for (unsigned i = 0; i < num; i++){ - ast *a = to_ast(cnsts[i]); - cnsts_vec[i] = a; - } - - ptr_vector itp_vec(num); // get interpolants in a vector - for (unsigned i = 0; i < num - 1; i++){ - ast *a = to_ast(itp[i]); - itp_vec[i] = a; - } - - ::vector parents_vec; // get parents in a vector - if (parents){ - parents_vec.resize(num); - for (unsigned i = 0; i < num; i++) - parents_vec[i] = parents[i]; - } - - ptr_vector theory_vec; // get background theory in a vector - if (theory){ - theory_vec.resize(num_theory); - for (unsigned i = 0; i < num_theory; i++) - theory_vec[i] = to_ast(theory[i]); - } - - bool res = iz3check(_m, - sp.get(), - itp_err, - cnsts_vec, - parents_vec, - itp_vec, - theory_vec); - - *error = res ? 0 : itp_err.str().c_str(); - return res; - } - - - static std::string Z3_profile_string; - - Z3_string Z3_interpolation_profile(Z3_context ctx){ - std::ostringstream f; - profiling::print(f); - Z3_profile_string = f.str(); - return Z3_profile_string.c_str(); - } - - - Z3_interpolation_options - Z3_mk_interpolation_options(){ - return (Z3_interpolation_options) new interpolation_options_struct; - } - - void - Z3_del_interpolation_options(Z3_interpolation_options opts){ - delete opts; - } - - void - Z3_set_interpolation_option(Z3_interpolation_options opts, - Z3_string name, - Z3_string value){ - opts->map[name] = value; - } - - Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p){ - Z3_TRY; - LOG_Z3_get_interpolant(c, pf, pat, p); - RESET_ERROR_CODE(); - - Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); - mk_c(c)->save_object(v); - - ast *_pf = to_ast(pf); - ast *_pat = to_ast(pat); - - ptr_vector interp; - ptr_vector cnsts; // to throw away - - ast_manager &_m = mk_c(c)->m(); - - iz3interpolate(_m, - _pf, - cnsts, - _pat, - interp, - (interpolation_options_struct *)0 // ignore params for now - ); - - // copy result back - for (unsigned i = 0; i < interp.size(); i++){ - v->m_ast_vector.push_back(interp[i]); - _m.dec_ref(interp[i]); - } - RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); - } - - Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, Z3_ast pat, Z3_params p, Z3_ast_vector *out_interp, Z3_model *model){ - Z3_TRY; - LOG_Z3_compute_interpolant(c, pat, p, out_interp, model); - RESET_ERROR_CODE(); - - - // params_ref &_p = to_params(p)->m_params; - params_ref _p; - _p.set_bool("proof", true); // this is currently useless - - scoped_proof_mode spm(mk_c(c)->m(), PGM_ENABLED); - scoped_ptr sf = mk_smt_solver_factory(); - scoped_ptr m_solver((*sf)(mk_c(c)->m(), _p, true, true, true, ::symbol::null)); - m_solver.get()->updt_params(_p); // why do we have to do this? - - - // some boilerplate stolen from Z3_solver_check - unsigned timeout = p?to_params(p)->m_params.get_uint("timeout", mk_c(c)->get_timeout()):UINT_MAX; - unsigned rlimit = p?to_params(p)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()):0; - bool use_ctrl_c = p?to_params(p)->m_params.get_bool("ctrl_c", false): false; - cancel_eh eh(mk_c(c)->m().limit()); - api::context::set_interruptable si(*(mk_c(c)), eh); - - ast *_pat = to_ast(pat); - - ptr_vector interp; - ptr_vector cnsts; // to throw away - - ast_manager &_m = mk_c(c)->m(); - - model_ref m; - lbool _status; - - { - scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); - scoped_timer timer(timeout, &eh); - scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); - try { - _status = iz3interpolate(_m, - *(m_solver.get()), - _pat, - cnsts, - interp, - m, - 0 // ignore params for now - ); - } - catch (z3_exception & ex) { - mk_c(c)->handle_exception(ex); - RETURN_Z3_compute_interpolant Z3_L_UNDEF; - } - } - - for (unsigned i = 0; i < cnsts.size(); i++) - _m.dec_ref(cnsts[i]); - - Z3_lbool status = of_lbool(_status); - - Z3_ast_vector_ref *v = 0; - *model = 0; - - if (_status == l_false){ - // copy result back - v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); - mk_c(c)->save_object(v); - for (unsigned i = 0; i < interp.size(); i++){ - v->m_ast_vector.push_back(interp[i]); - _m.dec_ref(interp[i]); - } - } - else { - model_ref mr; - m_solver.get()->get_model(mr); - if(mr.get()){ - Z3_model_ref *tmp_val = alloc(Z3_model_ref, *mk_c(c)); - tmp_val->m_model = mr.get(); - mk_c(c)->save_object(tmp_val); - *model = of_model(tmp_val); - } - } - - *out_interp = of_ast_vector(v); - - RETURN_Z3_compute_interpolant status; - Z3_CATCH_RETURN(Z3_L_UNDEF); - } - - -}; - - -static void tokenize(const std::string &str, std::vector &tokens){ - for (unsigned i = 0; i < str.size();){ - if (str[i] == ' '){ i++; continue; } - unsigned beg = i; - while (i < str.size() && str[i] != ' ')i++; - if (i > beg) - tokens.push_back(str.substr(beg, i - beg)); - } -} - -static void get_file_params(const char *filename, hash_map ¶ms){ - std::ifstream f(filename); - if (f){ - std::string first_line; - std::getline(f, first_line); - // std::cout << "first line: '" << first_line << "'" << std::endl; - if (first_line.size() >= 2 && first_line[0] == ';' && first_line[1] == '!'){ - std::vector tokens; - tokenize(first_line.substr(2, first_line.size() - 2), tokens); - for (unsigned i = 0; i < tokens.size(); i++){ - std::string &tok = tokens[i]; - size_t eqpos = tok.find('='); - if (eqpos != std::string::npos){ - std::string left = tok.substr(0, eqpos); - std::string right = tok.substr(eqpos + 1, tok.size() - eqpos - 1); - params[left] = right; - } - } - } - f.close(); - } -} - -extern "C" { - -#if 0 - static void iZ3_write_seq(Z3_context ctx, int num, Z3_ast *cnsts, const char *filename, int num_theory, Z3_ast *theory){ - int num_fmlas = num+num_theory; - std::vector fmlas(num_fmlas); - if(num_theory) - std::copy(theory,theory+num_theory,fmlas.begin()); - for(int i = 0; i < num_theory; i++) - fmlas[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),fmlas[i]); - std::copy(cnsts,cnsts+num,fmlas.begin()+num_theory); - Z3_string smt = Z3_benchmark_to_smtlib_string(ctx,"none","AUFLIA","unknown","",num_fmlas-1,&fmlas[0],fmlas[num_fmlas-1]); - std::ofstream f(filename); - if(num_theory) - f << ";! THEORY=" << num_theory << "\n"; - f << smt; - f.close(); - } - - void Z3_write_interpolation_problem(Z3_context ctx, int num, Z3_ast *cnsts, unsigned *parents, const char *filename, int num_theory, Z3_ast *theory){ - if(!parents){ - iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); - return; - } - std::vector tcnsts(num); - hash_map syms; - for(int j = 0; j < num - 1; j++){ - std::ostringstream oss; - oss << "$P" << j; - std::string name = oss.str(); - Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); - Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); - syms[j] = symbol; - tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); - } - tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); - for(int j = num-2; j >= 0; j--){ - int parent = parents[j]; - // assert(parent >= 0 && parent < num); - tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); - } - iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); - } -#else - - - static Z3_ast and_vec(Z3_context ctx, svector &c){ - return (c.size() > 1) ? Z3_mk_and(ctx, c.size(), &c[0]) : c[0]; - } - - static Z3_ast parents_vector_to_tree(Z3_context ctx, int num, Z3_ast *cnsts, unsigned *parents){ - Z3_ast res; - if (!parents){ - res = Z3_mk_interpolant(ctx, cnsts[0]); - for (int i = 1; i < num - 1; i++){ - Z3_ast bar[2] = { res, cnsts[i] }; - res = Z3_mk_interpolant(ctx, Z3_mk_and(ctx, 2, bar)); - } - if (num > 1){ - Z3_ast bar[2] = { res, cnsts[num - 1] }; - res = Z3_mk_and(ctx, 2, bar); - } - } - else { - std::vector > chs(num); - for (int i = 0; i < num - 1; i++){ - svector &c = chs[i]; - c.push_back(cnsts[i]); - Z3_ast foo = Z3_mk_interpolant(ctx, and_vec(ctx, c)); - chs[parents[i]].push_back(foo); - } - { - svector &c = chs[num - 1]; - c.push_back(cnsts[num - 1]); - res = and_vec(ctx, c); - } - } - Z3_inc_ref(ctx, res); - return res; - } - - void Z3_write_interpolation_problem(Z3_context ctx, unsigned num, Z3_ast *cnsts, unsigned *parents, const char *filename, unsigned num_theory, Z3_ast *theory){ - std::ofstream f(filename); - if (num > 0){ -#if 0 - // Suggested shorthand: - ptr_vector cnsts_vec; - cnsts_vec.append(num, to_exprs(cnsts)); - cnsts_vec.append(num_theory, to_exprs(theory)); -#endif - ptr_vector cnsts_vec(num); // get constraints in a vector - for (unsigned i = 0; i < num; i++){ - expr *a = to_expr(cnsts[i]); - cnsts_vec[i] = a; - } - for (unsigned i = 0; i < num_theory; i++){ - expr *a = to_expr(theory[i]); - cnsts_vec.push_back(a); - } - Z3_ast tree = parents_vector_to_tree(ctx, num, cnsts, parents); - iz3pp(mk_c(ctx)->m(), cnsts_vec, to_expr(tree), f); - Z3_dec_ref(ctx, tree); - } - f.close(); - -#if 0 - - - if(!parents){ - iZ3_write_seq(ctx,num,cnsts,filename,num_theory,theory); - return; - } - std::vector tcnsts(num); - hash_map syms; - for(int j = 0; j < num - 1; j++){ - std::ostringstream oss; - oss << "$P" << j; - std::string name = oss.str(); - Z3_symbol s = Z3_mk_string_symbol(ctx, name.c_str()); - Z3_ast symbol = Z3_mk_const(ctx, s, Z3_mk_bool_sort(ctx)); - syms[j] = symbol; - tcnsts[j] = Z3_mk_implies(ctx,cnsts[j],symbol); - } - tcnsts[num-1] = Z3_mk_implies(ctx,cnsts[num-1],Z3_mk_false(ctx)); - for(int j = num-2; j >= 0; j--){ - int parent = parents[j]; - // assert(parent >= 0 && parent < num); - tcnsts[parent] = Z3_mk_implies(ctx,syms[j],tcnsts[parent]); - } - iZ3_write_seq(ctx,num,&tcnsts[0],filename,num_theory,theory); -#endif - - } - - -#endif - - static std::vector read_cnsts; - static std::vector read_parents; - static std::ostringstream read_error; - static std::string read_msg; - static std::vector read_theory; - - static bool iZ3_parse(Z3_context ctx, const char *filename, const char **error, svector &assertions){ - read_error.clear(); - try { - std::string foo(filename); - Z3_ast assrts = Z3_parse_smtlib2_file(ctx, filename, 0, 0, 0, 0, 0, 0); - Z3_app app = Z3_to_app(ctx, assrts); - int nconjs = Z3_get_app_num_args(ctx, app); - assertions.resize(nconjs); - for (int k = 0; k < nconjs; k++) - assertions[k] = Z3_get_app_arg(ctx, app, k); - } - catch (...) { - read_error << "SMTLIB parse error: " << Z3_get_parser_error(ctx); - read_msg = read_error.str(); - *error = read_msg.c_str(); - return false; - } - Z3_set_error_handler(ctx, 0); - return true; - } - - - int Z3_read_interpolation_problem(Z3_context ctx, unsigned *_num, Z3_ast *cnsts[], unsigned *parents[], const char *filename, Z3_string_ptr error, unsigned *ret_num_theory, Z3_ast *theory[]){ - - hash_map file_params; - get_file_params(filename, file_params); - - unsigned num_theory = 0; - if (file_params.find("THEORY") != file_params.end()) - num_theory = atoi(file_params["THEORY"].c_str()); - - svector assertions; - if (!iZ3_parse(ctx, filename, error, assertions)) - return false; - - if (num_theory > assertions.size()) - num_theory = assertions.size(); - unsigned num = assertions.size() - num_theory; - - read_cnsts.resize(num); - read_parents.resize(num); - read_theory.resize(num_theory); - - for (unsigned j = 0; j < num_theory; j++) - read_theory[j] = assertions[j]; - for (unsigned j = 0; j < num; j++) - read_cnsts[j] = assertions[j + num_theory]; - - if (ret_num_theory) - *ret_num_theory = num_theory; - if (theory) - *theory = &read_theory[0]; - - if (!parents){ - *_num = num; - *cnsts = &read_cnsts[0]; - return true; - } - - for (unsigned j = 0; j < num; j++) - read_parents[j] = SHRT_MAX; - - hash_map pred_map; - - for (unsigned j = 0; j < num; j++){ - Z3_ast lhs = 0, rhs = read_cnsts[j]; - - if (Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, Z3_to_app(ctx, rhs))) == Z3_OP_IMPLIES){ - Z3_app app1 = Z3_to_app(ctx, rhs); - Z3_ast lhs1 = Z3_get_app_arg(ctx, app1, 0); - Z3_ast rhs1 = Z3_get_app_arg(ctx, app1, 1); - if (Z3_get_decl_kind(ctx, Z3_get_app_decl(ctx, Z3_to_app(ctx, lhs1))) == Z3_OP_AND){ - Z3_app app2 = Z3_to_app(ctx, lhs1); - int nconjs = Z3_get_app_num_args(ctx, app2); - for (int k = nconjs - 1; k >= 0; --k) - rhs1 = Z3_mk_implies(ctx, Z3_get_app_arg(ctx, app2, k), rhs1); - rhs = rhs1; - } - } - - while (1){ - Z3_app app = Z3_to_app(ctx, rhs); - Z3_func_decl func = Z3_get_app_decl(ctx, app); - Z3_decl_kind dk = Z3_get_decl_kind(ctx, func); - if (dk == Z3_OP_IMPLIES){ - if (lhs){ - Z3_ast child = lhs; - if (pred_map.find(child) == pred_map.end()){ - read_error << "formula " << j + 1 << ": unknown: " << Z3_ast_to_string(ctx, child); - goto fail; - } - int child_num = pred_map[child]; - if (read_parents[child_num] != SHRT_MAX){ - read_error << "formula " << j + 1 << ": multiple reference: " << Z3_ast_to_string(ctx, child); - goto fail; - } - read_parents[child_num] = j; - } - lhs = Z3_get_app_arg(ctx, app, 0); - rhs = Z3_get_app_arg(ctx, app, 1); - } - else { - if (!lhs){ - read_error << "formula " << j + 1 << ": should be (implies {children} fmla parent)"; - goto fail; - } - read_cnsts[j] = lhs; - Z3_ast name = rhs; - if (pred_map.find(name) != pred_map.end()){ - read_error << "formula " << j + 1 << ": duplicate symbol"; - goto fail; - } - pred_map[name] = j; - break; - } - } - } - - for (unsigned j = 0; j < num - 1; j++) - if (read_parents[j] == SHRT_MAX){ - read_error << "formula " << j + 1 << ": unreferenced"; - goto fail; - } - - *_num = num; - *cnsts = &read_cnsts[0]; - *parents = &read_parents[0]; - return true; - - fail: - read_msg = read_error.str(); - *error = read_msg.c_str(); - return false; - - } -} - - -#if 0 -/** Constant reprepresenting a root of a formula tree for tree interpolation */ -#define IZ3_ROOT SHRT_MAX - -/** This function uses Z3 to determine satisfiability of a set of - constraints. If UNSAT, an interpolant is returned, based on the - refutation generated by Z3. If SAT, a model is returned. - - If "parents" is non-null, computes a tree interpolant. The tree is - defined by the array "parents". This array maps each formula in - the tree to its parent, where formulas are indicated by their - integer index in "cnsts". The parent of formula n must have index - greater than n. The last formula is the root of the tree. Its - parent entry should be the constant IZ3_ROOT. - - If "parents" is null, computes a sequence interpolant. - - \param ctx The Z3 context. Must be generated by iz3_mk_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints (AST's in context ctx) - \param parents The parents vector defining the tree structure - \param options Interpolation options (may be NULL) - \param interps Array to return interpolants (size at least num-1, may be NULL) - \param model Returns a Z3 model if constraints SAT (may be NULL) - \param labels Returns relevant labels if SAT (may be NULL) - \param incremental - - VERY IMPORTANT: All the Z3 formulas in cnsts must be in Z3 - context ctx. The model and interpolants returned are also - in this context. - - The return code is as in Z3_check_assumptions, that is, - - Z3_L_FALSE = constraints UNSAT (interpolants returned) - Z3_L_TRUE = constraints SAT (model returned) - Z3_L_UNDEF = Z3 produced no result, or interpolation not possible - - Currently, this function supports integer and boolean variables, - as well as arrays over these types, with linear arithmetic, - uninterpreted functions and quantifiers over integers (that is - AUFLIA). Interpolants are produced in AULIA. However, some - uses of array operations may cause quantifiers to appear in the - interpolants even when there are no quantifiers in the input formulas. - Although quantifiers may appear in the input formulas, Z3 may give up in - this case, returning Z3_L_UNDEF. - - If "incremental" is true, cnsts must contain exactly the set of - formulas that are currently asserted in the context. If false, - there must be no formulas currently asserted in the context. - Setting "incremental" to true makes it posisble to incrementally - add and remove constraints from the context until the context - becomes UNSAT, at which point an interpolant is computed. Caution - must be used, however. Before popping the context, if you wish to - keep the interolant formulas, you *must* preserve them by using - Z3_persist_ast. Also, if you want to simplify the interpolant - formulas using Z3_simplify, you must first pop all of the - assertions in the context (or use a different context). Otherwise, - the formulas will be simplified *relative* to these constraints, - which is almost certainly not what you want. - - - Current limitations on tree interpolants. In a tree interpolation - problem, each constant (0-ary function symbol) must occur only - along one path from root to leaf. Function symbols (of arity > 0) - are considered to have global scope (i.e., may appear in any - interpolant formula). - - def_API('Z3_interpolate', BOOL, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(PARAMS), _out_array(1, AST), _out(MODEL), _out(LITERALS), _in(UINT), _in(UINT), _in_array(9, AST))) -*/ - -Z3_lbool Z3_API Z3_interpolate(Z3_context ctx, - unsigned num, - Z3_ast *cnsts, - unsigned *parents, - Z3_params options, - Z3_ast *interps, - Z3_model *model, - Z3_literals *labels, - unsigned incremental, - unsigned num_theory, - Z3_ast *theory); -#endif diff --git a/src/api/api_log.cpp b/src/api/api_log.cpp index 410110d9a..1bdbb8735 100644 --- a/src/api/api_log.cpp +++ b/src/api/api_log.cpp @@ -21,15 +21,15 @@ Revision History: #include "util/util.h" #include "util/version.h" -std::ostream * g_z3_log = 0; +std::ostream * g_z3_log = nullptr; bool g_z3_log_enabled = false; extern "C" { void Z3_close_log_unsafe(void) { - if (g_z3_log != 0) { + if (g_z3_log != nullptr) { dealloc(g_z3_log); g_z3_log_enabled = false; - g_z3_log = 0; + g_z3_log = nullptr; } } @@ -40,12 +40,12 @@ extern "C" { #pragma omp critical (z3_log) { #endif - if (g_z3_log != 0) + if (g_z3_log != nullptr) Z3_close_log_unsafe(); g_z3_log = alloc(std::ofstream, filename); if (g_z3_log->bad() || g_z3_log->fail()) { dealloc(g_z3_log); - g_z3_log = 0; + g_z3_log = nullptr; res = Z3_FALSE; } else { @@ -61,13 +61,13 @@ extern "C" { } void Z3_API Z3_append_log(Z3_string str) { - if (g_z3_log == 0) + if (g_z3_log == nullptr) return; #ifdef Z3_LOG_SYNC #pragma omp critical (z3_log) { #endif - if (g_z3_log != 0) + if (g_z3_log != nullptr) _Z3_append_log(static_cast(str)); #ifdef Z3_LOG_SYNC } @@ -75,7 +75,7 @@ extern "C" { } void Z3_API Z3_close_log(void) { - if (g_z3_log != 0) { + if (g_z3_log != nullptr) { #ifdef Z3_LOG_SYNC #pragma omp critical (z3_log) { diff --git a/src/api/api_model.cpp b/src/api/api_model.cpp index bda33f186..7eb7d2fdd 100644 --- a/src/api/api_model.cpp +++ b/src/api/api_model.cpp @@ -38,7 +38,7 @@ extern "C" { m_ref->m_model = alloc(model, mk_c(c)->m()); mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_model_inc_ref(Z3_context c, Z3_model m) { @@ -65,14 +65,14 @@ extern "C" { Z3_TRY; LOG_Z3_model_get_const_interp(c, m, a); RESET_ERROR_CODE(); - CHECK_NON_NULL(m, 0); + CHECK_NON_NULL(m, nullptr); expr * r = to_model_ref(m)->get_const_interp(to_func_decl(a)); if (!r) { - RETURN_Z3(0); + RETURN_Z3(nullptr); } mk_c(c)->save_ast_trail(r); RETURN_Z3(of_expr(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_model_has_interp(Z3_context c, Z3_model m, Z3_func_decl a) { @@ -91,17 +91,17 @@ extern "C" { Z3_TRY; LOG_Z3_model_get_func_interp(c, m, f); RESET_ERROR_CODE(); - CHECK_NON_NULL(m, 0); + CHECK_NON_NULL(m, nullptr); func_interp * _fi = to_model_ref(m)->get_func_interp(to_func_decl(f)); if (!_fi) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } Z3_func_interp_ref * fi = alloc(Z3_func_interp_ref, *mk_c(c), to_model_ref(m)); fi->m_func_interp = _fi; mk_c(c)->save_object(fi); RETURN_Z3(of_func_interp(fi)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_model_get_num_consts(Z3_context c, Z3_model m) { @@ -117,16 +117,16 @@ extern "C" { Z3_TRY; LOG_Z3_model_get_const_decl(c, m, i); RESET_ERROR_CODE(); - CHECK_NON_NULL(m, 0); + CHECK_NON_NULL(m, nullptr); model * _m = to_model_ref(m); if (i < _m->get_num_constants()) { RETURN_Z3(of_func_decl(_m->get_constant(i))); } else { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_model_get_num_funcs(Z3_context c, Z3_model m) { @@ -139,11 +139,11 @@ extern "C" { } Z3_func_decl get_model_func_decl_core(Z3_context c, Z3_model m, unsigned i) { - CHECK_NON_NULL(m, 0); + CHECK_NON_NULL(m, nullptr); model * _m = to_model_ref(m); if (i >= _m->get_num_functions()) { SET_ERROR_CODE(Z3_IOB); - return 0; + return nullptr; } return of_func_decl(_m->get_function(i)); } @@ -154,19 +154,20 @@ extern "C" { RESET_ERROR_CODE(); Z3_func_decl r = get_model_func_decl_core(c, m, i); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_model_eval(Z3_context c, Z3_model m, Z3_ast t, Z3_bool model_completion, Z3_ast * v) { Z3_TRY; LOG_Z3_model_eval(c, m, t, model_completion, v); - if (v) *v = 0; + if (v) *v = nullptr; RESET_ERROR_CODE(); CHECK_NON_NULL(m, Z3_FALSE); CHECK_IS_EXPR(t, Z3_FALSE); model * _m = to_model_ref(m); expr_ref result(mk_c(c)->m()); - _m->eval(to_expr(t), result, model_completion == Z3_TRUE); + model::scoped_model_completion _scm(*_m, model_completion == Z3_TRUE); + result = (*_m)(to_expr(t)); mk_c(c)->save_ast_trail(result.get()); *v = of_ast(result.get()); RETURN_Z3_model_eval Z3_TRUE; @@ -187,11 +188,11 @@ extern "C" { RESET_ERROR_CODE(); if (i >= to_model_ref(m)->get_num_uninterpreted_sorts()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } sort * s = to_model_ref(m)->get_uninterpreted_sort(i); RETURN_Z3(of_sort(s)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_model_get_sort_universe(Z3_context c, Z3_model m, Z3_sort s) { @@ -200,7 +201,7 @@ extern "C" { RESET_ERROR_CODE(); if (!to_model_ref(m)->has_uninterpreted_sort(to_sort(s))) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } ptr_vector const & universe = to_model_ref(m)->get_universe(to_sort(s)); Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); @@ -210,7 +211,19 @@ extern "C" { v->m_ast_vector.push_back(universe[i]); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); + } + + Z3_model Z3_API Z3_model_translate(Z3_context c, Z3_model m, Z3_context target) { + Z3_TRY; + LOG_Z3_model_translate(c, m, target); + RESET_ERROR_CODE(); + Z3_model_ref* dst = alloc(Z3_model_ref, *mk_c(target)); + ast_translation tr(mk_c(c)->m(), mk_c(target)->m()); + dst->m_model = to_model_ref(m)->translate(tr); + mk_c(target)->save_object(dst); + RETURN_Z3(of_model(dst)); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_is_as_array(Z3_context c, Z3_ast a) { @@ -230,9 +243,9 @@ extern "C" { } else { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_func_interp Z3_API Z3_add_func_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast else_val) { @@ -247,7 +260,7 @@ extern "C" { mdl->register_decl(d, f_ref->m_func_interp); f_ref->m_func_interp->set_else(to_expr(else_val)); RETURN_Z3(of_func_interp(f_ref)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_add_const_interp(Z3_context c, Z3_model m, Z3_func_decl f, Z3_ast a) { @@ -298,30 +311,30 @@ extern "C" { Z3_TRY; LOG_Z3_func_interp_get_entry(c, f, i); RESET_ERROR_CODE(); - CHECK_NON_NULL(f, 0); + CHECK_NON_NULL(f, nullptr); if (i >= to_func_interp_ref(f)->num_entries()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } Z3_func_entry_ref * e = alloc(Z3_func_entry_ref, *mk_c(c), to_func_interp(f)->m_model.get()); e->m_func_interp = to_func_interp_ref(f); e->m_func_entry = to_func_interp_ref(f)->get_entry(i); mk_c(c)->save_object(e); RETURN_Z3(of_func_entry(e)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_func_interp_get_else(Z3_context c, Z3_func_interp f) { Z3_TRY; LOG_Z3_func_interp_get_else(c, f); RESET_ERROR_CODE(); - CHECK_NON_NULL(f, 0); + CHECK_NON_NULL(f, nullptr); expr * e = to_func_interp_ref(f)->get_else(); if (e) { mk_c(c)->save_ast_trail(e); } RETURN_Z3(of_expr(e)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_func_interp_set_else(Z3_context c, Z3_func_interp f, Z3_ast else_value) { @@ -387,7 +400,7 @@ extern "C" { expr * v = to_func_entry_ref(e)->get_result(); mk_c(c)->save_ast_trail(v); RETURN_Z3(of_expr(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_func_entry_get_num_args(Z3_context c, Z3_func_entry e) { @@ -404,39 +417,12 @@ extern "C" { RESET_ERROR_CODE(); if (i >= to_func_entry(e)->m_func_interp->get_arity()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } expr * r = to_func_entry(e)->m_func_entry->get_arg(i); RETURN_Z3(of_expr(r)); - Z3_CATCH_RETURN(0); - } - - // ---------------------------- - // - // DEPRECATED API - // - // ---------------------------- - - void Z3_API Z3_del_model(Z3_context c, Z3_model m) { - Z3_model_dec_ref(c, m); - } - - unsigned Z3_API Z3_get_model_num_constants(Z3_context c, Z3_model m) { - return Z3_model_get_num_consts(c, m); - } - - Z3_func_decl Z3_API Z3_get_model_constant(Z3_context c, Z3_model m, unsigned i) { - return Z3_model_get_const_decl(c, m, i); - } - - unsigned Z3_API Z3_get_model_num_funcs(Z3_context c, Z3_model m) { - return Z3_model_get_num_funcs(c, m); - } - - Z3_func_decl Z3_API Z3_get_model_func_decl(Z3_context c, Z3_model m, unsigned i) { - return Z3_model_get_func_decl(c, m, i); - } - + Z3_CATCH_RETURN(nullptr); + } unsigned get_model_func_num_entries_core(Z3_context c, Z3_model m, unsigned i) { RESET_ERROR_CODE(); @@ -479,7 +465,7 @@ extern "C" { Z3_TRY; LOG_Z3_model_to_string(c, m); RESET_ERROR_CODE(); - CHECK_NON_NULL(m, 0); + CHECK_NON_NULL(m, nullptr); std::ostringstream buffer; std::string result; if (mk_c(c)->get_print_mode() == Z3_PRINT_SMTLIB2_COMPLIANT) { @@ -495,7 +481,7 @@ extern "C" { result = buffer.str(); } return mk_c(c)->mk_external_string(result); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } }; diff --git a/src/api/api_model.h b/src/api/api_model.h index 64427cfb8..fa94e9728 100644 --- a/src/api/api_model.h +++ b/src/api/api_model.h @@ -24,7 +24,7 @@ Revision History: struct Z3_model_ref : public api::object { model_ref m_model; Z3_model_ref(api::context& c): api::object(c) {} - virtual ~Z3_model_ref() {} + ~Z3_model_ref() override {} }; inline Z3_model_ref * to_model(Z3_model s) { return reinterpret_cast(s); } @@ -34,8 +34,8 @@ inline model * to_model_ref(Z3_model s) { return to_model(s)->m_model.get(); } struct Z3_func_interp_ref : public api::object { model_ref m_model; // must have it to prevent reference to m_func_interp to be killed. func_interp * m_func_interp; - Z3_func_interp_ref(api::context& c, model * m): api::object(c), m_model(m), m_func_interp(0) {} - virtual ~Z3_func_interp_ref() {} + Z3_func_interp_ref(api::context& c, model * m): api::object(c), m_model(m), m_func_interp(nullptr) {} + ~Z3_func_interp_ref() override {} }; inline Z3_func_interp_ref * to_func_interp(Z3_func_interp s) { return reinterpret_cast(s); } @@ -46,8 +46,8 @@ struct Z3_func_entry_ref : public api::object { model_ref m_model; // must have it to prevent reference to m_func_entry to be killed. func_interp * m_func_interp; func_entry const * m_func_entry; - Z3_func_entry_ref(api::context& c, model * m):api::object(c), m_model(m), m_func_interp(0), m_func_entry(0) {} - virtual ~Z3_func_entry_ref() {} + Z3_func_entry_ref(api::context& c, model * m):api::object(c), m_model(m), m_func_interp(nullptr), m_func_entry(nullptr) {} + ~Z3_func_entry_ref() override {} }; inline Z3_func_entry_ref * to_func_entry(Z3_func_entry s) { return reinterpret_cast(s); } diff --git a/src/api/api_numeral.cpp b/src/api/api_numeral.cpp index 9a778d6e0..de9886571 100644 --- a/src/api/api_numeral.cpp +++ b/src/api/api_numeral.cpp @@ -52,11 +52,11 @@ extern "C" { LOG_Z3_mk_numeral(c, n, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { - RETURN_Z3(0); + RETURN_Z3(nullptr); } if (!n) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } sort * _ty = to_sort(ty); bool is_float = mk_c(c)->fpautil().is_float(_ty); @@ -73,11 +73,11 @@ extern "C" { ('P' == *m) || ('+' == *m))))) { SET_ERROR_CODE(Z3_PARSER_ERROR); - return 0; + RETURN_Z3(nullptr); } ++m; } - ast * a = 0; + ast * a = nullptr; if (_ty->get_family_id() == mk_c(c)->get_fpa_fid()) { // avoid expanding floats into huge rationals. fpa_util & fu = mk_c(c)->fpautil(); @@ -89,7 +89,7 @@ extern "C" { else a = mk_c(c)->mk_numeral_core(rational(n), _ty); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_int(Z3_context c, int value, Z3_sort ty) { @@ -97,11 +97,11 @@ extern "C" { LOG_Z3_mk_int(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { - RETURN_Z3(0); + RETURN_Z3(nullptr); } ast * a = mk_c(c)->mk_numeral_core(rational(value), to_sort(ty)); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_unsigned_int(Z3_context c, unsigned value, Z3_sort ty) { @@ -109,37 +109,37 @@ extern "C" { LOG_Z3_mk_unsigned_int(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { - RETURN_Z3(0); + RETURN_Z3(nullptr); } ast * a = mk_c(c)->mk_numeral_core(rational(value), to_sort(ty)); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } - Z3_ast Z3_API Z3_mk_int64(Z3_context c, long long value, Z3_sort ty) { + Z3_ast Z3_API Z3_mk_int64(Z3_context c, int64_t value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_int64(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { - RETURN_Z3(0); + RETURN_Z3(nullptr); } rational n(value, rational::i64()); ast* a = mk_c(c)->mk_numeral_core(n, to_sort(ty)); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } - Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, unsigned long long value, Z3_sort ty) { + Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, uint64_t value, Z3_sort ty) { Z3_TRY; LOG_Z3_mk_unsigned_int64(c, value, ty); RESET_ERROR_CODE(); if (!check_numeral_sort(c, ty)) { - RETURN_Z3(0); + RETURN_Z3(nullptr); } rational n(value, rational::ui64()); ast * a = mk_c(c)->mk_numeral_core(n, to_sort(ty)); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_is_numeral_ast(Z3_context c, Z3_ast a) { @@ -172,7 +172,7 @@ extern "C" { if (mk_c(c)->bvutil().is_numeral(e, r, bv_size)) { return Z3_TRUE; } - uint64 v; + uint64_t v; if (mk_c(c)->datalog_util().is_numeral(e, v)) { r = rational(v, rational::ui64()); return Z3_TRUE; @@ -262,7 +262,7 @@ extern "C" { Z3_CATCH_RETURN(""); } - Z3_bool Z3_API Z3_get_numeral_small(Z3_context c, Z3_ast a, long long* num, long long* den) { + Z3_bool Z3_API Z3_get_numeral_small(Z3_context c, Z3_ast a, int64_t* num, int64_t* den) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_small(c, a, num, den); @@ -296,7 +296,7 @@ extern "C" { SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_FALSE; } - long long l; + int64_t l; if (Z3_get_numeral_int64(c, v, &l) && l >= INT_MIN && l <= INT_MAX) { *i = static_cast(l); return Z3_TRUE; @@ -314,7 +314,7 @@ extern "C" { SET_ERROR_CODE(Z3_INVALID_ARG); return Z3_FALSE; } - unsigned long long l; + uint64_t l; if (Z3_get_numeral_uint64(c, v, &l) && (l <= 0xFFFFFFFF)) { *u = static_cast(l); return Z3_TRUE; @@ -323,7 +323,7 @@ extern "C" { Z3_CATCH_RETURN(Z3_FALSE); } - Z3_bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, unsigned long long* u) { + Z3_bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, uint64_t* u) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_uint64(c, v, u); @@ -343,7 +343,7 @@ extern "C" { Z3_CATCH_RETURN(Z3_FALSE); } - Z3_bool Z3_API Z3_get_numeral_int64(Z3_context c, Z3_ast v, long long* i) { + Z3_bool Z3_API Z3_get_numeral_int64(Z3_context c, Z3_ast v, int64_t* i) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_int64(c, v, i); @@ -362,7 +362,7 @@ extern "C" { Z3_CATCH_RETURN(Z3_FALSE); } - Z3_bool Z3_API Z3_get_numeral_rational_int64(Z3_context c, Z3_ast v, long long* num, long long* den) { + Z3_bool Z3_API Z3_get_numeral_rational_int64(Z3_context c, Z3_ast v, int64_t* num, int64_t* den) { Z3_TRY; // This function invokes Z3_get_numeral_rational, but it is still ok to add LOG command here because it does not return a Z3 object. LOG_Z3_get_numeral_rational_int64(c, v, num, den); @@ -397,7 +397,7 @@ extern "C" { } ast * a = mk_c(c)->mk_numeral_core(r, mk_c(c)->bvutil().mk_sort(sz)); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } }; diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index 0d96a6719..21c55f428 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -17,8 +17,8 @@ Revision History: --*/ #include #include "util/cancel_eh.h" -#include "util/file_path.h" #include "util/scoped_timer.h" +#include "util/file_path.h" #include "parsers/smt2/smt2parser.h" #include "opt/opt_context.h" #include "opt/opt_cmds.h" @@ -31,12 +31,13 @@ Revision History: #include "api/api_model.h" #include "api/api_ast_vector.h" + extern "C" { struct Z3_optimize_ref : public api::object { opt::context* m_opt; - Z3_optimize_ref(api::context& c): api::object(c), m_opt(0) {} - virtual ~Z3_optimize_ref() { dealloc(m_opt); } + Z3_optimize_ref(api::context& c): api::object(c), m_opt(nullptr) {} + ~Z3_optimize_ref() override { dealloc(m_opt); } }; inline Z3_optimize_ref * to_optimize(Z3_optimize o) { return reinterpret_cast(o); } inline Z3_optimize of_optimize(Z3_optimize_ref * o) { return reinterpret_cast(o); } @@ -50,7 +51,7 @@ extern "C" { o->m_opt = alloc(opt::context,mk_c(c)->m()); mk_c(c)->save_object(o); RETURN_Z3(of_optimize(o)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_optimize_inc_ref(Z3_context c, Z3_optimize o) { @@ -139,8 +140,16 @@ extern "C" { r = to_optimize_ptr(o)->optimize(); } catch (z3_exception& ex) { - mk_c(c)->handle_exception(ex); + if (!mk_c(c)->m().canceled()) { + mk_c(c)->handle_exception(ex); + } r = l_undef; + if (ex.msg() == std::string("canceled") && mk_c(c)->m().canceled()) { + to_optimize_ptr(o)->set_reason_unknown(ex.msg()); + } + else { + mk_c(c)->handle_exception(ex); + } } // to_optimize_ref(d).cleanup(); } @@ -171,7 +180,7 @@ extern "C" { } mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p) { @@ -195,7 +204,7 @@ extern "C" { to_optimize_ptr(o)->collect_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } // get lower value or current approximation @@ -206,7 +215,7 @@ extern "C" { expr_ref e = to_optimize_ptr(o)->get_lower(idx); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } // get upper or current approximation @@ -217,7 +226,7 @@ extern "C" { expr_ref e = to_optimize_ptr(o)->get_upper(idx); mk_c(c)->save_ast_trail(e); RETURN_Z3(of_expr(e)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } // get lower value or current approximation @@ -231,7 +240,7 @@ extern "C" { mk_c(c)->save_object(v); v->m_ast_vector.append(es.size(), (ast*const*)es.c_ptr()); RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } // get upper or current approximation @@ -245,7 +254,7 @@ extern "C" { mk_c(c)->save_object(v); v->m_ast_vector.append(es.size(), (ast*const*)es.c_ptr()); RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_optimize_to_string(Z3_context c, Z3_optimize o) { @@ -277,7 +286,7 @@ extern "C" { mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } static void Z3_optimize_from_stream( @@ -296,6 +305,11 @@ extern "C" { parse_wcnf(*to_optimize_ptr(opt), s, h); return; } + if (ext && std::string("lp") == ext) { + unsigned_vector h; + parse_lp(*to_optimize_ptr(opt), s, h); + return; + } scoped_ptr ctx = alloc(cmd_context, false, &m); install_opt_cmds(*ctx.get(), to_optimize_ptr(opt)); std::stringstream errstrm; @@ -367,7 +381,7 @@ extern "C" { v->m_ast_vector.push_back(h); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_optimize_get_objectives(Z3_context c, Z3_optimize o) { @@ -381,7 +395,7 @@ extern "C" { v->m_ast_vector.push_back(to_optimize_ptr(o)->get_objective(i)); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/api_params.cpp b/src/api/api_params.cpp index 3f3cbed1d..d021ed6ad 100644 --- a/src/api/api_params.cpp +++ b/src/api/api_params.cpp @@ -34,7 +34,7 @@ extern "C" { mk_c(c)->save_object(p); Z3_params r = of_params(p); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } /** @@ -172,11 +172,11 @@ extern "C" { RESET_ERROR_CODE(); if (i >= to_param_descrs_ptr(p)->size()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } Z3_symbol result = of_symbol(to_param_descrs_ptr(p)->get_param_name(i)); return result; - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_param_descrs_get_documentation(Z3_context c, Z3_param_descrs p, Z3_symbol s) { @@ -184,12 +184,12 @@ extern "C" { LOG_Z3_param_descrs_get_documentation(c, p, s); RESET_ERROR_CODE(); char const* result = to_param_descrs_ptr(p)->get_descr(to_symbol(s)); - if (result == 0) { + if (result == nullptr) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } return mk_c(c)->mk_external_string(result); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_param_descrs_to_string(Z3_context c, Z3_param_descrs p) { diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index d5a98672b..d791dc2a5 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -20,9 +20,13 @@ Revision History: #include "api/api_log_macros.h" #include "api/api_context.h" #include "api/api_util.h" +#include "api/api_ast_vector.h" #include "cmd_context/cmd_context.h" +#include "smt/smt_solver.h" #include "parsers/smt2/smt2parser.h" #include "solver/solver_na2as.h" +#include "tactic/portfolio/smt_strategic_solver.h" + extern "C" { @@ -38,7 +42,7 @@ extern "C" { // --------------- // Support for SMTLIB2 - Z3_ast parse_smtlib2_stream(bool exec, Z3_context c, std::istream& is, + Z3_ast_vector parse_smtlib2_stream(bool exec, Z3_context c, std::istream& is, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], @@ -48,6 +52,8 @@ extern "C" { Z3_TRY; scoped_ptr ctx = alloc(cmd_context, false, &(mk_c(c)->m())); ctx->set_ignore_check(true); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + mk_c(c)->save_object(v); for (unsigned i = 0; i < num_decls; ++i) { ctx->insert(to_symbol(decl_names[i]), to_func_decl(decls[i])); } @@ -66,7 +72,7 @@ extern "C" { ctx = nullptr; mk_c(c)->m_parser_error_buffer = errstrm.str(); SET_ERROR_CODE(Z3_PARSER_ERROR); - return of_ast(mk_c(c)->m().mk_true()); + return of_ast_vector(v); } } catch (z3_exception& e) { @@ -74,16 +80,18 @@ extern "C" { mk_c(c)->m_parser_error_buffer = errstrm.str(); ctx = nullptr; SET_ERROR_CODE(Z3_PARSER_ERROR); - return of_ast(mk_c(c)->m().mk_true()); + return of_ast_vector(v); } ptr_vector::const_iterator it = ctx->begin_assertions(); ptr_vector::const_iterator end = ctx->end_assertions(); - unsigned size = static_cast(end - it); - return of_ast(mk_c(c)->mk_and(size, it)); - Z3_CATCH_RETURN(0); + for (; it != end; ++it) { + v->m_ast_vector.push_back(*it); + } + return of_ast_vector(v); + Z3_CATCH_RETURN(nullptr); } - Z3_ast Z3_API Z3_parse_smtlib2_string(Z3_context c, Z3_string str, + Z3_ast_vector Z3_API Z3_parse_smtlib2_string(Z3_context c, Z3_string str, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], @@ -94,12 +102,12 @@ extern "C" { LOG_Z3_parse_smtlib2_string(c, str, num_sorts, sort_names, sorts, num_decls, decl_names, decls); std::string s(str); std::istringstream is(s); - Z3_ast r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + Z3_ast_vector r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } - Z3_ast Z3_API Z3_parse_smtlib2_file(Z3_context c, Z3_string file_name, + Z3_ast_vector Z3_API Z3_parse_smtlib2_file(Z3_context c, Z3_string file_name, unsigned num_sorts, Z3_symbol const sort_names[], Z3_sort const sorts[], @@ -110,11 +118,41 @@ extern "C" { LOG_Z3_parse_smtlib2_string(c, file_name, num_sorts, sort_names, sorts, num_decls, decl_names, decls); std::ifstream is(file_name); if (!is) { - SET_ERROR_CODE(Z3_PARSER_ERROR); - return 0; + SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR); + return nullptr; } - Z3_ast r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); + Z3_ast_vector r = parse_smtlib2_stream(false, c, is, num_sorts, sort_names, sorts, num_decls, decl_names, decls); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); + } + + Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context c, Z3_string str) { + std::stringstream ous; + Z3_TRY; + LOG_Z3_eval_smtlib2_string(c, str); + if (!mk_c(c)->cmd()) { + mk_c(c)->cmd() = alloc(cmd_context, false, &(mk_c(c)->m())); + mk_c(c)->cmd()->set_solver_factory(mk_smt_strategic_solver_factory()); + } + scoped_ptr& ctx = mk_c(c)->cmd(); + std::string s(str); + std::istringstream is(s); + ctx->set_regular_stream(ous); + ctx->set_diagnostic_stream(ous); + try { + if (!parse_smt2_commands(*ctx.get(), is)) { + mk_c(c)->m_parser_error_buffer = ous.str(); + SET_ERROR_CODE(Z3_PARSER_ERROR); + RETURN_Z3(mk_c(c)->mk_external_string(ous.str())); + } + } + catch (z3_exception& e) { + if (ous.str().empty()) ous << e.msg(); + mk_c(c)->m_parser_error_buffer = ous.str(); + SET_ERROR_CODE(Z3_PARSER_ERROR); + RETURN_Z3(mk_c(c)->mk_external_string(ous.str())); + } + RETURN_Z3(mk_c(c)->mk_external_string(ous.str())); + Z3_CATCH_RETURN(mk_c(c)->mk_external_string(ous.str())); } }; diff --git a/src/api/api_pb.cpp b/src/api/api_pb.cpp index e6f8f77b4..8116789f9 100644 --- a/src/api/api_pb.cpp +++ b/src/api/api_pb.cpp @@ -34,7 +34,7 @@ extern "C" { mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_atleast(Z3_context c, unsigned num_args, @@ -48,7 +48,7 @@ extern "C" { mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args, @@ -66,7 +66,7 @@ extern "C" { mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_pbge(Z3_context c, unsigned num_args, @@ -84,7 +84,7 @@ extern "C" { mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_pbeq(Z3_context c, unsigned num_args, @@ -102,7 +102,7 @@ extern "C" { mk_c(c)->save_ast_trail(a); check_sorts(c, a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/api_polynomial.cpp b/src/api/api_polynomial.cpp index 9be73289f..35ece4a59 100644 --- a/src/api/api_polynomial.cpp +++ b/src/api/api_polynomial.cpp @@ -50,7 +50,7 @@ extern "C" { if (!converter.to_polynomial(to_expr(p), _p, d) || !converter.to_polynomial(to_expr(q), _q, d)) { SET_ERROR_CODE(Z3_INVALID_ARG); - return 0; + return nullptr; } Z3_ast_vector_ref* result = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); mk_c(c)->save_object(result); @@ -74,7 +74,7 @@ extern "C" { } } RETURN_Z3(of_ast_vector(result)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } }; diff --git a/src/api/api_qe.cpp b/src/api/api_qe.cpp index 34f21d64b..92517b02b 100644 --- a/src/api/api_qe.cpp +++ b/src/api/api_qe.cpp @@ -52,21 +52,21 @@ extern "C" Z3_TRY; LOG_Z3_qe_model_project (c, m, num_bounds, bound, body); RESET_ERROR_CODE(); - + app_ref_vector vars(mk_c(c)->m ()); if (!to_apps(num_bounds, bound, vars)) { SET_ERROR_CODE (Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } expr_ref result (mk_c(c)->m ()); result = to_expr (body); model_ref model (to_model_ref (m)); - spacer::qe_project (mk_c(c)->m (), vars, result, model); + spacer::qe_project (mk_c(c)->m (), vars, result, *model); mk_c(c)->save_ast_trail (result.get ()); return of_expr (result.get ()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_qe_model_project_skolem (Z3_context c, @@ -83,7 +83,7 @@ extern "C" ast_manager& man = mk_c(c)->m (); app_ref_vector vars(man); if (!to_apps(num_bounds, bound, vars)) { - RETURN_Z3(0); + RETURN_Z3(nullptr); } expr_ref result (mk_c(c)->m ()); @@ -103,7 +103,7 @@ extern "C" } return of_expr (result.get ()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_model_extrapolate (Z3_context c, @@ -119,18 +119,15 @@ extern "C" facts.push_back (to_expr (fml)); flatten_and (facts); - spacer::model_evaluator_util mev (mk_c(c)->m()); - mev.set_model (*model); - expr_ref_vector lits (mk_c(c)->m()); - spacer::compute_implicant_literals (mev, facts, lits); + spacer::compute_implicant_literals (*model, facts, lits); expr_ref result (mk_c(c)->m ()); result = mk_and (lits); mk_c(c)->save_ast_trail (result.get ()); return of_expr (result.get ()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_qe_lite (Z3_context c, Z3_ast_vector vars, Z3_ast body) @@ -145,7 +142,7 @@ extern "C" app *a = to_app (vVars.get (i)); if (a->get_kind () != AST_APP) { SET_ERROR_CODE (Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } vApps.push_back (a); } @@ -167,7 +164,7 @@ extern "C" mk_c(c)->save_ast_trail (result.get ()); return of_expr (result); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } } diff --git a/src/api/api_quant.cpp b/src/api/api_quant.cpp index e56505e6d..6e0162024 100644 --- a/src/api/api_quant.cpp +++ b/src/api/api_quant.cpp @@ -37,10 +37,10 @@ extern "C" { c, is_forall, weight, - 0, - 0, + nullptr, + nullptr, num_patterns, patterns, - 0, 0, + 0, nullptr, num_decls, sorts, decl_names, body @@ -104,7 +104,7 @@ extern "C" { } 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_quantifier_ex( @@ -166,17 +166,17 @@ extern "C" { ptr_vector bound_asts; if (num_patterns > 0 && num_no_patterns > 0) { SET_ERROR_CODE(Z3_INVALID_USAGE); - RETURN_Z3(0); + RETURN_Z3(nullptr); } if (num_bound == 0) { SET_ERROR_CODE(Z3_INVALID_USAGE); - RETURN_Z3(0); + RETURN_Z3(nullptr); } for (unsigned i = 0; i < num_bound; ++i) { app* a = to_app(bound[i]); if (a->get_kind() != AST_APP) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } symbol s(to_app(a)->get_decl()->get_name()); names.push_back(of_symbol(s)); @@ -184,7 +184,7 @@ extern "C" { bound_asts.push_back(a); if (a->get_family_id() != null_family_id || a->get_num_args() != 0) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } } // Abstract patterns @@ -205,7 +205,7 @@ extern "C" { expr_ref result(mk_c(c)->m()); if (!is_app(to_expr(no_patterns[i]))) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } app* pat = to_app(to_expr(no_patterns[i])); expr_abstract(mk_c(c)->m(), 0, num_bound, bound_asts.c_ptr(), pat, result); @@ -224,7 +224,7 @@ extern "C" { names.size(), types.c_ptr(), names.c_ptr(), of_ast(abs_body.get())); RETURN_Z3(result); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_quantifier_const(Z3_context c, @@ -235,10 +235,10 @@ extern "C" { unsigned num_patterns, Z3_pattern const patterns[], Z3_ast body) { - return Z3_mk_quantifier_const_ex(c, is_forall, weight, 0, 0, + return Z3_mk_quantifier_const_ex(c, is_forall, weight, nullptr, nullptr, num_bound, bound, num_patterns, patterns, - 0, 0, + 0, nullptr, body); } @@ -269,13 +269,13 @@ extern "C" { for (unsigned i = 0; i < num_patterns; ++i) { if (!is_app(to_expr(terms[i]))) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } } app* a = mk_c(c)->m().mk_pattern(num_patterns, reinterpret_cast(to_exprs(terms))); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_pattern(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_bound(Z3_context c, unsigned index, Z3_sort ty) { @@ -285,7 +285,7 @@ extern "C" { ast* a = mk_c(c)->m().mk_var(index, to_sort(ty)); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_is_quantifier_forall(Z3_context c, Z3_ast a) { @@ -344,9 +344,9 @@ extern "C" { } else { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } @@ -376,9 +376,9 @@ extern "C" { } else { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_symbol Z3_API Z3_get_quantifier_bound_name(Z3_context c, Z3_ast a, unsigned i) { @@ -391,9 +391,9 @@ extern "C" { } else { SET_ERROR_CODE(Z3_SORT_ERROR); - return 0; + return nullptr; } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_get_quantifier_bound_sort(Z3_context c, Z3_ast a, unsigned i) { @@ -407,9 +407,9 @@ extern "C" { } else { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_get_quantifier_body(Z3_context c, Z3_ast a) { @@ -423,9 +423,9 @@ extern "C" { } else { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_quantifier_num_bound(Z3_context c, Z3_ast a) { @@ -470,9 +470,9 @@ extern "C" { } else { SET_ERROR_CODE(Z3_SORT_ERROR); - RETURN_Z3(0); + RETURN_Z3(nullptr); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_pattern_to_ast(Z3_context c, Z3_pattern p) { diff --git a/src/api/api_rcf.cpp b/src/api/api_rcf.cpp index 84c250114..3d65cb1cd 100644 --- a/src/api/api_rcf.cpp +++ b/src/api/api_rcf.cpp @@ -62,7 +62,7 @@ extern "C" { rcnumeral r; rcfm(c).set(r, q); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mk_small_int(Z3_context c, int val) { @@ -73,7 +73,7 @@ extern "C" { rcnumeral r; rcfm(c).set(r, val); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mk_pi(Z3_context c) { @@ -84,7 +84,7 @@ extern "C" { rcnumeral r; rcfm(c).mk_pi(r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mk_e(Z3_context c) { @@ -95,7 +95,7 @@ extern "C" { rcnumeral r; rcfm(c).mk_e(r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mk_infinitesimal(Z3_context c) { @@ -106,7 +106,7 @@ extern "C" { rcnumeral r; rcfm(c).mk_infinitesimal(r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_rcf_mk_roots(Z3_context c, unsigned n, Z3_rcf_num const a[], Z3_rcf_num roots[]) { @@ -145,7 +145,7 @@ extern "C" { rcnumeral r; rcfm(c).add(to_rcnumeral(a), to_rcnumeral(b), r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_sub(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { @@ -156,7 +156,7 @@ extern "C" { rcnumeral r; rcfm(c).sub(to_rcnumeral(a), to_rcnumeral(b), r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_mul(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { @@ -167,7 +167,7 @@ extern "C" { rcnumeral r; rcfm(c).mul(to_rcnumeral(a), to_rcnumeral(b), r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_div(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { @@ -178,7 +178,7 @@ extern "C" { rcnumeral r; rcfm(c).div(to_rcnumeral(a), to_rcnumeral(b), r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_neg(Z3_context c, Z3_rcf_num a) { @@ -189,7 +189,7 @@ extern "C" { rcnumeral r; rcfm(c).neg(to_rcnumeral(a), r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_inv(Z3_context c, Z3_rcf_num a) { @@ -200,7 +200,7 @@ extern "C" { rcnumeral r; rcfm(c).inv(to_rcnumeral(a), r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_rcf_num Z3_API Z3_rcf_power(Z3_context c, Z3_rcf_num a, unsigned k) { @@ -211,7 +211,7 @@ extern "C" { rcnumeral r; rcfm(c).power(to_rcnumeral(a), k, r); RETURN_Z3(from_rcnumeral(r)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_rcf_lt(Z3_context c, Z3_rcf_num a, Z3_rcf_num b) { diff --git a/src/api/api_seq.cpp b/src/api/api_seq.cpp index 44003a5fb..84cfdca32 100644 --- a/src/api/api_seq.cpp +++ b/src/api/api_seq.cpp @@ -31,7 +31,7 @@ extern "C" { sort * ty = mk_c(c)->sutil().str.mk_seq(to_sort(domain)); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_re_sort(Z3_context c, Z3_sort domain) { @@ -41,7 +41,7 @@ extern "C" { sort * ty = mk_c(c)->sutil().re.mk_re(to_sort(domain)); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_string(Z3_context c, Z3_string str) { @@ -52,7 +52,7 @@ extern "C" { app* a = mk_c(c)->sutil().str.mk_string(s); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_sort Z3_API Z3_mk_string_sort(Z3_context c) { @@ -62,7 +62,7 @@ extern "C" { sort* ty = mk_c(c)->sutil().str.mk_string_sort(); mk_c(c)->save_ast_trail(ty); RETURN_Z3(of_sort(ty)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_bool Z3_API Z3_is_seq_sort(Z3_context c, Z3_sort s) { @@ -152,7 +152,7 @@ extern "C" { app* a = hi == 0 ? mk_c(c)->sutil().re.mk_loop(to_expr(r), lo) : mk_c(c)->sutil().re.mk_loop(to_expr(r), lo, hi); mk_c(c)->save_ast_trail(a); RETURN_Z3(of_ast(a)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } MK_UNARY(Z3_mk_re_plus, mk_c(c)->get_seq_fid(), OP_RE_PLUS, SKIP); @@ -165,7 +165,7 @@ extern "C" { MK_BINARY(Z3_mk_re_range, mk_c(c)->get_seq_fid(), OP_RE_RANGE, SKIP); MK_SORTED(Z3_mk_re_empty, mk_c(c)->sutil().re.mk_empty); - MK_SORTED(Z3_mk_re_full, mk_c(c)->sutil().re.mk_full); + MK_SORTED(Z3_mk_re_full, mk_c(c)->sutil().re.mk_full_seq); diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 0a0039f4c..951b9e51e 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -17,6 +17,11 @@ Revision History: --*/ #include +#include "util/scoped_ctrl_c.h" +#include "util/cancel_eh.h" +#include "util/file_path.h" +#include "util/scoped_timer.h" +#include "ast/ast_pp.h" #include "api/z3.h" #include "api/api_log_macros.h" #include "api/api_context.h" @@ -28,8 +33,8 @@ Revision History: #include "solver/tactic2solver.h" #include "util/scoped_ctrl_c.h" #include "util/cancel_eh.h" -#include "util/file_path.h" #include "util/scoped_timer.h" +#include "util/file_path.h" #include "tactic/portfolio/smt_strategic_solver.h" #include "smt/smt_solver.h" #include "smt/smt_implied_equalities.h" @@ -40,6 +45,7 @@ Revision History: #include "sat/sat_solver.h" #include "sat/tactic/goal2sat.h" + extern "C" { static void init_solver_core(Z3_context c, Z3_solver _s) { @@ -57,7 +63,7 @@ extern "C" { } static void init_solver(Z3_context c, Z3_solver s) { - if (to_solver(s)->m_solver.get() == 0) + if (to_solver(s)->m_solver.get() == nullptr) init_solver_core(c, s); } @@ -69,7 +75,7 @@ extern "C" { mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_solver Z3_API Z3_mk_solver(Z3_context c) { @@ -80,7 +86,7 @@ extern "C" { mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_solver Z3_API Z3_mk_solver_for_logic(Z3_context c, Z3_symbol logic) { @@ -91,7 +97,7 @@ extern "C" { std::ostringstream strm; strm << "logic '" << to_symbol(logic) << "' is not recognized"; throw default_exception(strm.str()); - RETURN_Z3(0); + RETURN_Z3(nullptr); } else { Z3_solver_ref * s = alloc(Z3_solver_ref, *mk_c(c), mk_smt_strategic_solver_factory(to_symbol(logic))); @@ -99,7 +105,7 @@ extern "C" { Z3_solver r = of_solver(s); RETURN_Z3(r); } - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_solver Z3_API Z3_mk_solver_from_tactic(Z3_context c, Z3_tactic t) { @@ -110,7 +116,7 @@ extern "C" { mk_c(c)->save_object(s); Z3_solver r = of_solver(s); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_solver Z3_API Z3_solver_translate(Z3_context c, Z3_solver s, Z3_context target) { @@ -118,13 +124,22 @@ extern "C" { LOG_Z3_solver_translate(c, s, target); RESET_ERROR_CODE(); params_ref const& p = to_solver(s)->m_params; - Z3_solver_ref * sr = alloc(Z3_solver_ref, *mk_c(target), 0); + Z3_solver_ref * sr = alloc(Z3_solver_ref, *mk_c(target), nullptr); init_solver(c, s); sr->m_solver = to_solver(s)->m_solver->translate(mk_c(target)->m(), p); mk_c(target)->save_object(sr); Z3_solver r = of_solver(sr); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); + } + + + void Z3_API Z3_solver_import_model_converter(Z3_context c, Z3_solver src, Z3_solver dst) { + Z3_TRY; + LOG_Z3_solver_import_model_converter(c, src, dst); + model_converter_ref mc = to_solver_ref(src)->get_model_converter(); + to_solver_ref(dst)->set_model_converter(mc.get()); + Z3_CATCH; } void solver_from_stream(Z3_context c, Z3_solver s, std::istream& is) { @@ -137,7 +152,7 @@ extern "C" { return; } - bool initialized = to_solver(s)->m_solver.get() != 0; + bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); ptr_vector::const_iterator it = ctx->begin_assertions(); @@ -145,7 +160,7 @@ extern "C" { for (; it != end; ++it) { to_solver_ref(s)->assert_expr(*it); } - // to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); + to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); } void Z3_API Z3_solver_from_string(Z3_context c, Z3_solver s, Z3_string c_str) { @@ -167,10 +182,10 @@ extern "C" { } else if (ext && std::string("dimacs") == ext) { ast_manager& m = to_solver_ref(s)->get_manager(); - sat::solver solver(to_solver_ref(s)->get_params(), m.limit(), nullptr); + sat::solver solver(to_solver_ref(s)->get_params(), m.limit()); parse_dimacs(is, solver); sat2goal s2g; - model_converter_ref mc; + ref mc; atom2bool_var a2b(m); goal g(m); s2g(solver, a2b, to_solver_ref(s)->get_params(), g, mc); @@ -190,13 +205,13 @@ extern "C" { RESET_ERROR_CODE(); std::ostringstream buffer; param_descrs descrs; - bool initialized = to_solver(s)->m_solver.get() != 0; + bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); to_solver_ref(s)->collect_param_descrs(descrs); context_params::collect_solver_param_descrs(descrs); if (!initialized) - to_solver(s)->m_solver = 0; + to_solver(s)->m_solver = nullptr; descrs.display(buffer); return mk_c(c)->mk_external_string(buffer.str()); Z3_CATCH_RETURN(""); @@ -208,16 +223,16 @@ extern "C" { RESET_ERROR_CODE(); Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref, *mk_c(c)); mk_c(c)->save_object(d); - bool initialized = to_solver(s)->m_solver.get() != 0; + bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); to_solver_ref(s)->collect_param_descrs(d->m_descrs); context_params::collect_solver_param_descrs(d->m_descrs); if (!initialized) - to_solver(s)->m_solver = 0; + to_solver(s)->m_solver = nullptr; Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_solver_set_params(Z3_context c, Z3_solver s, Z3_params p) { @@ -288,7 +303,7 @@ extern "C" { Z3_TRY; LOG_Z3_solver_reset(c, s); RESET_ERROR_CODE(); - to_solver(s)->m_solver = 0; + to_solver(s)->m_solver = nullptr; Z3_CATCH; } @@ -321,6 +336,7 @@ extern "C" { to_solver_ref(s)->assert_expr(to_expr(a), to_expr(p)); Z3_CATCH; } + Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s) { Z3_TRY; @@ -334,6 +350,22 @@ extern "C" { v->m_ast_vector.push_back(to_solver_ref(s)->get_assertion(i)); } RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(nullptr); + } + + + Z3_ast_vector Z3_API Z3_solver_get_units(Z3_context c, Z3_solver s) { + Z3_TRY; + LOG_Z3_solver_get_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_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); } @@ -360,7 +392,9 @@ extern "C" { } catch (z3_exception & ex) { to_solver_ref(s)->set_reason_unknown(eh); - mk_c(c)->handle_exception(ex); + if (!mk_c(c)->m().canceled()) { + mk_c(c)->handle_exception(ex); + } return Z3_L_UNDEF; } } @@ -375,7 +409,7 @@ extern "C" { LOG_Z3_solver_check(c, s); RESET_ERROR_CODE(); init_solver(c, s); - return _solver_check(c, s, 0, 0); + return _solver_check(c, s, 0, nullptr); Z3_CATCH_RETURN(Z3_L_UNDEF); } @@ -397,13 +431,13 @@ extern "C" { to_solver_ref(s)->get_model(_m); if (!_m) { SET_ERROR_CODE(Z3_INVALID_USAGE); - RETURN_Z3(0); + RETURN_Z3(nullptr); } Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); m_ref->m_model = _m; mk_c(c)->save_object(m_ref); RETURN_Z3(of_model(m_ref)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_solver_get_proof(Z3_context c, Z3_solver s) { @@ -414,11 +448,11 @@ extern "C" { proof * p = to_solver_ref(s)->get_proof(); if (!p) { SET_ERROR_CODE(Z3_INVALID_USAGE); - RETURN_Z3(0); + RETURN_Z3(nullptr); } mk_c(c)->save_ast_trail(p); RETURN_Z3(of_ast(p)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_solver_get_unsat_core(Z3_context c, Z3_solver s) { @@ -426,15 +460,15 @@ extern "C" { LOG_Z3_solver_get_unsat_core(c, s); RESET_ERROR_CODE(); init_solver(c, s); - ptr_vector core; + expr_ref_vector core(mk_c(c)->m()); to_solver_ref(s)->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 (unsigned i = 0; i < core.size(); i++) { - v->m_ast_vector.push_back(core[i]); + for (expr* e : core) { + v->m_ast_vector.push_back(e); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_solver_get_reason_unknown(Z3_context c, Z3_solver s) { @@ -458,7 +492,7 @@ extern "C" { mk_c(c)->save_object(st); Z3_stats r = of_stats(st); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_solver_to_string(Z3_context c, Z3_solver s) { @@ -502,24 +536,22 @@ extern "C" { init_solver(c, s); expr_ref_vector _assumptions(m), _consequences(m), _variables(m); ast_ref_vector const& __assumptions = to_ast_vector_ref(assumptions); - unsigned sz = __assumptions.size(); - for (unsigned i = 0; i < sz; ++i) { - if (!is_expr(__assumptions[i])) { + for (ast* e : __assumptions) { + if (!is_expr(e)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE); return Z3_L_UNDEF; } - _assumptions.push_back(to_expr(__assumptions[i])); + _assumptions.push_back(to_expr(e)); } ast_ref_vector const& __variables = to_ast_vector_ref(variables); - sz = __variables.size(); - for (unsigned i = 0; i < sz; ++i) { - if (!is_expr(__variables[i])) { + for (ast* a : __variables) { + if (!is_expr(a)) { _assumptions.finalize(); _consequences.finalize(); _variables.finalize(); SET_ERROR_CODE(Z3_INVALID_USAGE); return Z3_L_UNDEF; } - _variables.push_back(to_expr(__variables[i])); + _variables.push_back(to_expr(a)); } lbool result = l_undef; unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); @@ -544,11 +576,55 @@ extern "C" { if (result == l_undef) { to_solver_ref(s)->set_reason_unknown(eh); } - for (unsigned i = 0; i < _consequences.size(); ++i) { - to_ast_vector_ref(consequences).push_back(_consequences[i].get()); + for (expr* e : _consequences) { + to_ast_vector_ref(consequences).push_back(e); } return static_cast(result); Z3_CATCH_RETURN(Z3_L_UNDEF); } + Z3_ast_vector Z3_API Z3_solver_cube(Z3_context c, Z3_solver s, Z3_ast_vector vs, unsigned cutoff) { + Z3_TRY; + LOG_Z3_solver_cube(c, s, vs, cutoff); + ast_manager& m = mk_c(c)->m(); + expr_ref_vector result(m), vars(m); + for (ast* a : to_ast_vector_ref(vs)) { + if (!is_expr(a)) { + SET_ERROR_CODE(Z3_INVALID_USAGE); + } + else { + vars.push_back(to_expr(a)); + } + } + unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); + unsigned rlimit = to_solver(s)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); + bool use_ctrl_c = to_solver(s)->m_params.get_bool("ctrl_c", false); + cancel_eh eh(mk_c(c)->m().limit()); + api::context::set_interruptable si(*(mk_c(c)), eh); + { + scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); + scoped_timer timer(timeout, &eh); + scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); + try { + result.append(to_solver_ref(s)->cube(vars, cutoff)); + } + catch (z3_exception & ex) { + mk_c(c)->handle_exception(ex); + return 0; + } + } + 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 : result) { + v->m_ast_vector.push_back(e); + } + to_ast_vector_ref(vs).reset(); + for (expr* a : vars) { + to_ast_vector_ref(vs).push_back(a); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(0); + } + + }; diff --git a/src/api/api_solver.h b/src/api/api_solver.h index 4f344a00d..90d1c2c65 100644 --- a/src/api/api_solver.h +++ b/src/api/api_solver.h @@ -26,8 +26,8 @@ struct Z3_solver_ref : public api::object { ref m_solver; params_ref m_params; symbol m_logic; - Z3_solver_ref(api::context& c, solver_factory * f): api::object(c), m_solver_factory(f), m_solver(0), m_logic(symbol::null) {} - virtual ~Z3_solver_ref() {} + Z3_solver_ref(api::context& c, solver_factory * f): api::object(c), m_solver_factory(f), m_solver(nullptr), m_logic(symbol::null) {} + ~Z3_solver_ref() override {} }; inline Z3_solver_ref * to_solver(Z3_solver s) { return reinterpret_cast(s); } diff --git a/src/api/api_stats.cpp b/src/api/api_stats.cpp index a92b908dc..2e1dac4de 100644 --- a/src/api/api_stats.cpp +++ b/src/api/api_stats.cpp @@ -130,7 +130,7 @@ extern "C" { Z3_CATCH_RETURN(0.0); } - __uint64 Z3_API Z3_get_estimated_alloc_size(void) { + uint64_t Z3_API Z3_get_estimated_alloc_size(void) { return memory::get_allocation_size(); } diff --git a/src/api/api_stats.h b/src/api/api_stats.h index 5ce616084..e5ad5d315 100644 --- a/src/api/api_stats.h +++ b/src/api/api_stats.h @@ -24,7 +24,7 @@ Revision History: struct Z3_stats_ref : public api::object { statistics m_stats; Z3_stats_ref(api::context& c): api::object(c) {} - virtual ~Z3_stats_ref() {} + ~Z3_stats_ref() override {} }; inline Z3_stats_ref * to_stats(Z3_stats s) { return reinterpret_cast(s); } diff --git a/src/api/api_tactic.cpp b/src/api/api_tactic.cpp index ccb1ce597..345284fd6 100644 --- a/src/api/api_tactic.cpp +++ b/src/api/api_tactic.cpp @@ -25,25 +25,25 @@ Revision History: #include "util/cancel_eh.h" #include "util/scoped_timer.h" -Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c), m_core(m) { +Z3_apply_result_ref::Z3_apply_result_ref(api::context& c, ast_manager & m): api::object(c) { } extern "C" { -#define RETURN_TACTIC(_t_) { \ +#define RETURN_TACTIC(_t_) { \ Z3_tactic_ref * _ref_ = alloc(Z3_tactic_ref, *mk_c(c)); \ - _ref_->m_tactic = _t_; \ - mk_c(c)->save_object(_ref_); \ - Z3_tactic _result_ = of_tactic(_ref_); \ - RETURN_Z3(_result_); \ + _ref_->m_tactic = _t_; \ + mk_c(c)->save_object(_ref_); \ + Z3_tactic _result_ = of_tactic(_ref_); \ + RETURN_Z3(_result_); \ } -#define RETURN_PROBE(_t_) { \ +#define RETURN_PROBE(_t_) { \ Z3_probe_ref * _ref_ = alloc(Z3_probe_ref, *mk_c(c)); \ - _ref_->m_probe = _t_; \ - mk_c(c)->save_object(_ref_); \ - Z3_probe _result_ = of_probe(_ref_); \ - RETURN_Z3(_result_); \ + _ref_->m_probe = _t_; \ + mk_c(c)->save_object(_ref_); \ + Z3_probe _result_ = of_probe(_ref_); \ + RETURN_Z3(_result_); \ } Z3_tactic Z3_API Z3_mk_tactic(Z3_context c, Z3_string name) { @@ -51,13 +51,13 @@ extern "C" { LOG_Z3_mk_tactic(c, name); RESET_ERROR_CODE(); tactic_cmd * t = mk_c(c)->find_tactic_cmd(symbol(name)); - if (t == 0) { + if (t == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } tactic * new_t = t->mk(mk_c(c)->m()); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_tactic_inc_ref(Z3_context c, Z3_tactic t) { @@ -81,13 +81,13 @@ extern "C" { LOG_Z3_mk_probe(c, name); RESET_ERROR_CODE(); probe_info * p = mk_c(c)->find_probe(symbol(name)); - if (p == 0) { + if (p == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG); - RETURN_Z3(0); + RETURN_Z3(nullptr); } probe * new_p = p->get(); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_probe_inc_ref(Z3_context c, Z3_probe p) { @@ -112,7 +112,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = and_then(to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_or_else(Z3_context c, Z3_tactic t1, Z3_tactic t2) { @@ -121,7 +121,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = or_else(to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_par_or(Z3_context c, unsigned num, Z3_tactic const ts[]) { @@ -134,7 +134,7 @@ extern "C" { } tactic * new_t = par(num, _ts.c_ptr()); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_par_and_then(Z3_context c, Z3_tactic t1, Z3_tactic t2) { @@ -143,7 +143,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = par_and_then(to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_try_for(Z3_context c, Z3_tactic t, unsigned ms) { @@ -152,7 +152,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = try_for(to_tactic_ref(t), ms); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_when(Z3_context c, Z3_probe p, Z3_tactic t) { @@ -161,7 +161,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = when(to_probe_ref(p), to_tactic_ref(t)); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_cond(Z3_context c, Z3_probe p, Z3_tactic t1, Z3_tactic t2) { @@ -170,7 +170,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = cond(to_probe_ref(p), to_tactic_ref(t1), to_tactic_ref(t2)); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_repeat(Z3_context c, Z3_tactic t, unsigned max) { @@ -179,7 +179,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = repeat(to_tactic_ref(t), max); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_skip(Z3_context c) { @@ -188,7 +188,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = mk_skip_tactic(); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_fail(Z3_context c) { @@ -197,7 +197,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = mk_fail_tactic(); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_fail_if(Z3_context c, Z3_probe p) { @@ -206,7 +206,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = fail_if(to_probe_ref(p)); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_fail_if_not_decided(Z3_context c) { @@ -215,7 +215,7 @@ extern "C" { RESET_ERROR_CODE(); tactic * new_t = mk_fail_if_undecided_tactic(); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_tactic Z3_API Z3_tactic_using_params(Z3_context c, Z3_tactic t, Z3_params p) { @@ -227,7 +227,7 @@ extern "C" { to_param_ref(p).validate(r); tactic * new_t = using_params(to_tactic_ref(t), to_param_ref(p)); RETURN_TACTIC(new_t); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_const(Z3_context c, double val) { @@ -236,7 +236,7 @@ extern "C" { RESET_ERROR_CODE(); probe * new_p = mk_const_probe(val); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_lt(Z3_context c, Z3_probe p1, Z3_probe p2) { @@ -245,7 +245,7 @@ extern "C" { RESET_ERROR_CODE(); probe * new_p = mk_lt(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_gt(Z3_context c, Z3_probe p1, Z3_probe p2) { @@ -254,7 +254,7 @@ extern "C" { RESET_ERROR_CODE(); probe * new_p = mk_gt(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_le(Z3_context c, Z3_probe p1, Z3_probe p2) { @@ -263,7 +263,7 @@ extern "C" { RESET_ERROR_CODE(); probe * new_p = mk_le(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_ge(Z3_context c, Z3_probe p1, Z3_probe p2) { @@ -272,7 +272,7 @@ extern "C" { RESET_ERROR_CODE(); probe * new_p = mk_ge(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_eq(Z3_context c, Z3_probe p1, Z3_probe p2) { @@ -281,7 +281,7 @@ extern "C" { RESET_ERROR_CODE(); probe * new_p = mk_eq(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_and(Z3_context c, Z3_probe p1, Z3_probe p2) { @@ -290,7 +290,7 @@ extern "C" { RESET_ERROR_CODE(); probe * new_p = mk_and(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_or(Z3_context c, Z3_probe p1, Z3_probe p2) { @@ -299,7 +299,7 @@ extern "C" { RESET_ERROR_CODE(); probe * new_p = mk_or(to_probe_ref(p1), to_probe_ref(p2)); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_probe Z3_API Z3_probe_not(Z3_context c, Z3_probe p) { @@ -308,7 +308,7 @@ extern "C" { RESET_ERROR_CODE(); probe * new_p = mk_not(to_probe_ref(p)); RETURN_PROBE(new_p); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } unsigned Z3_API Z3_get_num_tactics(Z3_context c) { @@ -372,7 +372,7 @@ extern "C" { to_tactic_ref(t)->collect_param_descrs(d->m_descrs); Z3_param_descrs r = of_param_descrs(d); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_string Z3_API Z3_tactic_get_descr(Z3_context c, Z3_string name) { @@ -380,7 +380,7 @@ extern "C" { LOG_Z3_tactic_get_descr(c, name); RESET_ERROR_CODE(); tactic_cmd * t = mk_c(c)->find_tactic_cmd(symbol(name)); - if (t == 0) { + if (t == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } @@ -393,7 +393,7 @@ extern "C" { LOG_Z3_probe_get_descr(c, name); RESET_ERROR_CODE(); probe_info * p = mk_c(c)->find_probe(symbol(name)); - if (p == 0) { + if (p == nullptr) { SET_ERROR_CODE(Z3_INVALID_ARG); return ""; } @@ -418,12 +418,14 @@ extern "C" { scoped_ctrl_c ctrlc(eh, false, use_ctrl_c); scoped_timer timer(timeout, &eh); try { - exec(*to_tactic_ref(t), new_goal, ref->m_subgoals, ref->m_mc, ref->m_pc, ref->m_core); + exec(*to_tactic_ref(t), new_goal, ref->m_subgoals); + ref->m_pc = new_goal->pc(); + ref->m_mc = new_goal->mc(); return of_apply_result(ref); } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); - return 0; + return nullptr; } } } @@ -443,7 +445,7 @@ extern "C" { params_ref p; Z3_apply_result r = _tactic_apply(c, t, g, p); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_apply_result Z3_API Z3_tactic_apply_ex(Z3_context c, Z3_tactic t, Z3_goal g, Z3_params p) { @@ -455,7 +457,7 @@ extern "C" { to_param_ref(p).validate(pd); Z3_apply_result r = _tactic_apply(c, t, g, to_param_ref(p)); RETURN_Z3(r); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } void Z3_API Z3_apply_result_inc_ref(Z3_context c, Z3_apply_result r) { @@ -503,32 +505,15 @@ extern "C" { RESET_ERROR_CODE(); if (i > to_apply_result(r)->m_subgoals.size()) { SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); + RETURN_Z3(nullptr); } Z3_goal_ref * g = alloc(Z3_goal_ref, *mk_c(c)); g->m_goal = to_apply_result(r)->m_subgoals[i]; mk_c(c)->save_object(g); Z3_goal result = of_goal(g); RETURN_Z3(result); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } - Z3_model Z3_API Z3_apply_result_convert_model(Z3_context c, Z3_apply_result r, unsigned i, Z3_model m) { - Z3_TRY; - LOG_Z3_apply_result_convert_model(c, r, i, m); - RESET_ERROR_CODE(); - if (i > to_apply_result(r)->m_subgoals.size()) { - SET_ERROR_CODE(Z3_IOB); - RETURN_Z3(0); - } - model_ref new_m = to_model_ref(m)->copy(); - if (to_apply_result(r)->m_mc) - to_apply_result(r)->m_mc->operator()(new_m, i); - Z3_model_ref * m_ref = alloc(Z3_model_ref, *mk_c(c)); - m_ref->m_model = new_m; - mk_c(c)->save_object(m_ref); - RETURN_Z3(of_model(m_ref)); - Z3_CATCH_RETURN(0); - } }; diff --git a/src/api/api_tactic.h b/src/api/api_tactic.h index fd2f05185..421c03fcd 100644 --- a/src/api/api_tactic.h +++ b/src/api/api_tactic.h @@ -29,30 +29,29 @@ namespace api { struct Z3_tactic_ref : public api::object { tactic_ref m_tactic; Z3_tactic_ref(api::context& c): api::object(c) {} - virtual ~Z3_tactic_ref() {} + ~Z3_tactic_ref() override {} }; struct Z3_probe_ref : public api::object { probe_ref m_probe; Z3_probe_ref(api::context& c):api::object(c) {} - virtual ~Z3_probe_ref() {} + ~Z3_probe_ref() override {} }; inline Z3_tactic_ref * to_tactic(Z3_tactic g) { return reinterpret_cast(g); } inline Z3_tactic of_tactic(Z3_tactic_ref * g) { return reinterpret_cast(g); } -inline tactic * to_tactic_ref(Z3_tactic g) { return g == 0 ? 0 : to_tactic(g)->m_tactic.get(); } +inline tactic * to_tactic_ref(Z3_tactic g) { return g == nullptr ? nullptr : to_tactic(g)->m_tactic.get(); } inline Z3_probe_ref * to_probe(Z3_probe g) { return reinterpret_cast(g); } inline Z3_probe of_probe(Z3_probe_ref * g) { return reinterpret_cast(g); } -inline probe * to_probe_ref(Z3_probe g) { return g == 0 ? 0 : to_probe(g)->m_probe.get(); } +inline probe * to_probe_ref(Z3_probe g) { return g == nullptr ? nullptr : to_probe(g)->m_probe.get(); } struct Z3_apply_result_ref : public api::object { goal_ref_buffer m_subgoals; model_converter_ref m_mc; proof_converter_ref m_pc; - expr_dependency_ref m_core; Z3_apply_result_ref(api::context& c, ast_manager & m); - virtual ~Z3_apply_result_ref() {} + ~Z3_apply_result_ref() override {} }; inline Z3_apply_result_ref * to_apply_result(Z3_apply_result g) { return reinterpret_cast(g); } diff --git a/src/api/api_util.h b/src/api/api_util.h index bc6781c2c..848d935c5 100644 --- a/src/api/api_util.h +++ b/src/api/api_util.h @@ -88,22 +88,22 @@ inline lbool to_lbool(Z3_lbool b) { return static_cast(b); } struct Z3_params_ref : public api::object { params_ref m_params; Z3_params_ref(api::context& c): api::object(c) {} - virtual ~Z3_params_ref() {} + ~Z3_params_ref() override {} }; inline Z3_params_ref * to_params(Z3_params p) { return reinterpret_cast(p); } inline Z3_params of_params(Z3_params_ref * p) { return reinterpret_cast(p); } -inline params_ref to_param_ref(Z3_params p) { return p == 0 ? params_ref() : to_params(p)->m_params; } +inline params_ref to_param_ref(Z3_params p) { return p == nullptr ? params_ref() : to_params(p)->m_params; } struct Z3_param_descrs_ref : public api::object { param_descrs m_descrs; Z3_param_descrs_ref(api::context& c): api::object(c) {} - virtual ~Z3_param_descrs_ref() {} + ~Z3_param_descrs_ref() override {} }; inline Z3_param_descrs_ref * to_param_descrs(Z3_param_descrs p) { return reinterpret_cast(p); } inline Z3_param_descrs of_param_descrs(Z3_param_descrs_ref * p) { return reinterpret_cast(p); } -inline param_descrs * to_param_descrs_ptr(Z3_param_descrs p) { return p == 0 ? 0 : &(to_param_descrs(p)->m_descrs); } +inline param_descrs * to_param_descrs_ptr(Z3_param_descrs p) { return p == nullptr ? nullptr : &(to_param_descrs(p)->m_descrs); } #define SKIP ((void) 0) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 8ff2be239..665ffb438 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -147,20 +147,12 @@ namespace z3 { Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } - void init_interp(config & c) { - m_ctx = Z3_mk_interpolation_context(c); - m_enable_exceptions = true; - Z3_set_error_handler(m_ctx, 0); - Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); - } context(context const & s); context & operator=(context const & s); public: - struct interpolation {}; context() { config c; init(c); } context(config & c) { init(c); } - context(config & c, interpolation) { init_interp(c); } ~context() { Z3_del_context(m_ctx); } operator Z3_context() const { return m_ctx; } @@ -187,8 +179,8 @@ namespace z3 { \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 very careful about using check_error() after calls that may result in an errornous - state. + have to be very careful about using check_error() after calls that may result in an + erroneous state. */ void set_enable_exceptions(bool f) { m_enable_exceptions = f; } @@ -213,7 +205,7 @@ namespace z3 { /** \brief Interrupt the current procedure being executed by any object managed by this context. - This is a soft interruption: there is no guarantee the object will actualy stop. + This is a soft interruption: there is no guarantee the object will actually stop. */ void interrupt() { Z3_interrupt(m_ctx); } @@ -267,6 +259,15 @@ namespace z3 { and in \c ts the predicates for testing if terms of the enumeration sort correspond to an enumeration. */ sort enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts); + + /** + \brief Return a tuple constructor. + \c name is the name of the returned constructor, + \c n are the number of arguments, \c names and \c sorts are their projected sorts. + \c projs is an output paramter. It contains the set of projection functions. + */ + func_decl tuple_sort(char const * name, unsigned n, char const * const * names, sort const* sorts, func_decl_vector & projs); + /** \brief create an uninterpreted sort with the name given by the string or symbol. */ @@ -294,21 +295,21 @@ namespace z3 { expr int_val(int n); expr int_val(unsigned n); - expr int_val(__int64 n); - expr int_val(__uint64 n); + expr int_val(int64_t n); + expr int_val(uint64_t n); expr int_val(char const * n); expr real_val(int n, int d); expr real_val(int n); expr real_val(unsigned n); - expr real_val(__int64 n); - expr real_val(__uint64 n); + expr real_val(int64_t n); + expr real_val(uint64_t n); expr real_val(char const * n); expr bv_val(int n, unsigned sz); expr bv_val(unsigned n, unsigned sz); - expr bv_val(__int64 n, unsigned sz); - expr bv_val(__uint64 n, unsigned sz); + expr bv_val(int64_t n, unsigned sz); + expr bv_val(uint64_t n, unsigned sz); expr bv_val(char const * n, unsigned sz); expr bv_val(unsigned n, bool const* bits); @@ -320,17 +321,12 @@ namespace z3 { /** \brief parsing */ - expr parse_string(char const* s); - expr parse_file(char const* file); + expr_vector parse_string(char const* s); + expr_vector parse_file(char const* file); - expr parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls); - expr parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls); + expr_vector parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls); + expr_vector parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls); - /** - \brief Interpolation support - */ - check_result compute_interpolant(expr const& pat, params const& p, expr_vector& interp, model& m); - expr_vector get_interpolant(expr const& proof, expr const& pat, params const& p); }; @@ -431,6 +427,7 @@ namespace z3 { void set(char const * k, unsigned n) { Z3_params_set_uint(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, double n) { Z3_params_set_double(ctx(), m_params, ctx().str_symbol(k), n); } void set(char const * k, symbol const & s) { Z3_params_set_symbol(ctx(), m_params, ctx().str_symbol(k), s); } + void set(char const * k, char const* s) { Z3_params_set_symbol(ctx(), m_params, ctx().str_symbol(k), ctx().str_symbol(s)); } friend std::ostream & operator<<(std::ostream & out, params const & p); }; @@ -660,8 +657,8 @@ namespace z3 { small integers, 64 bit integers or rational or decimal strings. */ bool is_numeral() const { return kind() == Z3_NUMERAL_AST; } - bool is_numeral_i64(__int64& i) const { bool r = 0 != Z3_get_numeral_int64(ctx(), m_ast, &i); check_error(); return r;} - bool is_numeral_u64(__uint64& i) const { bool r = 0 != Z3_get_numeral_uint64(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral_i64(int64_t& i) const { bool r = 0 != Z3_get_numeral_int64(ctx(), m_ast, &i); check_error(); return r;} + bool is_numeral_u64(uint64_t& i) const { bool r = 0 != Z3_get_numeral_uint64(ctx(), m_ast, &i); check_error(); return r;} bool is_numeral_i(int& i) const { bool r = 0 != Z3_get_numeral_int(ctx(), m_ast, &i); check_error(); return r;} bool is_numeral_u(unsigned& i) const { bool r = 0 != Z3_get_numeral_uint(ctx(), m_ast, &i); check_error(); return r;} bool is_numeral(std::string& s) const { if (!is_numeral()) return false; s = Z3_get_numeral_string(ctx(), m_ast); check_error(); return true; } @@ -709,7 +706,7 @@ namespace z3 { It only makes sense to use this function if the caller can ensure that the result is an integer or if exceptions are enabled. - If exceptions are disabled, then use the the is_numeral_i function. + If exceptions are disabled, then use the is_numeral_i function. \pre is_numeral() */ @@ -729,7 +726,7 @@ namespace z3 { It only makes sense to use this function if the caller can ensure that the result is an integer or if exceptions are enabled. - If exceptions are disabled, then use the the is_numeral_u function. + If exceptions are disabled, then use the is_numeral_u function. \pre is_numeral() */ unsigned get_numeral_uint() const { @@ -744,35 +741,35 @@ namespace z3 { } /** - \brief Return __int64 value of numeral, throw if result cannot fit in - __int64 + \brief Return \c int64_t value of numeral, throw if result cannot fit in + \c int64_t. \pre is_numeral() */ - __int64 get_numeral_int64() const { + int64_t get_numeral_int64() const { assert(is_numeral()); - __int64 result = 0; + int64_t result = 0; if (!is_numeral_i64(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; - Z3_THROW(exception("numeral does not fit in machine __int64")); + Z3_THROW(exception("numeral does not fit in machine int64_t")); } return result; } /** - \brief Return __uint64 value of numeral, throw if result cannot fit in - __uint64 + \brief Return \c uint64_t value of numeral, throw if result cannot fit in + \c uint64_t. \pre is_numeral() */ - __uint64 get_numeral_uint64() const { + uint64_t get_numeral_uint64() const { assert(is_numeral()); - __uint64 result = 0; + uint64_t result = 0; if (!is_numeral_u64(result)) { assert(ctx().enable_exceptions()); if (!ctx().enable_exceptions()) return 0; - Z3_THROW(exception("numeral does not fit in machine __uint64")); + Z3_THROW(exception("numeral does not fit in machine uint64_t")); } return result; } @@ -835,7 +832,6 @@ namespace z3 { */ friend expr operator!(expr const & a); - /** \brief Return an expression representing a and b. @@ -892,6 +888,16 @@ namespace z3 { friend expr ite(expr const & c, expr const & t, expr const & e); + bool is_true() const { return is_app() && Z3_OP_TRUE == decl().decl_kind(); } + bool is_false() const { return is_app() && Z3_OP_FALSE == decl().decl_kind(); } + bool is_not() const { return is_app() && Z3_OP_NOT == decl().decl_kind(); } + bool is_and() const { return is_app() && Z3_OP_AND == decl().decl_kind(); } + bool is_or() const { return is_app() && Z3_OP_OR == decl().decl_kind(); } + bool is_xor() const { return is_app() && Z3_OP_XOR == decl().decl_kind(); } + 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); @@ -989,7 +995,7 @@ namespace z3 { /** \brief sequence and regular expression operations. - + is overloaeded as sequence concatenation and regular expression union. + + 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 { @@ -1809,9 +1815,11 @@ namespace z3 { Z3_model_inc_ref(ctx(), m); } public: + struct translate {}; 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() { Z3_model_dec_ref(ctx(), m_model); } operator Z3_model() const { return m_model; } model & operator=(model const & s) { @@ -1904,7 +1912,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; } @@ -2022,6 +2030,97 @@ 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); + } + + class cube_iterator { + solver& m_solver; + unsigned& m_cutoff; + expr_vector& m_vars; + expr_vector m_cube; + bool m_end; + bool m_empty; + + void inc() { + assert(!m_end && !m_empty); + m_cube = m_solver.cube(m_vars, m_cutoff); + m_cutoff = 0xFFFFFFFF; + if (m_cube.size() == 1 && m_cube[0].is_false()) { + m_cube = z3::expr_vector(m_solver.ctx()); + m_end = true; + } + else if (m_cube.empty()) { + m_empty = true; + } + } + public: + cube_iterator(solver& s, expr_vector& vars, unsigned& cutoff, bool end): + m_solver(s), + m_cutoff(cutoff), + m_vars(vars), + m_cube(s.ctx()), + m_end(end), + m_empty(false) { + if (!m_end) { + inc(); + } + } + + cube_iterator& operator++() { + assert(!m_end); + if (m_empty) { + m_end = true; + } + else { + inc(); + } + return *this; + } + 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; + }; + bool operator!=(cube_iterator const& other) { + return other.m_end != m_end; + }; + + }; + + class cube_generator { + solver& m_solver; + unsigned m_cutoff; + expr_vector m_default_vars; + expr_vector& m_vars; + public: + cube_generator(solver& s): + m_solver(s), + m_cutoff(0xFFFFFFFF), + m_default_vars(s.ctx()), + m_vars(m_default_vars) + {} + + cube_generator(solver& s, expr_vector& vars): + m_solver(s), + m_cutoff(0xFFFFFFFF), + m_default_vars(s.ctx()), + m_vars(vars) + {} + + cube_iterator begin() { return cube_iterator(m_solver, m_vars, m_cutoff, false); } + cube_iterator end() { return cube_iterator(m_solver, m_vars, m_cutoff, true); } + void set_cutoff(unsigned c) { m_cutoff = c; } + }; + + cube_generator cubes() { return cube_generator(*this); } + cube_generator cubes(expr_vector& vars) { return cube_generator(*this, vars); } + }; inline std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } @@ -2045,7 +2144,6 @@ namespace z3 { return *this; } void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } - // fails for some compilers: // void add(expr_vector const& v) { check_context(*this, v); for (expr e : v) add(e); } unsigned size() const { return Z3_goal_size(ctx(), m_goal); } expr operator[](int i) const { assert(0 <= i); Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } @@ -2056,6 +2154,17 @@ namespace z3 { unsigned num_exprs() const { return Z3_goal_num_exprs(ctx(), m_goal); } bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } + model convert_model(model const & m) const { + check_context(*this, m); + Z3_model new_m = Z3_goal_convert_model(ctx(), m_goal, m); + check_error(); + return model(ctx(), new_m); + } + model get_model() const { + Z3_model new_m = Z3_goal_convert_model(ctx(), m_goal, 0); + check_error(); + return model(ctx(), new_m); + } expr as_expr() const { unsigned n = size(); if (n == 0) @@ -2069,6 +2178,7 @@ namespace z3 { return expr(ctx(), Z3_mk_and(ctx(), n, args.ptr())); } } + std::string dimacs() const { return std::string(Z3_goal_to_dimacs_string(ctx(), m_goal)); } friend std::ostream & operator<<(std::ostream & out, goal const & g); }; inline std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } @@ -2093,12 +2203,6 @@ namespace z3 { } unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } goal operator[](int i) const { assert(0 <= i); Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } - model convert_model(model const & m, unsigned i = 0) const { - check_context(*this, m); - Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); - check_error(); - return model(ctx(), new_m); - } friend std::ostream & operator<<(std::ostream & out, apply_result const & r); }; inline std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } @@ -2268,6 +2372,7 @@ namespace z3 { class optimize : public object { Z3_optimize m_opt; + public: class handle { unsigned m_h; @@ -2276,6 +2381,17 @@ namespace z3 { unsigned h() const { return m_h; } }; optimize(context& c):object(c) { m_opt = Z3_mk_optimize(c); Z3_optimize_inc_ref(c, m_opt); } + optimize(optimize& o):object(o) { + Z3_optimize_inc_ref(o.ctx(), o.m_opt); + m_opt = o.m_opt; + } + optimize& operator=(optimize const& o) { + Z3_optimize_inc_ref(o.ctx(), o.m_opt); + Z3_optimize_dec_ref(ctx(), m_opt); + m_opt = o.m_opt; + m_ctx = o.m_ctx; + return *this; + } ~optimize() { Z3_optimize_dec_ref(ctx(), m_opt); } operator Z3_optimize() const { return m_opt; } void add(expr const& e) { @@ -2417,6 +2533,19 @@ namespace z3 { for (unsigned i = 0; i < n; i++) { cs.push_back(func_decl(*this, _cs[i])); ts.push_back(func_decl(*this, _ts[i])); } return s; } + inline func_decl context::tuple_sort(char const * name, unsigned n, char const * const * names, sort const* sorts, func_decl_vector & projs) { + array _names(n); + array _sorts(n); + for (unsigned i = 0; i < n; i++) { _names[i] = Z3_mk_string_symbol(*this, names[i]); _sorts[i] = sorts[i]; } + array _projs(n); + Z3_symbol _name = Z3_mk_string_symbol(*this, name); + Z3_func_decl tuple; + sort _ignore_s = to_sort(*this, Z3_mk_tuple_sort(*this, _name, n, _names.ptr(), _sorts.ptr(), &tuple, _projs.ptr())); + check_error(); + for (unsigned i = 0; i < n; i++) { projs.push_back(func_decl(*this, _projs[i])); } + return func_decl(*this, tuple); + } + inline sort context::uninterpreted_sort(char const* name) { Z3_symbol _name = Z3_mk_string_symbol(*this, name); return to_sort(*this, Z3_mk_uninterpreted_sort(*this, _name)); @@ -2511,22 +2640,22 @@ namespace z3 { inline expr context::int_val(int n) { Z3_ast r = Z3_mk_int(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(unsigned n) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } - inline expr context::int_val(__int64 n) { Z3_ast r = Z3_mk_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } - inline expr context::int_val(__uint64 n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } + inline expr context::int_val(int64_t n) { Z3_ast r = Z3_mk_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } + inline expr context::int_val(uint64_t n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::int_val(char const * n) { Z3_ast r = Z3_mk_numeral(m_ctx, n, int_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(int n, int d) { Z3_ast r = Z3_mk_real(m_ctx, n, d); check_error(); return expr(*this, r); } inline expr context::real_val(int n) { Z3_ast r = Z3_mk_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(unsigned n) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } - inline expr context::real_val(__int64 n) { Z3_ast r = Z3_mk_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } - inline expr context::real_val(__uint64 n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } + inline expr context::real_val(int64_t n) { Z3_ast r = Z3_mk_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } + inline expr context::real_val(uint64_t n) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } inline expr context::real_val(char const * n) { Z3_ast r = Z3_mk_numeral(m_ctx, n, real_sort()); check_error(); return expr(*this, r); } - inline expr context::bv_val(int n, unsigned sz) { Z3_ast r = Z3_mk_int(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } - inline expr context::bv_val(unsigned n, unsigned sz) { Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } - inline expr context::bv_val(__int64 n, unsigned sz) { Z3_ast r = Z3_mk_int64(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } - inline expr context::bv_val(__uint64 n, unsigned sz) { Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } - inline expr context::bv_val(char const * n, unsigned sz) { Z3_ast r = Z3_mk_numeral(m_ctx, n, bv_sort(sz)); check_error(); return expr(*this, r); } + inline expr context::bv_val(int n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_int(m_ctx, n, s); check_error(); return expr(*this, r); } + inline expr context::bv_val(unsigned n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_unsigned_int(m_ctx, n, s); check_error(); return expr(*this, r); } + 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) { array _bits(n); for (unsigned i = 0; i < n; ++i) _bits[i] = bits[i] ? 1 : 0; @@ -2643,6 +2772,12 @@ namespace z3 { inline func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { return range.ctx().function(name, d1, d2, d3, d4, d5, range); } + inline func_decl function(char const* name, sort_vector const& domain, sort const& range) { + return range.ctx().function(name, domain, range); + } + inline func_decl function(std::string const& name, sort_vector const& domain, sort const& range) { + return range.ctx().function(name.c_str(), domain, range); + } inline expr select(expr const & a, expr const & i) { check_context(a, i); @@ -2821,22 +2956,19 @@ namespace z3 { - inline expr interpolant(expr const& a) { - return expr(a.ctx(), Z3_mk_interpolant(a.ctx(), a)); + inline expr_vector context::parse_string(char const* s) { + 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); + check_error(); + return expr_vector(*this, r); } - inline expr context::parse_string(char const* s) { - Z3_ast r = Z3_parse_smtlib2_string(*this, s, 0, 0, 0, 0, 0, 0); - check_parser_error(); - return expr(*this, r); - } - inline expr context::parse_file(char const* s) { - Z3_ast r = Z3_parse_smtlib2_file(*this, s, 0, 0, 0, 0, 0, 0); - check_parser_error(); - return expr(*this, r); - } - - inline expr context::parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { + inline expr_vector context::parse_string(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { array sort_names(sorts.size()); array decl_names(decls.size()); array sorts1(sorts); @@ -2847,12 +2979,13 @@ namespace z3 { for (unsigned i = 0; i < decls.size(); ++i) { decl_names[i] = decls[i].name(); } - Z3_ast r = Z3_parse_smtlib2_string(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); - check_parser_error(); - return expr(*this, r); + + Z3_ast_vector r = Z3_parse_smtlib2_string(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); + check_error(); + return expr_vector(*this, r); } - inline expr context::parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { + inline expr_vector context::parse_file(char const* s, sort_vector const& sorts, func_decl_vector const& decls) { array sort_names(sorts.size()); array decl_names(decls.size()); array sorts1(sorts); @@ -2863,33 +2996,12 @@ namespace z3 { for (unsigned i = 0; i < decls.size(); ++i) { decl_names[i] = decls[i].name(); } - Z3_ast r = Z3_parse_smtlib2_file(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); - check_parser_error(); - return expr(*this, r); + Z3_ast_vector r = Z3_parse_smtlib2_file(*this, s, sorts.size(), sort_names.ptr(), sorts1.ptr(), decls.size(), decl_names.ptr(), decls1.ptr()); + check_error(); + return expr_vector(*this, r); } - inline check_result context::compute_interpolant(expr const& pat, params const& p, expr_vector& i, model& m) { - Z3_ast_vector interp = 0; - Z3_model mdl = 0; - Z3_lbool r = Z3_compute_interpolant(*this, pat, p, &interp, &mdl); - switch (r) { - case Z3_L_FALSE: - i = expr_vector(*this, interp); - break; - case Z3_L_TRUE: - m = model(*this, mdl); - break; - case Z3_L_UNDEF: - break; - } - return to_check_result(r); - } - - inline expr_vector context::get_interpolant(expr const& proof, expr const& pat, params const& p) { - return expr_vector(*this, Z3_get_interpolant(*this, proof, pat, p)); - } - inline expr expr::substitute(expr_vector const& src, expr_vector const& dst) { assert(src.size() == dst.size()); array _src(src.size()); diff --git a/src/api/dotnet/AlgebraicNum.cs b/src/api/dotnet/AlgebraicNum.cs index 66552f1a0..3687e1f83 100644 --- a/src/api/dotnet/AlgebraicNum.cs +++ b/src/api/dotnet/AlgebraicNum.cs @@ -3,11 +3,11 @@ Copyright (c) 2012 Microsoft Corporation Module Name: - IntNum.cs + AlgebraicNum.cs Abstract: - Z3 Managed API: Int Numerals + Z3 Managed API: Algebraic Numerals Author: diff --git a/src/api/dotnet/ApplyResult.cs b/src/api/dotnet/ApplyResult.cs index 608be7080..db2922460 100644 --- a/src/api/dotnet/ApplyResult.cs +++ b/src/api/dotnet/ApplyResult.cs @@ -55,19 +55,6 @@ namespace Microsoft.Z3 } } - /// - /// Convert a model for the subgoal into a model for the original - /// goal g, that the ApplyResult was obtained from. - /// - /// A model for g - public Model ConvertModel(uint i, Model m) - { - Contract.Requires(m != null); - Contract.Ensures(Contract.Result() != null); - - return new Model(Context, Native.Z3_apply_result_convert_model(Context.nCtx, NativeObject, i, m.NativeObject)); - } - /// /// A string representation of the ApplyResult. /// diff --git a/src/api/dotnet/BitVecNum.cs b/src/api/dotnet/BitVecNum.cs index c6ac471f6..66054761a 100644 --- a/src/api/dotnet/BitVecNum.cs +++ b/src/api/dotnet/BitVecNum.cs @@ -3,11 +3,11 @@ Copyright (c) 2012 Microsoft Corporation Module Name: - IntNum.cs + BitVecNum.cs Abstract: - Z3 Managed API: Int Numerals + Z3 Managed API: BitVec Numerals Author: diff --git a/src/api/dotnet/CMakeLists.txt b/src/api/dotnet/CMakeLists.txt index add1b0ded..76516bf39 100644 --- a/src/api/dotnet/CMakeLists.txt +++ b/src/api/dotnet/CMakeLists.txt @@ -80,7 +80,6 @@ set(Z3_DOTNET_ASSEMBLY_SOURCES_IN_SRC_TREE Global.cs Goal.cs IDecRefQueue.cs - InterpolationContext.cs IntExpr.cs IntNum.cs IntSort.cs diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index ac23d1dbc..38da21370 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -2262,7 +2262,7 @@ namespace Microsoft.Z3 /// Maps f on the argument arrays. /// /// - /// Eeach element of args must be of an array sort [domain_i -> range_i]. + /// Each element of args must be of an array sort [domain_i -> range_i]. /// The function declaration f must have type range_1 .. range_n -> range. /// v must have sort range. The sort of the result is [domain_i -> range]. /// @@ -2515,7 +2515,7 @@ namespace Microsoft.Z3 /// - /// Concatentate sequences. + /// Concatenate sequences. /// public SeqExpr MkConcat(params SeqExpr[] t) { @@ -2862,7 +2862,7 @@ namespace Microsoft.Z3 } /// - /// Create a Term of a given sort. This function can be use to create numerals that fit in a machine integer. + /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral @@ -2878,7 +2878,7 @@ namespace Microsoft.Z3 } /// - /// Create a Term of a given sort. This function can be use to create numerals that fit in a machine integer. + /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral @@ -2894,7 +2894,7 @@ namespace Microsoft.Z3 } /// - /// Create a Term of a given sort. This function can be use to create numerals that fit in a machine integer. + /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral @@ -2910,7 +2910,7 @@ namespace Microsoft.Z3 } /// - /// Create a Term of a given sort. This function can be use to create numerals that fit in a machine integer. + /// Create a Term of a given sort. This function can be used to create numerals that fit in a machine integer. /// It is slightly faster than MakeNumeral since it is not necessary to parse a string. /// /// Value of the numeral @@ -3211,7 +3211,7 @@ namespace Microsoft.Z3 /// Create an existential Quantifier. /// /// - /// Creates an existential quantifier using de-Brujin indexed variables. + /// Creates an existential quantifier using de-Bruijn indexed variables. /// (). /// public Quantifier MkExists(Sort[] sorts, Symbol[] names, Expr body, uint weight = 1, Pattern[] patterns = null, Expr[] noPatterns = null, Symbol quantifierID = null, Symbol skolemID = null) @@ -3325,7 +3325,7 @@ namespace Microsoft.Z3 /// Parse the given string using the SMT-LIB2 parser. /// /// A conjunction of assertions in the scope (up to push/pop) at the end of the string. - public BoolExpr ParseSMTLIB2String(string str, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) + public BoolExpr[] ParseSMTLIB2String(string str, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { Contract.Ensures(Contract.Result() != null); @@ -3335,16 +3335,17 @@ namespace Microsoft.Z3 uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); - return (BoolExpr)Expr.Create(this, Native.Z3_parse_smtlib2_string(nCtx, str, + ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_string(nCtx, str, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); + return assertions.ToBoolExprArray(); } /// /// Parse the given file using the SMT-LIB2 parser. /// /// - public BoolExpr ParseSMTLIB2File(string fileName, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) + public BoolExpr[] ParseSMTLIB2File(string fileName, Symbol[] sortNames = null, Sort[] sorts = null, Symbol[] declNames = null, FuncDecl[] decls = null) { Contract.Ensures(Contract.Result() != null); @@ -3354,9 +3355,10 @@ namespace Microsoft.Z3 uint cd = AST.ArrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); - return (BoolExpr)Expr.Create(this, Native.Z3_parse_smtlib2_file(nCtx, fileName, + ASTVector assertions = new ASTVector(this, Native.Z3_parse_smtlib2_file(nCtx, fileName, AST.ArrayLength(sorts), Symbol.ArrayToNative(sortNames), AST.ArrayToNative(sorts), AST.ArrayLength(decls), Symbol.ArrayToNative(declNames), AST.ArrayToNative(decls))); + return assertions.ToBoolExprArray(); } #endregion @@ -3597,7 +3599,7 @@ namespace Microsoft.Z3 } /// - /// Create a tactic that fails if the goal is not triviall satisfiable (i.e., empty) + /// Create a tactic that fails if the goal is not trivially satisfiable (i.e., empty) /// or trivially unsatisfiable (i.e., contains `false'). /// public Tactic FailIfNotDecided() @@ -4656,7 +4658,7 @@ namespace Microsoft.Z3 /// Conversion of a floating-point term into a bit-vector. /// /// - /// Produces a term that represents the conversion of the floating-poiunt term t into a + /// Produces a term that represents the conversion of the floating-point term t into a /// bit-vector term of size sz in 2's complement format (signed when signed==true). If necessary, /// the result will be rounded according to rounding mode rm. /// @@ -4677,7 +4679,7 @@ namespace Microsoft.Z3 /// Conversion of a floating-point term into a real-numbered term. /// /// - /// Produces a term that represents the conversion of the floating-poiunt term t into a + /// Produces a term that represents the conversion of the floating-point term t into a /// real number. Note that this type of conversion will often result in non-linear /// constraints over real terms. /// @@ -4696,7 +4698,7 @@ namespace Microsoft.Z3 /// /// The size of the resulting bit-vector is automatically determined. Note that /// IEEE 754-2008 allows multiple different representations of NaN. This conversion - /// knows only one NaN and it will always produce the same bit-vector represenatation of + /// knows only one NaN and it will always produce the same bit-vector representation of /// that NaN. /// /// FloatingPoint term. diff --git a/src/api/dotnet/Expr.cs b/src/api/dotnet/Expr.cs index 4fd306052..f09eecbdd 100644 --- a/src/api/dotnet/Expr.cs +++ b/src/api/dotnet/Expr.cs @@ -317,14 +317,6 @@ namespace Microsoft.Z3 #endregion - #region Interpolation - /// - /// Indicates whether the term is marked for interpolation. - /// - /// - public bool IsInterpolant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_INTERP; } } - #endregion - #region Arithmetic Terms /// /// Indicates whether the term is of integer sort. @@ -932,7 +924,7 @@ namespace Microsoft.Z3 /// Indicates whether the term is a proof by condensed transitivity of a relation /// /// - /// Condensed transitivity proof. This proof object is only used if the parameter PROOF_MODE is 1. + /// Condensed transitivity proof. /// It combines several symmetry and transitivity proofs. /// Example: /// T1: (R a b) @@ -959,7 +951,7 @@ namespace Microsoft.Z3 /// Tn: (R t_n s_n) /// [monotonicity T1 ... Tn]: (R (f t_1 ... t_n) (f s_1 ... s_n)) /// Remark: if t_i == s_i, then the antecedent Ti is suppressed. - /// That is, reflexivity proofs are supressed to save space. + /// That is, reflexivity proofs are suppressed to save space. /// public bool IsProofMonotonicity { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_MONOTONICITY; } } @@ -1002,7 +994,7 @@ namespace Microsoft.Z3 public bool IsProofAndElimination { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_AND_ELIM; } } /// - /// Indicates whether the term is a proof by eliminiation of not-or + /// Indicates whether the term is a proof by elimination of not-or /// /// /// Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). @@ -1035,14 +1027,11 @@ namespace Microsoft.Z3 /// /// /// A proof for rewriting an expression t into an expression s. - /// This proof object is used if the parameter PROOF_MODE is 1. /// This proof object can have n antecedents. /// The antecedents are proofs for equalities used as substitution rules. - /// The object is also used in a few cases if the parameter PROOF_MODE is 2. - /// The cases are: + /// The object is used in a few cases: /// - When applying contextual simplification (CONTEXT_SIMPLIFIER=true) /// - When converting bit-vectors to Booleans (BIT2BOOL=true) - /// - When pulling ite expression up (PULL_CHEAP_ITE_TREES=true) /// public bool IsProofRewriteStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_REWRITE_STAR; } } @@ -1054,15 +1043,6 @@ namespace Microsoft.Z3 /// public bool IsProofPullQuant { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } } - /// - /// Indicates whether the term is a proof for pulling quantifiers out. - /// - /// - /// A proof for (iff P Q) where Q is in prenex normal form. - /// This proof object is only used if the parameter PROOF_MODE is 1. - /// This proof object has no antecedents - /// - public bool IsProofPullQuantStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; } } /// /// Indicates whether the term is a proof for pushing quantifiers in. @@ -1112,7 +1092,7 @@ namespace Microsoft.Z3 public bool IsProofQuantInst { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_QUANT_INST; } } /// - /// Indicates whether the term is a hypthesis marker. + /// Indicates whether the term is a hypothesis marker. /// /// Mark a hypothesis in a natural deduction style proof. public bool IsProofHypothesis { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_HYPOTHESIS; } } @@ -1304,28 +1284,6 @@ namespace Microsoft.Z3 /// public bool IsProofNNFNeg { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } } - /// - /// Indicates whether the term is a proof for (~ P Q) here Q is in negation normal form. - /// - /// - /// A proof for (~ P Q) where Q is in negation normal form. - /// - /// This proof object is only used if the parameter PROOF_MODE is 1. - /// - /// This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. - /// - public bool IsProofNNFStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_NNF_STAR; } } - - /// - /// Indicates whether the term is a proof for (~ P Q) where Q is in conjunctive normal form. - /// - /// - /// A proof for (~ P Q) where Q is in conjunctive normal form. - /// This proof object is only used if the parameter PROOF_MODE is 1. - /// This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. - /// - public bool IsProofCNFStar { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_PR_CNF_STAR; } } - /// /// Indicates whether the term is a proof for a Skolemization step /// @@ -1433,7 +1391,7 @@ namespace Microsoft.Z3 /// /// Filter (restrict) a relation with respect to a predicate. /// The first argument is a relation. - /// The second argument is a predicate with free de-Brujin indices + /// The second argument is a predicate with free de-Bruijn indices /// corresponding to the columns of the relation. /// So the first column in the relation has index 0. /// @@ -1649,7 +1607,7 @@ namespace Microsoft.Z3 public bool IsFPMul { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_MUL; } } /// - /// Indicates whether the term is a floating-point divison term + /// Indicates whether the term is a floating-point division term /// public bool IsFPDiv { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_DIV; } } @@ -1709,7 +1667,7 @@ namespace Microsoft.Z3 public bool IsFPLe { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_LE; } } /// - /// Indicates whether the term is a floating-point greater-than or erqual term + /// Indicates whether the term is a floating-point greater-than or equal term /// public bool IsFPGe { get { return IsApp && FuncDecl.DeclKind == Z3_decl_kind.Z3_OP_FPA_GE; } } @@ -1789,7 +1747,7 @@ namespace Microsoft.Z3 #region Bound Variables /// - /// The de-Burijn index of a bound variable. + /// The de-Bruijn index of a bound variable. /// /// /// Bound variables are indexed by de-Bruijn indices. It is perhaps easiest to explain diff --git a/src/api/dotnet/Goal.cs b/src/api/dotnet/Goal.cs index 521b453f8..03e573538 100644 --- a/src/api/dotnet/Goal.cs +++ b/src/api/dotnet/Goal.cs @@ -174,6 +174,21 @@ namespace Microsoft.Z3 get { return Native.Z3_goal_is_decided_unsat(Context.nCtx, NativeObject) != 0; } } + /// + /// Convert a model for the goal into a model of the + /// original goal from which this goal was derived. + /// + /// A model for g + public Model ConvertModel(Model m) + { + Contract.Ensures(Contract.Result() != null); + if (m != null) + return new Model(Context, Native.Z3_goal_convert_model(Context.nCtx, NativeObject, m.NativeObject)); + else + return new Model(Context, Native.Z3_goal_convert_model(Context.nCtx, NativeObject, IntPtr.Zero)); + } + + /// /// Translates (copies) the Goal to the target Context . /// @@ -208,6 +223,15 @@ namespace Microsoft.Z3 return Native.Z3_goal_to_string(Context.nCtx, NativeObject); } + /// + /// Goal to DIMACS formatted string conversion. + /// + /// A string representation of the Goal. + public string ToDimacs() + { + return Native.Z3_goal_to_dimacs_string(Context.nCtx, NativeObject); + } + /// /// Goal to BoolExpr conversion. /// diff --git a/src/api/dotnet/InterpolationContext.cs b/src/api/dotnet/InterpolationContext.cs deleted file mode 100644 index 3f2feb5a6..000000000 --- a/src/api/dotnet/InterpolationContext.cs +++ /dev/null @@ -1,164 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Diagnostics.Contracts; -using System.Runtime.InteropServices; - -namespace Microsoft.Z3 -{ - /// - /// The InterpolationContext is suitable for generation of interpolants. - /// - /// For more information on interpolation please refer - /// too the C/C++ API, which is well documented. - [ContractVerification(true)] - public class InterpolationContext : Context - { - - /// - /// Constructor. - /// - public InterpolationContext() : base() { } - - /// - /// Constructor. - /// - /// - public InterpolationContext(Dictionary settings) : base(settings) { } - - #region Terms - /// - /// Create an expression that marks a formula position for interpolation. - /// - public BoolExpr MkInterpolant(BoolExpr a) - { - Contract.Requires(a != null); - Contract.Ensures(Contract.Result() != null); - - CheckContextMatch(a); - return new BoolExpr(this, Native.Z3_mk_interpolant(nCtx, a.NativeObject)); - } - #endregion - - /// - /// Computes an interpolant. - /// - /// For more information on interpolation please refer - /// too the function Z3_get_interpolant in the C/C++ API, which is - /// well documented. - public BoolExpr[] GetInterpolant(Expr pf, Expr pat, Params p) - { - Contract.Requires(pf != null); - Contract.Requires(pat != null); - Contract.Requires(p != null); - Contract.Ensures(Contract.Result() != null); - - CheckContextMatch(pf); - CheckContextMatch(pat); - CheckContextMatch(p); - - ASTVector seq = new ASTVector(this, Native.Z3_get_interpolant(nCtx, pf.NativeObject, pat.NativeObject, p.NativeObject)); - return seq.ToBoolExprArray(); - } - - /// - /// Computes an interpolant. - /// - /// For more information on interpolation please refer - /// too the function Z3_compute_interpolant in the C/C++ API, which is - /// well documented. - public Z3_lbool ComputeInterpolant(Expr pat, Params p, out BoolExpr[] interp, out Model model) - { - Contract.Requires(pat != null); - Contract.Requires(p != null); - Contract.Ensures(Contract.ValueAtReturn(out interp) != null); - Contract.Ensures(Contract.ValueAtReturn(out model) != null); - - CheckContextMatch(pat); - CheckContextMatch(p); - - IntPtr i = IntPtr.Zero, m = IntPtr.Zero; - int r = Native.Z3_compute_interpolant(nCtx, pat.NativeObject, p.NativeObject, ref i, ref m); - interp = new ASTVector(this, i).ToBoolExprArray(); - model = new Model(this, m); - return (Z3_lbool)r; - } - - /// - /// Return a string summarizing cumulative time used for interpolation. - /// - /// For more information on interpolation please refer - /// too the function Z3_interpolation_profile in the C/C++ API, which is - /// well documented. - public string InterpolationProfile() - { - return Native.Z3_interpolation_profile(nCtx); - } - - /// - /// Checks the correctness of an interpolant. - /// - /// For more information on interpolation please refer - /// too the function Z3_check_interpolant in the C/C++ API, which is - /// well documented. - public int CheckInterpolant(Expr[] cnsts, uint[] parents, BoolExpr[] interps, out string error, Expr[] theory) - { - Contract.Requires(cnsts.Length == parents.Length); - Contract.Requires(cnsts.Length == interps.Length + 1); - IntPtr n_err_str; - int r = Native.Z3_check_interpolant(nCtx, - (uint)cnsts.Length, - Expr.ArrayToNative(cnsts), - parents, - Expr.ArrayToNative(interps), - out n_err_str, - (uint)theory.Length, - Expr.ArrayToNative(theory)); - error = Marshal.PtrToStringAnsi(n_err_str); - return r; - } - - /// - /// Reads an interpolation problem from a file. - /// - /// For more information on interpolation please refer - /// too the function Z3_read_interpolation_problem in the C/C++ API, which is - /// well documented. - public int ReadInterpolationProblem(string filename, out Expr[] cnsts, out uint[] parents, out string error, out Expr[] theory) - { - uint num = 0, num_theory = 0; - IntPtr[] n_cnsts; - IntPtr[] n_theory; - IntPtr n_err_str; - int r = Native.Z3_read_interpolation_problem(nCtx, ref num, out n_cnsts, out parents, filename, out n_err_str, ref num_theory, out n_theory); - error = Marshal.PtrToStringAnsi(n_err_str); - cnsts = new Expr[num]; - parents = new uint[num]; - theory = new Expr[num_theory]; - for (int i = 0; i < num; i++) - cnsts[i] = Expr.Create(this, n_cnsts[i]); - for (int i = 0; i < num_theory; i++) - theory[i] = Expr.Create(this, n_theory[i]); - return r; - } - - /// - /// Writes an interpolation problem to a file. - /// - /// For more information on interpolation please refer - /// too the function Z3_write_interpolation_problem in the C/C++ API, which is - /// well documented. - public void WriteInterpolationProblem(string filename, Expr[] cnsts, uint[] parents, Expr[] theory) - { - Contract.Requires(cnsts.Length == parents.Length); - Native.Z3_write_interpolation_problem(nCtx, (uint)cnsts.Length, Expr.ArrayToNative(cnsts), parents, filename, (uint)theory.Length, Expr.ArrayToNative(theory)); - } - } -} diff --git a/src/api/dotnet/Model.cs b/src/api/dotnet/Model.cs index 12992fa8a..d11a57052 100644 --- a/src/api/dotnet/Model.cs +++ b/src/api/dotnet/Model.cs @@ -253,7 +253,7 @@ namespace Microsoft.Z3 /// The uninterpreted sorts that the model has an interpretation for. /// /// - /// Z3 also provides an intepretation for uninterpreted sorts used in a formula. + /// Z3 also provides an interpretation for uninterpreted sorts used in a formula. /// The interpretation for a sort is a finite set of distinct values. We say this finite set is /// the "universe" of the sort. /// diff --git a/src/api/dotnet/Params.cs b/src/api/dotnet/Params.cs index 37d73d5b1..5b143d525 100644 --- a/src/api/dotnet/Params.cs +++ b/src/api/dotnet/Params.cs @@ -91,7 +91,7 @@ namespace Microsoft.Z3 public Params Add(string name, bool value) { Native.Z3_params_set_bool(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, (value) ? 1 : 0); - return this; + return this; } /// @@ -100,7 +100,7 @@ namespace Microsoft.Z3 public Params Add(string name, uint value) { Native.Z3_params_set_uint(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, value); - return this; + return this; } /// diff --git a/src/api/dotnet/Solver.cs b/src/api/dotnet/Solver.cs index a176e790b..a288990a2 100644 --- a/src/api/dotnet/Solver.cs +++ b/src/api/dotnet/Solver.cs @@ -57,7 +57,7 @@ namespace Microsoft.Z3 } } - /// + /// /// Sets parameter on the solver /// public void Set(string name, bool value) { Parameters = Context.MkParams().Add(name, value); } @@ -266,6 +266,20 @@ namespace Microsoft.Z3 } } + /// + /// Currently inferred units. + /// + public BoolExpr[] Units + { + get + { + Contract.Ensures(Contract.Result() != null); + + ASTVector assertions = new ASTVector(Context, Native.Z3_solver_get_units(Context.nCtx, NativeObject)); + return assertions.ToBoolExprArray(); + } + } + /// /// Checks whether the assertions in the solver are consistent or not. /// @@ -331,10 +345,10 @@ namespace Microsoft.Z3 } /// - /// The model of the last Check. + /// The model of the last Check(params Expr[] assumptions). /// /// - /// The result is null if Check was not invoked before, + /// The result is null if Check(params Expr[] assumptions) was not invoked before, /// if its results was not SATISFIABLE, or if model production is not enabled. /// public Model Model @@ -350,10 +364,10 @@ namespace Microsoft.Z3 } /// - /// The proof of the last Check. + /// The proof of the last Check(params Expr[] assumptions). /// /// - /// The result is null if Check was not invoked before, + /// The result is null if Check(params Expr[] assumptions) was not invoked before, /// if its results was not UNSATISFIABLE, or if proof production is disabled. /// public Expr Proof @@ -400,6 +414,42 @@ namespace Microsoft.Z3 } } + /// + /// Backtrack level that can be adjusted by conquer process + /// + public uint BacktrackLevel { get; set; } + + /// + /// Variables available and returned by the cuber. + /// + public BoolExpr[] CubeVariables { get; set; } + + + /// + /// Return a set of cubes. + /// + public IEnumerable Cube() + { + ASTVector cv = new ASTVector(Context); + if (CubeVariables != null) + foreach (var b in CubeVariables) cv.Push(b); + + while (true) { + var lvl = BacktrackLevel; + BacktrackLevel = uint.MaxValue; + ASTVector r = new ASTVector(Context, Native.Z3_solver_cube(Context.nCtx, NativeObject, cv.NativeObject, lvl)); + var v = r.ToBoolExprArray(); + CubeVariables = cv.ToBoolExprArray(); + if (v.Length == 1 && v[0].IsFalse) { + break; + } + yield return v; + if (v.Length == 0) { + break; + } + } + } + /// /// Create a clone of the current solver with respect to ctx. /// @@ -410,6 +460,13 @@ namespace Microsoft.Z3 return new Solver(ctx, Native.Z3_solver_translate(Context.nCtx, NativeObject, ctx.nCtx)); } + /// + /// Import model converter from other solver. + /// + public void ImportModelConverter(Solver src) + { + Native.Z3_solver_import_model_converter(Context.nCtx, src.NativeObject, NativeObject); + } /// /// Solver statistics. @@ -437,6 +494,7 @@ namespace Microsoft.Z3 : base(ctx, obj) { Contract.Requires(ctx != null); + this.BacktrackLevel = uint.MaxValue; } internal class DecRefQueue : IDecRefQueue diff --git a/src/api/dotnet/Statistics.cs b/src/api/dotnet/Statistics.cs index cf4b703fe..c94af625c 100644 --- a/src/api/dotnet/Statistics.cs +++ b/src/api/dotnet/Statistics.cs @@ -56,7 +56,7 @@ namespace Microsoft.Z3 public bool IsDouble { get { return m_is_double; } } /// - /// The string representation of the the entry's value. + /// The string representation of the entry's value. /// public string Value { diff --git a/src/api/java/ApplyResult.java b/src/api/java/ApplyResult.java index 6fafbd888..6cfedd404 100644 --- a/src/api/java/ApplyResult.java +++ b/src/api/java/ApplyResult.java @@ -46,19 +46,6 @@ public class ApplyResult extends Z3Object { return res; } - /** - * Convert a model for the subgoal {@code i} into a model for the - * original goal {@code g}, that the ApplyResult was obtained from. - * - * @return A model for {@code g} - * @throws Z3Exception - **/ - public Model convertModel(int i, Model m) - { - return new Model(getContext(), - Native.applyResultConvertModel(getContext().nCtx(), getNativeObject(), i, m.getNativeObject())); - } - /** * A string representation of the ApplyResult. **/ diff --git a/src/api/java/CMakeLists.txt b/src/api/java/CMakeLists.txt index dce2bc4ea..7f0774955 100644 --- a/src/api/java/CMakeLists.txt +++ b/src/api/java/CMakeLists.txt @@ -137,7 +137,6 @@ set(Z3_JAVA_JAR_SOURCE_FILES GoalDecRefQueue.java Goal.java IDecRefQueue.java - InterpolationContext.java IntExpr.java IntNum.java IntSort.java diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 3458f4d97..dee9dbaf6 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -934,7 +934,7 @@ public class Context implements AutoCloseable { * exposed. It follows the semantics prescribed by the SMT-LIB standard. * * You can take the floor of a real by creating an auxiliary integer Term - * {@code k} and and asserting + * {@code k} and asserting * {@code MakeInt2Real(k) <= t1 < MkInt2Real(k)+1}. The argument * must be of integer sort. **/ @@ -1978,7 +1978,7 @@ public class Context implements AutoCloseable { } /** - * Concatentate sequences. + * Concatenate sequences. */ public SeqExpr mkConcat(SeqExpr... t) { @@ -2234,7 +2234,7 @@ public class Context implements AutoCloseable { } /** - * Create a Term of a given sort. This function can be use to create + * Create a Term of a given sort. This function can be used to create * numerals that fit in a machine integer. It is slightly faster than * {@code MakeNumeral} since it is not necessary to parse a string. * @@ -2250,7 +2250,7 @@ public class Context implements AutoCloseable { } /** - * Create a Term of a given sort. This function can be use to create + * Create a Term of a given sort. This function can be used to create * numerals that fit in a machine integer. It is slightly faster than * {@code MakeNumeral} since it is not necessary to parse a string. * @@ -2438,7 +2438,7 @@ public class Context implements AutoCloseable { } /** - * Creates an existential quantifier using de-Brujin indexed variables. + * Creates an existential quantifier using de-Bruijn indexed variables. * @see #mkForall(Sort[],Symbol[],Expr,int,Pattern[],Expr[],Symbol,Symbol) **/ public Quantifier mkExists(Sort[] sorts, Symbol[] names, Expr body, @@ -2543,14 +2543,16 @@ public class Context implements AutoCloseable { /** * 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. + * @return A conjunction of assertions. + * + * If the string contains push/pop commands, the + * set of assertions returned are the ones in the + * last scope level. **/ - public BoolExpr parseSMTLIB2String(String str, Symbol[] sortNames, + public BoolExpr[] parseSMTLIB2String(String str, Symbol[] sortNames, Sort[] sorts, Symbol[] declNames, FuncDecl[] decls) { - int csn = Symbol.arrayLength(sortNames); int cs = Sort.arrayLength(sorts); int cdn = Symbol.arrayLength(declNames); @@ -2558,17 +2560,18 @@ public class Context implements AutoCloseable { if (csn != cs || cdn != cd) { throw new Z3Exception("Argument size mismatch"); } - return (BoolExpr) Expr.create(this, Native.parseSmtlib2String(nCtx(), + ASTVector v = new ASTVector(this, Native.parseSmtlib2String(nCtx(), str, AST.arrayLength(sorts), Symbol.arrayToNative(sortNames), AST.arrayToNative(sorts), AST.arrayLength(decls), Symbol.arrayToNative(declNames), AST.arrayToNative(decls))); + return v.ToBoolExprArray(); } /** * Parse the given file using the SMT-LIB2 parser. * @see #parseSMTLIB2String **/ - public BoolExpr parseSMTLIB2File(String fileName, Symbol[] sortNames, + public BoolExpr[] parseSMTLIB2File(String fileName, Symbol[] sortNames, Sort[] sorts, Symbol[] declNames, FuncDecl[] decls) { @@ -2578,11 +2581,12 @@ public class Context implements AutoCloseable { int cd = AST.arrayLength(decls); if (csn != cs || cdn != cd) throw new Z3Exception("Argument size mismatch"); - return (BoolExpr) Expr.create(this, Native.parseSmtlib2File(nCtx(), + ASTVector v = new ASTVector(this, Native.parseSmtlib2File(nCtx(), fileName, AST.arrayLength(sorts), Symbol.arrayToNative(sortNames), AST.arrayToNative(sorts), AST.arrayLength(decls), Symbol.arrayToNative(declNames), AST.arrayToNative(decls))); + return v.ToBoolExprArray(); } /** @@ -2781,7 +2785,7 @@ public class Context implements AutoCloseable { } /** - * Create a tactic that fails if the goal is not triviall satisfiable (i.e., + * Create a tactic that fails if the goal is not trivially satisfiable (i.e., * empty) or trivially unsatisfiable (i.e., contains `false'). **/ public Tactic failIfNotDecided() @@ -3769,7 +3773,7 @@ public class Context implements AutoCloseable { * @param sz Size of the resulting bit-vector. * @param signed Indicates whether the result is a signed or unsigned bit-vector. * Remarks: - * Produces a term that represents the conversion of the floating-poiunt term t into a + * Produces a term that represents the conversion of the floating-point term t into a * bit-vector term of size sz in 2's complement format (signed when signed==true). If necessary, * the result will be rounded according to rounding mode rm. * @throws Z3Exception @@ -3786,7 +3790,7 @@ public class Context implements AutoCloseable { * Conversion of a floating-point term into a real-numbered term. * @param t FloatingPoint term * Remarks: - * Produces a term that represents the conversion of the floating-poiunt term t into a + * Produces a term that represents the conversion of the floating-point term t into a * real number. Note that this type of conversion will often result in non-linear * constraints over real terms. * @throws Z3Exception @@ -3802,7 +3806,7 @@ public class Context implements AutoCloseable { * Remarks: * The size of the resulting bit-vector is automatically determined. Note that * IEEE 754-2008 allows multiple different representations of NaN. This conversion - * knows only one NaN and it will always produce the same bit-vector represenatation of + * knows only one NaN and it will always produce the same bit-vector representation of * that NaN. * @throws Z3Exception **/ diff --git a/src/api/java/Expr.java b/src/api/java/Expr.java index d3793a24b..db5c33e79 100644 --- a/src/api/java/Expr.java +++ b/src/api/java/Expr.java @@ -1398,8 +1398,7 @@ public class Expr extends AST /** * Indicates whether the term is a proof by condensed transitivity of a * relation - * Remarks: Condensed transitivity proof. This proof object is - * only used if the parameter PROOF_MODE is 1. It combines several symmetry + * Remarks: Condensed transitivity proof. It combines several symmetry * and transitivity proofs. Example: T1: (R a b) T2: (R c b) T3: (R c d) * [trans* T1 T2 T3]: (R a d) R must be a symmetric and transitive relation. * @@ -1421,7 +1420,7 @@ public class Expr extends AST * Remarks: T1: * (R t_1 s_1) ... Tn: (R t_n s_n) [monotonicity T1 ... Tn]: (R (f t_1 ... * t_n) (f s_1 ... s_n)) Remark: if t_i == s_i, then the antecedent Ti is - * suppressed. That is, reflexivity proofs are supressed to save space. + * suppressed. That is, reflexivity proofs are suppressed to save space. * * @throws Z3Exception on error * @return a boolean @@ -1473,7 +1472,7 @@ public class Expr extends AST } /** - * Indicates whether the term is a proof by eliminiation of not-or + * Indicates whether the term is a proof by elimination of not-or * Remarks: * Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). * T1: (not (or l_1 ... l_n)) [not-or-elim T1]: (not l_i) * @throws Z3Exception on error * @return a boolean @@ -1506,14 +1505,11 @@ public class Expr extends AST /** * Indicates whether the term is a proof by rewriting * Remarks: A proof for - * rewriting an expression t into an expression s. This proof object is used - * if the parameter PROOF_MODE is 1. This proof object can have n + * rewriting an expression t into an expression s. This proof object can have n * antecedents. The antecedents are proofs for equalities used as - * substitution rules. The object is also used in a few cases if the - * parameter PROOF_MODE is 2. The cases are: - When applying contextual + * substitution rules. The object is used in a few cases . The cases are: - When applying contextual * simplification (CONTEXT_SIMPLIFIER=true) - When converting bit-vectors to - * Booleans (BIT2BOOL=true) - When pulling ite expression up - * (PULL_CHEAP_ITE_TREES=true) + * Booleans (BIT2BOOL=true) * @throws Z3Exception on error * @return a boolean **/ @@ -1534,17 +1530,6 @@ public class Expr extends AST return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT; } - /** - * Indicates whether the term is a proof for pulling quantifiers out. - * - * Remarks: A proof for (iff P Q) where Q is in prenex normal form. This * proof object is only used if the parameter PROOF_MODE is 1. This proof * object has no antecedents - * @throws Z3Exception on error - * @return a boolean - **/ - public boolean isProofPullQuantStar() - { - return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_PULL_QUANT_STAR; - } /** * Indicates whether the term is a proof for pushing quantifiers in. @@ -1605,7 +1590,7 @@ public class Expr extends AST } /** - * Indicates whether the term is a hypthesis marker. + * Indicates whether the term is a hypothesis marker. * Remarks: Mark a * hypothesis in a natural deduction style proof. * @throws Z3Exception on error @@ -1804,38 +1789,6 @@ public class Expr extends AST return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_NEG; } - /** - * Indicates whether the term is a proof for (~ P Q) here Q is in negation - * normal form. - * Remarks: A proof for (~ P Q) where Q is in negation normal - * form. - * - * This proof object is only used if the parameter PROOF_MODE is 1. - * - * This proof object may have n antecedents. Each antecedent is a - * PR_DEF_INTRO. - * @throws Z3Exception on error - * @return a boolean - **/ - public boolean isProofNNFStar() - { - return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_NNF_STAR; - } - - /** - * Indicates whether the term is a proof for (~ P Q) where Q is in - * conjunctive normal form. - * Remarks: A proof for (~ P Q) where Q is in - * conjunctive normal form. This proof object is only used if the parameter - * PROOF_MODE is 1. This proof object may have n antecedents. Each - * antecedent is a PR_DEF_INTRO. - * @throws Z3Exception on error - * @return a boolean - **/ - public boolean isProofCNFStar() - { - return isApp() && getFuncDecl().getDeclKind() == Z3_decl_kind.Z3_OP_PR_CNF_STAR; - } /** * Indicates whether the term is a proof for a Skolemization step @@ -1987,7 +1940,7 @@ public class Expr extends AST * Indicates whether the term is a relation filter * Remarks: Filter * (restrict) a relation with respect to a predicate. The first argument is - * a relation. The second argument is a predicate with free de-Brujin + * a relation. The second argument is a predicate with free de-Bruijn * indices corresponding to the columns of the relation. So the first column * in the relation has index 0. * @throws Z3Exception on error @@ -2094,7 +2047,7 @@ public class Expr extends AST } /** - * The de-Burijn index of a bound variable. + * The de-Bruijn index of a bound variable. * Remarks: Bound variables are * indexed by de-Bruijn indices. It is perhaps easiest to explain the * meaning of de-Bruijn indices by indicating the compilation process from diff --git a/src/api/java/Goal.java b/src/api/java/Goal.java index 25b1fe511..903325850 100644 --- a/src/api/java/Goal.java +++ b/src/api/java/Goal.java @@ -240,6 +240,21 @@ public class Goal extends Z3Object { (unsatCores), (proofs))); } + /** + * Convert a model for the goal into a model of the + * original goal from which this goal was derived. + * + * @return A model for {@code g} + * @throws Z3Exception + **/ + public Model convertModel(Model m) + { + return new Model(getContext(), + Native.goalConvertModel(getContext().nCtx(), getNativeObject(), m.getNativeObject())); + } + + + @Override void incRef() { Native.goalIncRef(getContext().nCtx(), getNativeObject()); diff --git a/src/api/java/InterpolationContext.java b/src/api/java/InterpolationContext.java deleted file mode 100644 index 99a63821f..000000000 --- a/src/api/java/InterpolationContext.java +++ /dev/null @@ -1,216 +0,0 @@ -/** -Copyright (c) 2012-2014 Microsoft Corporation - -Module Name: - - InterpolationContext.java - -Abstract: - -Author: - - @author Christoph Wintersteiger (cwinter) 2012-03-15 - -Notes: - -**/ - -package com.microsoft.z3; - -import com.microsoft.z3.enumerations.Z3_lbool; - -import java.util.Map; - -/** - * The InterpolationContext is suitable for generation of interpolants. - * - * Remarks: For more information on interpolation please refer - * too the C/C++ API, which is well documented. - **/ -public class InterpolationContext extends Context -{ - /** - * Constructor. - **/ - public static InterpolationContext mkContext() - { - long m_ctx; - synchronized(creation_lock) { - m_ctx = Native.mkInterpolationContext(0); - } - return new InterpolationContext(m_ctx); - } - - /** - * Constructor. - * - * - * Remarks: - * @see Context#Context - **/ - public static InterpolationContext mkContext(Map settings) - { - long m_ctx; - synchronized(creation_lock) { - long cfg = Native.mkConfig(); - for (Map.Entry kv : settings.entrySet()) - Native.setParamValue(cfg, kv.getKey(), kv.getValue()); - m_ctx = Native.mkInterpolationContext(cfg); - Native.delConfig(cfg); - } - return new InterpolationContext(m_ctx); - } - - private InterpolationContext(long m_ctx) { - super(m_ctx); - } - - /** - * Create an expression that marks a formula position for interpolation. - * @throws Z3Exception - **/ - public BoolExpr MkInterpolant(BoolExpr a) - { - checkContextMatch(a); - return new BoolExpr(this, Native.mkInterpolant(nCtx(), a.getNativeObject())); - } - - /** - * Computes an interpolant. - * Remarks: For more information on interpolation please refer - * too the function Z3_get_interpolant in the C/C++ API, which is - * well documented. - * @throws Z3Exception - **/ - public BoolExpr[] GetInterpolant(Expr pf, Expr pat, Params p) - { - checkContextMatch(pf); - checkContextMatch(pat); - checkContextMatch(p); - - ASTVector seq = new ASTVector(this, Native.getInterpolant(nCtx(), pf.getNativeObject(), pat.getNativeObject(), p.getNativeObject())); - return seq.ToBoolExprArray(); - } - - public class ComputeInterpolantResult - { - public Z3_lbool status = Z3_lbool.Z3_L_UNDEF; - public BoolExpr[] interp = null; - public Model model = null; - }; - - /** - * Computes an interpolant. - * Remarks: For more information on interpolation please refer - * too the function Z3_compute_interpolant in the C/C++ API, which is - * well documented. - * @throws Z3Exception - **/ - public ComputeInterpolantResult ComputeInterpolant(Expr pat, Params p) - { - checkContextMatch(pat); - checkContextMatch(p); - - ComputeInterpolantResult res = new ComputeInterpolantResult(); - Native.LongPtr n_i = new Native.LongPtr(); - Native.LongPtr n_m = new Native.LongPtr(); - res.status = Z3_lbool.fromInt(Native.computeInterpolant(nCtx(), pat.getNativeObject(), p.getNativeObject(), n_i, n_m)); - if (res.status == Z3_lbool.Z3_L_FALSE) - res.interp = (new ASTVector(this, n_i.value)).ToBoolExprArray(); - if (res.status == Z3_lbool.Z3_L_TRUE) - res.model = new Model(this, n_m.value); - return res; - } - - /// - /// Return a string summarizing cumulative time used for interpolation. - /// - /// Remarks: For more information on interpolation please refer - /// too the function Z3_interpolation_profile in the C/C++ API, which is - /// well documented. - public String InterpolationProfile() - { - return Native.interpolationProfile(nCtx()); - } - - public class CheckInterpolantResult - { - public int return_value = 0; - public String error = null; - } - - /// - /// Checks the correctness of an interpolant. - /// - /// Remarks: For more information on interpolation please refer - /// too the function Z3_check_interpolant in the C/C++ API, which is - /// well documented. - public CheckInterpolantResult CheckInterpolant(Expr[] cnsts, int[] parents, BoolExpr[] interps, String error, Expr[] theory) - { - CheckInterpolantResult res = new CheckInterpolantResult(); - Native.StringPtr n_err_str = new Native.StringPtr(); - res.return_value = Native.checkInterpolant(nCtx(), - cnsts.length, - Expr.arrayToNative(cnsts), - parents, - Expr.arrayToNative(interps), - n_err_str, - theory.length, - Expr.arrayToNative(theory)); - res.error = n_err_str.value; - return res; - } - - public class ReadInterpolationProblemResult - { - public int return_value = 0; - public Expr[] cnsts; - public int[] parents; - public String error; - public Expr[] theory; - }; - - /// - /// Reads an interpolation problem from a file. - /// - /// Remarks: For more information on interpolation please refer - /// too the function Z3_read_interpolation_problem in the C/C++ API, which is - /// well documented. - public ReadInterpolationProblemResult ReadInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) - { - ReadInterpolationProblemResult res = new ReadInterpolationProblemResult(); - - Native.IntPtr n_num = new Native.IntPtr(); - Native.IntPtr n_num_theory = new Native.IntPtr(); - Native.ObjArrayPtr n_cnsts = new Native.ObjArrayPtr(); - Native.UIntArrayPtr n_parents = new Native.UIntArrayPtr(); - Native.ObjArrayPtr n_theory = new Native.ObjArrayPtr(); - Native.StringPtr n_err_str = new Native.StringPtr(); - res.return_value = Native.readInterpolationProblem(nCtx(), n_num, n_cnsts, n_parents, filename, n_err_str, n_num_theory, n_theory); - int num = n_num.value; - int num_theory = n_num_theory.value; - res.error = n_err_str.value; - res.cnsts = new Expr[num]; - res.parents = new int[num]; - theory = new Expr[num_theory]; - for (int i = 0; i < num; i++) - { - res.cnsts[i] = Expr.create(this, n_cnsts.value[i]); - res.parents[i] = n_parents.value[i]; - } - for (int i = 0; i < num_theory; i++) - res.theory[i] = Expr.create(this, n_theory.value[i]); - return res; - } - - /// - /// Writes an interpolation problem to a file. - /// - /// Remarks: For more information on interpolation please refer - /// too the function Z3_write_interpolation_problem in the C/C++ API, which is - /// well documented. - public void WriteInterpolationProblem(String filename, Expr[] cnsts, int[] parents, String error, Expr[] theory) - { - Native.writeInterpolationProblem(nCtx(), cnsts.length, Expr.arrayToNative(cnsts), parents, filename, theory.length, Expr.arrayToNative(theory)); - } -} diff --git a/src/api/java/Model.java b/src/api/java/Model.java index 9c7013aca..d809b6790 100644 --- a/src/api/java/Model.java +++ b/src/api/java/Model.java @@ -239,7 +239,7 @@ public class Model extends Z3Object { /** * The uninterpreted sorts that the model has an interpretation for. - * Remarks: Z3 also provides an intepretation for uninterpreted sorts used + * Remarks: Z3 also provides an interpretation for uninterpreted sorts used * in a formula. The interpretation for a sort is a finite set of distinct * values. We say this finite set is the "universe" of the sort. * diff --git a/src/api/java/Solver.java b/src/api/java/Solver.java index 5bd4e65ba..0370a9571 100644 --- a/src/api/java/Solver.java +++ b/src/api/java/Solver.java @@ -121,22 +121,6 @@ public class Solver extends Z3Object { } } - /** - * Load solver assertions from a file. - */ - public void fromFile(String file) - { - Native.solverFromFile(getContext().nCtx(), getNativeObject(), file); - } - - /** - * Load solver assertions from a string. - */ - public void fromString(String str) - { - Native.solverFromString(getContext().nCtx(), getNativeObject(), str); - } - /** * Assert multiple constraints into the solver, and track them (in the @@ -188,6 +172,23 @@ public class Solver extends Z3Object { constraint.getNativeObject(), p.getNativeObject()); } + /// + /// Load solver assertions from a file. + /// + public void fromFile(String file) + { + Native.solverFromFile(getContext().nCtx(), getNativeObject(), file); + } + + /// + /// Load solver assertions from a string. + /// + public void fromString(String str) + { + Native.solverFromString(getContext().nCtx(), getNativeObject(), str); + } + + /** * The number of assertions in the solver. * diff --git a/src/api/java/Statistics.java b/src/api/java/Statistics.java index 356cbeadb..d509424ed 100644 --- a/src/api/java/Statistics.java +++ b/src/api/java/Statistics.java @@ -65,7 +65,7 @@ public class Statistics extends Z3Object { } /** - * The string representation of the the entry's value. + * The string representation of the entry's value. * * @throws Z3Exception **/ diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index ce305bb4e..a676f8c43 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1402,7 +1402,6 @@ struct let is_rewrite (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_REWRITE) let is_rewrite_star (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_REWRITE_STAR) let is_pull_quant (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_PULL_QUANT) - let is_pull_quant_star (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_PULL_QUANT_STAR) let is_push_quant (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_PUSH_QUANT) let is_elim_unused_vars (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_ELIM_UNUSED_VARS) let is_der (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_DER) @@ -1419,8 +1418,6 @@ struct let is_iff_oeq (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_IFF_OEQ) let is_nnf_pos (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_NNF_POS) let is_nnf_neg (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_NNF_NEG) - let is_nnf_star (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_NNF_STAR) - let is_cnf_star (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_CNF_STAR) let is_skolemize (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_SKOLEMIZE) let is_modus_ponens_oeq (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_MODUS_PONENS_OEQ) let is_theory_lemma (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_PR_TH_LEMMA) @@ -1657,7 +1654,6 @@ struct mk_list f n let get_subgoal (x:apply_result) (i:int) = Z3native.apply_result_get_subgoal (gc x) x i - let convert_model (x:apply_result) (i:int) (m:Model.model) = Z3native.apply_result_convert_model (gc x) x i m let to_string (x:apply_result) = Z3native.apply_result_to_string (gc x) x end @@ -1994,56 +1990,10 @@ struct if csn <> cs || cdn <> cd then raise (Error "Argument size mismatch") else - Z3native.parse_smtlib2_string ctx file_name + Z3native.parse_smtlib2_file ctx file_name cs sort_names sorts cd decl_names decls end -module Interpolation = -struct - let mk_interpolant = Z3native.mk_interpolant - - let mk_interpolation_context (settings:(string * string) list) = - let cfg = Z3native.mk_config () in - let f e = Z3native.set_param_value cfg (fst e) (snd e) in - List.iter f settings; - let res = Z3native.mk_interpolation_context cfg in - Z3native.del_config cfg; - Z3native.set_ast_print_mode res (int_of_ast_print_mode PRINT_SMTLIB2_COMPLIANT); - Z3native.set_internal_error_handler res; - res - - let get_interpolant (ctx:context) (pf:expr) (pat:expr) (p:Params.params) = - let av = Z3native.get_interpolant ctx pf pat p in - AST.ASTVector.to_expr_list av - - let compute_interpolant (ctx:context) (pat:expr) (p:Params.params) = - let (r, interp, model) = Z3native.compute_interpolant ctx pat p in - let res = lbool_of_int r in - match res with - | L_TRUE -> (res, None, Some model) - | L_FALSE -> (res, Some (AST.ASTVector.to_expr_list interp), None) - | _ -> (res, None, None) - - let get_interpolation_profile = Z3native.interpolation_profile - - let read_interpolation_problem (ctx:context) (filename:string) = - let (r, num, cnsts, parents, error, num_theory, theory) = - Z3native.read_interpolation_problem ctx filename - in - match r with - | 0 -> raise (Error "Interpolation problem could not be read.") - | _ -> (cnsts, parents, theory) - - let check_interpolant (ctx:context) (num:int) (cnsts:Expr.expr list) (parents:int list) (interps:Expr.expr list) (num_theory:int) (theory:Expr.expr list) = - let (r, str) = Z3native.check_interpolant ctx num cnsts parents interps num_theory theory in - match (lbool_of_int r) with - | L_UNDEF -> raise (Error "Interpolant could not be verified.") - | L_FALSE -> raise (Error "Interpolant could not be verified.") - | _ -> () - - let write_interpolation_problem (ctx:context) (num:int) (cnsts:Expr.expr list) (parents:int list) (filename:string) (num_theory:int) (theory:Expr.expr list) = - Z3native.write_interpolation_problem ctx num cnsts parents filename num_theory theory -end let set_global_param = Z3native.global_param_set diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 9f4cd9cd9..9b424b508 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -536,7 +536,7 @@ sig @return A Term with the given value and sort *) val mk_numeral_string : context -> string -> Sort.sort -> expr - (** Create a numeral of a given sort. This function can be use to create numerals that fit in a machine integer. + (** Create a numeral of a given sort. This function can be used to create numerals that fit in a machine integer. It is slightly faster than [MakeNumeral] since it is not necessary to parse a string. @return A Term with the given value and sort *) val mk_numeral_int : context -> int -> Sort.sort -> expr @@ -667,7 +667,7 @@ sig end - (** The de-Burijn index of a bound variable. + (** The de-Bruijn index of a bound variable. Bound variables are indexed by de-Bruijn indices. It is perhaps easiest to explain the meaning of de-Bruijn indices by indicating the compilation process from @@ -830,7 +830,7 @@ sig (** Maps f on the argument arrays. - Eeach element of [args] must be of an array sort [[domain_i -> range_i]]. + Each element of [args] must be of an array sort [[domain_i -> range_i]]. The function declaration [f] must have type [ range_1 .. range_n -> range]. [v] must have sort range. The sort of the result is [[domain_i -> range]]. {!Z3Array.mk_sort} @@ -962,7 +962,7 @@ sig Filter (restrict) a relation with respect to a predicate. The first argument is a relation. - The second argument is a predicate with free de-Brujin indices + The second argument is a predicate with free de-Bruijn indices corresponding to the columns of the relation. So the first column in the relation has index 0. *) val is_filter : Expr.expr -> bool @@ -2085,7 +2085,7 @@ sig (** Indicates whether an expression is a floating-point lt expression *) val is_lt : Expr.expr -> bool - (** Indicates whether an expression is a floating-point geqexpression *) + (** Indicates whether an expression is a floating-point geq expression *) val is_geq : Expr.expr -> bool (** Indicates whether an expression is a floating-point gt expression *) @@ -2233,7 +2233,7 @@ sig (** Conversion of a 2's complement unsigned bit-vector term into a term of FloatingPoint sort. *) val mk_to_fp_unsigned : context -> Expr.expr -> Expr.expr -> Sort.sort -> Expr.expr - (** C1onversion of a floating-point term into an unsigned bit-vector. *) + (** Conversion of a floating-point term into an unsigned bit-vector. *) val mk_to_ubv : context -> Expr.expr -> Expr.expr -> int -> Expr.expr (** Conversion of a floating-point term into a signed bit-vector. *) @@ -2362,7 +2362,7 @@ sig (** Indicates whether the term is a proof by condensed transitivity of a relation - Condensed transitivity proof. This proof object is only used if the parameter PROOF_MODE is 1. + Condensed transitivity proof. It combines several symmetry and transitivity proofs. Example: T1: (R a b) @@ -2385,7 +2385,7 @@ sig Tn: (R t_n s_n) [monotonicity T1 ... Tn]: (R (f t_1 ... t_n) (f s_1 ... s_n)) Remark: if t_i == s_i, then the antecedent Ti is suppressed. - That is, reflexivity proofs are supressed to save space. *) + That is, reflexivity proofs are suppressed to save space. *) val is_monotonicity : Expr.expr -> bool (** Indicates whether the term is a quant-intro proof @@ -2417,7 +2417,7 @@ sig [and-elim T1]: l_i *) val is_and_elimination : Expr.expr -> bool - (** Indicates whether the term is a proof by eliminiation of not-or + (** Indicates whether the term is a proof by elimination of not-or Given a proof for (not (or l_1 ... l_n)), produces a proof for (not l_i). T1: (not (or l_1 ... l_n)) @@ -2443,14 +2443,11 @@ sig (** Indicates whether the term is a proof by rewriting A proof for rewriting an expression t into an expression s. - This proof object is used if the parameter PROOF_MODE is 1. This proof object can have n antecedents. The antecedents are proofs for equalities used as substitution rules. - The object is also used in a few cases if the parameter PROOF_MODE is 2. - The cases are: + The object is also used in a few cases. The cases are: - When applying contextual simplification (CONTEXT_SIMPLIFIER=true) - - When converting bit-vectors to Booleans (BIT2BOOL=true) - - When pulling ite expression up (PULL_CHEAP_ITE_TREES=true) *) + - When converting bit-vectors to Booleans (BIT2BOOL=true) *) val is_rewrite_star : Expr.expr -> bool (** Indicates whether the term is a proof for pulling quantifiers out. @@ -2458,13 +2455,6 @@ sig A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. *) val is_pull_quant : Expr.expr -> bool - (** Indicates whether the term is a proof for pulling quantifiers out. - - A proof for (iff P Q) where Q is in prenex normal form. - This proof object is only used if the parameter PROOF_MODE is 1. - This proof object has no antecedents *) - val is_pull_quant_star : Expr.expr -> bool - (** Indicates whether the term is a proof for pushing quantifiers in. A proof for: @@ -2500,7 +2490,7 @@ sig A proof of (or (not (forall (x) (P x))) (P a)) *) val is_quant_inst : Expr.expr -> bool - (** Indicates whether the term is a hypthesis marker. + (** Indicates whether the term is a hypothesis marker. Mark a hypothesis in a natural deduction style proof. *) val is_hypothesis : Expr.expr -> bool @@ -2658,22 +2648,6 @@ sig (and (or r_1 r_2) (or r_1' r_2'))) *) val is_nnf_neg : Expr.expr -> bool - (** Indicates whether the term is a proof for (~ P Q) here Q is in negation normal form. - - A proof for (~ P Q) where Q is in negation normal form. - - This proof object is only used if the parameter PROOF_MODE is 1. - - This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. *) - val is_nnf_star : Expr.expr -> bool - - (** Indicates whether the term is a proof for (~ P Q) where Q is in conjunctive normal form. - - A proof for (~ P Q) where Q is in conjunctive normal form. - This proof object is only used if the parameter PROOF_MODE is 1. - This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. *) - val is_cnf_star : Expr.expr -> bool - (** Indicates whether the term is a proof for a Skolemization step Proof for: @@ -2882,7 +2856,7 @@ sig (** The uninterpreted sorts that the model has an interpretation for. - Z3 also provides an intepretation for uninterpreted sorts used in a formula. + Z3 also provides an interpretation for uninterpreted sorts used in a formula. The interpretation for a sort is a finite set of distinct values. We say this finite set is the "universe" of the sort. {!get_num_sorts} @@ -2990,11 +2964,6 @@ sig (** Retrieves a subgoal from the apply_result. *) val get_subgoal : apply_result -> int -> Goal.goal - (** Convert a model for a subgoal into a model for the original - goal [g], that the ApplyResult was obtained from. - #return A model for [g] *) - val convert_model : apply_result -> int -> Model.model -> Model.model - (** A string representation of the ApplyResult. *) val to_string : apply_result -> string end @@ -3056,7 +3025,7 @@ sig (** Create a tactic that fails if the probe evaluates to false. *) val fail_if : context -> Probe.probe -> tactic - (** Create a tactic that fails if the goal is not triviall satisfiable (i.e., empty) + (** Create a tactic that fails if the goal is not trivially satisfiable (i.e., empty) or trivially unsatisfiable (i.e., contains `false'). *) val fail_if_not_decided : context -> tactic @@ -3105,7 +3074,7 @@ sig (** True if the entry is float-valued. *) val is_float : statistics_entry -> bool - (** The string representation of the the entry's value. *) + (** The string representation of the entry's value. *) val to_string_value : statistics_entry -> string (** The string representation of the entry (key and value) *) @@ -3370,7 +3339,7 @@ sig (** Assert a constraints into the optimize solver. *) val add : optimize -> Expr.expr list -> unit - (** Asssert a soft constraint. + (** Assert a soft constraint. Supply integer weight and string that identifies a group of soft constraints. *) val add_soft : optimize -> Expr.expr -> string -> Symbol.symbol -> handle @@ -3443,60 +3412,13 @@ sig (** Parse the given string using the SMT-LIB2 parser. - {!parse_smtlib_string} @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 - (** Parse the given file using the SMT-LIB2 parser. - {!parse_smtlib2_string} *) + (** 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 end -(** Interpolation *) -module Interpolation : -sig - - (** Create an AST node marking a formula position for interpolation. - The expression must have Boolean sort. *) - val mk_interpolant : context -> Expr.expr -> Expr.expr - - (** The interpolation context is suitable for generation of interpolants. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val mk_interpolation_context : (string * string) list -> context - - (** Gets an interpolant. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val get_interpolant : context -> Expr.expr -> Expr.expr -> Params.params -> Expr.expr list - - (** Computes an interpolant. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val compute_interpolant : context -> Expr.expr -> Params.params -> (Z3enums.lbool * Expr.expr list option * Model.model option) - - (** Retrieves an interpolation profile. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val get_interpolation_profile : context -> string - - (** Read an interpolation problem from file. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val read_interpolation_problem : context -> string -> (Expr.expr list * int list * Expr.expr list) - - (** Check the correctness of an interpolant. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val check_interpolant : context -> int -> Expr.expr list -> int list -> Expr.expr list -> int -> Expr.expr list -> unit - - (** Write an interpolation problem to file suitable for reading with - Z3_read_interpolation_problem. - For more information on interpolation please refer - too the C/C++ API, which is well documented. *) - val write_interpolation_problem : context -> int -> Expr.expr list -> int list -> string -> int -> Expr.expr list -> unit - -end (** Set a global (or module) parameter, which is shared by all Z3 contexts. diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 237f35433..5e83a4181 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -90,6 +90,9 @@ def _z3_assert(cond, msg): if not cond: raise Z3Exception(msg) +def _z3_check_cint_overflow(n, name): + _z3_assert(ctypes.c_int(n).value == n, name + " is too large") + def open_log(fname): """Log interaction to a file. This function must be invoked immediately after init(). """ Z3_open_log(fname) @@ -114,15 +117,26 @@ def _symbol2py(ctx, s): # Hack for having nary functions that can receive one argument that is the # list of arguments. +# Use this when function takes a single list of arguments def _get_args(args): - try: - if len(args) == 1 and (isinstance(args[0], tuple) or isinstance(args[0], list)): + try: + if len(args) == 1 and (isinstance(args[0], tuple) or isinstance(args[0], list)): return args[0] - elif len(args) == 1 and (isinstance(args[0], set) or isinstance(args[0], AstVector)): + elif len(args) == 1 and (isinstance(args[0], set) or isinstance(args[0], AstVector)): return [arg for arg in args[0]] + else: + return args + except: # len is not necessarily defined when args is not a sequence (use reflection?) + return args + +# Use this when function takes multiple arguments +def _get_args_ast_list(args): + try: + if isinstance(args, set) or isinstance(args, AstVector) or isinstance(args, tuple): + return [arg for arg in args] else: return args - except: # len is not necessarily defined when args is not a sequence (use reflection?) + except: return args def _to_param_value(val): @@ -182,7 +196,7 @@ class Context: """Interrupt a solver performing a satisfiability test, a tactic processing a goal, or simplify functions. This method can be invoked from a thread different from the one executing the - interruptable procedure. + interruptible procedure. """ Z3_interrupt(self.ref()) @@ -365,6 +379,9 @@ class AstRef(Z3PPObject): _z3_assert(isinstance(target, Context), "argument must be a Z3 context") return _to_ast_ref(Z3_translate(self.ctx.ref(), self.as_ast(), target.ref()), target) + def __copy__(self): + return self.translate(self.ctx) + def hash(self): """Return a hashcode for the `self`. @@ -596,7 +613,7 @@ def _sort(ctx, a): return _to_sort_ref(Z3_get_sort(ctx.ref(), a), ctx) def DeclareSort(name, ctx=None): - """Create a new uninterpred sort named `name`. + """Create a new uninterpreted sort named `name`. If `ctx=None`, then the new sort is declared in the global Z3Py context. @@ -718,7 +735,7 @@ class FuncDeclRef(AstRef): The arguments must be Z3 expressions. This method assumes that the sorts of the elements in `args` match the sorts of the - domain. Limited coersion is supported. For example, if + domain. Limited coercion is supported. For example, if args[0] is a Python integer, and the function expects a Z3 integer, then the argument is automatically converted into a Z3 integer. @@ -1401,6 +1418,17 @@ def is_or(a): """ return is_app_of(a, Z3_OP_OR) +def is_implies(a): + """Return `True` if `a` is a Z3 implication expression. + + >>> p, q = Bools('p q') + >>> is_implies(Implies(p, q)) + True + >>> is_implies(And(p, q)) + False + """ + return is_app_of(a, Z3_OP_IMPLIES) + def is_not(a): """Return `True` if `a` is a Z3 not expression. @@ -1873,13 +1901,17 @@ def is_quantifier(a): def _mk_quantifier(is_forall, vs, body, weight=1, qid="", skid="", patterns=[], no_patterns=[]): if __debug__: - _z3_assert(is_bool(body), "Z3 expression expected") + _z3_assert(is_bool(body) or is_app(vs) or (len(vs) > 0 and is_app(vs[0])), "Z3 expression expected") _z3_assert(is_const(vs) or (len(vs) > 0 and all([ is_const(v) for v in vs])), "Invalid bounded variable(s)") _z3_assert(all([is_pattern(a) or is_expr(a) for a in patterns]), "Z3 patterns expected") - _z3_assert(all([is_expr(p) for p in no_patterns]), "no patterns are Z3 expressions") - ctx = body.ctx + _z3_assert(all([is_expr(p) for p in no_patterns]), "no patterns are Z3 expressions") if is_app(vs): + ctx = vs.ctx vs = [vs] + else: + ctx = vs[0].ctx + if not is_expr(body): + body = BoolVal(body, ctx) num_vars = len(vs) if num_vars == 0: return body @@ -2422,7 +2454,7 @@ def is_rational_value(a): return is_arith(a) and a.is_real() and _is_numeral(a.ctx, a.as_ast()) def is_algebraic_value(a): - """Return `True` if `a` is an algerbraic value of sort Real. + """Return `True` if `a` is an algebraic value of sort Real. >>> is_algebraic_value(RealVal("3/5")) False @@ -2740,6 +2772,8 @@ def _py2expr(a, ctx=None): return IntVal(a, ctx) if isinstance(a, float): return RealVal(a, ctx) + if is_expr(a): + return a if __debug__: _z3_assert(False, "Python bool, int, long or float expected") @@ -3568,6 +3602,14 @@ def BV2Int(a, is_signed=False): ## investigate problem with bv2int return ArithRef(Z3_mk_bv2int(ctx.ref(), a.as_ast(), is_signed), ctx) +def Int2BV(a, num_bits): + """Return the z3 expression Int2BV(a, num_bits). + It is a bit-vector of width num_bits and represents the + modulo of a by 2^num_bits + """ + ctx = a.ctx + return BitVecRef(Z3_mk_int2bv(ctx.ref(), num_bits, a.as_ast()), ctx) + def BitVecSort(sz, ctx=None): """Return a Z3 bit-vector sort of the given size. If `ctx=None`, then the global context is used. @@ -4366,6 +4408,117 @@ def is_store(a): """ return is_app_of(a, Z3_OP_STORE) +######################################### +# +# Sets +# +######################################### + + +def SetSort(s): + """ Create a set sort over element sort s""" + return ArraySort(s, BoolSort()) + +def EmptySet(s): + """Create the empty set + >>> EmptySet(IntSort()) + K(Int, False) + """ + ctx = s.ctx + return ArrayRef(Z3_mk_empty_set(ctx.ref(), s.ast), ctx) + +def FullSet(s): + """Create the full set + >>> FullSet(IntSort()) + K(Int, True) + """ + ctx = s.ctx + return ArrayRef(Z3_mk_full_set(ctx.ref(), s.ast), ctx) + +def SetUnion(*args): + """ Take the union of sets + >>> a = Const('a', SetSort(IntSort())) + >>> b = Const('b', SetSort(IntSort())) + >>> SetUnion(a, b) + union(a, b) + """ + args = _get_args(args) + ctx = _ctx_from_ast_arg_list(args) + _args, sz = _to_ast_array(args) + return ArrayRef(Z3_mk_set_union(ctx.ref(), sz, _args), ctx) + +def SetIntersect(*args): + """ Take the union of sets + >>> a = Const('a', SetSort(IntSort())) + >>> b = Const('b', SetSort(IntSort())) + >>> SetIntersect(a, b) + intersect(a, b) + """ + args = _get_args(args) + ctx = _ctx_from_ast_arg_list(args) + _args, sz = _to_ast_array(args) + return ArrayRef(Z3_mk_set_intersect(ctx.ref(), sz, _args), ctx) + +def SetAdd(s, e): + """ Add element e to set s + >>> a = Const('a', SetSort(IntSort())) + >>> SetAdd(a, 1) + Store(a, 1, True) + """ + ctx = _ctx_from_ast_arg_list([s,e]) + e = _py2expr(e, ctx) + return ArrayRef(Z3_mk_set_add(ctx.ref(), s.as_ast(), e.as_ast()), ctx) + +def SetDel(s, e): + """ Remove element e to set s + >>> a = Const('a', SetSort(IntSort())) + >>> SetDel(a, 1) + Store(a, 1, False) + """ + ctx = _ctx_from_ast_arg_list([s,e]) + e = _py2expr(e, ctx) + return ArrayRef(Z3_mk_set_del(ctx.ref(), s.as_ast(), e.as_ast()), ctx) + +def SetComplement(s): + """ The complement of set s + >>> a = Const('a', SetSort(IntSort())) + >>> SetComplement(a) + complement(a) + """ + ctx = s.ctx + return ArrayRef(Z3_mk_set_complement(ctx.ref(), s.as_ast()), ctx) + +def SetDifference(a, b): + """ The set difference of a and b + >>> a = Const('a', SetSort(IntSort())) + >>> b = Const('b', SetSort(IntSort())) + >>> SetDifference(a, b) + difference(a, b) + """ + ctx = _ctx_from_ast_arg_list([a, b]) + return ArrayRef(Z3_mk_set_difference(ctx.ref(), a.as_ast(), b.as_ast()), ctx) + +def IsMember(e, s): + """ Check if e is a member of set s + >>> a = Const('a', SetSort(IntSort())) + >>> IsMember(1, a) + a[1] + """ + ctx = _ctx_from_ast_arg_list([s,e]) + e = _py2expr(e, ctx) + return BoolRef(Z3_mk_set_member(ctx.ref(), e.as_ast(), s.as_ast()), ctx) + +def IsSubset(a, b): + """ Check if a is a subset of b + >>> a = Const('a', SetSort(IntSort())) + >>> b = Const('b', SetSort(IntSort())) + >>> IsSubset(a, b) + subset(a, b) + """ + ctx = _ctx_from_ast_arg_list([a, b]) + return BoolRef(Z3_mk_set_subset(ctx.ref(), a.as_ast(), b.as_ast()), ctx) + + ######################################### # # Datatypes @@ -4423,7 +4576,7 @@ class Datatype: """Declare constructor named `name` with the given accessors `args`. Each accessor is a pair `(name, sort)`, where `name` is a string and `sort` a Z3 sort or a reference to the datatypes being declared. - In the followin example `List.declare('cons', ('car', IntSort()), ('cdr', List))` + In the following example `List.declare('cons', ('car', IntSort()), ('cdr', List))` declares the constructor named `cons` that builds a new List using an integer and a List. It also declares the accessors `car` and `cdr`. The accessor `car` extracts the integer of a `cons` cell, and `cdr` the list of a `cons` cell. After all constructors were declared, we use the method create() to create @@ -4437,13 +4590,13 @@ class Datatype: if __debug__: _z3_assert(isinstance(name, str), "String expected") _z3_assert(name != "", "Constructor name cannot be empty") - return self.declare_core(name, "is_" + name, *args) + return self.declare_core(name, "is-" + name, *args) def __repr__(self): return "Datatype(%s, %s)" % (self.name, self.constructors) def create(self): - """Create a Z3 datatype based on the constructors declared using the mehtod `declare()`. + """Create a Z3 datatype based on the constructors declared using the method `declare()`. The function `CreateDatatypes()` must be used to define mutually recursive datatypes. @@ -4561,7 +4714,7 @@ def CreateDatatypes(*ds): cref = cref() setattr(dref, cref_name, cref) rref = dref.recognizer(j) - setattr(dref, rref.name(), rref) + setattr(dref, "is_" + cref_name, rref) for k in range(cref_arity): aref = dref.accessor(j, k) setattr(dref, aref.name(), aref) @@ -4615,16 +4768,16 @@ class DatatypeSortRef(SortRef): >>> List.num_constructors() 2 >>> List.recognizer(0) - is_cons + is(cons) >>> List.recognizer(1) - is_nil + is(nil) >>> simplify(List.is_nil(List.cons(10, List.nil))) False >>> simplify(List.is_cons(List.cons(10, List.nil))) True >>> l = Const('l', List) >>> simplify(List.is_cons(l)) - is_cons(l) + is(cons, l) """ if __debug__: _z3_assert(idx < self.num_constructors(), "Invalid recognizer index") @@ -5010,6 +5163,35 @@ class Goal(Z3PPObject): """ self.assert_exprs(*args) + def convert_model(self, model): + """Retrieve model from a satisfiable goal + >>> a, b = Ints('a b') + >>> g = Goal() + >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) + >>> t = Then(Tactic('split-clause'), Tactic('solve-eqs')) + >>> r = t(g) + >>> r[0] + [Or(b == 0, b == 1), Not(0 <= b)] + >>> r[1] + [Or(b == 0, b == 1), Not(1 <= b)] + >>> # Remark: the subgoal r[0] is unsatisfiable + >>> # Creating a solver for solving the second subgoal + >>> s = Solver() + >>> s.add(r[1]) + >>> s.check() + sat + >>> s.model() + [b = 0] + >>> # Model s.model() does not assign a value to `a` + >>> # It is a model for subgoal `r[1]`, but not for goal `g` + >>> # The method convert_model creates a model for `g` from a model for `r[1]`. + >>> r[1].convert_model(s.model()) + [b = 0, a = 1] + """ + if __debug__: + _z3_assert(isinstance(model, ModelRef), "Z3 Model expected") + return ModelRef(Z3_goal_convert_model(self.ctx.ref(), self.goal, model.model), self.ctx) + def __repr__(self): return obj_to_string(self) @@ -5017,6 +5199,10 @@ class Goal(Z3PPObject): """Return a textual representation of the s-expression representing the goal.""" return Z3_goal_to_string(self.ctx.ref(), self.goal) + def dimacs(self): + """Return a textual representation of the goal in DIMACS format.""" + return Z3_goal_to_dimacs_string(self.ctx.ref(), self.goal) + def translate(self, target): """Copy goal `self` to context `target`. @@ -5040,6 +5226,12 @@ class Goal(Z3PPObject): _z3_assert(isinstance(target, Context), "target must be a context") return Goal(goal=Z3_goal_translate(self.ctx.ref(), self.goal, target.ref()), ctx=target) + def __copy__(self): + return self.translate(self.ctx) + + def __deepcopy__(self): + return self.translate(self.ctx) + def simplify(self, *arguments, **keywords): """Return a new simplified goal. @@ -5132,9 +5324,18 @@ class AstVector(Z3PPObject): >>> A[1] y """ - if i >= self.__len__(): - raise IndexError - return _to_ast_ref(Z3_ast_vector_get(self.ctx.ref(), self.vector, i), self.ctx) + + if isinstance(i, int): + if i < 0: + i += self.__len__() + + if i >= self.__len__(): + raise IndexError + return _to_ast_ref(Z3_ast_vector_get(self.ctx.ref(), self.vector, i), self.ctx) + + elif isinstance(i, slice): + return [_to_ast_ref(Z3_ast_vector_get(self.ctx.ref(), self.vector, ii), self.ctx) for ii in range(*i.indices(self.__len__()))] + def __setitem__(self, i, v): """Update AST at position `i`. @@ -5213,6 +5414,12 @@ class AstVector(Z3PPObject): """ return AstVector(Z3_ast_vector_translate(self.ctx.ref(), self.vector, other_ctx.ref()), other_ctx) + def __copy__(self): + return self.translate(self.ctx) + + def __deepcopy__(self): + return self.translate(self.ctx) + def __repr__(self): return obj_to_string(self) @@ -5550,6 +5757,17 @@ class FuncInterp(Z3PPObject): raise IndexError return FuncEntry(Z3_func_interp_get_entry(self.ctx.ref(), self.f, idx), self.ctx) + def translate(self, other_ctx): + """Copy model 'self' to context 'other_ctx'. + """ + return ModelRef(Z3_model_translate(self.ctx.ref(), self.model, other_ctx.ref()), other_ctx) + + def __copy__(self): + return self.translate(self.ctx) + + def __deepcopy__(self): + return self.translate(self.ctx) + def as_list(self): """Return the function interpretation as a Python list. >>> f = Function('f', IntSort(), IntSort()) @@ -5579,9 +5797,6 @@ class ModelRef(Z3PPObject): self.ctx = ctx Z3_model_inc_ref(self.ctx.ref(), self.model) - def __deepcopy__(self, memo={}): - return ModelRef(self.m, self.ctx) - def __del__(self): if self.ctx.ref() is not None: Z3_model_dec_ref(self.ctx.ref(), self.model) @@ -5698,7 +5913,7 @@ class ModelRef(Z3PPObject): return None def num_sorts(self): - """Return the number of unintepreted sorts that contain an interpretation in the model `self`. + """Return the number of uninterpreted sorts that contain an interpretation in the model `self`. >>> A = DeclareSort('A') >>> a, b = Consts('a b', A) @@ -5713,7 +5928,7 @@ class ModelRef(Z3PPObject): return int(Z3_model_get_num_sorts(self.ctx.ref(), self.model)) def get_sort(self, idx): - """Return the unintepreted sort at position `idx` < self.num_sorts(). + """Return the uninterpreted sort at position `idx` < self.num_sorts(). >>> A = DeclareSort('A') >>> B = DeclareSort('B') @@ -5753,7 +5968,7 @@ class ModelRef(Z3PPObject): return [ self.get_sort(i) for i in range(self.num_sorts()) ] def get_universe(self, s): - """Return the intepretation for the uninterpreted sort `s` in the model `self`. + """Return the interpretation for the uninterpreted sort `s` in the model `self`. >>> A = DeclareSort('A') >>> a, b = Consts('a b', A) @@ -5773,7 +5988,7 @@ class ModelRef(Z3PPObject): return None def __getitem__(self, idx): - """If `idx` is an integer, then the declaration at position `idx` in the model `self` is returned. If `idx` is a declaration, then the actual interpreation is returned. + """If `idx` is an integer, then the declaration at position `idx` in the model `self` is returned. If `idx` is a declaration, then the actual interpretation is returned. The elements can be retrieved using position or the actual declaration. @@ -5817,7 +6032,7 @@ class ModelRef(Z3PPObject): return None def decls(self): - """Return a list with all symbols that have an interpreation in the model `self`. + """Return a list with all symbols that have an interpretation in the model `self`. >>> f = Function('f', IntSort(), IntSort()) >>> x = Int('x') >>> s = Solver() @@ -5835,6 +6050,20 @@ class ModelRef(Z3PPObject): r.append(FuncDeclRef(Z3_model_get_func_decl(self.ctx.ref(), self.model, i), self.ctx)) return r + def translate(self, target): + """Translate `self` to the context `target`. That is, return a copy of `self` in the context `target`. + """ + if __debug__: + _z3_assert(isinstance(target, Context), "argument must be a Z3 context") + model = Z3_model_translate(self.ctx.ref(), self.model, target.ref()) + return Model(model, target) + + def __copy__(self): + return self.translate(self.ctx) + + def __deepcopy__(self): + return self.translate(self.ctx) + def is_as_array(n): """Return true if n is a Z3 expression of the form (_ as-array f).""" return isinstance(n, ExprRef) and Z3_is_as_array(n.ctx.ref(), n.as_ast()) @@ -6030,6 +6259,7 @@ class Solver(Z3PPObject): def __init__(self, solver=None, ctx=None): assert solver is None or ctx is not None self.ctx = _get_ctx(ctx) + self.backtrack_level = 4000000000 self.solver = None if solver is None: self.solver = Z3_mk_solver(self.ctx.ref()) @@ -6037,9 +6267,6 @@ class Solver(Z3PPObject): self.solver = solver Z3_solver_inc_ref(self.ctx.ref(), self.solver) - def __deepcopy__(self, memo={}): - return Solver(self.solver, self.ctx) - def __del__(self): if self.solver is not None and self.ctx.ref() is not None: Z3_solver_dec_ref(self.ctx.ref(), self.solver) @@ -6339,10 +6566,37 @@ class Solver(Z3PPObject): except Z3Exception as e: _handle_parse_error(e, self.ctx) + def cube(self, vars = None): + """Get set of cubes""" + self.cube_vs = AstVector(None, self.ctx) + if vars is not None: + for v in vars: + self.cube_vs.push(v) + while True: + lvl = self.backtrack_level + self.backtrack_level = 4000000000 + r = AstVector(Z3_solver_cube(self.ctx.ref(), self.solver, self.cube_vs.vector, lvl), self.ctx) + if (len(r) == 1 and is_false(r[0])): + return + yield r + if (len(r) == 0): + return + + def cube_vars(self): + return self.cube_vs + 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. @@ -6357,6 +6611,11 @@ class Solver(Z3PPObject): """ return AstVector(Z3_solver_get_assertions(self.ctx.ref(), self.solver), self.ctx) + def units(self): + """Return an AST vector containing all currently inferred units. + """ + return AstVector(Z3_solver_get_units(self.ctx.ref(), self.solver), self.ctx) + def statistics(self): """Return statistics for the last `check()`. @@ -6413,6 +6672,12 @@ class Solver(Z3PPObject): solver = Z3_solver_translate(self.ctx.ref(), self.solver, target.ref()) return Solver(solver, target) + def __copy__(self): + return self.translate(self.ctx) + + def __deepcopy__(self): + return self.translate(self.ctx) + def sexpr(self): """Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format. @@ -6758,8 +7023,8 @@ class FiniteDomainSortRef(SortRef): def size(self): """Return the size of the finite domain sort""" - r = (ctype.c_ulonglong * 1)() - if Z3_get_finite_domain_sort_size(self.ctx_ref(), self.ast(), r): + r = (ctypes.c_ulonglong * 1)() + if Z3_get_finite_domain_sort_size(self.ctx_ref(), self.ast, r): return r[0] else: raise Z3Exception("Failed to retrieve finite domain sort size") @@ -7119,36 +7384,6 @@ class ApplyResult(Z3PPObject): """Return a textual representation of the s-expression representing the set of subgoals in `self`.""" return Z3_apply_result_to_string(self.ctx.ref(), self.result) - def convert_model(self, model, idx=0): - """Convert a model for a subgoal into a model for the original goal. - - >>> a, b = Ints('a b') - >>> g = Goal() - >>> g.add(Or(a == 0, a == 1), Or(b == 0, b == 1), a > b) - >>> t = Then(Tactic('split-clause'), Tactic('solve-eqs')) - >>> r = t(g) - >>> r[0] - [Or(b == 0, b == 1), Not(0 <= b)] - >>> r[1] - [Or(b == 0, b == 1), Not(1 <= b)] - >>> # Remark: the subgoal r[0] is unsatisfiable - >>> # Creating a solver for solving the second subgoal - >>> s = Solver() - >>> s.add(r[1]) - >>> s.check() - sat - >>> s.model() - [b = 0] - >>> # Model s.model() does not assign a value to `a` - >>> # It is a model for subgoal `r[1]`, but not for goal `g` - >>> # The method convert_model creates a model for `g` from a model for `r[1]`. - >>> r.convert_model(s.model(), 1) - [b = 0, a = 1] - """ - if __debug__: - _z3_assert(idx < len(self), "index out of bounds") - _z3_assert(isinstance(model, ModelRef), "Z3 Model expected") - return ModelRef(Z3_apply_result_convert_model(self.ctx.ref(), self.result, idx, model.model), self.ctx) def as_expr(self): """Return a Z3 expression consisting of all subgoals. @@ -7387,6 +7622,19 @@ def With(t, *args, **keys): p = args2params(args, keys, t.ctx) return Tactic(Z3_tactic_using_params(t.ctx.ref(), t.tactic, p.params), t.ctx) +def WithParams(t, p): + """Return a tactic that applies tactic `t` using the given configuration options. + + >>> x, y = Ints('x y') + >>> p = ParamsRef() + >>> p.set("som", True) + >>> t = WithParams(Tactic('simplify'), p) + >>> t((x + 1)*(y + 2) == 0) + [[2*x + y + x*y == -2]] + """ + t = _to_tactic(t, None) + return Tactic(Z3_tactic_using_params(t.ctx.ref(), t.tactic, p.params), t.ctx) + def Repeat(t, max=4294967295, ctx=None): """Return a tactic that keeps applying `t` until the goal is not modified anymore or the maximum number of iterations `max` is reached. @@ -7873,8 +8121,10 @@ def AtLeast(*args): return BoolRef(Z3_mk_atleast(ctx.ref(), sz, _args, k), ctx) -def _pb_args_coeffs(args): - args = _get_args(args) +def _pb_args_coeffs(args, default_ctx = None): + args = _get_args_ast_list(args) + if len(args) == 0: + return _get_ctx(default_ctx), 0, (Ast * 0)(), (ctypes.c_int * 0)() args, coeffs = zip(*args) if __debug__: _z3_assert(len(args) > 0, "Non empty list of arguments expected") @@ -7885,6 +8135,7 @@ def _pb_args_coeffs(args): _args, sz = _to_ast_array(args) _coeffs = (ctypes.c_int * len(coeffs))() for i in range(len(coeffs)): + _z3_check_cint_overflow(coeffs[i], "coefficient") _coeffs[i] = coeffs[i] return ctx, sz, _args, _coeffs @@ -7894,6 +8145,7 @@ def PbLe(args, k): >>> a, b, c = Bools('a b c') >>> f = PbLe(((a,1),(b,3),(c,2)), 3) """ + _z3_check_cint_overflow(k, "k") ctx, sz, _args, _coeffs = _pb_args_coeffs(args) return BoolRef(Z3_mk_pble(ctx.ref(), sz, _args, _coeffs, k), ctx) @@ -7903,15 +8155,17 @@ def PbGe(args, k): >>> a, b, c = Bools('a b c') >>> f = PbGe(((a,1),(b,3),(c,2)), 3) """ + _z3_check_cint_overflow(k, "k") ctx, sz, _args, _coeffs = _pb_args_coeffs(args) return BoolRef(Z3_mk_pbge(ctx.ref(), sz, _args, _coeffs, k), ctx) -def PbEq(args, k): +def PbEq(args, k, ctx = None): """Create a Pseudo-Boolean inequality k constraint. >>> a, b, c = Bools('a b c') >>> f = PbEq(((a,1),(b,3),(c,2)), 3) """ + _z3_check_cint_overflow(k, "k") ctx, sz, _args, _coeffs = _pb_args_coeffs(args) return BoolRef(Z3_mk_pbeq(ctx.ref(), sz, _args, _coeffs, k), ctx) @@ -8110,19 +8364,19 @@ def parse_smt2_string(s, sorts={}, decls={}, ctx=None): the symbol table used for the SMT 2.0 parser. >>> parse_smt2_string('(declare-const x Int) (assert (> x 0)) (assert (< x 10))') - And(x > 0, x < 10) + [x > 0, x < 10] >>> x, y = Ints('x y') >>> f = Function('f', IntSort(), IntSort()) >>> parse_smt2_string('(assert (> (+ foo (g bar)) 0))', decls={ 'foo' : x, 'bar' : y, 'g' : f}) - x + f(y) > 0 + [x + f(y) > 0] >>> parse_smt2_string('(declare-const a U) (assert (> a 0))', sorts={ 'U' : IntSort() }) - a > 0 + [a > 0] """ ctx = _get_ctx(ctx) ssz, snames, ssorts = _dict2sarray(sorts, ctx) dsz, dnames, ddecls = _dict2darray(decls, ctx) try: - return _to_expr_ref(Z3_parse_smtlib2_string(ctx.ref(), s, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) + return AstVector(Z3_parse_smtlib2_string(ctx.ref(), s, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) except Z3Exception as e: _handle_parse_error(e, ctx) @@ -8134,151 +8388,11 @@ def parse_smt2_file(f, sorts={}, decls={}, ctx=None): ctx = _get_ctx(ctx) ssz, snames, ssorts = _dict2sarray(sorts, ctx) dsz, dnames, ddecls = _dict2darray(decls, ctx) - try: - return _to_expr_ref(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) + try: + return AstVector(Z3_parse_smtlib2_file(ctx.ref(), f, ssz, snames, ssorts, dsz, dnames, ddecls), ctx) except Z3Exception as e: _handle_parse_error(e, ctx) -def Interpolant(a,ctx=None): - """Create an interpolation operator. - - The argument is an interpolation pattern (see tree_interpolant). - - >>> x = Int('x') - >>> print(Interpolant(x>0)) - interp(x > 0) - """ - ctx = _get_ctx(_ctx_from_ast_arg_list([a], ctx)) - s = BoolSort(ctx) - a = s.cast(a) - return BoolRef(Z3_mk_interpolant(ctx.ref(), a.as_ast()), ctx) - -def tree_interpolant(pat,p=None,ctx=None): - """Compute interpolant for a tree of formulas. - - The input is an interpolation pattern over a set of formulas C. - The pattern pat is a formula combining the formulas in C using - logical conjunction and the "interp" operator (see Interp). This - interp operator is logically the identity operator. It marks the - sub-formulas of the pattern for which interpolants should be - computed. The interpolant is a map sigma from marked subformulas - to formulas, such that, for each marked subformula phi of pat - (where phi sigma is phi with sigma(psi) substituted for each - subformula psi of phi such that psi in dom(sigma)): - - 1) phi sigma implies sigma(phi), and - - 2) sigma(phi) is in the common uninterpreted vocabulary between - the formulas of C occurring in phi and those not occurring in - phi - - and moreover pat sigma implies false. In the simplest case - an interpolant for the pattern "(and (interp A) B)" maps A - to an interpolant for A /\ B. - - The return value is a vector of formulas representing sigma. This - vector contains sigma(phi) for each marked subformula of pat, in - pre-order traversal. This means that subformulas of phi occur before phi - in the vector. Also, subformulas that occur multiply in pat will - occur multiply in the result vector. - - If pat is satisfiable, raises an object of class ModelRef - that represents a model of pat. - - If neither a proof of unsatisfiability nor a model is obtained - (for example, because of a timeout, or because models are disabled) - then None is returned. - - If parameters p are supplied, these are used in creating the - solver that determines satisfiability. - - >>> x = Int('x') - >>> y = Int('y') - >>> print(tree_interpolant(And(Interpolant(x < 0), Interpolant(y > 2), x == y))) - [Not(x >= 0), Not(y <= 2)] - - # >>> g = And(Interpolant(x<0),x<2) - # >>> try: - # ... print tree_interpolant(g).sexpr() - # ... except ModelRef as m: - # ... print m.sexpr() - (define-fun x () Int - (- 1)) - """ - f = pat - ctx = _get_ctx(_ctx_from_ast_arg_list([f], ctx)) - ptr = (AstVectorObj * 1)() - mptr = (Model * 1)() - if p is None: - p = ParamsRef(ctx) - res = Z3_compute_interpolant(ctx.ref(),f.as_ast(),p.params,ptr,mptr) - if res == Z3_L_FALSE: - return AstVector(ptr[0],ctx) - if mptr[0]: - raise ModelRef(mptr[0], ctx) - return None - -def binary_interpolant(a,b,p=None,ctx=None): - """Compute an interpolant for a binary conjunction. - - If a & b is unsatisfiable, returns an interpolant for a & b. - This is a formula phi such that - - 1) a implies phi - 2) b implies not phi - 3) All the uninterpreted symbols of phi occur in both a and b. - - If a & b is satisfiable, raises an object of class ModelRef - that represents a model of a &b. - - If neither a proof of unsatisfiability nor a model is obtained - (for example, because of a timeout, or because models are disabled) - then None is returned. - - If parameters p are supplied, these are used in creating the - solver that determines satisfiability. - - x = Int('x') - print(binary_interpolant(x<0,x>2)) - Not(x >= 0) - """ - f = And(Interpolant(a),b) - ti = tree_interpolant(f,p,ctx) - return ti[0] if ti is not None else None - -def sequence_interpolant(v,p=None,ctx=None): - """Compute interpolant for a sequence of formulas. - - If len(v) == N, and if the conjunction of the formulas in v is - unsatisfiable, the interpolant is a sequence of formulas w - such that len(w) = N-1 and v[0] implies w[0] and for i in 0..N-1: - - 1) w[i] & v[i+1] implies w[i+1] (or false if i+1 = N) - 2) All uninterpreted symbols in w[i] occur in both v[0]..v[i] - and v[i+1]..v[n] - - Requires len(v) >= 1. - - If a & b is satisfiable, raises an object of class ModelRef - that represents a model of a & b. - - If neither a proof of unsatisfiability nor a model is obtained - (for example, because of a timeout, or because models are disabled) - then None is returned. - - If parameters p are supplied, these are used in creating the - solver that determines satisfiability. - - x = Int('x') - y = Int('y') - print(sequence_interpolant([x < 0, y == x , y > 2])) - [Not(x >= 0), Not(y >= 0)] - """ - f = v[0] - for i in range(1,len(v)): - f = And(Interpolant(f),v[i]) - return tree_interpolant(f,p,ctx) - ######################################### # @@ -8744,7 +8858,10 @@ class FPNumRef(FPRef): 1.25 """ def significand_as_long(self): - return Z3_fpa_get_numeral_significand_uint64(self.ctx.ref(), self.as_ast()) + ptr = (ctypes.c_ulonglong * 1)() + if not Z3_fpa_get_numeral_significand_uint64(self.ctx.ref(), self.as_ast(), ptr): + raise Z3Exception("error retrieving the significand of a numeral.") + return ptr[0] """The significand of the numeral as a bit-vector expression. @@ -8801,7 +8918,7 @@ class FPNumRef(FPRef): def isSubnormal(self): return Z3_fpa_is_numeral_subnormal(self.ctx.ref(), self.as_ast()) - """Indicates whether the numeral is postitive.""" + """Indicates whether the numeral is positive.""" def isPositive(self): return Z3_fpa_is_numeral_positive(self.ctx.ref(), self.as_ast()) @@ -9183,7 +9300,7 @@ def fpMul(rm, a, b, ctx=None): return _mk_fp_bin(Z3_mk_fpa_mul, rm, a, b, ctx) def fpDiv(rm, a, b, ctx=None): - """Create a Z3 floating-point divison expression. + """Create a Z3 floating-point division expression. >>> s = FPSort(8, 24) >>> rm = RNE() @@ -9210,7 +9327,7 @@ def fpRem(a, b, ctx=None): return _mk_fp_bin_norm(Z3_mk_fpa_rem, a, b, ctx) def fpMin(a, b, ctx=None): - """Create a Z3 floating-point minimium expression. + """Create a Z3 floating-point minimum expression. >>> s = FPSort(8, 24) >>> rm = RNE() @@ -9597,7 +9714,7 @@ def fpToIEEEBV(x, ctx=None): The size of the resulting bit-vector is automatically determined. Note that IEEE 754-2008 allows multiple different representations of NaN. This conversion - knows only one NaN and it will always produce the same bit-vector represenatation of + knows only one NaN and it will always produce the same bit-vector representation of that NaN. >>> x = FP('x', FPSort(8, 24)) @@ -9743,6 +9860,14 @@ def String(name, ctx=None): ctx = _get_ctx(ctx) return SeqRef(Z3_mk_const(ctx.ref(), to_symbol(name, ctx), StringSort(ctx).ast), ctx) +def SubString(s, offset, length): + """Extract substring or subsequence starting at offset""" + return Extract(s, offset, length) + +def SubSeq(s, offset, length): + """Extract substring or subsequence starting at offset""" + return Extract(s, offset, length) + def Strings(names, ctx=None): """Return a tuple of String constants. """ ctx = _get_ctx(ctx) @@ -9772,13 +9897,13 @@ def Empty(s): raise Z3Exception("Non-sequence, non-regular expression sort passed to Empty") def Full(s): - """Create the regular expression that accepts the universal langauge + """Create the regular expression that accepts the universal language >>> e = Full(ReSort(SeqSort(IntSort()))) >>> print(e) re.all >>> e1 = Full(ReSort(StringSort())) >>> print(e1) - re.allchar + re.all """ if isinstance(s, ReSortRef): return ReRef(Z3_mk_re_full(s.ctx_ref(), s.ast), s.ctx) diff --git a/src/api/python/z3/z3printer.py b/src/api/python/z3/z3printer.py index aef71be2f..8fd0c182e 100644 --- a/src/api/python/z3/z3printer.py +++ b/src/api/python/z3/z3printer.py @@ -36,7 +36,7 @@ _z3_op_to_str = { Z3_OP_CONCAT : 'Concat', Z3_OP_EXTRACT : 'Extract', Z3_OP_BV2INT : 'BV2Int', Z3_OP_ARRAY_MAP : 'Map', Z3_OP_SELECT : 'Select', Z3_OP_STORE : 'Store', Z3_OP_CONST_ARRAY : 'K', Z3_OP_ARRAY_EXT : 'Ext', - Z3_OP_PB_AT_MOST : 'AtMost', Z3_OP_PB_LE : 'PbLe', Z3_OP_PB_GE : 'PbGe' + Z3_OP_PB_AT_MOST : 'AtMost', Z3_OP_PB_LE : 'PbLe', Z3_OP_PB_GE : 'PbGe', Z3_OP_PB_EQ : 'PbEq' } # List of infix operators @@ -485,7 +485,9 @@ class PP: raise StopPPException() def pp(self, f, indent): - if f.is_string(): + if isinstance(f, str): + sef.pp_string(f, indent) + elif f.is_string(): self.pp_string(f, indent) elif f.is_indent(): self.pp(f.child, min(indent + f.indent, self.max_indent)) @@ -846,10 +848,17 @@ class Formatter: else: return seq1('MultiPattern', [ self.pp_expr(arg, d+1, xs) for arg in a.children() ]) + def pp_is(self, a, d, xs): + f = a.params()[0] + return self.pp_fdecl(f, a, d, xs) + def pp_map(self, a, d, xs): + f = z3.get_map_func(a) + return self.pp_fdecl(f, a, d, xs) + + def pp_fdecl(self, f, a, d, xs): r = [] sz = 0 - f = z3.get_map_func(a) r.append(to_format(f.name())) for child in a.children(): r.append(self.pp_expr(child, d+1, xs)) @@ -909,6 +918,8 @@ class Formatter: return self.pp_unary_param(a, d, xs) elif k == Z3_OP_EXTRACT: return self.pp_extract(a, d, xs) + elif k == Z3_OP_DT_IS: + return self.pp_is(a, d, xs) elif k == Z3_OP_ARRAY_MAP: return self.pp_map(a, d, xs) elif k == Z3_OP_CONST_ARRAY: @@ -919,6 +930,8 @@ class Formatter: return self.pp_pbcmp(a, d, f, xs) elif k == Z3_OP_PB_GE: return self.pp_pbcmp(a, d, f, xs) + elif k == Z3_OP_PB_EQ: + return self.pp_pbcmp(a, d, f, xs) elif z3.is_pattern(a): return self.pp_pattern(a, d, xs) elif self.is_infix(k): @@ -963,6 +976,14 @@ class Formatter: else: return to_format(self.pp_unknown()) + def pp_decl(self, f): + k = f.kind() + if k == Z3_OP_DT_IS or k == Z3_OP_ARRAY_MAP: + g = f.params()[0] + r = [ to_format(g.name()) ] + return seq1(self.pp_name(f), r) + return self.pp_name(f) + def pp_seq_core(self, f, a, d, xs): self.visited = self.visited + 1 if d > self.max_depth or self.visited > self.max_visited: @@ -1054,7 +1075,7 @@ class Formatter: elif z3.is_sort(a): return self.pp_sort(a) elif z3.is_func_decl(a): - return self.pp_name(a) + return self.pp_decl(a) elif isinstance(a, z3.Goal) or isinstance(a, z3.AstVector): return self.pp_seq(a, 0, []) elif isinstance(a, z3.Solver): diff --git a/src/api/z3.h b/src/api/z3.h index 99929f33b..382bc4b07 100644 --- a/src/api/z3.h +++ b/src/api/z3.h @@ -21,7 +21,9 @@ Notes: #ifndef Z3_H_ #define Z3_H_ -#include +#include +#include +#include #include "z3_macros.h" #include "z3_api.h" #include "z3_ast_containers.h" @@ -30,7 +32,6 @@ Notes: #include "z3_rcf.h" #include "z3_fixedpoint.h" #include "z3_optimization.h" -#include "z3_interp.h" #include "z3_fpa.h" #include "z3_spacer.h" #endif diff --git a/src/api/z3_algebraic.h b/src/api/z3_algebraic.h index faa41bfea..49c61afef 100644 --- a/src/api/z3_algebraic.h +++ b/src/api/z3_algebraic.h @@ -31,7 +31,7 @@ extern "C" { /** @name Algebraic Numbers */ /*@{*/ /** - \brief Return Z3_TRUE if \c can be used as value in the Z3 real algebraic + \brief Return Z3_TRUE if \c a can be used as value in the Z3 real algebraic number package. def_API('Z3_algebraic_is_value', BOOL, (_in(CONTEXT), _in(AST))) diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 064476b9b..3e47833b4 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -36,14 +36,6 @@ DEFINE_TYPE(Z3_fixedpoint); DEFINE_TYPE(Z3_optimize); DEFINE_TYPE(Z3_rcf_num); -#ifndef __int64 -#define __int64 long long -#endif - -#ifndef __uint64 -#define __uint64 unsigned long long -#endif - /** \defgroup capi C API */ /*@{*/ @@ -80,9 +72,9 @@ DEFINE_TYPE(Z3_rcf_num); */ /** - \brief Z3 Boolean type. It is just an alias for \c int. + \brief Z3 Boolean type. It is just an alias for \c bool. */ -typedef int Z3_bool; +typedef bool Z3_bool; /** \brief Z3 string type. It is just an alias for \ccode{const char *}. @@ -91,14 +83,14 @@ typedef const char * Z3_string; typedef Z3_string * Z3_string_ptr; /** - \brief True value. It is just an alias for \c 1. + \brief True value. It is just an alias for \c true. */ -#define Z3_TRUE 1 +#define Z3_TRUE true /** - \brief False value. It is just an alias for \c 0. + \brief False value. It is just an alias for \c false. */ -#define Z3_FALSE 0 +#define Z3_FALSE false /** \brief Lifted Boolean type: \c false, \c undefined, \c true. @@ -220,8 +212,6 @@ typedef enum - Z3_OP_OEQ Binary equivalence modulo namings. This binary predicate is used in proof terms. It captures equisatisfiability and equivalence modulo renamings. - - Z3_OP_INTERP Marks a sub-formula for interpolation. - - Z3_OP_ANUM Arithmetic numeral. - Z3_OP_AGNUM Arithmetic algebraic numeral. Algebraic numbers are used to represent irrational numbers in Z3. @@ -270,7 +260,7 @@ typedef enum - Z3_OP_ARRAY_MAP Array map operator. It satisfies map[f](a1,..,a_n)[i] = f(a1[i],...,a_n[i]) for every i. - - Z3_OP_SET_UNION Set union between two Booelan arrays (two arrays whose range type is Boolean). The function is binary. + - Z3_OP_SET_UNION Set union between two Boolean arrays (two arrays whose range type is Boolean). The function is binary. - Z3_OP_SET_INTERSECT Set intersection between two Boolean arrays. The function is binary. @@ -406,7 +396,7 @@ typedef enum - Z3_OP_BSMUL_NO_UDFL: check that bit-wise signed multiplication does not underflow. Signed multiplication underflows if the operands have opposite signs and the result of multiplication - does not fit within the avaialble bits. Z3_mk_bvmul_no_underflow. + does not fit within the available bits. Z3_mk_bvmul_no_underflow. - Z3_OP_BSDIV_I: Binary signed division. It has the same semantics as Z3_OP_BSDIV, but created in a context where the second operand can be assumed to be non-zero. @@ -459,7 +449,7 @@ typedef enum [trans T1 T2]: (R t u) } - - Z3_OP_PR_TRANSITIVITY_STAR: Condensed transitivity proof. This proof object is only used if the parameter PROOF_MODE is 1. + - Z3_OP_PR_TRANSITIVITY_STAR: Condensed transitivity proof. It combines several symmetry and transitivity proofs. Example: @@ -485,7 +475,7 @@ typedef enum [monotonicity T1 ... Tn]: (R (f t_1 ... t_n) (f s_1 ... s_n)) } Remark: if t_i == s_i, then the antecedent Ti is suppressed. - That is, reflexivity proofs are supressed to save space. + That is, reflexivity proofs are suppressed to save space. - Z3_OP_PR_QUANT_INTRO: Given a proof for (~ p q), produces a proof for (~ (forall (x) p) (forall (x) q)). @@ -539,21 +529,14 @@ typedef enum } - Z3_OP_PR_REWRITE_STAR: A proof for rewriting an expression t into an expression s. - This proof object is used if the parameter PROOF_MODE is 1. This proof object can have n antecedents. The antecedents are proofs for equalities used as substitution rules. - The object is also used in a few cases if the parameter PROOF_MODE is 2. - The cases are: + The proof rule is used in a few cases. The cases are: - When applying contextual simplification (CONTEXT_SIMPLIFIER=true) - When converting bit-vectors to Booleans (BIT2BOOL=true) - - When pulling ite expression up (PULL_CHEAP_ITE_TREES=true) - Z3_OP_PR_PULL_QUANT: A proof for (iff (f (forall (x) q(x)) r) (forall (x) (f (q x) r))). This proof object has no antecedents. - - Z3_OP_PR_PULL_QUANT_STAR: A proof for (iff P Q) where Q is in prenex normal form. - This proof object is only used if the parameter PROOF_MODE is 1. - This proof object has no antecedents. - - Z3_OP_PR_PUSH_QUANT: A proof for: \nicebox{ @@ -726,15 +709,6 @@ typedef enum [nnf-neg T1 T2 T3 T4]: (~ (not (iff s_1 s_2)) (and (or r_1 r_2) (or r_1' r_2'))) } - - Z3_OP_PR_NNF_STAR: A proof for (~ P Q) where Q is in negation normal form. - - This proof object is only used if the parameter PROOF_MODE is 1. - - This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. - - - Z3_OP_PR_CNF_STAR: A proof for (~ P Q) where Q is in conjunctive normal form. - This proof object is only used if the parameter PROOF_MODE is 1. - This proof object may have n antecedents. Each antecedent is a PR_DEF_INTRO. - Z3_OP_PR_SKOLEMIZE: Proof for: @@ -832,7 +806,7 @@ typedef enum - Z3_OP_RA_FILTER: Filter (restrict) a relation with respect to a predicate. The first argument is a relation. - The second argument is a predicate with free de-Brujin indices + The second argument is a predicate with free de-Bruijn indices corresponding to the columns of the relation. So the first column in the relation has index 0. @@ -876,6 +850,8 @@ typedef enum - Z3_OP_DT_RECOGNISER: datatype recognizer. + - Z3_OP_DT_IS: datatype recognizer. + - Z3_OP_DT_ACCESSOR: datatype accessor. - Z3_OP_DT_UPDATE_FIELD: datatype field update. @@ -969,7 +945,7 @@ typedef enum - Z3_OP_FPA_TO_FP: Floating-point conversion (various) - - Z3_OP_FPA_TO_FP_UNSIGNED: Floating-point conversion from unsigend bit-vector + - Z3_OP_FPA_TO_FP_UNSIGNED: Floating-point conversion from unsigned bit-vector - Z3_OP_FPA_TO_UBV: Floating-point conversion to unsigned bit-vector @@ -984,7 +960,7 @@ typedef enum of non-relevant terms in theory_fpa) - Z3_OP_FPA_BV2RM: Conversion of a 3-bit bit-vector term to a - floating-point rouding-mode term + floating-point rounding-mode term The conversion uses the following values: 0 = 000 = Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN, @@ -1013,7 +989,6 @@ typedef enum { Z3_OP_NOT, Z3_OP_IMPLIES, Z3_OP_OEQ, - Z3_OP_INTERP, // Arithmetic Z3_OP_ANUM = 0x200, @@ -1140,7 +1115,6 @@ typedef enum { Z3_OP_PR_REWRITE, Z3_OP_PR_REWRITE_STAR, Z3_OP_PR_PULL_QUANT, - Z3_OP_PR_PULL_QUANT_STAR, Z3_OP_PR_PUSH_QUANT, Z3_OP_PR_ELIM_UNUSED_VARS, Z3_OP_PR_DER, @@ -1157,8 +1131,6 @@ typedef enum { Z3_OP_PR_IFF_OEQ, Z3_OP_PR_NNF_POS, Z3_OP_PR_NNF_NEG, - Z3_OP_PR_NNF_STAR, - Z3_OP_PR_CNF_STAR, Z3_OP_PR_SKOLEMIZE, Z3_OP_PR_MODUS_PONENS_OEQ, Z3_OP_PR_TH_LEMMA, @@ -1220,6 +1192,7 @@ typedef enum { // Datatypes Z3_OP_DT_CONSTRUCTOR=0x800, Z3_OP_DT_RECOGNISER, + Z3_OP_DT_IS, Z3_OP_DT_ACCESSOR, Z3_OP_DT_UPDATE_FIELD, @@ -1331,7 +1304,7 @@ typedef enum { - Z3_IOB: Index out of bounds. - Z3_INVALID_ARG: Invalid argument was provided. - Z3_PARSER_ERROR: An error occurred when parsing a string or file. - - Z3_NO_PARSER: Parser output is not available, that is, user didn't invoke #Z3_parse_smtlib_string or #Z3_parse_smtlib_file. + - Z3_NO_PARSER: Parser output is not available, that is, user didn't invoke #Z3_parse_smtlib2_string or #Z3_parse_smtlib2_file. - Z3_INVALID_PATTERN: Invalid pattern was used to build a quantifier. - Z3_MEMOUT_FAIL: A memory allocation failure was encountered. - Z3_FILE_ACCESS_ERRROR: A file could not be accessed. @@ -1474,7 +1447,6 @@ extern "C" { /*@{*/ /** - \deprecated \brief Create a configuration object for the Z3 context object. Configurations are created in order to assign parameters prior to creating @@ -1507,7 +1479,6 @@ extern "C" { Z3_config Z3_API Z3_mk_config(void); /** - \deprecated \brief Delete the given configuration object. \sa Z3_mk_config @@ -1517,7 +1488,6 @@ extern "C" { void Z3_API Z3_del_config(Z3_config c); /** - \deprecated \brief Set a configuration parameter. The following parameters can be set for @@ -1534,7 +1504,6 @@ extern "C" { /*@{*/ /** - \deprecated \brief Create a context using the given configuration. After a context is created, the configuration cannot be changed, @@ -1614,7 +1583,6 @@ extern "C" { void Z3_API Z3_dec_ref(Z3_context c, Z3_ast a); /** - \deprecated \brief Set a value of a context parameter. \sa Z3_global_param_set @@ -1864,7 +1832,7 @@ extern "C" { def_API('Z3_mk_finite_domain_sort', SORT, (_in(CONTEXT), _in(SYMBOL), _in(UINT64))) */ - Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, __uint64 size); + Z3_sort Z3_API Z3_mk_finite_domain_sort(Z3_context c, Z3_symbol name, uint64_t size); /** \brief Create an array type. @@ -1922,7 +1890,7 @@ extern "C" { \param c logical context \param name name of the enumeration sort. - \param n number of elemenets in enumeration sort. + \param n number of elements in enumeration sort. \param enum_names names of the enumerated elements. \param enum_consts constants corresponding to the enumerated elements. \param enum_testers predicates testing if terms of the enumeration sort correspond to an enumeration. @@ -2857,9 +2825,8 @@ extern "C" { /** \brief Create an \c n bit bit-vector from the integer argument \c t1. - NB. This function is essentially treated as uninterpreted. - So you cannot expect Z3 to precisely reflect the semantics of this function - when solving constraints with this function. + The resulting bit-vector has \c n bits, where the i'th bit (counting + from 0 to \c n-1) is 1 if \c (t1 div 2^i) mod 2 is 1. The node \c t1 must have integer sort. @@ -2874,9 +2841,6 @@ extern "C" { and in the range \ccode{[0..2^N-1]}, where N are the number of bits in \c t1. If \c is_signed is true, \c t1 is treated as a signed bit-vector. - This function is essentially treated as uninterpreted. - So you cannot expect Z3 to precisely reflect the semantics of this function - when solving constraints with this function. The node \c t1 must have a bit-vector sort. @@ -3186,7 +3150,7 @@ extern "C" { \param c logical context. \param num numerator of rational. - \param den denomerator of rational. + \param den denominator of rational. \pre den != 0 @@ -3201,7 +3165,7 @@ extern "C" { /** \brief Create a numeral of an int, bit-vector, or finite-domain sort. - This function can be use to create numerals that fit in a machine integer. + This function can be used to create numerals that fit in a machine integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral @@ -3213,7 +3177,7 @@ extern "C" { /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. - This function can be use to create numerals that fit in a machine unsinged integer. + This function can be used to create numerals that fit in a machine unsigned integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral @@ -3225,26 +3189,26 @@ extern "C" { /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. - This function can be use to create numerals that fit in a machine __int64 integer. + This function can be used to create numerals that fit in a machine \c int64_t integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_int64', AST, (_in(CONTEXT), _in(INT64), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_int64(Z3_context c, __int64 v, Z3_sort ty); + Z3_ast Z3_API Z3_mk_int64(Z3_context c, int64_t v, Z3_sort ty); /** \brief Create a numeral of a int, bit-vector, or finite-domain sort. - This function can be use to create numerals that fit in a machine __uint64 integer. + This function can be used to create numerals that fit in a machine \c uint64_t integer. It is slightly faster than #Z3_mk_numeral since it is not necessary to parse a string. \sa Z3_mk_numeral def_API('Z3_mk_unsigned_int64', AST, (_in(CONTEXT), _in(UINT64), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, __uint64 v, Z3_sort ty); + Z3_ast Z3_API Z3_mk_unsigned_int64(Z3_context c, uint64_t v, Z3_sort ty); /** \brief create a bit-vector numeral from a vector of Booleans. @@ -3493,8 +3457,8 @@ extern "C" { Z3_ast Z3_API Z3_mk_re_range(Z3_context c, Z3_ast lo, Z3_ast hi); /** - \brief Create a regular expression loop. The supplied regular expression \c r is repated - between \c lo and \c hi times. The \c lo should be below \c hi with one exection: when + \brief Create a regular expression loop. The supplied regular expression \c r is repeated + between \c lo and \c hi times. The \c lo should be below \c hi with one exception: when supplying the value \c hi as 0, the meaning is to repeat the argument \c r at least \c lo number of times, and with an unbounded upper bound. @@ -3893,7 +3857,7 @@ extern "C" { def_API('Z3_get_finite_domain_sort_size', BOOL, (_in(CONTEXT), _in(SORT), _out(UINT64))) */ - Z3_bool_opt Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, __uint64* r); + Z3_bool_opt Z3_API Z3_get_finite_domain_sort_size(Z3_context c, Z3_sort s, uint64_t* r); /** \brief Return the domain of the given array sort. @@ -4248,7 +4212,7 @@ extern "C" { Z3_sort Z3_API Z3_get_decl_sort_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** - \brief Return the expresson value associated with an expression parameter. + \brief Return the expression value associated with an expression parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_AST @@ -4257,7 +4221,7 @@ extern "C" { Z3_ast Z3_API Z3_get_decl_ast_parameter(Z3_context c, Z3_func_decl d, unsigned idx); /** - \brief Return the expresson value associated with an expression parameter. + \brief Return the expression value associated with an expression parameter. \pre Z3_get_decl_parameter_kind(c, d, idx) == Z3_PARAMETER_FUNC_DECL @@ -4327,7 +4291,7 @@ extern "C" { /** \brief Return a hash code for the given AST. - The hash code is structural. You can use Z3_get_ast_id interchangably with + The hash code is structural. You can use Z3_get_ast_id interchangeably with this function. def_API('Z3_get_ast_hash', UINT, (_in(CONTEXT), _in(AST))) @@ -4375,7 +4339,7 @@ extern "C" { Z3_bool Z3_API Z3_is_numeral_ast(Z3_context c, Z3_ast a); /** - \brief Return true if the give AST is a real algebraic number. + \brief Return true if the given AST is a real algebraic number. def_API('Z3_is_algebraic_number', BOOL, (_in(CONTEXT), _in(AST))) */ @@ -4450,7 +4414,7 @@ extern "C" { def_API('Z3_get_numeral_small', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _out(INT64))) */ - Z3_bool Z3_API Z3_get_numeral_small(Z3_context c, Z3_ast a, __int64* num, __int64* den); + Z3_bool Z3_API Z3_get_numeral_small(Z3_context c, Z3_ast a, int64_t* num, int64_t* den); /** \brief Similar to #Z3_get_numeral_string, but only succeeds if @@ -4478,7 +4442,7 @@ extern "C" { /** \brief Similar to #Z3_get_numeral_string, but only succeeds if - the value can fit in a machine __uint64 int. Return Z3_TRUE if the call succeeded. + the value can fit in a machine \c uint64_t int. Return Z3_TRUE if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST @@ -4486,11 +4450,11 @@ extern "C" { def_API('Z3_get_numeral_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) */ - Z3_bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, __uint64* u); + Z3_bool Z3_API Z3_get_numeral_uint64(Z3_context c, Z3_ast v, uint64_t* u); /** \brief Similar to #Z3_get_numeral_string, but only succeeds if - the value can fit in a machine __int64 int. Return Z3_TRUE if the call succeeded. + the value can fit in a machine \c int64_t int. Return Z3_TRUE if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST @@ -4498,11 +4462,11 @@ extern "C" { def_API('Z3_get_numeral_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64))) */ - Z3_bool Z3_API Z3_get_numeral_int64(Z3_context c, Z3_ast v, __int64* i); + Z3_bool Z3_API Z3_get_numeral_int64(Z3_context c, Z3_ast v, int64_t* i); /** \brief Similar to #Z3_get_numeral_string, but only succeeds if - the value can fit as a rational number as machine __int64 int. Return Z3_TRUE if the call succeeded. + the value can fit as a rational number as machine \c int64_t int. Return Z3_TRUE if the call succeeded. \pre Z3_get_ast_kind(c, v) == Z3_NUMERAL_AST @@ -4510,7 +4474,7 @@ extern "C" { def_API('Z3_get_numeral_rational_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _out(INT64))) */ - Z3_bool Z3_API Z3_get_numeral_rational_int64(Z3_context c, Z3_ast v, __int64* num, __int64* den); + Z3_bool Z3_API Z3_get_numeral_rational_int64(Z3_context c, Z3_ast v, int64_t* num, int64_t* den); /** \brief Return a lower bound for the given real algebraic number. @@ -4556,7 +4520,7 @@ extern "C" { Z3_ast Z3_API Z3_get_pattern(Z3_context c, Z3_pattern p, unsigned idx); /** - \brief Return index of de-Brujin bound variable. + \brief Return index of de-Bruijn bound variable. \pre Z3_get_ast_kind(a) == Z3_VAR_AST @@ -4659,7 +4623,7 @@ extern "C" { Provides an interface to the AST simplifier used by Z3. It returns an AST object which is equal to the argument. - The returned AST is simplified using algebraic simplificaiton rules, + The returned AST is simplified using algebraic simplification rules, such as constant propagation (propagating true/false over logical connectives). def_API('Z3_simplify', AST, (_in(CONTEXT), _in(AST))) @@ -4861,9 +4825,9 @@ extern "C" { Z3_func_decl Z3_API Z3_model_get_func_decl(Z3_context c, Z3_model m, unsigned i); /** - \brief Return the number of uninterpreted sorts that \c m assigs an interpretation to. + \brief Return the number of uninterpreted sorts that \c m assigns an interpretation to. - Z3 also provides an intepretation for uninterpreted sorts used in a formua. + Z3 also provides an interpretation for uninterpreted sorts used in a formula. The interpretation for a sort \c s is a finite set of distinct values. We say this finite set is the "universe" of \c s. @@ -4896,6 +4860,13 @@ extern "C" { */ Z3_ast_vector Z3_API Z3_model_get_sort_universe(Z3_context c, Z3_model m, Z3_sort s); + /** + \brief translate model from context c to context \c dst. + + def_API('Z3_model_translate', MODEL, (_in(CONTEXT), _in(MODEL), _in(CONTEXT))) + */ + Z3_model Z3_API Z3_model_translate(Z3_context c, Z3_model m, Z3_context dst); + /** \brief The \ccode{(_ as-array f)} AST node is a construct for assigning interpretations for arrays in Z3. It is the array such that forall indices \c i we have that \ccode{(select (_ as-array f) i)} is equal to \ccode{(f i)}. @@ -4964,7 +4935,7 @@ extern "C" { unsigned Z3_API Z3_func_interp_get_num_entries(Z3_context c, Z3_func_interp f); /** - \brief Return a "point" of the given function intepretation. It represents the + \brief Return a "point" of the given function interpretation. It represents the value of \c f in a particular point. \pre i < Z3_func_interp_get_num_entries(c, f) @@ -5006,7 +4977,7 @@ extern "C" { \brief add a function entry to a function interpretation. \param c logical context - \param fi a function interpregation to be updated. + \param fi a function interpretation to be updated. \param args list of arguments. They should be constant values (such as integers) and be of the same types as the domain of the function. \param value value of the function when the parameters match args. @@ -5201,9 +5172,9 @@ extern "C" { It returns a formula comprising of the conjunction of assertions in the scope (up to push/pop) at the end of the string. - def_API('Z3_parse_smtlib2_string', AST, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) + def_API('Z3_parse_smtlib2_string', AST_VECTOR, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ - Z3_ast Z3_API Z3_parse_smtlib2_string(Z3_context c, + Z3_ast_vector Z3_API Z3_parse_smtlib2_string(Z3_context c, Z3_string str, unsigned num_sorts, Z3_symbol const sort_names[], @@ -5215,9 +5186,9 @@ extern "C" { /** \brief Similar to #Z3_parse_smtlib2_string, but reads the benchmark from a file. - def_API('Z3_parse_smtlib2_file', AST, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) + def_API('Z3_parse_smtlib2_file', AST_VECTOR, (_in(CONTEXT), _in(STRING), _in(UINT), _in_array(2, SYMBOL), _in_array(2, SORT), _in(UINT), _in_array(5, SYMBOL), _in_array(5, FUNC_DECL))) */ - Z3_ast Z3_API Z3_parse_smtlib2_file(Z3_context c, + Z3_ast_vector Z3_API Z3_parse_smtlib2_file(Z3_context c, Z3_string file_name, unsigned num_sorts, Z3_symbol const sort_names[], @@ -5227,6 +5198,17 @@ extern "C" { Z3_func_decl const decls[]); + /** + \brief Parse and evaluate and SMT-LIB2 command sequence. The state from a previous call is saved so the next + evaluation builds on top of the previous call. + + \returns output generated from processing commands. + + def_API('Z3_eval_smtlib2_string', STRING, (_in(CONTEXT), _in(STRING),)) + */ + + Z3_string Z3_API Z3_eval_smtlib2_string(Z3_context, Z3_string str); + /** \brief Retrieve that last error message information generated from parsing. @@ -5459,12 +5441,21 @@ extern "C" { Z3_bool Z3_API Z3_goal_is_decided_unsat(Z3_context c, Z3_goal g); /** - \brief Copy a goal \c g from the context \c source to a the context \c target. + \brief Copy a goal \c g from the context \c source to the context \c target. def_API('Z3_goal_translate', GOAL, (_in(CONTEXT), _in(GOAL), _in(CONTEXT))) */ Z3_goal Z3_API Z3_goal_translate(Z3_context source, Z3_goal g, Z3_context target); + /** + \brief Convert a model of the formulas of a goal to a model of an original goal. + The model may be null, in which case the returned model is valid if the goal was + established satisfiable. + + def_API('Z3_goal_convert_model', MODEL, (_in(CONTEXT), _in(GOAL), _in(MODEL))) + */ + Z3_model Z3_API Z3_goal_convert_model(Z3_context c, Z3_goal g, Z3_model m); + /** \brief Convert a goal into a string. @@ -5472,6 +5463,13 @@ extern "C" { */ Z3_string Z3_API Z3_goal_to_string(Z3_context c, Z3_goal g); + /** + \brief Convert a goal into a DIMACS formatted string. + + def_API('Z3_goal_to_dimacs_string', STRING, (_in(CONTEXT), _in(GOAL))) + */ + Z3_string Z3_API Z3_goal_to_dimacs_string(Z3_context c, Z3_goal g); + /*@}*/ /** @name Tactics and Probes */ @@ -5824,14 +5822,6 @@ extern "C" { */ Z3_goal Z3_API Z3_apply_result_get_subgoal(Z3_context c, Z3_apply_result r, unsigned i); - /** - \brief Convert a model for the subgoal \c Z3_apply_result_get_subgoal(c, r, i) into a model for the original goal \c g. - Where \c g is the goal used to create \c r using \c Z3_tactic_apply(c, t, g). - - def_API('Z3_apply_result_convert_model', MODEL, (_in(CONTEXT), _in(APPLY_RESULT), _in(UINT), _in(MODEL))) - */ - Z3_model Z3_API Z3_apply_result_convert_model(Z3_context c, Z3_apply_result r, unsigned i, Z3_model m); - /*@}*/ /** @name Solvers*/ @@ -5925,12 +5915,19 @@ extern "C" { Z3_solver Z3_API Z3_mk_solver_from_tactic(Z3_context c, Z3_tactic t); /** - \brief Copy a solver \c s from the context \c source to a the context \c target. + \brief Copy a solver \c s from the context \c source to the context \c target. def_API('Z3_solver_translate', SOLVER, (_in(CONTEXT), _in(SOLVER), _in(CONTEXT))) */ Z3_solver Z3_API Z3_solver_translate(Z3_context source, Z3_solver s, Z3_context target); + /** + \brief Ad-hoc method for importing model convertion from solver. + + def_API('Z3_solver_import_model_converter', VOID, (_in(CONTEXT), _in(SOLVER), _in(SOLVER))) + */ + void Z3_API Z3_solver_import_model_converter(Z3_context ctx, Z3_solver src, Z3_solver dst); + /** \brief Return a string describing all solver available parameters. @@ -6031,13 +6028,6 @@ extern "C" { */ void Z3_API Z3_solver_assert_and_track(Z3_context c, Z3_solver s, Z3_ast a, Z3_ast p); - /** - \brief Return the set of asserted formulas on the solver. - - def_API('Z3_solver_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) - */ - Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s); - /** \brief load solver assertions from a file. @@ -6052,6 +6042,20 @@ extern "C" { */ void Z3_API Z3_solver_from_string(Z3_context c, Z3_solver s, Z3_string file_name); + /** + \brief Return the set of asserted formulas on the solver. + + def_API('Z3_solver_get_assertions', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) + */ + Z3_ast_vector Z3_API Z3_solver_get_assertions(Z3_context c, Z3_solver s); + + /** + \brief Return the set of units modulo model conversion. + + def_API('Z3_solver_get_units', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) + */ + Z3_ast_vector Z3_API Z3_solver_get_units(Z3_context c, Z3_solver s); + /** \brief Check whether the assertions in a given solver are consistent or not. @@ -6119,6 +6123,28 @@ extern "C" { Z3_ast_vector assumptions, Z3_ast_vector variables, Z3_ast_vector consequences); + + + /** + \brief extract a next cube for a solver. The last cube is the constant \c true or \c false. + The number of (non-constant) cubes is by default 1. For the sat solver cubing is controlled + using parameters sat.lookahead.cube.cutoff and sat.lookahead.cube.fraction. + + The third argument is a vector of variables that may be used for cubing. + The contents of the vector is only used in the first call. The initial list of variables + is used in subsequent calls until it returns the unsatisfiable cube. + The vector is modified to contain a set of Autarky variables that occor in clauses that + are affected by the (last literal in the) cube. These variables could be used by a different + cuber (on a different solver object) for further recursive cubing. + + The last argument is a backtracking level. It instructs the cube process to backtrack below + the indicated level for the next cube. + + def_API('Z3_solver_cube', AST_VECTOR, (_in(CONTEXT), _in(SOLVER), _in(AST_VECTOR), _in(UINT))) + */ + + Z3_ast_vector Z3_API Z3_solver_cube(Z3_context c, Z3_solver s, Z3_ast_vector vars, unsigned backtrack_level); + /** \brief Retrieve the model for the last #Z3_solver_check or #Z3_solver_check_assumptions @@ -6255,7 +6281,7 @@ extern "C" { def_API('Z3_get_estimated_alloc_size', UINT64, ()) */ - __uint64 Z3_API Z3_get_estimated_alloc_size(void); + uint64_t Z3_API Z3_get_estimated_alloc_size(void); /*@}*/ diff --git a/src/api/z3_fixedpoint.h b/src/api/z3_fixedpoint.h index dc98fdb30..b4c3eee49 100644 --- a/src/api/z3_fixedpoint.h +++ b/src/api/z3_fixedpoint.h @@ -363,10 +363,22 @@ extern "C" { void Z3_API Z3_fixedpoint_set_reduce_assign_callback( Z3_context c ,Z3_fixedpoint d, Z3_fixedpoint_reduce_assign_callback_fptr cb); - /** \brief Register a callback for buildling terms based on the relational operators. */ + /** \brief Register a callback for building terms based on the relational operators. */ void Z3_API Z3_fixedpoint_set_reduce_app_callback( Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr cb); + typedef void (*Z3_fixedpoint_new_lemma_eh)(void *state, Z3_ast lemma, unsigned level); + typedef void (*Z3_fixedpoint_predecessor_eh)(void *state); + typedef void (*Z3_fixedpoint_unfold_eh)(void *state); + + /** \brief set export callback for lemmas */ + void Z3_API Z3_fixedpoint_add_callback(Z3_context ctx, Z3_fixedpoint f, void *state, + Z3_fixedpoint_new_lemma_eh new_lemma_eh, + Z3_fixedpoint_predecessor_eh predecessor_eh, + Z3_fixedpoint_unfold_eh unfold_eh); + + void Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl); + /*@}*/ /*@}*/ diff --git a/src/api/z3_fpa.h b/src/api/z3_fpa.h index e92b728d7..f6001e87d 100644 --- a/src/api/z3_fpa.h +++ b/src/api/z3_fpa.h @@ -349,7 +349,7 @@ extern "C" { def_API('Z3_mk_fpa_numeral_int64_uint64', AST, (_in(CONTEXT), _in(BOOL), _in(INT64), _in(UINT64), _in(SORT))) */ - Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(Z3_context c, Z3_bool sgn, __int64 exp, __uint64 sig, Z3_sort ty); + Z3_ast Z3_API Z3_mk_fpa_numeral_int64_uint64(Z3_context c, Z3_bool sgn, int64_t exp, uint64_t sig, Z3_sort ty); /** \brief Floating-point absolute value @@ -433,7 +433,7 @@ extern "C" { \param c logical context \param rm term of RoundingMode sort \param t1 term of FloatingPoint sort - \param t2 term of FloatingPoint sor + \param t2 term of FloatingPoint sort \param t3 term of FloatingPoint sort The result is round((t1 * t2) + t3) @@ -756,7 +756,7 @@ extern "C" { /** \brief Conversion of a floating-point term into an unsigned bit-vector. - Produces a term that represents the conversion of the floating-poiunt term t into a + Produces a term that represents the conversion of the floating-point term t into a bit-vector term of size sz in unsigned 2's complement format. If necessary, the result will be rounded according to rounding mode rm. @@ -772,7 +772,7 @@ extern "C" { /** \brief Conversion of a floating-point term into a signed bit-vector. - Produces a term that represents the conversion of the floating-poiunt term t into a + Produces a term that represents the conversion of the floating-point term t into a bit-vector term of size sz in signed 2's complement format. If necessary, the result will be rounded according to rounding mode rm. @@ -788,7 +788,7 @@ extern "C" { /** \brief Conversion of a floating-point term into a real-numbered term. - Produces a term that represents the conversion of the floating-poiunt term t into a + Produces a term that represents the conversion of the floating-point term t into a real number. Note that this type of conversion will often result in non-linear constraints over real terms. @@ -956,7 +956,7 @@ extern "C" { def_API('Z3_fpa_get_numeral_significand_uint64', BOOL, (_in(CONTEXT), _in(AST), _out(UINT64))) */ - Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, __uint64 * n); + Z3_bool Z3_API Z3_fpa_get_numeral_significand_uint64(Z3_context c, Z3_ast t, uint64_t * n); /** \brief Return the exponent value of a floating-point numeral as a string. @@ -985,7 +985,7 @@ extern "C" { def_API('Z3_fpa_get_numeral_exponent_int64', BOOL, (_in(CONTEXT), _in(AST), _out(INT64), _in(BOOL))) */ - Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, __int64 * n, Z3_bool biased); + Z3_bool Z3_API Z3_fpa_get_numeral_exponent_int64(Z3_context c, Z3_ast t, int64_t * n, Z3_bool biased); /** \brief Retrieves the exponent of a floating-point literal as a bit-vector expression. @@ -1011,7 +1011,7 @@ extern "C" { determined. Note that IEEE 754-2008 allows multiple different representations of NaN. This conversion - knows only one NaN and it will always produce the same bit-vector represenatation of + knows only one NaN and it will always produce the same bit-vector representation of that NaN. def_API('Z3_mk_fpa_to_ieee_bv', AST, (_in(CONTEXT),_in(AST))) diff --git a/src/api/z3_interp.h b/src/api/z3_interp.h deleted file mode 100644 index bcee0e22d..000000000 --- a/src/api/z3_interp.h +++ /dev/null @@ -1,284 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - z3_interp.h - -Abstract: - - API for interpolation - -Author: - - Kenneth McMillan (kenmcmil) - -Notes: - ---*/ -#ifndef Z3_INTERPOLATION_H_ -#define Z3_INTERPOLATION_H_ - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - - /** \defgroup capi C API */ - /*@{*/ - - /** @name Interpolation facilities */ - /*@{*/ - /** - \brief Create an AST node marking a formula position for interpolation. - - The node \c a must have Boolean sort. - - def_API('Z3_mk_interpolant', AST, (_in(CONTEXT), _in(AST))) - */ - Z3_ast Z3_API Z3_mk_interpolant(Z3_context c, Z3_ast a); - - - /** \brief This function generates a Z3 context suitable for generation of - interpolants. Formulas can be generated as abstract syntax trees in - this context using the Z3 C API. - - Interpolants are also generated as AST's in this context. - - If cfg is non-null, it will be used as the base configuration - for the Z3 context. This makes it possible to set Z3 options - to be used during interpolation. This feature should be used - with some caution however, as it may be that certain Z3 options - are incompatible with interpolation. - - def_API('Z3_mk_interpolation_context', CONTEXT, (_in(CONFIG),)) - - */ - - Z3_context Z3_API Z3_mk_interpolation_context(Z3_config cfg); - - /** Compute an interpolant from a refutation. This takes a proof of - "false" from a set of formulas C, and an interpolation - pattern. The pattern pat is a formula combining the formulas in C - using logical conjunction and the "interp" operator (see - #Z3_mk_interpolant). This interp operator is logically the identity - operator. It marks the sub-formulas of the pattern for which interpolants should - be computed. The interpolant is a map sigma from marked subformulas to - formulas, such that, for each marked subformula phi of pat (where phi sigma - is phi with sigma(psi) substituted for each subformula psi of phi such that - psi in dom(sigma)): - - 1) phi sigma implies sigma(phi), and - - 2) sigma(phi) is in the common uninterpreted vocabulary between - the formulas of C occurring in phi and those not occurring in - phi - - and moreover pat sigma implies false. In the simplest case - an interpolant for the pattern "(and (interp A) B)" maps A - to an interpolant for A /\ B. - - The return value is a vector of formulas representing sigma. The - vector contains sigma(phi) for each marked subformula of pat, in - pre-order traversal. This means that subformulas of phi occur before phi - in the vector. Also, subformulas that occur multiply in pat will - occur multiply in the result vector. - - In particular, calling Z3_get_interpolant on a pattern of the - form (interp ... (interp (and (interp A_1) A_2)) ... A_N) will - result in a sequence interpolant for A_1, A_2,... A_N. - - Neglecting interp markers, the pattern must be a conjunction of - formulas in C, the set of premises of the proof. Otherwise an - error is flagged. - - Any premises of the proof not present in the pattern are - treated as "background theory". Predicate and function symbols - occurring in the background theory are treated as interpreted and - thus always allowed in the interpolant. - - Interpolant may not necessarily be computable from all - proofs. To be sure an interpolant can be computed, the proof - must be generated by an SMT solver for which interpoaltion is - supported, and the premises must be expressed using only - theories and operators for which interpolation is supported. - - Currently, the only SMT solver that is supported is the legacy - SMT solver. Such a solver is available as the default solver in - \c Z3_context objects produced by #Z3_mk_interpolation_context. - Currently, the theories supported are equality with - uninterpreted functions, linear integer arithmetic, and the - theory of arrays (in SMT-LIB terms, this is AUFLIA). - Quantifiers are allowed. Use of any other operators (including - "labels") may result in failure to compute an interpolant from a - proof. - - Parameters: - - \param c logical context. - \param pf a refutation from premises (assertions) C - \param pat an interpolation pattern over C - \param p parameters - - def_API('Z3_get_interpolant', AST_VECTOR, (_in(CONTEXT), _in(AST), _in(AST), _in(PARAMS))) - */ - - Z3_ast_vector Z3_API Z3_get_interpolant(Z3_context c, Z3_ast pf, Z3_ast pat, Z3_params p); - - /* Compute an interpolant for an unsatisfiable conjunction of formulas. - - This takes as an argument an interpolation pattern as in - #Z3_get_interpolant. This is a conjunction, some subformulas of - which are marked with the "interp" operator (see #Z3_mk_interpolant). - - The conjunction is first checked for unsatisfiability. The result - of this check is returned in the out parameter "status". If the result - is unsat, an interpolant is computed from the refutation as in #Z3_get_interpolant - and returned as a vector of formulas. Otherwise the return value is - an empty formula. - - See #Z3_get_interpolant for a discussion of supported theories. - - The advantage of this function over #Z3_get_interpolant is that - it is not necessary to create a suitable SMT solver and generate - a proof. The disadvantage is that it is not possible to use the - solver incrementally. - - Parameters: - - \param c logical context. - \param pat an interpolation pattern - \param p parameters for solver creation - \param status returns the status of the sat check - \param model returns model if satisfiable - - Return value: status of SAT check - - def_API('Z3_compute_interpolant', INT, (_in(CONTEXT), _in(AST), _in(PARAMS), _out(AST_VECTOR), _out(MODEL))) - */ - - Z3_lbool Z3_API Z3_compute_interpolant(Z3_context c, - Z3_ast pat, - Z3_params p, - Z3_ast_vector *interp, - Z3_model *model); - - /** Return a string summarizing cumulative time used for - interpolation. This string is purely for entertainment purposes - and has no semantics. - - \param ctx The context (currently ignored) - - - def_API('Z3_interpolation_profile', STRING, (_in(CONTEXT),)) - */ - - Z3_string Z3_API Z3_interpolation_profile(Z3_context ctx); - - /** - \brief Read an interpolation problem from file. - - \param ctx The Z3 context. This resets the error handler of ctx. - \param filename The file name to read. - \param num Returns length of sequence. - \param cnsts Returns sequence of formulas (do not free) - \param parents Returns the parents vector (or NULL for sequence) - \param error Returns an error message in case of failure (do not free the string) - \param num_theory Number of theory terms - \param theory Theory terms - - Returns true on success. - - File formats: Currently two formats are supported, based on - SMT-LIB2. For sequence interpolants, the sequence of constraints is - represented by the sequence of "assert" commands in the file. - - For tree interpolants, one symbol of type bool is associated to - each vertex of the tree. For each vertex v there is an "assert" - of the form: - - (implies (and c1 ... cn f) v) - - where c1 .. cn are the children of v (which must precede v in the file) - and f is the formula assiciated to node v. The last formula in the - file is the root vertex, and is represented by the predicate "false". - - A solution to a tree interpolation problem can be thought of as a - valuation of the vertices that makes all the implications true - where each value is represented using the common symbols between - the formulas in the subtree and the remainder of the formulas. - - def_API('Z3_read_interpolation_problem', INT, (_in(CONTEXT), _out(UINT), _out_managed_array(1, AST), _out_managed_array(1, UINT), _in(STRING), _out(STRING), _out(UINT), _out_managed_array(6, AST))) - - */ - - int Z3_API Z3_read_interpolation_problem(Z3_context ctx, - unsigned *num, - Z3_ast *cnsts[], - unsigned *parents[], - Z3_string filename, - Z3_string_ptr error, - unsigned *num_theory, - Z3_ast *theory[]); - - - - /** Check the correctness of an interpolant. The Z3 context must - have no constraints asserted when this call is made. That means - that after interpolating, you must first fully pop the Z3 - context before calling this. See Z3_interpolate for meaning of parameters. - - \param ctx The Z3 context. Must be generated by Z3_mk_interpolation_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints (AST's in context ctx) - \param parents The parents vector (or NULL for sequence) - \param interps The interpolant to check - \param error Returns an error message if interpolant incorrect (do not free the string) - \param num_theory Number of theory terms - \param theory Theory terms - - Return value is Z3_L_TRUE if interpolant is verified, Z3_L_FALSE if - incorrect, and Z3_L_UNDEF if unknown. - - def_API('Z3_check_interpolant', INT, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in_array(1, AST), _out(STRING), _in(UINT), _in_array(6, AST))) - */ - - int Z3_API Z3_check_interpolant(Z3_context ctx, - unsigned num, - Z3_ast cnsts[], - unsigned parents[], - Z3_ast *interps, - Z3_string_ptr error, - unsigned num_theory, - Z3_ast theory[]); - - /** Write an interpolation problem to file suitable for reading with - Z3_read_interpolation_problem. The output file is a sequence - of SMT-LIB2 format commands, suitable for reading with command-line Z3 - or other interpolating solvers. - - \param ctx The Z3 context. Must be generated by z3_mk_interpolation_context - \param num The number of constraints in the sequence - \param cnsts Array of constraints - \param parents The parents vector (or NULL for sequence) - \param filename The file name to write - \param num_theory Number of theory terms - \param theory Theory terms - - def_API('Z3_write_interpolation_problem', VOID, (_in(CONTEXT), _in(UINT), _in_array(1, AST), _in_array(1, UINT), _in(STRING), _in(UINT), _in_array(5, AST))) - */ - - void Z3_API Z3_write_interpolation_problem(Z3_context ctx, - unsigned num, - Z3_ast cnsts[], - unsigned parents[], - Z3_string filename, - unsigned num_theory, - Z3_ast theory[]); - /*@}*/ - /*@}*/ - -#ifdef __cplusplus -} -#endif // __cplusplus - -#endif diff --git a/src/api/z3_logger.h b/src/api/z3_logger.h index dd2816bff..357d79bcb 100644 --- a/src/api/z3_logger.h +++ b/src/api/z3_logger.h @@ -23,8 +23,8 @@ static std::ostream & operator<<(std::ostream & out, ll_escaped const & d); static void __declspec(noinline) R() { *g_z3_log << "R\n"; g_z3_log->flush(); } static void __declspec(noinline) P(void * obj) { *g_z3_log << "P " << obj << "\n"; g_z3_log->flush(); } -static void __declspec(noinline) I(__int64 i) { *g_z3_log << "I " << i << "\n"; g_z3_log->flush(); } -static void __declspec(noinline) U(__uint64 u) { *g_z3_log << "U " << u << "\n"; g_z3_log->flush(); } +static void __declspec(noinline) I(int64_t i) { *g_z3_log << "I " << i << "\n"; g_z3_log->flush(); } +static void __declspec(noinline) U(uint64_t u) { *g_z3_log << "U " << u << "\n"; g_z3_log->flush(); } static void __declspec(noinline) D(double d) { *g_z3_log << "D " << d << "\n"; g_z3_log->flush(); } static void __declspec(noinline) S(Z3_string str) { *g_z3_log << "S \"" << ll_escaped(str) << "\"\n"; g_z3_log->flush(); } static void __declspec(noinline) Sy(Z3_symbol sym) { diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index 84dcfc363..0e8297879 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -40,8 +40,8 @@ struct z3_replayer::imp { int m_line; // line svector m_string; symbol m_id; - __int64 m_int64; - __uint64 m_uint64; + int64_t m_int64; + uint64_t m_uint64; double m_double; float m_float; size_t m_ptr; @@ -85,8 +85,8 @@ struct z3_replayer::imp { struct value { value_kind m_kind; union { - __int64 m_int; - __uint64 m_uint; + int64_t m_int; + uint64_t m_uint; double m_double; char const * m_str; void * m_obj; @@ -95,8 +95,8 @@ struct z3_replayer::imp { value():m_kind(OBJECT), m_int(0) {} value(void * obj):m_kind(OBJECT), m_obj(obj) {} value(value_kind k, char const * str):m_kind(k), m_str(str) {} - value(value_kind k, __uint64 u):m_kind(k), m_uint(u) {} - value(value_kind k, __int64 i):m_kind(k), m_int(i) {} + value(value_kind k, uint64_t u):m_kind(k), m_uint(u) {} + value(value_kind k, int64_t i):m_kind(k), m_int(i) {} value(value_kind k, double d):m_kind(k), m_double(d) {} value(value_kind k, float f):m_kind(k), m_float(f) {} }; @@ -342,7 +342,7 @@ struct z3_replayer::imp { unsigned asz = m_args.size(); if (sz > asz) throw z3_replayer_exception("invalid array size"); - __uint64 aidx; + uint64_t aidx; value_kind nk; for (unsigned i = asz - sz; i < asz; i++) { if (m_args[i].m_kind != k) @@ -400,7 +400,7 @@ struct z3_replayer::imp { #define TICK_FREQUENCY 100000 void parse() { - unsigned long long counter = 0; + uint64_t counter = 0; unsigned tick = 0; while (true) { IF_VERBOSE(1, { @@ -430,10 +430,10 @@ struct z3_replayer::imp { next(); skip_blank(); read_ptr(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "P " << m_ptr << "\n";); if (m_ptr == 0) { - m_args.push_back(0); + m_args.push_back(nullptr); } else { - void * obj = 0; + void * obj = nullptr; if (!m_heap.find(m_ptr, obj)) throw z3_replayer_exception("invalid pointer"); m_args.push_back(value(obj)); @@ -453,7 +453,7 @@ struct z3_replayer::imp { // push null symbol next(); TRACE("z3_replayer", tout << "[" << m_line << "] " << "N\n";); - m_args.push_back(value(SYMBOL, static_cast(0))); + m_args.push_back(value(SYMBOL, static_cast(nullptr))); break; case '$': { // push symbol @@ -577,7 +577,7 @@ struct z3_replayer::imp { return static_cast(m_args[pos].m_int); } - __int64 get_int64(unsigned pos) const { + int64_t get_int64(unsigned pos) const { check_arg(pos, INT64); return m_args[pos].m_int; } @@ -587,7 +587,7 @@ struct z3_replayer::imp { return static_cast(m_args[pos].m_uint); } - __uint64 get_uint64(unsigned pos) const { + uint64_t get_uint64(unsigned pos) const { check_arg(pos, UINT64); return m_args[pos].m_uint; } @@ -630,6 +630,12 @@ struct z3_replayer::imp { return m_int_arrays[idx].c_ptr(); } + bool * get_bool_array(unsigned pos) const { + check_arg(pos, UINT_ARRAY); + unsigned idx = static_cast(m_args[pos].m_uint); + return reinterpret_cast(m_unsigned_arrays[idx].c_ptr()); + } + Z3_symbol * get_symbol_array(unsigned pos) const { check_arg(pos, SYMBOL_ARRAY); unsigned idx = static_cast(m_args[pos].m_uint); @@ -650,7 +656,7 @@ struct z3_replayer::imp { return reinterpret_cast(&(m_args[pos].m_int)); } - __int64 * get_int64_addr(unsigned pos) { + int64_t * get_int64_addr(unsigned pos) { check_arg(pos, INT64); return &(m_args[pos].m_int); } @@ -660,7 +666,7 @@ struct z3_replayer::imp { return reinterpret_cast(&(m_args[pos].m_uint)); } - __uint64 * get_uint64_addr(unsigned pos) { + uint64_t * get_uint64_addr(unsigned pos) { check_arg(pos, UINT64); return &(m_args[pos].m_uint); } @@ -689,7 +695,7 @@ struct z3_replayer::imp { } void reset() { - m_result = 0; + m_result = nullptr; m_args.reset(); m_obj_arrays.reset(); m_sym_arrays.reset(); @@ -725,11 +731,11 @@ unsigned z3_replayer::get_uint(unsigned pos) const { return m_imp->get_uint(pos); } -__int64 z3_replayer::get_int64(unsigned pos) const { +int64_t z3_replayer::get_int64(unsigned pos) const { return m_imp->get_int64(pos); } -__uint64 z3_replayer::get_uint64(unsigned pos) const { +uint64_t z3_replayer::get_uint64(unsigned pos) const { return m_imp->get_uint64(pos); } @@ -761,6 +767,10 @@ int * z3_replayer::get_int_array(unsigned pos) const { return m_imp->get_int_array(pos); } +bool * z3_replayer::get_bool_array(unsigned pos) const { + return m_imp->get_bool_array(pos); +} + Z3_symbol * z3_replayer::get_symbol_array(unsigned pos) const { return m_imp->get_symbol_array(pos); } @@ -773,7 +783,7 @@ int * z3_replayer::get_int_addr(unsigned pos) { return m_imp->get_int_addr(pos); } -__int64 * z3_replayer::get_int64_addr(unsigned pos) { +int64_t * z3_replayer::get_int64_addr(unsigned pos) { return m_imp->get_int64_addr(pos); } @@ -781,7 +791,7 @@ unsigned * z3_replayer::get_uint_addr(unsigned pos) { return m_imp->get_uint_addr(pos); } -__uint64 * z3_replayer::get_uint64_addr(unsigned pos) { +uint64_t * z3_replayer::get_uint64_addr(unsigned pos) { return m_imp->get_uint64_addr(pos); } diff --git a/src/api/z3_replayer.h b/src/api/z3_replayer.h index e1b32af75..8e423cc09 100644 --- a/src/api/z3_replayer.h +++ b/src/api/z3_replayer.h @@ -40,8 +40,8 @@ public: int get_int(unsigned pos) const; unsigned get_uint(unsigned pos) const; - __int64 get_int64(unsigned pos) const; - __uint64 get_uint64(unsigned pos) const; + int64_t get_int64(unsigned pos) const; + uint64_t get_uint64(unsigned pos) const; float get_float(unsigned pos) const; double get_double(unsigned pos) const; bool get_bool(unsigned pos) const; @@ -51,13 +51,14 @@ public: unsigned * get_uint_array(unsigned pos) const; int * get_int_array(unsigned pos) const; + bool * get_bool_array(unsigned pos) const; Z3_symbol * get_symbol_array(unsigned pos) const; void ** get_obj_array(unsigned pos) const; int * get_int_addr(unsigned pos); - __int64 * get_int64_addr(unsigned pos); + int64_t * get_int64_addr(unsigned pos); unsigned * get_uint_addr(unsigned pos); - __uint64 * get_uint64_addr(unsigned pos); + uint64_t * get_uint64_addr(unsigned pos); Z3_string * get_str_addr(unsigned pos); void ** get_obj_addr(unsigned pos); diff --git a/src/ast/CMakeLists.txt b/src/ast/CMakeLists.txt index 4dcdd2a35..80543bb05 100644 --- a/src/ast/CMakeLists.txt +++ b/src/ast/CMakeLists.txt @@ -24,7 +24,6 @@ z3_add_component(ast expr_map.cpp expr_stat.cpp expr_substitution.cpp - factor_equivs.cpp for_each_ast.cpp for_each_expr.cpp format.cpp diff --git a/src/ast/act_cache.cpp b/src/ast/act_cache.cpp index 74a7d6fca..14f0985b4 100644 --- a/src/ast/act_cache.cpp +++ b/src/ast/act_cache.cpp @@ -187,8 +187,8 @@ void act_cache::insert(expr * k, expr * v) { */ expr * act_cache::find(expr * k) { map::key_value * entry = m_table.find_core(k); - if (entry == 0) - return 0; + if (entry == nullptr) + return nullptr; if (GET_TAG(entry->m_value) == 0) { entry->m_value = TAG(expr*, entry->m_value, 1); SASSERT(GET_TAG(entry->m_value) == 1); diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index 85c54d1fa..fe5bc3af5 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -65,7 +65,7 @@ struct arith_decl_plugin::algebraic_numbers_wrapper { }; arith_decl_plugin::algebraic_numbers_wrapper & arith_decl_plugin::aw() const { - if (m_aw == 0) + if (m_aw == nullptr) const_cast(this)->m_aw = alloc(algebraic_numbers_wrapper, m_manager->limit()); return *m_aw; } @@ -81,7 +81,7 @@ app * arith_decl_plugin::mk_numeral(algebraic_numbers::anum const & val, bool is return mk_numeral(rval, is_int); } else { - if (is_int) { + if (is_int) { m_manager->raise_exception("invalid irrational value passed as an integer"); } unsigned idx = aw().mk_id(val); @@ -100,7 +100,7 @@ app * arith_decl_plugin::mk_numeral(sexpr const * p, unsigned i) { void arith_decl_plugin::del(parameter const & p) { SASSERT(p.is_external()); - if (m_aw != 0) { + if (m_aw != nullptr) { aw().recycle_id(p.get_ext_id()); } } @@ -222,56 +222,56 @@ void arith_decl_plugin::set_manager(ast_manager * m, family_id id) { } arith_decl_plugin::arith_decl_plugin(): - m_aw(0), + m_aw(nullptr), m_intv_sym("Int"), m_realv_sym("Real"), m_rootv_sym("RootObject"), - m_real_decl(0), - m_int_decl(0), - m_r_le_decl(0), - m_r_ge_decl(0), - m_r_lt_decl(0), - m_r_gt_decl(0), - m_r_add_decl(0), - m_r_sub_decl(0), - m_r_uminus_decl(0), - m_r_mul_decl(0), - m_r_div_decl(0), - m_i_le_decl(0), - m_i_ge_decl(0), - m_i_lt_decl(0), - m_i_gt_decl(0), - m_i_add_decl(0), - m_i_sub_decl(0), - m_i_uminus_decl(0), - m_i_mul_decl(0), - m_i_div_decl(0), - m_i_mod_decl(0), - m_i_rem_decl(0), - m_to_real_decl(0), - m_to_int_decl(0), - m_is_int_decl(0), - m_r_power_decl(0), - m_i_power_decl(0), - m_r_abs_decl(0), - m_i_abs_decl(0), - m_sin_decl(0), - m_cos_decl(0), - m_tan_decl(0), - m_asin_decl(0), - m_acos_decl(0), - m_atan_decl(0), - m_sinh_decl(0), - m_cosh_decl(0), - m_tanh_decl(0), - m_asinh_decl(0), - m_acosh_decl(0), - m_atanh_decl(0), - m_pi(0), - m_e(0), - m_neg_root_decl(0), - m_u_asin_decl(0), - m_u_acos_decl(0), + m_real_decl(nullptr), + m_int_decl(nullptr), + m_r_le_decl(nullptr), + m_r_ge_decl(nullptr), + m_r_lt_decl(nullptr), + m_r_gt_decl(nullptr), + m_r_add_decl(nullptr), + m_r_sub_decl(nullptr), + m_r_uminus_decl(nullptr), + m_r_mul_decl(nullptr), + m_r_div_decl(nullptr), + m_i_le_decl(nullptr), + m_i_ge_decl(nullptr), + m_i_lt_decl(nullptr), + m_i_gt_decl(nullptr), + m_i_add_decl(nullptr), + m_i_sub_decl(nullptr), + m_i_uminus_decl(nullptr), + m_i_mul_decl(nullptr), + m_i_div_decl(nullptr), + m_i_mod_decl(nullptr), + m_i_rem_decl(nullptr), + m_to_real_decl(nullptr), + m_to_int_decl(nullptr), + m_is_int_decl(nullptr), + m_r_power_decl(nullptr), + m_i_power_decl(nullptr), + m_r_abs_decl(nullptr), + m_i_abs_decl(nullptr), + m_sin_decl(nullptr), + m_cos_decl(nullptr), + m_tan_decl(nullptr), + m_asin_decl(nullptr), + m_acos_decl(nullptr), + m_atan_decl(nullptr), + m_sinh_decl(nullptr), + m_cosh_decl(nullptr), + m_tanh_decl(nullptr), + m_asinh_decl(nullptr), + m_acosh_decl(nullptr), + m_atanh_decl(nullptr), + m_pi(nullptr), + m_e(nullptr), + m_neg_root_decl(nullptr), + m_u_asin_decl(nullptr), + m_u_acos_decl(nullptr), m_convert_int_numerals_to_real(false) { } @@ -335,7 +335,7 @@ sort * arith_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete switch (k) { case REAL_SORT: return m_real_decl; case INT_SORT: return m_int_decl; - default: return 0; + default: return nullptr; } } @@ -380,7 +380,7 @@ inline func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, bool is_real) { //case OP_MOD_0: return m_mod_0_decl; case OP_U_ASIN: return m_u_asin_decl; case OP_U_ACOS: return m_u_acos_decl; - default: return 0; + default: return nullptr; } } @@ -408,7 +408,7 @@ app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) { if (u_val < MAX_SMALL_NUM_TO_CACHE) { if (is_int && !m_convert_int_numerals_to_real) { app * r = m_small_ints.get(u_val, 0); - if (r == 0) { + if (r == nullptr) { parameter p[2] = { parameter(val), parameter(1) }; r = m_manager->mk_const(m_manager->mk_const_decl(m_intv_sym, m_int_decl, func_decl_info(m_family_id, OP_NUM, 2, p))); m_manager->inc_ref(r); @@ -418,7 +418,7 @@ app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) { } else { app * r = m_small_reals.get(u_val, 0); - if (r == 0) { + if (r == nullptr) { parameter p[2] = { parameter(val), parameter(0) }; r = m_manager->mk_const(m_manager->mk_const_decl(m_realv_sym, m_real_decl, func_decl_info(m_family_id, OP_NUM, 2, p))); m_manager->inc_ref(r); @@ -440,7 +440,7 @@ app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) { func_decl * arith_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity) { if (!(num_parameters == 2 && arity == 0 && parameters[0].is_rational() && parameters[1].is_int())) { m_manager->raise_exception("invalid numeral declaration"); - return 0; + return nullptr; } if (parameters[1].get_int() != 0) return m_manager->mk_const_decl(m_intv_sym, m_int_decl, func_decl_info(m_family_id, OP_NUM, num_parameters, parameters)); @@ -480,7 +480,7 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters return mk_num_decl(num_parameters, parameters, arity); if (arity == 0 && !is_const_op(k)) { m_manager->raise_exception("no arguments supplied to arithmetical operator"); - return 0; + return nullptr; } if (m_manager->int_real_coercions() && use_coercion(k)) { return mk_func_decl(fix_kind(k, arity), has_real_arg(arity, domain, m_real_decl)); @@ -497,7 +497,7 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters return mk_num_decl(num_parameters, parameters, num_args); if (num_args == 0 && !is_const_op(k)) { m_manager->raise_exception("no arguments supplied to arithmetical operator"); - return 0; + return nullptr; } if (m_manager->int_real_coercions() && use_coercion(k)) { return mk_func_decl(fix_kind(k, num_args), has_real_arg(m_manager, num_args, args, m_real_decl)); @@ -638,10 +638,39 @@ bool arith_recognizers::is_numeral(expr const * n, rational & val, bool & is_int return true; } +#define IS_INT_EXPR_DEPTH_LIMIT 100 +bool arith_recognizers::is_int_expr(expr const *e) const { + if (is_int(e)) return true; + if (is_uninterp(e)) return false; + ptr_buffer todo; + todo.push_back(e); + rational r; + unsigned i = 0; + while (!todo.empty()) { + ++i; + if (i > IS_INT_EXPR_DEPTH_LIMIT) {return false;} + e = todo.back(); + todo.pop_back(); + if (is_to_real(e)) { + // pass + } + else if (is_numeral(e, r) && r.is_int()) { + // pass + } + else if (is_add(e) || is_mul(e)) { + todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + } + else { + return false; + } + } + return true; +} + arith_util::arith_util(ast_manager & m): arith_recognizers(m.mk_family_id("arith")), m_manager(m), - m_plugin(0) { + m_plugin(nullptr) { } void arith_util::init_plugin() { diff --git a/src/ast/arith_decl_plugin.h b/src/ast/arith_decl_plugin.h index 4b24ea5e6..d7340297b 100644 --- a/src/ast/arith_decl_plugin.h +++ b/src/ast/arith_decl_plugin.h @@ -145,7 +145,7 @@ protected: bool m_convert_int_numerals_to_real; func_decl * mk_func_decl(decl_kind k, bool is_real); - virtual void set_manager(ast_manager * m, family_id id); + void set_manager(ast_manager * m, family_id id) override; decl_kind fix_kind(decl_kind k, unsigned arity); void check_arity(unsigned arity, unsigned expected_arity); func_decl * mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity); @@ -153,38 +153,38 @@ protected: public: arith_decl_plugin(); - virtual ~arith_decl_plugin(); - virtual void finalize(); + ~arith_decl_plugin() override; + void finalize() override; algebraic_numbers::manager & am() const; algebraic_numbers_wrapper & aw() const; - virtual void del(parameter const & p); - virtual parameter translate(parameter const & p, decl_plugin & target); + void del(parameter const & p) override; + parameter translate(parameter const & p, decl_plugin & target) override; - virtual decl_plugin * mk_fresh() { + decl_plugin * mk_fresh() override { return alloc(arith_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned num_args, expr * const * args, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned num_args, expr * const * args, sort * range) override; - virtual bool is_value(app * e) const; + bool is_value(app * e) const override; - virtual bool is_unique_value(app * e) const; + bool is_unique_value(app * e) const override; - virtual bool are_equal(app * a, app * b) const; + bool are_equal(app * a, app * b) const override; - virtual bool are_distinct(app * a, app * b) const; + bool are_distinct(app * a, app * b) const override; - virtual void get_op_names(svector & op_names, symbol const & logic); + void get_op_names(svector & op_names, symbol const & logic) override; - virtual void get_sort_names(svector & sort_names, symbol const & logic); + void get_sort_names(svector & sort_names, symbol const & logic) override; app * mk_numeral(rational const & n, bool is_int); @@ -197,9 +197,9 @@ public: app * mk_e() const { return m_e; } - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; - virtual bool is_considered_uninterpreted(func_decl * f) { + bool is_considered_uninterpreted(func_decl * f) override { if (f->get_family_id() != get_family_id()) return false; switch (f->get_decl_kind()) @@ -244,6 +244,8 @@ public: return false; } + bool is_int_expr(expr const * e) const; + bool is_le(expr const * n) const { return is_app_of(n, m_afid, OP_LE); } bool is_ge(expr const * n) const { return is_app_of(n, m_afid, OP_GE); } bool is_lt(expr const * n) const { return is_app_of(n, m_afid, OP_LT); } @@ -370,7 +372,7 @@ public: app * mk_lt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LT, arg1, arg2); } app * mk_gt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GT, arg1, arg2); } - app * mk_add(unsigned num_args, expr * const * args) const { return m_manager.mk_app(m_afid, OP_ADD, num_args, args); } + app * mk_add(unsigned num_args, expr * const * args) const { return num_args == 1 && is_app(args[0]) ? to_app(args[0]) : m_manager.mk_app(m_afid, OP_ADD, num_args, args); } app * mk_add(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2); } app * mk_add(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(m_afid, OP_ADD, arg1, arg2, arg3); } @@ -378,7 +380,7 @@ public: app * mk_sub(unsigned num_args, expr * const * args) const { return m_manager.mk_app(m_afid, OP_SUB, num_args, args); } app * mk_mul(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2); } app * mk_mul(expr * arg1, expr * arg2, expr* arg3) const { return m_manager.mk_app(m_afid, OP_MUL, arg1, arg2, arg3); } - app * mk_mul(unsigned num_args, expr * const * args) const { return m_manager.mk_app(m_afid, OP_MUL, num_args, args); } + app * mk_mul(unsigned num_args, expr * const * args) const { return num_args == 1 && is_app(args[0]) ? to_app(args[0]) : m_manager.mk_app(m_afid, OP_MUL, num_args, args); } app * mk_uminus(expr * arg) const { return m_manager.mk_app(m_afid, OP_UMINUS, arg); } app * mk_div(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_DIV, arg1, arg2); } app * mk_idiv(expr * arg1, expr * arg2) { return m_manager.mk_app(m_afid, OP_IDIV, arg1, arg2); } @@ -533,4 +535,3 @@ inline app_ref operator>(app_ref const& x, app_ref const& y) { } #endif /* ARITH_DECL_PLUGIN_H_ */ - diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp index 1e789b7e5..af451f9c5 100644 --- a/src/ast/array_decl_plugin.cpp +++ b/src/ast/array_decl_plugin.cpp @@ -44,7 +44,7 @@ sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete if (k == _SET_SORT) { if (num_parameters != 1) { m_manager->raise_exception("invalid array sort definition, invalid number of parameters"); - return 0; + return nullptr; } parameter params[2] = { parameters[0], parameter(m_manager->mk_bool_sort()) }; return mk_sort(ARRAY_SORT, 2, params); @@ -52,13 +52,13 @@ sort * array_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete SASSERT(k == ARRAY_SORT); if (num_parameters < 2) { m_manager->raise_exception("invalid array sort definition, invalid number of parameters"); - return 0; + return nullptr; } for (unsigned i = 0; i < num_parameters; i++) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) { m_manager->raise_exception("invalid array sort definition, parameter is not a sort"); - return 0; + return nullptr; } } sort * range = to_sort(parameters[num_parameters - 1].get_ast()); @@ -120,15 +120,15 @@ bool array_decl_plugin::is_array_sort(sort* s) const { func_decl * array_decl_plugin::mk_const(sort * s, unsigned arity, sort * const * domain) { if (arity != 1) { m_manager->raise_exception("invalid const array definition, invalid domain size"); - return 0; + return nullptr; } if (!is_array_sort(s)) { m_manager->raise_exception("invalid const array definition, parameter is not an array sort"); - return 0; + return nullptr; } if (!m_manager->compatible_sorts(get_array_range(s), domain[0])) { m_manager->raise_exception("invalid const array definition, sort mismatch between array range and argument"); - return 0; + return nullptr; } parameter param(s); func_decl_info info(m_family_id, OP_CONST_ARRAY, 1, ¶m); @@ -142,11 +142,11 @@ func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const* buffer << "map expects to take as many arguments as the function being mapped, " << "it was given " << arity << " but expects " << f->get_arity(); m_manager->raise_exception(buffer.str().c_str()); - return 0; + return nullptr; } if (arity == 0) { m_manager->raise_exception("don't use map on constants"); - return 0; + return nullptr; } // // check that each domain[i] is an array sort @@ -159,14 +159,14 @@ func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const* std::ostringstream buffer; buffer << "map expects an array sort as argument at position " << i; m_manager->raise_exception(buffer.str().c_str()); - return 0; + return nullptr; } if (get_array_arity(domain[i]) != dom_arity) { std::ostringstream buffer; buffer << "map expects all arguments to have the same array domain, " << "this is not the case for argument " << i; m_manager->raise_exception(buffer.str().c_str()); - return 0; + return nullptr; } for (unsigned j = 0; j < dom_arity; ++j) { if (get_array_domain(domain[i],j) != get_array_domain(domain[0],j)) { @@ -174,7 +174,7 @@ func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const* buffer << "map expects all arguments to have the same array domain, " << "this is not the case for argument " << i; m_manager->raise_exception(buffer.str().c_str()); - return 0; + return nullptr; } } if (get_array_range(domain[i]) != f->get_domain(i)) { @@ -182,7 +182,7 @@ func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const* buffer << "map expects the argument at position " << i << " to have the array range the same as the function"; m_manager->raise_exception(buffer.str().c_str()); - return 0; + return nullptr; } } vector parameters; @@ -211,19 +211,19 @@ func_decl * array_decl_plugin::mk_map(func_decl* f, unsigned arity, sort* const* func_decl * array_decl_plugin::mk_default(unsigned domain_size, sort * const * domain) { if (domain_size != 1) { m_manager->raise_exception("invalid default array definition, invalid domain size"); - return 0; + return nullptr; } // check that domain[0] is an array sort. unsigned num_parameters = domain[0]->get_num_parameters(); if (num_parameters <= 1) { m_manager->raise_exception("parameter mismatch. There should be more than one parameter to defaults"); - return 0; + return nullptr; } parameter param(domain[0]->get_parameter(num_parameters-1)); if (!param.is_ast() || !is_sort(param.get_ast())) { m_manager->raise_exception("last parameter should be a sort"); - return 0; + return nullptr; } sort * s = to_sort(param.get_ast()); @@ -235,7 +235,7 @@ func_decl * array_decl_plugin::mk_default(unsigned domain_size, sort * const * d func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) { if (arity <= 1) { m_manager->raise_exception("select takes at least two arguments"); - return 0; + return nullptr; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); @@ -245,7 +245,7 @@ func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) { std::stringstream strm; strm << "select requires " << num_parameters << " arguments, but was provided with " << arity << " arguments"; m_manager->raise_exception(strm.str().c_str()); - return 0; + return nullptr; } ptr_buffer new_domain; // we need this because of coercions. new_domain.push_back(s); @@ -255,7 +255,7 @@ func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) { !m_manager->compatible_sorts(domain[i+1], to_sort(parameters[i].get_ast()))) { m_manager->raise_exception("domain sort and parameter do not match"); UNREACHABLE(); - return 0; + return nullptr; } new_domain.push_back(to_sort(parameters[i].get_ast())); } @@ -267,7 +267,7 @@ func_decl* array_decl_plugin::mk_select(unsigned arity, sort * const * domain) { func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) { if (arity < 3) { m_manager->raise_exception("store takes at least 3 arguments"); - return 0; + return nullptr; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); @@ -275,7 +275,7 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) { if (!is_array_sort(s)) { m_manager->raise_exception("store expects the first argument sort to be an array"); UNREACHABLE(); - return 0; + return nullptr; } if (arity != num_parameters+1) { std::ostringstream buffer; @@ -283,19 +283,19 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) { << ", instead it was passed " << (arity - 1) << "arguments"; m_manager->raise_exception(buffer.str().c_str()); UNREACHABLE(); - return 0; + return nullptr; } ptr_buffer new_domain; // we need this because of coercions. new_domain.push_back(s); for (unsigned i = 0; i < num_parameters; ++i) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) { m_manager->raise_exception("expecting sort parameter"); - return 0; + return nullptr; } if (!m_manager->compatible_sorts(to_sort(parameters[i].get_ast()), domain[i+1])) { m_manager->raise_exception("domain sort and parameter do not match"); UNREACHABLE(); - return 0; + return nullptr; } new_domain.push_back(to_sort(parameters[i].get_ast())); } @@ -307,13 +307,13 @@ func_decl * array_decl_plugin::mk_store(unsigned arity, sort * const * domain) { func_decl * array_decl_plugin::mk_array_ext(unsigned arity, sort * const * domain, unsigned i) { if (arity != 2 || domain[0] != domain[1]) { UNREACHABLE(); - return 0; + return nullptr; } sort * s = domain[0]; unsigned num_parameters = s->get_num_parameters(); if (num_parameters == 0 || i >= num_parameters - 1) { UNREACHABLE(); - return 0; + return nullptr; } sort * r = to_sort(s->get_parameter(i).get_ast()); parameter param(i); @@ -362,11 +362,11 @@ func_decl * array_decl_plugin::mk_set_union(unsigned arity, sort * const * domai if (arity == 0) { m_manager->raise_exception("union takes at least one argument"); - return 0; + return nullptr; } sort * s = domain[0]; if (!check_set_arguments(arity, domain)) { - return 0; + return nullptr; } parameter param(s); func_decl_info info(m_family_id, OP_SET_UNION, 1, ¶m); @@ -381,10 +381,10 @@ func_decl * array_decl_plugin::mk_set_intersect(unsigned arity, sort * const * d if (arity == 0) { m_manager->raise_exception("intersection takes at least one argument"); - return 0; + return nullptr; } if (!check_set_arguments(arity, domain)) { - return 0; + return nullptr; } func_decl_info info(m_family_id, OP_SET_INTERSECT); info.set_associative(); @@ -397,10 +397,10 @@ func_decl * array_decl_plugin::mk_set_intersect(unsigned arity, sort * const * d func_decl * array_decl_plugin::mk_set_difference(unsigned arity, sort * const * domain) { if (arity != 2) { m_manager->raise_exception("set difference takes precisely two arguments"); - return 0; + return nullptr; } if (!check_set_arguments(arity, domain)) { - return 0; + return nullptr; } return m_manager->mk_func_decl(m_set_difference_sym, arity, domain, domain[0], func_decl_info(m_family_id, OP_SET_DIFFERENCE)); @@ -409,10 +409,10 @@ func_decl * array_decl_plugin::mk_set_difference(unsigned arity, sort * const * func_decl * array_decl_plugin::mk_set_complement(unsigned arity, sort * const * domain) { if (arity != 1) { m_manager->raise_exception("set complement takes one argument"); - return 0; + return nullptr; } if (!check_set_arguments(arity, domain)) { - return 0; + return nullptr; } return m_manager->mk_func_decl(m_set_complement_sym, arity, domain, domain[0], func_decl_info(m_family_id, OP_SET_COMPLEMENT)); @@ -421,10 +421,10 @@ func_decl * array_decl_plugin::mk_set_complement(unsigned arity, sort * const * func_decl * array_decl_plugin::mk_set_subset(unsigned arity, sort * const * domain) { if (arity != 2) { m_manager->raise_exception("subset takes two arguments"); - return 0; + return nullptr; } if (!check_set_arguments(arity, domain)) { - return 0; + return nullptr; } sort * bool_sort = m_manager->mk_bool_sort(); return m_manager->mk_func_decl(m_set_subset_sym, arity, domain, bool_sort, @@ -456,20 +456,20 @@ func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters sort * s = to_sort(parameters[0].get_ast()); return mk_const(s, arity, domain); } - else if (range != 0) { + else if (range != nullptr) { return mk_const(range, arity, domain); } else { m_manager->raise_exception("array operation requires one sort parameter (the array sort)"); UNREACHABLE(); - return 0; + return nullptr; } } case OP_ARRAY_MAP: { if (num_parameters != 1 || !parameters[0].is_ast() || !is_func_decl(parameters[0].get_ast())) { m_manager->raise_exception("array operation requires one function declaration parameter (the function to be mapped)"); UNREACHABLE(); - return 0; + return nullptr; } func_decl * f = to_func_decl(parameters[0].get_ast()); return mk_map(f, arity, domain); @@ -480,7 +480,7 @@ func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters } if (num_parameters != 1 || !parameters[0].is_int()) { UNREACHABLE(); - return 0; + return nullptr; } return mk_array_ext(arity, domain, parameters[0].get_int()); case OP_ARRAY_DEFAULT: @@ -506,12 +506,12 @@ func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters tout << "as-array-bug: " << to_func_decl(parameters[0].get_ast())->get_name() << " " << to_func_decl(parameters[0].get_ast())->get_arity() << std::endl;); m_manager->raise_exception("as-array takes one parameter, a function declaration with arity greater than zero"); UNREACHABLE(); - return 0; + return nullptr; } func_decl * f = to_func_decl(parameters[0].get_ast()); return mk_as_array(f); } - default: return 0; + default: return nullptr; } } diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h index 911bb3f27..beaccfbd3 100644 --- a/src/ast/array_decl_plugin.h +++ b/src/ast/array_decl_plugin.h @@ -98,9 +98,9 @@ class array_decl_plugin : public decl_plugin { bool is_array_sort(sort* s) const; public: array_decl_plugin(); - virtual ~array_decl_plugin() {} + ~array_decl_plugin() override {} - virtual decl_plugin * mk_fresh() { + decl_plugin * mk_fresh() override { return alloc(array_decl_plugin); } @@ -111,23 +111,23 @@ class array_decl_plugin : public decl_plugin { // parameters[n-1] - nth dimension // parameters[n] - range // - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; // // Contract for func_decl: // parameters[0] - array sort // Contract for others: // no parameters - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; - virtual void get_op_names(svector & op_names, symbol const & logic); + void get_op_names(svector & op_names, symbol const & logic) override; - virtual void get_sort_names(svector & sort_names, symbol const & logic); + void get_sort_names(svector & sort_names, symbol const & logic) override; - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; - virtual bool is_fully_interp(sort * s) const; + bool is_fully_interp(sort * s) const override; }; class array_recognizers { @@ -161,11 +161,11 @@ public: bool is_as_array_tree(expr * n); app * mk_store(unsigned num_args, expr * const * args) { - return m_manager.mk_app(m_fid, OP_STORE, 0, 0, num_args, args); + return m_manager.mk_app(m_fid, OP_STORE, 0, nullptr, num_args, args); } app * mk_select(unsigned num_args, expr * const * args) { - return m_manager.mk_app(m_fid, OP_SELECT, 0, 0, num_args, args); + return m_manager.mk_app(m_fid, OP_SELECT, 0, nullptr, num_args, args); } app * mk_map(func_decl * f, unsigned num_args, expr * const * args) { @@ -191,7 +191,7 @@ public: app * mk_as_array(func_decl * f) { parameter param(f); - return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, ¶m, 0, 0, 0); + return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, ¶m, 0, nullptr, nullptr); } }; diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 3824325b3..681f64a25 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -405,7 +405,7 @@ sort * get_sort(expr const * n) { break; default: UNREACHABLE(); - return 0; + return nullptr; } } } @@ -434,18 +434,18 @@ bool compare_nodes(ast const * n1, ast const * n2) { } switch (n1->get_kind()) { case AST_SORT: - if ((to_sort(n1)->get_info() == 0) != (to_sort(n2)->get_info() == 0)) { + if ((to_sort(n1)->get_info() == nullptr) != (to_sort(n2)->get_info() == nullptr)) { return false; } - if (to_sort(n1)->get_info() != 0 && !(*to_sort(n1)->get_info() == *to_sort(n2)->get_info())) { + if (to_sort(n1)->get_info() != nullptr && !(*to_sort(n1)->get_info() == *to_sort(n2)->get_info())) { return false; } return to_sort(n1)->get_name() == to_sort(n2)->get_name(); case AST_FUNC_DECL: - if ((to_func_decl(n1)->get_info() == 0) != (to_func_decl(n2)->get_info() == 0)) { + if ((to_func_decl(n1)->get_info() == nullptr) != (to_func_decl(n2)->get_info() == nullptr)) { return false; } - if (to_func_decl(n1)->get_info() != 0 && !(*to_func_decl(n1)->get_info() == *to_func_decl(n2)->get_info())) { + if (to_func_decl(n1)->get_info() != nullptr && !(*to_func_decl(n1)->get_info() == *to_func_decl(n2)->get_info())) { return false; } return @@ -549,13 +549,13 @@ unsigned get_node_hash(ast const * n) { switch (n->get_kind()) { case AST_SORT: - if (to_sort(n)->get_info() == 0) + if (to_sort(n)->get_info() == nullptr) return to_sort(n)->get_name().hash(); else return combine_hash(to_sort(n)->get_name().hash(), to_sort(n)->get_info()->hash()); case AST_FUNC_DECL: return ast_array_hash(to_func_decl(n)->get_domain(), to_func_decl(n)->get_arity(), - to_func_decl(n)->get_info() == 0 ? + to_func_decl(n)->get_info() == nullptr ? to_func_decl(n)->get_name().hash() : combine_hash(to_func_decl(n)->get_name().hash(), to_func_decl(n)->get_info()->hash())); case AST_APP: return ast_array_hash(to_app(n)->get_args(), @@ -587,13 +587,13 @@ void ast_table::erase(ast * n) { unsigned idx = h & mask; cell * c = m_table + idx; SASSERT(!c->is_free()); - cell * prev = 0; + cell * prev = nullptr; while (true) { if (c->m_data == n) { m_size--; - if (prev == 0) { + if (prev == nullptr) { cell * next = c->m_next; - if (next == 0) { + if (next == nullptr) { m_used_slots--; c->mark_free(); SASSERT(c->is_free()); @@ -638,49 +638,46 @@ func_decl * decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, para // ----------------------------------- basic_decl_plugin::basic_decl_plugin(): - m_bool_sort(0), - m_true_decl(0), - m_false_decl(0), - m_and_decl(0), - m_or_decl(0), - m_iff_decl(0), - m_xor_decl(0), - m_not_decl(0), - m_interp_decl(0), - m_implies_decl(0), + m_bool_sort(nullptr), + m_true_decl(nullptr), + m_false_decl(nullptr), + m_and_decl(nullptr), + m_or_decl(nullptr), + m_xor_decl(nullptr), + m_not_decl(nullptr), + m_implies_decl(nullptr), - m_proof_sort(0), - m_undef_decl(0), - m_true_pr_decl(0), - m_asserted_decl(0), - m_goal_decl(0), - m_modus_ponens_decl(0), - m_reflexivity_decl(0), - m_symmetry_decl(0), - m_transitivity_decl(0), - m_quant_intro_decl(0), - m_and_elim_decl(0), - m_not_or_elim_decl(0), - m_rewrite_decl(0), - m_pull_quant_decl(0), - m_pull_quant_star_decl(0), - m_push_quant_decl(0), - m_elim_unused_vars_decl(0), - m_der_decl(0), - m_quant_inst_decl(0), + m_proof_sort(nullptr), + m_undef_decl(nullptr), + m_true_pr_decl(nullptr), + m_asserted_decl(nullptr), + m_goal_decl(nullptr), + m_modus_ponens_decl(nullptr), + m_reflexivity_decl(nullptr), + m_symmetry_decl(nullptr), + m_transitivity_decl(nullptr), + m_quant_intro_decl(nullptr), + m_and_elim_decl(nullptr), + m_not_or_elim_decl(nullptr), + m_rewrite_decl(nullptr), + m_pull_quant_decl(nullptr), + m_push_quant_decl(nullptr), + m_elim_unused_vars_decl(nullptr), + m_der_decl(nullptr), + m_quant_inst_decl(nullptr), - m_hypothesis_decl(0), - m_iff_true_decl(0), - m_iff_false_decl(0), - m_commutativity_decl(0), - m_def_axiom_decl(0), - m_lemma_decl(0), + m_hypothesis_decl(nullptr), + m_iff_true_decl(nullptr), + m_iff_false_decl(nullptr), + m_commutativity_decl(nullptr), + m_def_axiom_decl(nullptr), + m_lemma_decl(nullptr), - m_def_intro_decl(0), - m_iff_oeq_decl(0), - m_skolemize_decl(0), - m_mp_oeq_decl(0), - m_hyper_res_decl0(0) { + m_def_intro_decl(nullptr), + m_iff_oeq_decl(nullptr), + m_skolemize_decl(nullptr), + m_mp_oeq_decl(nullptr), + m_hyper_res_decl0(nullptr) { } bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort * const * domain) const { @@ -790,7 +787,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_param } default: UNREACHABLE(); - return 0; + return nullptr; } } @@ -827,7 +824,6 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_paren case PR_REWRITE: return mk_proof_decl("rewrite", k, 0, m_rewrite_decl); case PR_REWRITE_STAR: return mk_proof_decl("rewrite*", k, num_parents, m_rewrite_star_decls); case PR_PULL_QUANT: return mk_proof_decl("pull-quant", k, 0, m_pull_quant_decl); - case PR_PULL_QUANT_STAR: return mk_proof_decl("pull-quant*", k, 0, m_pull_quant_star_decl); case PR_PUSH_QUANT: return mk_proof_decl("push-quant", k, 0, m_push_quant_decl); case PR_ELIM_UNUSED_VARS: return mk_proof_decl("elim-unused", k, 0, m_elim_unused_vars_decl); case PR_DER: return mk_proof_decl("der", k, 0, m_der_decl); @@ -844,15 +840,13 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_paren case PR_IFF_OEQ: return mk_proof_decl("iff~", k, 1, m_iff_oeq_decl); case PR_NNF_POS: return mk_proof_decl("nnf-pos", k, num_parents, m_nnf_pos_decls); case PR_NNF_NEG: return mk_proof_decl("nnf-neg", k, num_parents, m_nnf_neg_decls); - case PR_NNF_STAR: return mk_proof_decl("nnf*", k, num_parents, m_nnf_star_decls); - case PR_CNF_STAR: return mk_proof_decl("cnf*", k, num_parents, m_cnf_star_decls); case PR_SKOLEMIZE: return mk_proof_decl("sk", k, 0, m_skolemize_decl); case PR_MODUS_PONENS_OEQ: return mk_proof_decl("mp~", k, 2, m_mp_oeq_decl); case PR_TH_LEMMA: return mk_proof_decl("th-lemma", k, num_parents, m_th_lemma_decls); case PR_HYPER_RESOLVE: return mk_proof_decl("hyper-res", k, num_parents, m_hyper_res_decl0); default: UNREACHABLE(); - return 0; + return nullptr; } } @@ -866,10 +860,8 @@ void basic_decl_plugin::set_manager(ast_manager * m, family_id id) { m_false_decl = mk_bool_op_decl("false", OP_FALSE); m_and_decl = mk_bool_op_decl("and", OP_AND, 2, true, true, true, true); m_or_decl = mk_bool_op_decl("or", OP_OR, 2, true, true, true, true); - m_iff_decl = mk_bool_op_decl("iff", OP_IFF, 2, false, true, false, false, true); m_xor_decl = mk_bool_op_decl("xor", OP_XOR, 2, true, true); m_not_decl = mk_bool_op_decl("not", OP_NOT, 1); - m_interp_decl = mk_bool_op_decl("interp", OP_INTERP, 1); m_implies_decl = mk_implies_decl(); m_proof_sort = m->mk_sort(symbol("Proof"), sort_info(id, PROOF_SORT)); @@ -894,19 +886,17 @@ void basic_decl_plugin::get_op_names(svector & op_names, symbol co op_names.push_back(builtin_name("or", OP_OR)); op_names.push_back(builtin_name("xor", OP_XOR)); op_names.push_back(builtin_name("not", OP_NOT)); - op_names.push_back(builtin_name("interp", OP_INTERP)); op_names.push_back(builtin_name("=>", OP_IMPLIES)); if (logic == symbol::null) { // user friendly aliases op_names.push_back(builtin_name("implies", OP_IMPLIES)); - op_names.push_back(builtin_name("iff", OP_IFF)); + op_names.push_back(builtin_name("iff", OP_EQ)); op_names.push_back(builtin_name("if_then_else", OP_ITE)); op_names.push_back(builtin_name("if", OP_ITE)); op_names.push_back(builtin_name("&&", OP_AND)); op_names.push_back(builtin_name("||", OP_OR)); op_names.push_back(builtin_name("equals", OP_EQ)); - op_names.push_back(builtin_name("equiv", OP_IFF)); - op_names.push_back(builtin_name("@@", OP_INTERP)); + op_names.push_back(builtin_name("equiv", OP_EQ)); } } @@ -927,8 +917,6 @@ void basic_decl_plugin::finalize() { DEC_REF(m_and_decl); DEC_REF(m_or_decl); DEC_REF(m_not_decl); - DEC_REF(m_interp_decl); - DEC_REF(m_iff_decl); DEC_REF(m_xor_decl); DEC_REF(m_implies_decl); DEC_ARRAY_REF(m_eq_decls); @@ -949,7 +937,6 @@ void basic_decl_plugin::finalize() { DEC_REF(m_not_or_elim_decl); DEC_REF(m_rewrite_decl); DEC_REF(m_pull_quant_decl); - DEC_REF(m_pull_quant_star_decl); DEC_REF(m_push_quant_decl); DEC_REF(m_elim_unused_vars_decl); DEC_REF(m_der_decl); @@ -975,8 +962,6 @@ void basic_decl_plugin::finalize() { DEC_ARRAY_REF(m_apply_def_decls); DEC_ARRAY_REF(m_nnf_pos_decls); DEC_ARRAY_REF(m_nnf_neg_decls); - DEC_ARRAY_REF(m_nnf_star_decls); - DEC_ARRAY_REF(m_cnf_star_decls); DEC_ARRAY_REF(m_th_lemma_decls); DEC_REF(m_hyper_res_decl0); @@ -1063,14 +1048,12 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; - case OP_INTERP: return m_interp_decl; - case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; - case OP_ITE: return arity == 3 ? mk_ite_decl(join(domain[1], domain[2])) : 0; + case OP_ITE: return arity == 3 ? mk_ite_decl(join(domain[1], domain[2])) : nullptr; // eq and oeq must have at least two arguments, they can have more since they are chainable - case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, join(arity, domain), m_eq_decls) : 0; - case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(arity, domain), m_oeq_decls) : 0; + case OP_EQ: return arity >= 2 ? mk_eq_decl_core("=", OP_EQ, join(arity, domain), m_eq_decls) : nullptr; + case OP_OEQ: return arity >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(arity, domain), m_oeq_decls) : nullptr; case OP_DISTINCT: { func_decl_info info(m_family_id, OP_DISTINCT); info.set_pairwise(); @@ -1106,14 +1089,12 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_AND: return m_and_decl; case OP_OR: return m_or_decl; case OP_NOT: return m_not_decl; - case OP_INTERP: return m_interp_decl; - case OP_IFF: return m_iff_decl; case OP_IMPLIES: return m_implies_decl; case OP_XOR: return m_xor_decl; - case OP_ITE: return num_args == 3 ? mk_ite_decl(join(m_manager->get_sort(args[1]), m_manager->get_sort(args[2]))): 0; + case OP_ITE: return num_args == 3 ? mk_ite_decl(join(m_manager->get_sort(args[1]), m_manager->get_sort(args[2]))): nullptr; // eq and oeq must have at least two arguments, they can have more since they are chainable - case OP_EQ: return num_args >= 2 ? mk_eq_decl_core("=", OP_EQ, join(num_args, args), m_eq_decls) : 0; - case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(num_args, args), m_oeq_decls) : 0; + case OP_EQ: return num_args >= 2 ? mk_eq_decl_core("=", OP_EQ, join(num_args, args), m_eq_decls) : nullptr; + case OP_OEQ: return num_args >= 2 ? mk_eq_decl_core("~", OP_OEQ, join(num_args, args), m_oeq_decls) : nullptr; case OP_DISTINCT: return decl_plugin::mk_func_decl(k, num_parameters, parameters, num_args, args, range); default: @@ -1134,7 +1115,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters expr * basic_decl_plugin::get_some_value(sort * s) { if (s == m_bool_sort) return m_manager->mk_false(); - return 0; + return nullptr; } bool basic_recognizers::is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const { @@ -1168,7 +1149,7 @@ void label_decl_plugin::set_manager(ast_manager * m, family_id id) { sort * label_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); - return 0; + return nullptr; } func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, @@ -1176,12 +1157,12 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters if (k == OP_LABEL) { if (arity != 1 || num_parameters < 2 || !parameters[0].is_int() || !parameters[1].is_symbol() || !m_manager->is_bool(domain[0])) { m_manager->raise_exception("invalid label declaration"); - return 0; + return nullptr; } for (unsigned i = 2; i < num_parameters; i++) { if (!parameters[i].is_symbol()) { m_manager->raise_exception("invalid label declaration"); - return 0; + return nullptr; } } return m_manager->mk_func_decl(parameters[0].get_int() ? m_lblpos : m_lblneg, arity, domain, domain[0], @@ -1191,15 +1172,15 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters SASSERT(k == OP_LABEL_LIT); if (arity != 0) { m_manager->raise_exception("invalid label literal declaration"); - return 0; + return nullptr; } for (unsigned i = 0; i < num_parameters; i++) { if (!parameters[i].is_symbol()) { m_manager->raise_exception("invalid label literal declaration"); - return 0; + return nullptr; } } - return m_manager->mk_func_decl(m_lbllit, 0, static_cast(0), m_manager->mk_bool_sort(), + return m_manager->mk_func_decl(m_lbllit, 0, static_cast(nullptr), m_manager->mk_bool_sort(), func_decl_info(m_family_id, OP_LABEL_LIT, num_parameters, parameters)); } } @@ -1212,7 +1193,7 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters sort * pattern_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); - return 0; + return nullptr; } func_decl * pattern_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, @@ -1230,7 +1211,7 @@ func_decl * pattern_decl_plugin::mk_func_decl(decl_kind k, unsigned num_paramete sort * model_value_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { UNREACHABLE(); - return 0; + return nullptr; } func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, @@ -1239,7 +1220,7 @@ func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_para if (arity != 0 || num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_ast() || !is_sort(parameters[1].get_ast())) { UNREACHABLE(); m_manager->raise_exception("invalid model value"); - return 0; + return nullptr; } int idx = parameters[0].get_int(); sort * s = to_sort(parameters[1].get_ast()); @@ -1247,7 +1228,7 @@ func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_para buffer << s->get_name().bare_str() << "!val!" << idx; func_decl_info info(m_family_id, k, num_parameters, parameters); info.m_private_parameters = true; - return m_manager->mk_func_decl(symbol(buffer.c_str()), 0, static_cast(0), s, info); + return m_manager->mk_func_decl(symbol(buffer.c_str()), 0, static_cast(nullptr), s, info); } bool model_value_decl_plugin::is_value(app* n) const { @@ -1274,7 +1255,7 @@ sort * user_sort_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter func_decl * user_sort_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { UNREACHABLE(); - return 0; + return nullptr; } decl_kind user_sort_plugin::register_name(symbol s) { @@ -1307,7 +1288,7 @@ ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_form m_expr_dependency_manager(*this, m_alloc), m_expr_dependency_array_manager(*this, m_alloc), m_proof_mode(m), - m_trace_stream(0), + m_trace_stream(nullptr), m_trace_stream_owner(false), m_rec_fun(":rec-fun") { @@ -1319,7 +1300,7 @@ ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_form if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true); else - m_format_manager = 0; + m_format_manager = nullptr; init(); } @@ -1336,7 +1317,7 @@ ast_manager::ast_manager(proof_gen_mode m, std::fstream * trace_stream, bool is_ if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, trace_stream, true); else - m_format_manager = 0; + m_format_manager = nullptr; init(); } @@ -1355,13 +1336,17 @@ ast_manager::ast_manager(ast_manager const & src, bool disable_proofs): copy_families_plugins(src); } +void ast_manager::update_fresh_id(ast_manager const& m) { + m_fresh_id = std::max(m_fresh_id, m.m_fresh_id); +} + void ast_manager::init() { m_int_real_coercions = true; m_debug_ref_count = false; m_fresh_id = 0; m_expr_id_gen.reset(0); m_decl_id_gen.reset(c_first_decl_id); - m_some_value_proc = 0; + m_some_value_proc = nullptr; m_basic_family_id = mk_family_id("basic"); m_label_family_id = mk_family_id("label"); m_pattern_family_id = mk_family_id("pattern"); @@ -1427,14 +1412,14 @@ ast_manager::~ast_manager() { switch (n->get_kind()) { case AST_SORT: { sort_info* info = to_sort(n)->get_info(); - if (info != 0) { + if (info != nullptr) { mark_array_ref(mark, info->get_num_parameters(), info->get_parameters()); } break; } case AST_FUNC_DECL: { func_decl_info* info = to_func_decl(n)->get_info(); - if (info != 0) { + if (info != nullptr) { mark_array_ref(mark, info->get_num_parameters(), info->get_parameters()); } mark_array_ref(mark, to_func_decl(n)->get_arity(), to_func_decl(n)->get_domain()); @@ -1476,14 +1461,14 @@ ast_manager::~ast_manager() { delete_node(a); } } - if (m_format_manager != 0) + if (m_format_manager != nullptr) dealloc(m_format_manager); if (m_trace_stream_owner) { std::fstream & tmp = * m_trace_stream; tmp << "[eof]\n"; tmp.close(); dealloc(m_trace_stream); - m_trace_stream = 0; + m_trace_stream = nullptr; } } @@ -1492,11 +1477,8 @@ void ast_manager::compact_memory() { unsigned capacity = m_ast_table.capacity(); if (capacity > 4*m_ast_table.size()) { ast_table new_ast_table; - ast_table::iterator it = m_ast_table.begin(); - ast_table::iterator end = m_ast_table.end(); - for (; it != end; ++it) { - new_ast_table.insert(*it); - } + for (ast* curr : m_ast_table) + new_ast_table.insert(curr); m_ast_table.swap(new_ast_table); IF_VERBOSE(10, verbose_stream() << "(ast-table :prev-capacity " << capacity << " :capacity " << m_ast_table.capacity() << " :size " << m_ast_table.size() << ")\n";); @@ -1510,21 +1492,16 @@ void ast_manager::compress_ids() { ptr_vector asts; m_expr_id_gen.cleanup(); m_decl_id_gen.cleanup(c_first_decl_id); - ast_table::iterator it = m_ast_table.begin(); - ast_table::iterator end = m_ast_table.end(); - for (; it != end; ++it) { - ast * n = *it; + for (ast * n : m_ast_table) { if (is_decl(n)) n->m_id = m_decl_id_gen.mk(); else n->m_id = m_expr_id_gen.mk(); asts.push_back(n); - } + } m_ast_table.finalize(); - ptr_vector::iterator it2 = asts.begin(); - ptr_vector::iterator end2 = asts.end(); - for (; it2 != end2; ++it2) - m_ast_table.insert(*it2); + for (ast* a : asts) + m_ast_table.insert(a); } void ast_manager::raise_exception(char const * msg) { @@ -1540,50 +1517,52 @@ void ast_manager::copy_families_plugins(ast_manager const & from) { tout << "fid: " << fid << " fidname: " << get_family_name(fid) << "\n"; }); ast_translation trans(const_cast(from), *this, false); + // Inheriting plugins can create new family ids. Since new family ids are + // assigned in the order that they are created, this can result in differing + // family ids. To avoid this, we first assign all family ids and only then inherit plugins. for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) { - SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid)); - SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid)); - symbol fid_name = from.get_family_name(fid); - TRACE("copy_families_plugins", tout << "copying: " << fid_name << ", src fid: " << fid - << ", target has_family: " << m_family_manager.has_family(fid) << "\n"; - if (m_family_manager.has_family(fid)) tout << get_family_id(fid_name) << "\n";); - if (!m_family_manager.has_family(fid)) { - family_id new_fid = mk_family_id(fid_name); - (void)new_fid; - TRACE("copy_families_plugins", tout << "new target fid created: " << new_fid << " fid_name: " << fid_name << "\n";); - } - TRACE("copy_families_plugins", tout << "target fid: " << get_family_id(fid_name) << "\n";); - SASSERT(fid == get_family_id(fid_name)); - if (from.has_plugin(fid) && !has_plugin(fid)) { - decl_plugin * new_p = from.get_plugin(fid)->mk_fresh(); - register_plugin(fid, new_p); - SASSERT(new_p->get_family_id() == fid); - SASSERT(has_plugin(fid)); - } - if (from.has_plugin(fid)) { - get_plugin(fid)->inherit(from.get_plugin(fid), trans); - } - SASSERT(from.m_family_manager.has_family(fid) == m_family_manager.has_family(fid)); - SASSERT(from.get_family_id(fid_name) == get_family_id(fid_name)); - SASSERT(!from.has_plugin(fid) || has_plugin(fid)); + symbol fid_name = from.get_family_name(fid); + if (!m_family_manager.has_family(fid)) { + family_id new_fid = mk_family_id(fid_name); + (void)new_fid; + TRACE("copy_families_plugins", tout << "new target fid created: " << new_fid << " fid_name: " << fid_name << "\n";); + } + } + for (family_id fid = 0; from.m_family_manager.has_family(fid); fid++) { + SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid)); + SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid)); + symbol fid_name = from.get_family_name(fid); + (void)fid_name; + TRACE("copy_families_plugins", tout << "copying: " << fid_name << ", src fid: " << fid + << ", target has_family: " << m_family_manager.has_family(fid) << "\n"; + if (m_family_manager.has_family(fid)) tout << get_family_id(fid_name) << "\n";); + TRACE("copy_families_plugins", tout << "target fid: " << get_family_id(fid_name) << "\n";); + SASSERT(fid == get_family_id(fid_name)); + if (from.has_plugin(fid) && !has_plugin(fid)) { + decl_plugin * new_p = from.get_plugin(fid)->mk_fresh(); + register_plugin(fid, new_p); + SASSERT(new_p->get_family_id() == fid); + SASSERT(has_plugin(fid)); + } + if (from.has_plugin(fid)) { + get_plugin(fid)->inherit(from.get_plugin(fid), trans); + } + SASSERT(from.m_family_manager.has_family(fid) == m_family_manager.has_family(fid)); + SASSERT(from.get_family_id(fid_name) == get_family_id(fid_name)); + SASSERT(!from.has_plugin(fid) || has_plugin(fid)); } } void ast_manager::set_next_expr_id(unsigned id) { - while (true) { - id = m_expr_id_gen.set_next_id(id); - ast_table::iterator it = m_ast_table.begin(); - ast_table::iterator end = m_ast_table.end(); - for (; it != end; ++it) { - ast * curr = *it; - if (curr->get_id() == id) - break; + try_again: + id = m_expr_id_gen.set_next_id(id); + for (ast * curr : m_ast_table) { + if (curr->get_id() == id) { + // id is in use, move to the next one. + ++id; + goto try_again; } - if (it == end) - return; - // id is in use, move to the next one. - id++; - } + } } unsigned ast_manager::get_node_size(ast const * n) { return ::get_node_size(n); } @@ -1600,7 +1579,7 @@ decl_plugin * ast_manager::get_plugin(family_id fid) const { bool ast_manager::is_value(expr* e) const { - decl_plugin const * p = 0; + decl_plugin const * p = nullptr; if (is_app(e)) { p = get_plugin(to_app(e)->get_family_id()); return p && p->is_value(to_app(e)); @@ -1609,7 +1588,7 @@ bool ast_manager::is_value(expr* e) const { } bool ast_manager::is_unique_value(expr* e) const { - decl_plugin const * p = 0; + decl_plugin const * p = nullptr; if (is_app(e)) { p = get_plugin(to_app(e)->get_family_id()); return p && p->is_unique_value(to_app(e)); @@ -1711,13 +1690,13 @@ ast * ast_manager::register_node_core(ast * n) { // increment reference counters switch (n->get_kind()) { case AST_SORT: - if (to_sort(n)->m_info != 0) { + if (to_sort(n)->m_info != nullptr) { to_sort(n)->m_info = alloc(sort_info, *(to_sort(n)->get_info())); to_sort(n)->m_info->init_eh(*this); } break; case AST_FUNC_DECL: - if (to_func_decl(n)->m_info != 0) { + if (to_func_decl(n)->m_info != nullptr) { to_func_decl(n)->m_info = alloc(func_decl_info, *(to_func_decl(n)->get_info())); to_func_decl(n)->m_info->init_eh(*this); } @@ -1820,14 +1799,14 @@ void ast_manager::delete_node(ast * n) { #endif switch (n->get_kind()) { case AST_SORT: - if (to_sort(n)->m_info != 0 && !m_debug_ref_count) { + if (to_sort(n)->m_info != nullptr && !m_debug_ref_count) { sort_info * info = to_sort(n)->get_info(); info->del_eh(*this); dealloc(info); } break; case AST_FUNC_DECL: - if (to_func_decl(n)->m_info != 0 && !m_debug_ref_count) { + if (to_func_decl(n)->m_info != nullptr && !m_debug_ref_count) { func_decl_info * info = to_func_decl(n)->get_info(); info->del_eh(*this); dealloc(info); @@ -1862,7 +1841,7 @@ sort * ast_manager::mk_sort(family_id fid, decl_kind k, unsigned num_parameters, decl_plugin * p = get_plugin(fid); if (p) return p->mk_sort(k, num_parameters, parameters); - return 0; + return nullptr; } func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, @@ -1870,7 +1849,7 @@ func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_p decl_plugin * p = get_plugin(fid); if (p) return p->mk_func_decl(k, num_parameters, parameters, arity, domain, range); - return 0; + return nullptr; } func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, @@ -1878,33 +1857,33 @@ func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_p decl_plugin * p = get_plugin(fid); if (p) return p->mk_func_decl(k, num_parameters, parameters, num_args, args, range); - return 0; + return nullptr; } app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { func_decl * decl = mk_func_decl(fid, k, num_parameters, parameters, num_args, args, range); - if (decl != 0) + if (decl != nullptr) return mk_app(decl, num_args, args); - return 0; + return nullptr; } app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args) { - return mk_app(fid, k, 0, 0, num_args, args); + return mk_app(fid, k, 0, nullptr, num_args, args); } app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg) { - return mk_app(fid, k, 0, 0, 1, &arg); + return mk_app(fid, k, 0, nullptr, 1, &arg); } app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; - return mk_app(fid, k, 0, 0, 2, args); + return mk_app(fid, k, 0, nullptr, 2, args); } app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3) { expr * args[3] = { arg1, arg2, arg3 }; - return mk_app(fid, k, 0, 0, 3, args); + return mk_app(fid, k, 0, nullptr, 3, args); } sort * ast_manager::mk_sort(symbol const & name, sort_info * info) { @@ -2075,8 +2054,8 @@ bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * co } app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const * args) { - app * r = 0; - app * new_node = 0; + app * r = nullptr; + app * new_node = nullptr; unsigned sz = app::get_obj_size(num_args); void * mem = allocate_node(sz); @@ -2184,7 +2163,7 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar << ") passed to function " << mk_pp(decl, *this); throw ast_exception(buffer.str().c_str()); } - app * r = 0; + app * r = nullptr; if (num_args == 1 && decl->is_chainable() && decl->get_arity() == 2) { r = mk_true(); } @@ -2213,7 +2192,7 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar r = mk_and(new_args.size(), new_args.c_ptr()); } } - if (r == 0) { + if (r == nullptr) { r = mk_app_core(decl, num_args, args); } SASSERT(r != 0); @@ -2234,9 +2213,10 @@ func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const } else { string_buffer<64> buffer; - buffer << prefix; if (prefix == symbol::null) buffer << "sk"; + else + buffer << prefix; buffer << "!"; if (suffix != symbol::null) buffer << suffix << "!"; @@ -2267,7 +2247,12 @@ var * ast_manager::mk_var(unsigned idx, sort * s) { unsigned sz = var::get_obj_size(); void * mem = allocate_node(sz); var * new_node = new (mem) var(idx, s); - return register_node(new_node); + var * r = register_node(new_node); + + if (m_trace_stream && r == new_node) { + *m_trace_stream << "[mk-var] #" << r->get_id() << "\n"; + } + return r; } app * ast_manager::mk_label(bool pos, unsigned num_names, symbol const * names, expr * n) { @@ -2300,7 +2285,7 @@ app * ast_manager::mk_label_lit(unsigned num_names, symbol const * names) { buffer p; for (unsigned i = 0; i < num_names; i++) p.push_back(parameter(names[i])); - return mk_app(m_label_family_id, OP_LABEL_LIT, p.size(), p.c_ptr(), 0, 0); + return mk_app(m_label_family_id, OP_LABEL_LIT, p.size(), p.c_ptr(), 0, nullptr); } app * ast_manager::mk_label_lit(symbol const & name) { @@ -2322,7 +2307,7 @@ app * ast_manager::mk_pattern(unsigned num_exprs, app * const * exprs) { for (unsigned i = 0; i < num_exprs; ++i) { SASSERT(is_app(exprs[i])); }}); - return mk_app(m_pattern_family_id, OP_PATTERN, 0, 0, num_exprs, (expr*const*)exprs); + return mk_app(m_pattern_family_id, OP_PATTERN, 0, nullptr, num_exprs, (expr*const*)exprs); } bool ast_manager::is_pattern(expr const * n) const { @@ -2419,7 +2404,7 @@ quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_pattern num_patterns, patterns, num_patterns == 0 ? q->get_num_no_patterns() : 0, - num_patterns == 0 ? q->get_no_patterns() : 0); + num_patterns == 0 ? q->get_no_patterns() : nullptr); } quantifier * ast_manager::update_quantifier(quantifier * q, unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns, expr * body) { @@ -2505,7 +2490,7 @@ quantifier * ast_manager::update_quantifier(quantifier * q, bool is_forall, unsi num_patterns, patterns, num_patterns == 0 ? q->get_num_no_patterns() : 0, - num_patterns == 0 ? q->get_no_patterns() : 0); + num_patterns == 0 ? q->get_no_patterns() : nullptr); } app * ast_manager::mk_distinct(unsigned num_args, expr * const * args) { @@ -2537,14 +2522,14 @@ app * ast_manager::mk_distinct_expanded(unsigned num_args, expr * const * args) // ----------------------------------- expr_dependency * ast_manager::mk_leaf(expr * t) { - if (t == 0) - return 0; + if (t == nullptr) + return nullptr; else return m_expr_dependency_manager.mk_leaf(t); } expr_dependency * ast_manager::mk_join(unsigned n, expr * const * ts) { - expr_dependency * d = 0; + expr_dependency * d = nullptr; for (unsigned i = 0; i < n; i++) d = mk_join(d, mk_leaf(ts[i])); return d; @@ -2563,7 +2548,7 @@ void ast_manager::linearize(expr_dependency * d, ptr_vector & ts) { app * ast_manager::mk_model_value(unsigned idx, sort * s) { parameter p[2] = { parameter(idx), parameter(s) }; - return mk_app(m_model_value_family_id, OP_MODEL_VALUE, 2, p, 0, static_cast(0)); + return mk_app(m_model_value_family_id, OP_MODEL_VALUE, 2, p, 0, static_cast(nullptr)); } expr * ast_manager::get_some_value(sort * s, some_value_proc * p) { @@ -2572,17 +2557,17 @@ expr * ast_manager::get_some_value(sort * s, some_value_proc * p) { } expr * ast_manager::get_some_value(sort * s) { - expr * v = 0; + expr * v = nullptr; if (m_some_value_proc) v = (*m_some_value_proc)(s); - if (v != 0) + if (v != nullptr) return v; family_id fid = s->get_family_id(); if (fid != null_family_id) { decl_plugin * p = get_plugin(fid); - if (p != 0) { + if (p != nullptr) { v = p->get_some_value(s); - if (v != 0) + if (v != nullptr) return v; } } @@ -2595,7 +2580,7 @@ bool ast_manager::is_fully_interp(sort * s) const { family_id fid = s->get_family_id(); SASSERT(fid != null_family_id); decl_plugin * p = get_plugin(fid); - if (p != 0) + if (p != nullptr) return p->is_fully_interp(s); return false; } @@ -2646,10 +2631,10 @@ proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { if (!p1 || !p2) return nullptr; SASSERT(has_fact(p1)); SASSERT(has_fact(p2)); - CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))), + CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_eq(get_fact(p2)) || is_oeq(get_fact(p2))), tout << mk_ll_pp(p1, *this) << "\n"; tout << mk_ll_pp(p2, *this) << "\n";); - SASSERT(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))); + SASSERT(is_implies(get_fact(p2)) || is_eq(get_fact(p2)) || is_oeq(get_fact(p2))); CTRACE("mk_modus_ponens", to_app(get_fact(p2))->get_arg(0) != get_fact(p1), tout << mk_pp(get_fact(p1), *this) << "\n" << mk_pp(get_fact(p2), *this) << "\n";); SASSERT(to_app(get_fact(p2))->get_arg(0) == get_fact(p1)); @@ -2727,8 +2712,6 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n"; tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";); SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() || - ((is_iff(get_fact(p1)) || is_eq(get_fact(p1))) && - (is_iff(get_fact(p2)) || is_eq(get_fact(p2)))) || ( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) && (is_eq(get_fact(p2)) || is_oeq(get_fact(p2))))); CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), @@ -2793,21 +2776,21 @@ proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proo SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); sort * d[2] = { s, s }; - return mk_monotonicity(mk_func_decl(m_basic_family_id, get_eq_op(f1), 0, 0, 2, d), f1, f2, num_proofs, proofs); + return mk_monotonicity(mk_func_decl(m_basic_family_id, get_eq_op(f1), 0, nullptr, 2, d), f1, f2, num_proofs, proofs); } proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); sort * d[2] = { s, s }; - return mk_monotonicity(mk_func_decl(m_basic_family_id, OP_OEQ, 0, 0, 2, d), f1, f2, num_proofs, proofs); + return mk_monotonicity(mk_func_decl(m_basic_family_id, OP_OEQ, 0, nullptr, 2, d), f1, f2, num_proofs, proofs); } proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) { if (!p) return nullptr; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); - SASSERT(is_iff(get_fact(p))); + SASSERT(is_eq(get_fact(p))); return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_iff(q1, q2)); } @@ -2850,12 +2833,6 @@ proof * ast_manager::mk_pull_quant(expr * e, quantifier * q) { return mk_app(m_basic_family_id, PR_PULL_QUANT, mk_iff(e, q)); } -proof * ast_manager::mk_pull_quant_star(expr * e, quantifier * q) { - if (proofs_disabled()) - return nullptr; - return mk_app(m_basic_family_id, PR_PULL_QUANT_STAR, mk_iff(e, q)); -} - proof * ast_manager::mk_push_quant(quantifier * q, expr * e) { if (proofs_disabled()) return nullptr; @@ -2900,8 +2877,7 @@ bool ast_manager::is_quant_inst(expr const* e, expr*& not_q_or_i, ptr_vectorget_arg(0), r1, r2) || - is_iff(to_app(e)->get_arg(0), r1, r2)); + VERIFY (is_eq(to_app(e)->get_arg(0), r1, r2)); return true; } else { @@ -2929,7 +2905,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro fact = mk_false(); } else { - CTRACE("mk_unit_resolution_bug", !is_or(f1), tout << mk_pp(f1, *this) << " " << mk_pp(f2, *this) << "\n";); + CTRACE("mk_unit_resolution_bug", !is_or(f1), tout << mk_ll_pp(f1, *this) << "\n" << mk_ll_pp(f2, *this) << "\n";); SASSERT(is_or(f1)); ptr_buffer new_lits; app const * cls = to_app(f1); @@ -3060,7 +3036,7 @@ proof * ast_manager::mk_iff_oeq(proof * p) { if (!p) return p; SASSERT(has_fact(p)); - SASSERT(is_iff(get_fact(p)) || is_oeq(get_fact(p))); + SASSERT(is_eq(get_fact(p)) || is_oeq(get_fact(p))); if (is_oeq(get_fact(p))) return p; @@ -3100,15 +3076,6 @@ proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * return mk_app(m_basic_family_id, PR_NNF_NEG, args.size(), args.c_ptr()); } -proof * ast_manager::mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (proofs_disabled()) - return nullptr; - ptr_buffer args; - args.append(num_proofs, (expr**) proofs); - args.push_back(mk_oeq(s, t)); - return mk_app(m_basic_family_id, PR_NNF_STAR, args.size(), args.c_ptr()); -} - proof * ast_manager::mk_skolemization(expr * q, expr * e) { if (proofs_disabled()) return nullptr; @@ -3117,15 +3084,6 @@ proof * ast_manager::mk_skolemization(expr * q, expr * e) { return mk_app(m_basic_family_id, PR_SKOLEMIZE, mk_oeq(q, e)); } -proof * ast_manager::mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (proofs_disabled()) - return nullptr; - ptr_buffer args; - args.append(num_proofs, (expr**) proofs); - args.push_back(mk_oeq(s, t)); - return mk_app(m_basic_family_id, PR_CNF_STAR, args.size(), args.c_ptr()); -} - proof * ast_manager::mk_and_elim(proof * p, unsigned i) { if (proofs_disabled()) return nullptr; diff --git a/src/ast/ast.h b/src/ast/ast.h index 2d06d03de..b6b71c6a3 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -53,6 +53,12 @@ Revision History: #pragma warning(disable : 4355) #endif +#ifdef _MSC_VER +# define Z3_NORETURN __declspec(noreturn) +#else +# define Z3_NORETURN [[noreturn]] +#endif + class ast; class ast_manager; @@ -126,7 +132,7 @@ public: case PARAM_INT: m_int = other.get_int(); break; case PARAM_AST: m_ast = other.get_ast(); break; case PARAM_SYMBOL: m_symbol = other.m_symbol; break; - case PARAM_RATIONAL: m_rational = 0; std::swap(m_rational, other.m_rational); break; + case PARAM_RATIONAL: m_rational = nullptr; std::swap(m_rational, other.m_rational); break; case PARAM_DOUBLE: m_dval = other.m_dval; break; case PARAM_EXTERNAL: m_ext_id = other.m_ext_id; break; default: @@ -252,7 +258,7 @@ class decl_info { public: bool m_private_parameters; decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, - unsigned num_parameters = 0, parameter const * parameters = 0, bool private_params = false); + unsigned num_parameters = 0, parameter const * parameters = nullptr, bool private_params = false); decl_info(decl_info const& other); ~decl_info() {} @@ -292,11 +298,11 @@ class sort_size { SS_FINITE_VERY_BIG, SS_INFINITE } m_kind; - uint64 m_size; // It is only meaningful if m_kind == SS_FINITE - sort_size(kind_t k, uint64 r):m_kind(k), m_size(r) {} + uint64_t m_size; // It is only meaningful if m_kind == SS_FINITE + sort_size(kind_t k, uint64_t r):m_kind(k), m_size(r) {} public: sort_size():m_kind(SS_INFINITE) {} - sort_size(uint64 const & sz):m_kind(SS_FINITE), m_size(sz) {} + sort_size(uint64_t const & sz):m_kind(SS_FINITE), m_size(sz) {} sort_size(sort_size const& other): m_kind(other.m_kind), m_size(other.m_size) {} explicit sort_size(rational const& r) { if (r.is_uint64()) { @@ -310,7 +316,7 @@ public: } static sort_size mk_infinite() { return sort_size(SS_INFINITE, 0); } static sort_size mk_very_big() { return sort_size(SS_FINITE_VERY_BIG, 0); } - static sort_size mk_finite(uint64 r) { return sort_size(SS_FINITE, r); } + static sort_size mk_finite(uint64_t r) { return sort_size(SS_FINITE, r); } bool is_infinite() const { return m_kind == SS_INFINITE; } bool is_very_big() const { return m_kind == SS_FINITE_VERY_BIG; } @@ -318,7 +324,7 @@ public: static bool is_very_big_base2(unsigned power) { return power >= 64; } - uint64 size() const { SASSERT(is_finite()); return m_size; } + uint64_t size() const { SASSERT(is_finite()); return m_size; } }; std::ostream& operator<<(std::ostream& out, sort_size const & ss); @@ -330,23 +336,23 @@ std::ostream& operator<<(std::ostream& out, sort_size const & ss); // ----------------------------------- /** - \brief Extra information that may be attached to intepreted sorts. + \brief Extra information that may be attached to interpreted sorts. */ class sort_info : public decl_info { sort_size m_num_elements; public: sort_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, - unsigned num_parameters = 0, parameter const * parameters = 0, bool private_parameters = false): + unsigned num_parameters = 0, parameter const * parameters = nullptr, bool private_parameters = false): decl_info(family_id, k, num_parameters, parameters, private_parameters) { } - sort_info(family_id family_id, decl_kind k, uint64 num_elements, - unsigned num_parameters = 0, parameter const * parameters = 0, bool private_parameters = false): + sort_info(family_id family_id, decl_kind k, uint64_t num_elements, + unsigned num_parameters = 0, parameter const * parameters = nullptr, bool private_parameters = false): decl_info(family_id, k, num_parameters, parameters, private_parameters), m_num_elements(num_elements) { } sort_info(family_id family_id, decl_kind k, sort_size const& num_elements, - unsigned num_parameters = 0, parameter const * parameters = 0, bool private_parameters = false): + unsigned num_parameters = 0, parameter const * parameters = nullptr, bool private_parameters = false): decl_info(family_id, k, num_parameters, parameters, private_parameters), m_num_elements(num_elements) { } sort_info(sort_info const& other) : decl_info(other), m_num_elements(other.m_num_elements) { @@ -384,7 +390,7 @@ struct func_decl_info : public decl_info { bool m_idempotent:1; bool m_skolem:1; - func_decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = 0); + func_decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = nullptr); ~func_decl_info() {} bool is_associative() const { return m_left_assoc && m_right_assoc; } @@ -559,12 +565,12 @@ public: unsigned get_decl_id() const { SASSERT(get_id() >= c_first_decl_id); return get_id() - c_first_decl_id; } symbol const & get_name() const { return m_name; } decl_info * get_info() const { return m_info; } - family_id get_family_id() const { return m_info == 0 ? null_family_id : m_info->get_family_id(); } - decl_kind get_decl_kind() const { return m_info == 0 ? null_decl_kind : m_info->get_decl_kind(); } - unsigned get_num_parameters() const { return m_info == 0 ? 0 : m_info->get_num_parameters(); } + family_id get_family_id() const { return m_info == nullptr ? null_family_id : m_info->get_family_id(); } + decl_kind get_decl_kind() const { return m_info == nullptr ? null_decl_kind : m_info->get_decl_kind(); } + unsigned get_num_parameters() const { return m_info == nullptr ? 0 : m_info->get_num_parameters(); } parameter const & get_parameter(unsigned idx) const { return m_info->get_parameter(idx); } - parameter const * get_parameters() const { return m_info == 0 ? 0 : m_info->get_parameters(); } - bool private_parameters() const { return m_info != 0 && m_info->private_parameters(); } + parameter const * get_parameters() const { return m_info == nullptr ? nullptr : m_info->get_parameters(); } + bool private_parameters() const { return m_info != nullptr && m_info->private_parameters(); } }; // ----------------------------------- @@ -581,8 +587,8 @@ class sort : public decl { sort(symbol const & name, sort_info * info):decl(AST_SORT, name, info) {} public: sort_info * get_info() const { return static_cast(m_info); } - bool is_infinite() const { return get_info() == 0 || get_info()->is_infinite(); } - bool is_very_big() const { return get_info() == 0 || get_info()->is_very_big(); } + bool is_infinite() const { return get_info() == nullptr || get_info()->is_infinite(); } + bool is_very_big() const { return get_info() == nullptr || get_info()->is_very_big(); } bool is_sort_of(family_id fid, decl_kind k) const { return get_family_id() == fid && get_decl_kind() == k; } sort_size const & get_num_elements() const { return get_info()->get_num_elements(); } void set_num_elements(sort_size const& s) { get_info()->set_num_elements(s); } @@ -607,21 +613,24 @@ class func_decl : public decl { func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info); public: func_decl_info * get_info() const { return static_cast(m_info); } - bool is_associative() const { return get_info() != 0 && get_info()->is_associative(); } - bool is_left_associative() const { return get_info() != 0 && get_info()->is_left_associative(); } - bool is_right_associative() const { return get_info() != 0 && get_info()->is_right_associative(); } - bool is_flat_associative() const { return get_info() != 0 && get_info()->is_flat_associative(); } - bool is_commutative() const { return get_info() != 0 && get_info()->is_commutative(); } - bool is_chainable() const { return get_info() != 0 && get_info()->is_chainable(); } - bool is_pairwise() const { return get_info() != 0 && get_info()->is_pairwise(); } - bool is_injective() const { return get_info() != 0 && get_info()->is_injective(); } - bool is_skolem() const { return get_info() != 0 && get_info()->is_skolem(); } - bool is_idempotent() const { return get_info() != 0 && get_info()->is_idempotent(); } + bool is_associative() const { return get_info() != nullptr && get_info()->is_associative(); } + bool is_left_associative() const { return get_info() != nullptr && get_info()->is_left_associative(); } + bool is_right_associative() const { return get_info() != nullptr && get_info()->is_right_associative(); } + bool is_flat_associative() const { return get_info() != nullptr && get_info()->is_flat_associative(); } + bool is_commutative() const { return get_info() != nullptr && get_info()->is_commutative(); } + bool is_chainable() const { return get_info() != nullptr && get_info()->is_chainable(); } + bool is_pairwise() const { return get_info() != nullptr && get_info()->is_pairwise(); } + bool is_injective() const { return get_info() != nullptr && get_info()->is_injective(); } + bool is_skolem() const { return get_info() != nullptr && get_info()->is_skolem(); } + bool is_idempotent() const { return get_info() != nullptr && get_info()->is_idempotent(); } unsigned get_arity() const { return m_arity; } sort * get_domain(unsigned idx) const { SASSERT(idx < get_arity()); return m_domain[idx]; } sort * const * get_domain() const { return m_domain; } sort * get_range() const { return m_range; } unsigned get_size() const { return get_obj_size(m_arity); } + sort * const * begin() const { return get_domain(); } + sort * const * end() const { return get_domain() + get_arity(); } + }; // ----------------------------------- @@ -932,7 +941,7 @@ struct builtin_name { }; /** - \brief Each family of intepreted function declarations and sorts must provide a plugin + \brief Each family of interpreted function declarations and sorts must provide a plugin to build sorts and decls of the family. */ class decl_plugin { @@ -951,7 +960,7 @@ protected: friend class ast_manager; public: - decl_plugin():m_manager(0), m_family_id(null_family_id) {} + decl_plugin():m_manager(nullptr), m_family_id(null_family_id) {} virtual ~decl_plugin() {} virtual void finalize() {} @@ -1007,7 +1016,7 @@ public: virtual void get_sort_names(svector & sort_names, symbol const & logic = symbol()) {} - virtual expr * get_some_value(sort * s) { return 0; } + virtual expr * get_some_value(sort * s) { return nullptr; } // Return true if the interpreted sort s does not depend on uninterpreted sorts. // This may be the case, for example, for array and datatype sorts. @@ -1032,15 +1041,15 @@ enum basic_sort_kind { }; enum basic_op_kind { - OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, OP_INTERP, LAST_BASIC_OP, + OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP, PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO, PR_DISTRIBUTIVITY, PR_AND_ELIM, PR_NOT_OR_ELIM, PR_REWRITE, PR_REWRITE_STAR, PR_PULL_QUANT, - PR_PULL_QUANT_STAR, PR_PUSH_QUANT, PR_ELIM_UNUSED_VARS, PR_DER, PR_QUANT_INST, + PR_PUSH_QUANT, PR_ELIM_UNUSED_VARS, PR_DER, PR_QUANT_INST, PR_HYPOTHESIS, PR_LEMMA, PR_UNIT_RESOLUTION, PR_IFF_TRUE, PR_IFF_FALSE, PR_COMMUTATIVITY, PR_DEF_AXIOM, - PR_DEF_INTRO, PR_APPLY_DEF, PR_IFF_OEQ, PR_NNF_POS, PR_NNF_NEG, PR_NNF_STAR, PR_SKOLEMIZE, PR_CNF_STAR, + PR_DEF_INTRO, PR_APPLY_DEF, PR_IFF_OEQ, PR_NNF_POS, PR_NNF_NEG, PR_SKOLEMIZE, PR_MODUS_PONENS_OEQ, PR_TH_LEMMA, PR_HYPER_RESOLVE, LAST_BASIC_PR }; @@ -1051,15 +1060,13 @@ protected: func_decl * m_false_decl; func_decl * m_and_decl; func_decl * m_or_decl; - func_decl * m_iff_decl; func_decl * m_xor_decl; func_decl * m_not_decl; - func_decl * m_interp_decl; func_decl * m_implies_decl; ptr_vector m_eq_decls; // cached eqs ptr_vector m_ite_decls; // cached ites - ptr_vector m_oeq_decls; // cached obsevational eqs + ptr_vector m_oeq_decls; // cached observational eqs sort * m_proof_sort; func_decl * m_undef_decl; func_decl * m_true_pr_decl; @@ -1074,7 +1081,6 @@ protected: func_decl * m_not_or_elim_decl; func_decl * m_rewrite_decl; func_decl * m_pull_quant_decl; - func_decl * m_pull_quant_star_decl; func_decl * m_push_quant_decl; func_decl * m_elim_unused_vars_decl; func_decl * m_der_decl; @@ -1100,8 +1106,6 @@ protected: ptr_vector m_apply_def_decls; ptr_vector m_nnf_pos_decls; ptr_vector m_nnf_neg_decls; - ptr_vector m_nnf_star_decls; - ptr_vector m_cnf_star_decls; ptr_vector m_th_lemma_decls; func_decl * m_hyper_res_decl0; @@ -1123,7 +1127,7 @@ protected: unsigned num_parameters, parameter const* params, unsigned num_parents); - virtual void set_manager(ast_manager * m, family_id id); + void set_manager(ast_manager * m, family_id id) override; func_decl * mk_eq_decl_core(char const * name, decl_kind k, sort * s, ptr_vector & cache); func_decl * mk_ite_decl(sort * s); sort* join(sort* s1, sort* s2); @@ -1132,36 +1136,36 @@ protected: public: basic_decl_plugin(); - virtual ~basic_decl_plugin() {} - virtual void finalize(); + ~basic_decl_plugin() override {} + void finalize() override; - virtual decl_plugin * mk_fresh() { + decl_plugin * mk_fresh() override { return alloc(basic_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) override; - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned num_args, expr * const * args, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned num_args, expr * const * args, sort * range) override; - virtual void get_op_names(svector & op_names, symbol const & logic); + void get_op_names(svector & op_names, symbol const & logic) override; - virtual void get_sort_names(svector & sort_names, symbol const & logic); + void get_sort_names(svector & sort_names, symbol const & logic) override; - virtual bool is_value(app* a) const; + bool is_value(app* a) const override; - virtual bool is_unique_value(app* a) const; + bool is_unique_value(app* a) const override; sort * mk_bool_sort() const { return m_bool_sort; } sort * mk_proof_sort() const { return m_proof_sort; } - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; }; -typedef app proof; /* a proof is just an applicaton */ +typedef app proof; /* a proof is just an application */ // ----------------------------------- // @@ -1182,15 +1186,15 @@ class label_decl_plugin : public decl_plugin { symbol m_lblneg; symbol m_lbllit; - virtual void set_manager(ast_manager * m, family_id id); + void set_manager(ast_manager * m, family_id id) override; public: label_decl_plugin(); - virtual ~label_decl_plugin(); + ~label_decl_plugin() override; - virtual decl_plugin * mk_fresh() { return alloc(label_decl_plugin); } + decl_plugin * mk_fresh() override { return alloc(label_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; /** contract: when label @@ -1204,8 +1208,8 @@ public: ... parameter[n-1] (symbol): label's tag. */ - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; }; // ----------------------------------- @@ -1220,16 +1224,16 @@ enum pattern_op_kind { /** \brief Patterns are used to group expressions. These expressions are using during E-matching for - heurisitic quantifier instantiation. + heuristic quantifier instantiation. */ class pattern_decl_plugin : public decl_plugin { public: - virtual decl_plugin * mk_fresh() { return alloc(pattern_decl_plugin); } + decl_plugin * mk_fresh() override { return alloc(pattern_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; }; // ----------------------------------- @@ -1245,33 +1249,33 @@ enum model_value_op_kind { /** \brief Values are used during model construction. All values are assumed to be different. Users should not use them, since they may - introduce unsoundess if the sort of a value is finite. + introduce unsoundness if the sort of a value is finite. Moreover, values should never be internalized in a logical context. However, values can be used during evaluation (i.e., simplification). - \remark Model values can be viewed as the partion ids in Z3 1.x. + \remark Model values can be viewed as the partition ids in Z3 1.x. */ class model_value_decl_plugin : public decl_plugin { public: model_value_decl_plugin() {} - virtual decl_plugin * mk_fresh() { return alloc(model_value_decl_plugin); } + decl_plugin * mk_fresh() override { return alloc(model_value_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; /** contract: parameter[0]: (integer) value idx parameter[1]: (ast) sort of the value. */ - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; - virtual bool is_value(app* n) const; + bool is_value(app* n) const override; - virtual bool is_unique_value(app* a) const; + bool is_unique_value(app* a) const override; }; // ----------------------------------- @@ -1286,11 +1290,11 @@ class user_sort_plugin : public decl_plugin { public: user_sort_plugin() {} - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; decl_kind register_name(symbol s); - virtual decl_plugin * mk_fresh(); + decl_plugin * mk_fresh() override; }; @@ -1339,9 +1343,9 @@ public: bool is_and(expr const * n) const { return is_app_of(n, m_fid, OP_AND); } bool is_not(expr const * n) const { return is_app_of(n, m_fid, OP_NOT); } bool is_eq(expr const * n) const { return is_app_of(n, m_fid, OP_EQ); } + bool is_iff(expr const* n) const { return is_eq(n) && is_bool(to_app(n)->get_arg(0)); } bool is_oeq(expr const * n) const { return is_app_of(n, m_fid, OP_OEQ); } bool is_distinct(expr const * n) const { return is_app_of(n, m_fid, OP_DISTINCT); } - bool is_iff(expr const * n) const { return is_app_of(n, m_fid, OP_IFF); } bool is_xor(expr const * n) const { return is_app_of(n, m_fid, OP_XOR); } bool is_ite(expr const * n) const { return is_app_of(n, m_fid, OP_ITE); } bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); } @@ -1356,7 +1360,6 @@ public: bool is_and(func_decl const * d) const { return is_decl_of(d, m_fid, OP_AND); } bool is_not(func_decl const * d) const { return is_decl_of(d, m_fid, OP_NOT); } bool is_eq(func_decl const * d) const { return is_decl_of(d, m_fid, OP_EQ); } - bool is_iff(func_decl const * d) const { return is_decl_of(d, m_fid, OP_IFF); } bool is_xor(func_decl const * d) const { return is_decl_of(d, m_fid, OP_XOR); } bool is_ite(func_decl const * d) const { return is_decl_of(d, m_fid, OP_ITE); } bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); } @@ -1364,13 +1367,13 @@ public: MATCH_UNARY(is_not); MATCH_BINARY(is_eq); - MATCH_BINARY(is_iff); MATCH_BINARY(is_implies); MATCH_BINARY(is_and); MATCH_BINARY(is_or); MATCH_BINARY(is_xor); MATCH_TERNARY(is_and); MATCH_TERNARY(is_or); + bool is_iff(expr const* n, expr*& lhs, expr*& rhs) const { return is_eq(n, lhs, rhs) && is_bool(lhs); } bool is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const; }; @@ -1448,6 +1451,8 @@ public: void show_id_gen(); + void update_fresh_id(ast_manager const& other); + protected: reslimit m_limit; small_object_allocator m_alloc; @@ -1491,14 +1496,14 @@ protected: public: - ast_manager(proof_gen_mode = PGM_DISABLED, char const * trace_file = 0, bool is_format_manager = false); + ast_manager(proof_gen_mode = PGM_DISABLED, char const * trace_file = nullptr, bool is_format_manager = false); ast_manager(proof_gen_mode, std::fstream * trace_stream, bool is_format_manager = false); ast_manager(ast_manager const & src, bool disable_proofs = false); ~ast_manager(); // propagate cancellation signal to decl_plugins - bool has_trace_stream() const { return m_trace_stream != 0; } + bool has_trace_stream() const { return m_trace_stream != nullptr; } std::ostream & trace_stream() { SASSERT(has_trace_stream()); return *m_trace_stream; } void enable_int_real_coercions(bool f) { m_int_real_coercions = f; } @@ -1515,9 +1520,9 @@ public: void compress_ids(); // Equivalent to throw ast_exception(msg) - void raise_exception(char const * msg); + Z3_NORETURN void raise_exception(char const * msg); - bool is_format_manager() const { return m_format_manager == 0; } + bool is_format_manager() const { return m_format_manager == nullptr; } ast_manager & get_format_manager() { return is_format_manager() ? *this : *m_format_manager; } @@ -1552,7 +1557,7 @@ public: decl_plugin * get_plugin(family_id fid) const; - bool has_plugin(family_id fid) const { return get_plugin(fid) != 0; } + bool has_plugin(family_id fid) const { return get_plugin(fid) != nullptr; } bool has_plugin(symbol const & s) const { return m_family_manager.has_family(s) && has_plugin(m_family_manager.get_family_id(s)); } @@ -1656,7 +1661,7 @@ public: bool is_bool(expr const * n) const; bool is_bool(sort const * s) const { return s == m_bool_sort; } - decl_kind get_eq_op(expr const * n) const { return is_bool(n) ? OP_IFF : OP_EQ; } + decl_kind get_eq_op(expr const * n) const { return OP_EQ; } private: sort * mk_sort(symbol const & name, sort_info * info); @@ -1664,7 +1669,7 @@ private: public: sort * mk_uninterpreted_sort(symbol const & name, unsigned num_parameters, parameter const * parameters); - sort * mk_uninterpreted_sort(symbol const & name) { return mk_uninterpreted_sort(name, 0, 0); } + sort * mk_uninterpreted_sort(symbol const & name) { return mk_uninterpreted_sort(name, 0, nullptr); } sort * mk_sort(symbol const & name, sort_info const & info) { if (info.get_family_id() == null_family_id) { @@ -1675,7 +1680,7 @@ public: } } - sort * mk_sort(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = 0); + sort * mk_sort(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr); sort * substitute(sort* s, unsigned n, sort * const * src, sort * const * dst); @@ -1694,13 +1699,13 @@ public: bool is_fully_interp(sort * s) const; func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range = 0); + unsigned arity, sort * const * domain, sort * range = nullptr); func_decl * mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned num_args, expr * const * args, sort * range = 0); + unsigned num_args, expr * const * args, sort * range = nullptr); - app * mk_app(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = 0, - unsigned num_args = 0, expr * const * args = 0, sort * range = 0); + app * mk_app(family_id fid, decl_kind k, unsigned num_parameters = 0, parameter const * parameters = nullptr, + unsigned num_args = 0, expr * const * args = nullptr, sort * range = nullptr); app * mk_app(family_id fid, decl_kind k, unsigned num_args, expr * const * args); @@ -1710,7 +1715,7 @@ public: app * mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, expr * arg3); - app * mk_const(family_id fid, decl_kind k) { return mk_app(fid, k, 0, static_cast(0)); } + app * mk_const(family_id fid, decl_kind k) { return mk_app(fid, k, 0, static_cast(nullptr)); } private: func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info * info); @@ -1721,13 +1726,13 @@ private: public: func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range) { - return mk_func_decl(name, arity, domain, range, static_cast(0)); + return mk_func_decl(name, arity, domain, range, static_cast(nullptr)); } func_decl * mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, func_decl_info const & info) { if (info.is_null()) { - return mk_func_decl(name, arity, domain, range, static_cast(0)); + return mk_func_decl(name, arity, domain, range, static_cast(nullptr)); } else { return mk_func_decl(name, arity, domain, range, & const_cast(info)); @@ -1739,12 +1744,20 @@ public: arity, domain); } + func_decl * mk_const_decl(const char* name, sort * s) { + return mk_func_decl(symbol(name), static_cast(0), nullptr, s); + } + + func_decl * mk_const_decl(std::string const& name, sort * s) { + return mk_func_decl(symbol(name.c_str()), static_cast(0), nullptr, s); + } + func_decl * mk_const_decl(symbol const & name, sort * s) { - return mk_func_decl(name, static_cast(0), 0, s); + return mk_func_decl(name, static_cast(0), nullptr, s); } func_decl * mk_const_decl(symbol const & name, sort * s, func_decl_info const & info) { - return mk_func_decl(name, static_cast(0), 0, s, info); + return mk_func_decl(name, static_cast(0), nullptr, s, info); } func_decl * mk_func_decl(symbol const & name, sort * domain, sort * range, func_decl_info const & info) { @@ -1798,13 +1811,21 @@ public: app * mk_const(func_decl * decl) { SASSERT(decl->get_arity() == 0); - return mk_app(decl, static_cast(0), static_cast(0)); + return mk_app(decl, static_cast(0), static_cast(nullptr)); } app * mk_const(symbol const & name, sort * s) { return mk_const(mk_const_decl(name, s)); } + app * mk_const(std::string const & name, sort * s) { + return mk_const(mk_const_decl(name, s)); + } + + app * mk_const(char const* name, sort * s) { + return mk_const(mk_const_decl(name, s)); + } + func_decl * mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, sort * range); @@ -1819,9 +1840,9 @@ public: return mk_fresh_func_decl(symbol(prefix), symbol::null, arity, domain, range); } - app * mk_fresh_const(char const * prefix, sort * s) { return mk_const(mk_fresh_func_decl(prefix, 0, 0, s)); } + app * mk_fresh_const(char const * prefix, sort * s) { return mk_const(mk_fresh_func_decl(prefix, 0, nullptr, s)); } - symbol mk_fresh_var_name(char const * prefix = 0); + symbol mk_fresh_var_name(char const * prefix = nullptr); var * mk_var(unsigned idx, sort * ty); @@ -1873,21 +1894,21 @@ public: quantifier * mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, - unsigned num_patterns = 0, expr * const * patterns = 0, - unsigned num_no_patterns = 0, expr * const * no_patterns = 0); + unsigned num_patterns = 0, expr * const * patterns = nullptr, + unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr); quantifier * mk_forall(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, - unsigned num_patterns = 0, expr * const * patterns = 0, - unsigned num_no_patterns = 0, expr * const * no_patterns = 0) { + unsigned num_patterns = 0, expr * const * patterns = nullptr, + unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) { return mk_quantifier(true, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); } quantifier * mk_exists(unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight = 0, symbol const & qid = symbol::null, symbol const & skid = symbol::null, - unsigned num_patterns = 0, expr * const * patterns = 0, - unsigned num_no_patterns = 0, expr * const * no_patterns = 0) { + unsigned num_patterns = 0, expr * const * patterns = nullptr, + unsigned num_no_patterns = 0, expr * const * no_patterns = nullptr) { return mk_quantifier(false, num_decls, decl_sorts, decl_names, body, weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); } @@ -1980,9 +2001,9 @@ public: bool is_and(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_AND); } bool is_not(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_NOT); } bool is_eq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_EQ); } + bool is_iff(expr const * n) const { return is_eq(n) && is_bool(to_app(n)->get_arg(0)); } bool is_oeq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_OEQ); } bool is_distinct(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_DISTINCT); } - bool is_iff(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_IFF); } bool is_xor(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_XOR); } bool is_ite(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_ITE); } bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); } @@ -1998,7 +2019,7 @@ public: bool is_and(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_AND); } bool is_not(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_NOT); } bool is_eq(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_EQ); } - bool is_iff(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_IFF); } + bool is_iff(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_EQ) && is_bool(d->get_range()); } bool is_xor(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_XOR); } bool is_ite(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_ITE); } bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); } @@ -2008,7 +2029,6 @@ public: MATCH_UNARY(is_not); MATCH_BINARY(is_eq); - MATCH_BINARY(is_iff); MATCH_BINARY(is_implies); MATCH_BINARY(is_and); MATCH_BINARY(is_or); @@ -2016,6 +2036,8 @@ public: MATCH_TERNARY(is_and); MATCH_TERNARY(is_or); + bool is_iff(expr const* n, expr*& lhs, expr*& rhs) const { return is_eq(n, lhs, rhs) && is_bool(lhs); } + bool is_ite(expr const* n, expr*& t1, expr*& t2, expr*& t3) const { if (is_ite(n)) { t1 = to_app(n)->get_arg(0); @@ -2028,7 +2050,7 @@ public: public: app * mk_eq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, get_eq_op(lhs), lhs, rhs); } - app * mk_iff(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_IFF, lhs, rhs); } + app * mk_iff(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_EQ, lhs, rhs); } app * mk_oeq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_OEQ, lhs, rhs); } app * mk_xor(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_XOR, lhs, rhs); } app * mk_ite(expr * c, expr * t, expr * e) { return mk_app(m_basic_family_id, OP_ITE, c, t, e); } @@ -2046,17 +2068,16 @@ public: app * mk_true() const { return m_true; } app * mk_false() const { return m_false; } app * mk_bool_val(bool b) { return b?m_true:m_false; } - app * mk_interp(expr * arg) { return mk_app(m_basic_family_id, OP_INTERP, arg); } func_decl* mk_and_decl() { sort* domain[2] = { m_bool_sort, m_bool_sort }; - return mk_func_decl(m_basic_family_id, OP_AND, 0, 0, 2, domain); + return mk_func_decl(m_basic_family_id, OP_AND, 0, nullptr, 2, domain); } - func_decl* mk_not_decl() { return mk_func_decl(m_basic_family_id, OP_NOT, 0, 0, 1, &m_bool_sort); } + func_decl* mk_not_decl() { return mk_func_decl(m_basic_family_id, OP_NOT, 0, nullptr, 1, &m_bool_sort); } func_decl* mk_or_decl() { sort* domain[2] = { m_bool_sort, m_bool_sort }; - return mk_func_decl(m_basic_family_id, OP_OR, 0, 0, 2, domain); + return mk_func_decl(m_basic_family_id, OP_OR, 0, nullptr, 2, domain); } // ----------------------------------- @@ -2145,6 +2166,23 @@ public: return n > 0 && get_sort(p->get_arg(n - 1)) != m_proof_sort; } expr * get_fact(proof const * p) const { SASSERT(is_proof(p)); SASSERT(has_fact(p)); return p->get_arg(p->get_num_args() - 1); } + + class proof_parents { + ast_manager& m; + proof * m_proof; + public: + proof_parents(ast_manager& m, proof * p): m(m), m_proof(p) {} + proof * const * begin() const { return (proof* const*)(m_proof->begin()); } + proof * const * end() const { + unsigned n = m_proof->get_num_args(); + return (proof* const*)(begin() + (m.has_fact(m_proof) ? n - 1 : n)); + } + }; + + proof_parents get_parents(proof* p) { + return proof_parents(*this, p); + } + unsigned get_num_parents(proof const * p) const { SASSERT(is_proof(p)); unsigned n = p->get_num_args(); @@ -2176,7 +2214,6 @@ public: proof * mk_oeq_rewrite(expr * s, expr * t); proof * mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_pull_quant(expr * e, quantifier * q); - proof * mk_pull_quant_star(expr * e, quantifier * q); proof * mk_push_quant(quantifier * q, expr * e); proof * mk_elim_unused_vars(quantifier * q, expr * r); proof * mk_der(quantifier * q, expr * r); @@ -2195,16 +2232,15 @@ public: proof * mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); - proof * mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); proof * mk_skolemization(expr * q, expr * e); - proof * mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs); + proof * mk_and_elim(proof * p, unsigned i); proof * mk_not_or_elim(proof * p, unsigned i); proof * mk_th_lemma(family_id tid, expr * fact, unsigned num_proofs, proof * const * proofs, - unsigned num_params = 0, parameter const* params = 0); + unsigned num_params = 0, parameter const* params = nullptr); protected: bool check_nnf_proof_parents(unsigned num_proofs, proof * const * proofs) const; @@ -2461,9 +2497,9 @@ class scoped_mark : public ast_mark { unsigned_vector m_lim; public: scoped_mark(ast_manager& m): m_stack(m) {} - virtual ~scoped_mark() {} - virtual void mark(ast * n, bool flag); - virtual void reset(); + ~scoped_mark() override {} + void mark(ast * n, bool flag) override; + void reset() override; void mark(ast * n); void push_scope(); void pop_scope(); diff --git a/src/ast/ast_ll_pp.cpp b/src/ast/ast_ll_pp.cpp index c00053780..cbdd94efb 100644 --- a/src/ast/ast_ll_pp.cpp +++ b/src/ast/ast_ll_pp.cpp @@ -319,11 +319,11 @@ void ast_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, } void ast_def_ll_pp(std::ostream & out, ast_manager & m, ast * n, ast_mark & visited, bool only_exprs, bool compact) { - ll_printer p(out, m, 0, only_exprs, compact); + ll_printer p(out, m, nullptr, only_exprs, compact); p.pp(n, visited); } void ast_ll_bounded_pp(std::ostream & out, ast_manager & m, ast * n, unsigned depth) { - ll_printer p(out, m, 0, false, true); + ll_printer p(out, m, nullptr, false, true); p.display_bounded(n, depth); } diff --git a/src/ast/ast_pp.h b/src/ast/ast_pp.h index 997b1a6e0..5629f847a 100644 --- a/src/ast/ast_pp.h +++ b/src/ast/ast_pp.h @@ -24,10 +24,10 @@ Revision History: #include "ast/ast_smt2_pp.h" struct mk_pp : public mk_ismt2_pp { - mk_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0): + mk_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr): mk_ismt2_pp(t, m, p, indent, num_vars, var_prefix) { } - mk_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0): + mk_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr): mk_ismt2_pp(t, m, indent, num_vars, var_prefix) { } }; diff --git a/src/ast/ast_pp_dot.cpp b/src/ast/ast_pp_dot.cpp index 1dfbe9aae..47da4d4f4 100644 --- a/src/ast/ast_pp_dot.cpp +++ b/src/ast/ast_pp_dot.cpp @@ -9,7 +9,8 @@ Abstract: Pretty-printer for proofs in Graphviz format #include "ast/ast_pp_dot.h" // string escaping for DOT -std::string escape_dot(std::string const & s) { +std::string escape_dot(const std::string &s) +{ std::string res; res.reserve(s.size()); // preallocate for (auto c : s) { diff --git a/src/ast/ast_pp_dot.h b/src/ast/ast_pp_dot.h index 537754e83..d233c4be1 100644 --- a/src/ast/ast_pp_dot.h +++ b/src/ast/ast_pp_dot.h @@ -4,10 +4,11 @@ Abstract: Pretty-printer for proofs in Graphviz format --*/ -#pragma once +#ifndef _AST_PP_DOT_ +#define _AST_PP_DOT_ #include -#include "ast_pp.h" +#include "ast/ast_pp.h" class ast_pp_dot { ast_manager & m_manager; @@ -21,4 +22,8 @@ class ast_pp_dot { ast_manager & get_manager() const { return m_manager; } }; -std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p); \ No newline at end of file +std::string escape_dot(std::string const & s); + +std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p); + +#endif /* AST_PP_DOT */ diff --git a/src/ast/ast_pp_util.cpp b/src/ast/ast_pp_util.cpp index 677422b8a..378710b36 100644 --- a/src/ast/ast_pp_util.cpp +++ b/src/ast/ast_pp_util.cpp @@ -36,36 +36,40 @@ void ast_pp_util::collect(expr_ref_vector const& es) { } void ast_pp_util::display_decls(std::ostream& out) { - smt2_pp_environment_dbg env(m); ast_smt_pp pp(m); + coll.order_deps(); unsigned n = coll.get_num_sorts(); for (unsigned i = 0; i < n; ++i) { - pp.display_ast_smt2(out, coll.get_sorts()[i], 0, 0, 0); + pp.display_ast_smt2(out, coll.get_sorts()[i], 0, 0, nullptr); } n = coll.get_num_decls(); for (unsigned i = 0; i < n; ++i) { func_decl* f = coll.get_func_decls()[i]; - if (f->get_family_id() == null_family_id) { - ast_smt2_pp(out, f, env); - out << "\n"; + if (f->get_family_id() == null_family_id && !m_removed.contains(f)) { + ast_smt2_pp(out, f, m_env) << "\n"; } } + SASSERT(coll.get_num_preds() == 0); } +void ast_pp_util::remove_decl(func_decl* f) { + m_removed.insert(f); +} + + void ast_pp_util::display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat) { if (neat) { - smt2_pp_environment_dbg env(m); - for (unsigned i = 0; i < fmls.size(); ++i) { + for (expr* f : fmls) { out << "(assert "; - ast_smt2_pp(out, fmls[i], env); + ast_smt2_pp(out, f, m_env); out << ")\n"; } } else { ast_smt_pp ll_smt2_pp(m); - for (unsigned i = 0; i < fmls.size(); ++i) { + for (expr* f : fmls) { out << "(assert "; - ll_smt2_pp.display_expr_smt2(out, fmls[i]); + ll_smt2_pp.display_expr_smt2(out, f); out << ")\n"; } } diff --git a/src/ast/ast_pp_util.h b/src/ast/ast_pp_util.h index 964a862a2..c3b8aa601 100644 --- a/src/ast/ast_pp_util.h +++ b/src/ast/ast_pp_util.h @@ -20,14 +20,18 @@ Revision History: #define AST_PP_UTIL_H_ #include "ast/decl_collector.h" +#include "ast/ast_smt2_pp.h" +#include "util/obj_hashtable.h" class ast_pp_util { ast_manager& m; + obj_hashtable m_removed; + smt2_pp_environment_dbg m_env; public: decl_collector coll; - ast_pp_util(ast_manager& m): m(m), coll(m, false) {} + ast_pp_util(ast_manager& m): m(m), m_env(m), coll(m, false) {} void collect(expr* e); @@ -35,9 +39,13 @@ class ast_pp_util { void collect(expr_ref_vector const& es); + void remove_decl(func_decl* f); + void display_decls(std::ostream& out); void display_asserts(std::ostream& out, expr_ref_vector const& fmls, bool neat = true); + + smt2_pp_environment& env() { return m_env; } }; #endif /* AST_PP_UTIL_H_ */ diff --git a/src/ast/ast_printer.cpp b/src/ast/ast_printer.cpp index b5cf60ee9..7f2460e06 100644 --- a/src/ast/ast_printer.cpp +++ b/src/ast/ast_printer.cpp @@ -25,24 +25,24 @@ class simple_ast_printer_context : public ast_printer_context { smt2_pp_environment_dbg & env() const { return *(m_env.get()); } public: simple_ast_printer_context(ast_manager & m):m_manager(m) { m_env = alloc(smt2_pp_environment_dbg, m); } - virtual ~simple_ast_printer_context() {} + ~simple_ast_printer_context() override {} ast_manager & m() const { return m_manager; } - virtual ast_manager & get_ast_manager() { return m_manager; } - virtual void display(std::ostream & out, sort * s, unsigned indent = 0) const { out << mk_ismt2_pp(s, m(), indent); } - virtual void display(std::ostream & out, expr * n, unsigned indent = 0) const { out << mk_ismt2_pp(n, m(), indent); } - virtual void display(std::ostream & out, func_decl * f, unsigned indent = 0) const { + ast_manager & get_ast_manager() override { return m_manager; } + void display(std::ostream & out, sort * s, unsigned indent = 0) const override { out << mk_ismt2_pp(s, m(), indent); } + void display(std::ostream & out, expr * n, unsigned indent = 0) const override { out << mk_ismt2_pp(n, m(), indent); } + void display(std::ostream & out, func_decl * f, unsigned indent = 0) const override { out << f->get_name(); } - virtual void pp(sort * s, format_ns::format_ref & r) const { mk_smt2_format(s, env(), params_ref(), r); } - virtual void pp(func_decl * f, format_ns::format_ref & r) const { mk_smt2_format(f, env(), params_ref(), r); } - virtual void pp(expr * n, format_ns::format_ref & r) const { + void pp(sort * s, format_ns::format_ref & r) const override { mk_smt2_format(s, env(), params_ref(), r); } + void pp(func_decl * f, format_ns::format_ref & r) const override { mk_smt2_format(f, env(), params_ref(), r, "declare-fun"); } + void pp(expr * n, format_ns::format_ref & r) const override { sbuffer buf; - mk_smt2_format(n, env(), params_ref(), 0, 0, r, buf); + mk_smt2_format(n, env(), params_ref(), 0, nullptr, r, buf); } - virtual void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const { + void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const override { mk_smt2_format(n, env(), params_ref(), num_vars, var_prefix, r, var_names); } - virtual void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const { + void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const override { NOT_IMPLEMENTED_YET(); } diff --git a/src/ast/ast_printer.h b/src/ast/ast_printer.h index 87fa8ef3d..c962fedc5 100644 --- a/src/ast/ast_printer.h +++ b/src/ast/ast_printer.h @@ -45,7 +45,7 @@ public: class ast_printer_context : public ast_printer { public: - virtual ~ast_printer_context() {} + ~ast_printer_context() override {} virtual ast_manager & get_ast_manager() = 0; virtual std::ostream & regular_stream() { return std::cout; } virtual std::ostream & diagnostic_stream() { return std::cerr; } diff --git a/src/ast/ast_smt2_pp.cpp b/src/ast/ast_smt2_pp.cpp index 0139cb0f0..9d27ffb24 100644 --- a/src/ast/ast_smt2_pp.cpp +++ b/src/ast/ast_smt2_pp.cpp @@ -31,7 +31,7 @@ using namespace format_ns; #define MAX_INDENT 16 #define SMALL_INDENT 2 -format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len) const { +format * smt2_pp_environment::pp_fdecl_name(symbol const & s, unsigned & len, bool is_skolem) const { ast_manager & m = get_manager(); if (is_smt2_quoted_symbol(s)) { std::string str = mk_smt2_quoted_symbol(s); @@ -63,13 +63,9 @@ format * smt2_pp_environment::pp_fdecl_name(func_decl * f, unsigned & len) const len = 3; return mk_string(m, "ite"); } - else if (m.is_iff(f)) { - len = 1; - return mk_string(m, "="); - } else { symbol s = f->get_name(); - return pp_fdecl_name(s, len); + return pp_fdecl_name(s, len, f->is_skolem()); } } @@ -116,8 +112,10 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) { (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast()))); if (f->get_parameter(i).is_int()) fs.push_back(mk_int(get_manager(), f->get_parameter(i).get_int())); - else if (f->get_parameter(i).is_rational()) - fs.push_back(mk_string(get_manager(), f->get_parameter(i).get_rational().to_string().c_str())); + else if (f->get_parameter(i).is_rational()) { + std::string str = f->get_parameter(i).get_rational().to_string(); + fs.push_back(mk_string(get_manager(), str.c_str())); + } else fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast()))); } @@ -236,7 +234,7 @@ format * smt2_pp_environment::pp_float_literal(app * t, bool use_bv_lits, bool u mpf_manager & fm = get_futil().fm(); scoped_mpf v(fm); ast_manager & m = get_manager(); - format * body = 0; + format * body = nullptr; string_buffer<> buf; VERIFY(get_futil().is_numeral(t, v)); if (fm.is_nan(v)) { @@ -387,7 +385,7 @@ format * smt2_pp_environment::pp_string_literal(app * t) { } format * smt2_pp_environment::pp_datalog_literal(app * t) { - uint64 v; + uint64_t v; VERIFY (get_dlutil().is_numeral(t, v)); std::ostringstream buffer; buffer << v; @@ -614,9 +612,9 @@ class smt2_printer { return f; ptr_buffer buf; buf.push_back(f); - for (unsigned i = 0; i < names.size(); i++) { - buf.push_back(pp_simple_attribute(is_pos ? ":lblpos " : ":lblneg ", names[i])); - } + for (symbol const& n : names) + buf.push_back(pp_simple_attribute(is_pos ? ":lblpos " : ":lblneg ", n)); + return mk_seq1(m(), buf.begin(), buf.end(), f2f(), "!"); } @@ -750,7 +748,7 @@ class smt2_printer { } buffer labels; bool is_pos; - format * f = 0; + format * f = nullptr; format ** it = m_format_stack.c_ptr() + fr.m_spos; format ** end = m_format_stack.c_ptr() + m_format_stack.size(); if (m().is_label(t, is_pos, labels)) { @@ -891,8 +889,21 @@ class smt2_printer { } } + void register_var_names(unsigned n) { + unsigned idx = 1; + for (unsigned i = 0; i < n; i++) { + symbol name = next_name("x", idx); + SASSERT(!m_var_names_set.contains(name)); + m_var_names.push_back(name); + m_var_names_set.insert(name); + } + } + void unregister_var_names(quantifier * q) { - unsigned num_decls = q->get_num_decls(); + unregister_var_names(q->get_num_decls()); + } + + void unregister_var_names(unsigned num_decls) { for (unsigned i = 0; i < num_decls; i++) { symbol s = m_var_names.back(); m_var_names.pop_back(); @@ -900,25 +911,28 @@ class smt2_printer { } } - format * pp_var_decls(quantifier * q) { + format * pp_var_args(unsigned num_decls, sort* const* srts) { ptr_buffer buf; - unsigned num_decls = q->get_num_decls(); SASSERT(num_decls <= m_var_names.size()); symbol * it = m_var_names.end() - num_decls; for (unsigned i = 0; i < num_decls; i++, it++) { - format * fs[1] = { m_env.pp_sort(q->get_decl_sort(i)) }; + format * fs[1] = { m_env.pp_sort(srts[i]) }; std::string var_name; if (is_smt2_quoted_symbol (*it)) { var_name = mk_smt2_quoted_symbol (*it); } else { - var_name = it->str ();\ + var_name = it->str (); } buf.push_back(mk_seq1(m(), fs, fs+1, f2f(), var_name.c_str ())); } return mk_seq5(m(), buf.begin(), buf.end(), f2f()); } + format * pp_var_decls(quantifier * q) { + return pp_var_args(q->get_num_decls(), q->get_decl_sorts()); + } + void process_quantifier(quantifier * q, frame & fr) { if (fr.m_idx == 0) { begin_scope(); @@ -1004,15 +1018,13 @@ class smt2_printer { void del_expr2alias_stack() { std::for_each(m_expr2alias_stack.begin(), m_expr2alias_stack.end(), delete_proc()); m_expr2alias_stack.reset(); - m_expr2alias = 0; + m_expr2alias = nullptr; } void reset_expr2alias_stack() { SASSERT(!m_expr2alias_stack.empty()); - ptr_vector::iterator it = m_expr2alias_stack.begin(); - ptr_vector::iterator end = m_expr2alias_stack.end(); - for (; it != end; ++it) - (*it)->reset(); + for (expr2alias * e : m_expr2alias_stack) + e->reset(); m_expr2alias = m_expr2alias_stack[0]; } @@ -1064,7 +1076,7 @@ public: m_manager(env.get_manager()), m_env(env), m_soccs(m_manager), - m_root(0), + m_root(nullptr), m_aliased_pps(fm()), m_next_alias_idx(1), m_format_stack(fm()) { @@ -1092,7 +1104,7 @@ public: void operator()(expr * n, unsigned num, char const * var_prefix, format_ref & r, sbuffer & var_names) { reset_var_names(); - if (var_prefix == 0) + if (var_prefix == nullptr) var_prefix = "x"; if (strcmp(var_prefix, ALIAS_PREFIX) == 0) { var_prefix = "_a"; @@ -1113,7 +1125,7 @@ public: r = m_env.pp_sort(s); } - void operator()(func_decl * f, format_ref & r) { + void operator()(func_decl * f, format_ref & r, char const* cmd) { unsigned arity = f->get_arity(); unsigned len; format * fname = m_env.pp_fdecl_name(f, len); @@ -1125,9 +1137,26 @@ public: } args[1] = mk_seq5(m(), buf.begin(), buf.end(), f2f()); args[2] = m_env.pp_sort(f->get_range()); - r = mk_seq1(m(), args, args+3, f2f(), "declare-fun"); + r = mk_seq1(m(), args, args+3, f2f(), cmd); } + + void operator()(func_decl * f, expr * e, format_ref & r, char const* cmd) { + unsigned len; + format * fname = m_env.pp_fdecl_name(f, len); + register_var_names(f->get_arity()); + format * args[4]; + args[0] = fname; + args[1] = pp_var_args(f->get_arity(), f->get_domain()); + args[2] = m_env.pp_sort(f->get_range()); + process(e, r); + args[3] = r; + r = mk_seq1(m(), args, args+4, f2f(), cmd); + unregister_var_names(f->get_arity()); + } + + + }; void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, @@ -1142,9 +1171,14 @@ void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, f pr(s, r); } -void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ref & r) { +void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) { smt2_printer pr(env, p); - pr(f, r); + pr(f, r, cmd); +} + +void mk_smt2_format(func_decl * f, expr * e, smt2_pp_environment & env, params_ref const & p, format_ref & r, char const* cmd) { + smt2_printer pr(env, p); + pr(f, e, r, cmd); } void mk_smt2_format(unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, @@ -1185,17 +1219,29 @@ std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & e return out; } -std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p, unsigned indent) { +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { ast_manager & m = env.get_manager(); format_ref r(fm(m)); sbuffer var_names; - mk_smt2_format(f, env, p, r); + mk_smt2_format(f, env, p, r, cmd); if (indent > 0) r = mk_indent(m, indent, r.get()); pp(out, r.get(), m, p); return out; } +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p, unsigned indent, char const* cmd) { + ast_manager & m = env.get_manager(); + format_ref r(fm(m)); + sbuffer var_names; + mk_smt2_format(f, e, env, p, r, cmd); + if (indent > 0) + r = mk_indent(m, indent, r.get()); + pp(out, r.get(), m, p); + return out; +} + + std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, smt2_pp_environment & env, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix) { ast_manager & m = env.get_manager(); @@ -1208,6 +1254,15 @@ std::ostream & ast_smt2_pp(std::ostream & out, unsigned sz, expr * const* es, sm return out; } +std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p) { + unsigned len; + ast_manager & m = env.get_manager(); + format_ref r(fm(m)); + r = env.pp_fdecl_name(s, len, is_skolem); + pp(out, r.get(), m, p); + return out; +} + mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent, unsigned num_vars, char const * var_prefix): m_ast(t), m_manager(m), @@ -1226,9 +1281,11 @@ mk_ismt2_pp::mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent, unsigned num m_var_prefix(var_prefix) { } + + std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) { smt2_pp_environment_dbg env(p.m_manager); - if (p.m_ast == 0) { + if (p.m_ast == nullptr) { out << "null"; } else if (is_expr(p.m_ast)) { @@ -1263,24 +1320,24 @@ std::ostream& operator<<(std::ostream& out, sort_ref const& e) { std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) { smt2_pp_environment_dbg env(e.get_manager()); params_ref p; - return ast_smt2_pp(out, e.size(), e.c_ptr(), env, p, 0, 0, 0); + return ast_smt2_pp(out, e.size(), e.c_ptr(), env, p, 0, 0, nullptr); } std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) { smt2_pp_environment_dbg env(e.get_manager()); params_ref p; - return ast_smt2_pp(out, e.size(), (expr*const*)e.c_ptr(), env, p, 0, 0, 0); + return ast_smt2_pp(out, e.size(), (expr*const*)e.c_ptr(), env, p, 0, 0, nullptr); } std::ostream& operator<<(std::ostream& out, func_decl_ref_vector const& e) { - for (unsigned i = 0; i < e.size(); ++i) - out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + for (func_decl* f : e) + out << mk_ismt2_pp(f, e.get_manager()) << "\n"; return out; } std::ostream& operator<<(std::ostream& out, sort_ref_vector const& e) { - for (unsigned i = 0; i < e.size(); ++i) - out << mk_ismt2_pp(e[i], e.get_manager()) << "\n"; + for (sort* s : e) + out << mk_ismt2_pp(s, e.get_manager()) << "\n"; return out; } diff --git a/src/ast/ast_smt2_pp.h b/src/ast/ast_smt2_pp.h index 3e8b1aa39..13093f138 100644 --- a/src/ast/ast_smt2_pp.h +++ b/src/ast/ast_smt2_pp.h @@ -31,10 +31,12 @@ Revision History: #include "ast/dl_decl_plugin.h" #include "ast/seq_decl_plugin.h" #include "ast/datatype_decl_plugin.h" +#include "ast/ast_smt_pp.h" #include "util/smt2_util.h" class smt2_pp_environment { protected: + mutable smt_renaming m_renaming; format_ns::format * mk_neg(format_ns::format * f) const; format_ns::format * mk_float(rational const & val) const; bool is_indexed_fdecl(func_decl * f) const; @@ -61,7 +63,7 @@ public: virtual format_ns::format * pp_string_literal(app * t); virtual format_ns::format * pp_sort(sort * s); virtual format_ns::format * pp_fdecl_ref(func_decl * f); - format_ns::format * pp_fdecl_name(symbol const & fname, unsigned & len) const; + format_ns::format * pp_fdecl_name(symbol const & fname, unsigned & len, bool is_skolem) const; format_ns::format * pp_fdecl_name(func_decl * f, unsigned & len) const; }; @@ -80,27 +82,29 @@ class smt2_pp_environment_dbg : public smt2_pp_environment { datalog::dl_decl_util m_dlutil; public: smt2_pp_environment_dbg(ast_manager & m):m_manager(m), m_autil(m), m_bvutil(m), m_arutil(m), m_futil(m), m_sutil(m), m_dtutil(m), m_dlutil(m) {} - virtual ast_manager & get_manager() const { return m_manager; } - virtual arith_util & get_autil() { return m_autil; } - virtual bv_util & get_bvutil() { return m_bvutil; } - virtual seq_util & get_sutil() { return m_sutil; } - virtual array_util & get_arutil() { return m_arutil; } - virtual fpa_util & get_futil() { return m_futil; } - virtual datalog::dl_decl_util& get_dlutil() { return m_dlutil; } - virtual datatype_util& get_dtutil() { return m_dtutil; } - virtual bool uses(symbol const & s) const { return false; } + ast_manager & get_manager() const override { return m_manager; } + arith_util & get_autil() override { return m_autil; } + bv_util & get_bvutil() override { return m_bvutil; } + seq_util & get_sutil() override { return m_sutil; } + array_util & get_arutil() override { return m_arutil; } + fpa_util & get_futil() override { return m_futil; } + datalog::dl_decl_util& get_dlutil() override { return m_dlutil; } + datatype_util& get_dtutil() override { return m_dtutil; } + bool uses(symbol const & s) const override { return false; } }; void mk_smt2_format(expr * n, smt2_pp_environment & env, params_ref const & p, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names); void mk_smt2_format(sort * s, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r); -void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r); +void mk_smt2_format(func_decl * f, smt2_pp_environment & env, params_ref const & p, format_ns::format_ref & r, char const* cmd); std::ostream & ast_smt2_pp(std::ostream & out, expr * n, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, - unsigned num_vars = 0, char const * var_prefix = 0); + unsigned num_vars = 0, char const * var_prefix = nullptr); std::ostream & ast_smt2_pp(std::ostream & out, sort * s, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); -std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0); +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "declare-fun"); +std::ostream & ast_smt2_pp(std::ostream & out, func_decl * f, expr* e, smt2_pp_environment & env, params_ref const & p = params_ref(), unsigned indent = 0, char const* cmd = "define-fun"); +std::ostream & ast_smt2_pp(std::ostream & out, symbol const& s, bool is_skolem, smt2_pp_environment & env, params_ref const& p = params_ref()); /** \brief Internal wrapper (for debugging purposes only) @@ -113,8 +117,8 @@ struct mk_ismt2_pp { unsigned m_indent; unsigned m_num_vars; char const * m_var_prefix; - mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0); - mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0); + mk_ismt2_pp(ast * t, ast_manager & m, params_ref const & p, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); + mk_ismt2_pp(ast * t, ast_manager & m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); }; std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p); diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index bad4b1984..22adf42d9 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -45,14 +45,6 @@ symbol smt_renaming::fix_symbol(symbol s, int k) { std::ostringstream buffer; char const * data = s.is_numerical() ? "" : s.bare_str(); - if (data[0] && !data[1]) { - switch (data[0]) { - case '/': data = "op_div"; break; - case '%': data = "op_mod"; break; - default: break; - } - } - if (k == 0 && *data) { if (s.is_numerical()) { return s; @@ -70,14 +62,17 @@ symbol smt_renaming::fix_symbol(symbol s, int k) { return symbol(buffer.str().c_str()); } - if (is_smt2_quoted_symbol(s)) { + if (!s.bare_str()) { + buffer << "null"; + } + else if (is_smt2_quoted_symbol(s)) { buffer << mk_smt2_quoted_symbol(s); } else { buffer << s; } if (k > 0) { - buffer << k; + buffer << "!" << k; } return symbol(buffer.str().c_str()); @@ -124,15 +119,30 @@ bool smt_renaming::all_is_legal(char const* s) { smt_renaming::smt_renaming() { for (unsigned i = 0; i < ARRAYSIZE(m_predef_names); ++i) { symbol s(m_predef_names[i]); - m_translate.insert(s, s); + m_translate.insert(s, sym_b(s, false)); m_rev_translate.insert(s, s); } } - -symbol smt_renaming::get_symbol(symbol s0) { +// Ensure that symbols that are used both with skolems and non-skolems are named apart. +symbol smt_renaming::get_symbol(symbol s0, bool is_skolem) { + sym_b sb; symbol s; - if (m_translate.find(s0, s)) { + if (m_translate.find(s0, sb)) { + if (is_skolem == sb.is_skolem) + return sb.name; + if (sb.name_aux != symbol::null) { + return sb.name_aux; + } + int k = 0; + symbol s1; + do { + s = fix_symbol(s0, k++); + } + while (s == s0 || (m_rev_translate.find(s, s1) && s1 != s0)); + m_rev_translate.insert(s, s0); + sb.name_aux = s; + m_translate.insert(s, sb); return s; } @@ -141,7 +151,7 @@ symbol smt_renaming::get_symbol(symbol s0) { s = fix_symbol(s0, k++); } while (m_rev_translate.contains(s)); - m_translate.insert(s0, s); + m_translate.insert(s0, sym_b(s, is_skolem)); m_rev_translate.insert(s, s0); return s; } @@ -202,7 +212,7 @@ class smt_printer { } void pp_decl(func_decl* d) { - symbol sym = m_renaming.get_symbol(d->get_name()); + symbol sym = m_renaming.get_symbol(d->get_name(), d->is_skolem()); if (d->get_family_id() == m_dt_fid) { datatype_util util(m_manager); if (util.is_recognizer(d)) { @@ -215,9 +225,6 @@ class smt_printer { else if (m_manager.is_ite(d)) { m_out << "ite"; } - else if (m_manager.is_iff(d)) { - m_out << "="; - } else if (m_manager.is_implies(d)) { m_out << "=>"; } @@ -313,7 +320,7 @@ class smt_printer { if (num_sorts > 0) { m_out << "("; } - m_out << m_renaming.get_symbol(s->get_name()); + m_out << m_renaming.get_symbol(s->get_name(), false); if (num_sorts > 0) { for (unsigned i = 0; i < num_sorts; ++i) { m_out << " "; @@ -324,7 +331,7 @@ class smt_printer { return; } else { - sym = m_renaming.get_symbol(s->get_name()); + sym = m_renaming.get_symbol(s->get_name(), false); } visit_params(true, sym, s->get_num_parameters(), s->get_parameters()); } @@ -395,17 +402,17 @@ class smt_printer { else if (m_manager.is_label(n, pos, names) && names.size() >= 1) { m_out << "(! "; pp_marked_expr(n->get_arg(0)); - m_out << (pos?":lblpos":":lblneg") << " " << m_renaming.get_symbol(names[0]) << ")"; + m_out << (pos?":lblpos":":lblneg") << " " << m_renaming.get_symbol(names[0], false) << ")"; } else if (m_manager.is_label_lit(n, names) && names.size() >= 1) { - m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0]) << ")"; + m_out << "(! true :lblpos " << m_renaming.get_symbol(names[0], false) << ")"; } else if (num_args == 0) { if (decl->private_parameters()) { - m_out << m_renaming.get_symbol(decl->get_name()); + m_out << m_renaming.get_symbol(decl->get_name(), decl->is_skolem()); } else { - symbol sym = m_renaming.get_symbol(decl->get_name()); + symbol sym = m_renaming.get_symbol(decl->get_name(), decl->is_skolem()); visit_params(false, sym, decl->get_num_parameters(), decl->get_parameters()); } } @@ -498,7 +505,7 @@ class smt_printer { for (unsigned i = 0; i < q->get_num_decls(); ++i) { sort* s = q->get_decl_sort(i); m_out << "("; - print_bound(m_renaming.get_symbol(q->get_decl_name(i))); + print_bound(m_renaming.get_symbol(q->get_decl_name(i), false)); m_out << " "; visit_sort(s, true); m_out << ") "; @@ -563,7 +570,7 @@ class smt_printer { unsigned num_decls = q->get_num_decls(); if (idx < num_decls) { unsigned offs = num_decls-idx-1; - symbol name = m_renaming.get_symbol(q->get_decl_name(offs)); + symbol name = m_renaming.get_symbol(q->get_decl_name(offs), false); print_bound(name); return; } @@ -702,7 +709,7 @@ class smt_printer { public: smt_printer(std::ostream& out, ast_manager& m, ptr_vector& ql, smt_renaming& rn, - symbol logic, bool no_lets, bool simplify_implies, unsigned indent, unsigned num_var_names = 0, char const* const* var_names = 0) : + symbol logic, bool no_lets, bool simplify_implies, unsigned indent, unsigned num_var_names = 0, char const* const* var_names = nullptr) : m_out(out), m_manager(m), m_qlists(ql), @@ -768,7 +775,7 @@ public: } m_mark.reset(); m_num_lets = 0; - m_top = 0; + m_top = nullptr; } void pp_dt(ast_mark& mark, sort* s) { @@ -805,15 +812,15 @@ public: m_out << ")"; } m_out << "("; - m_out << m_renaming.get_symbol(d->name()); + m_out << m_renaming.get_symbol(d->name(), false); m_out << " "; bool first_constr = true; for (datatype::constructor* f : *d) { if (!first_constr) m_out << " "; else first_constr = false; m_out << "("; - m_out << m_renaming.get_symbol(f->name()); + m_out << m_renaming.get_symbol(f->name(), false); for (datatype::accessor* a : *f) { - m_out << " (" << m_renaming.get_symbol(a->name()) << " "; + m_out << " (" << m_renaming.get_symbol(a->name(), false) << " "; visit_sort(a->range()); m_out << ")"; } @@ -952,6 +959,10 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { strm << "; " << m_attributes.c_str(); } +#if 0 + decls.display_decls(strm); +#else + decls.order_deps(); ast_mark sort_mark; for (unsigned i = 0; i < decls.get_num_sorts(); ++i) { sort* s = decls.get_sorts()[i]; @@ -978,18 +989,19 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) { strm << "\n"; } } +#endif - for (unsigned i = 0; i < m_assumptions.size(); ++i) { + for (expr* a : m_assumptions) { smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); strm << "(assert\n "; - p(m_assumptions[i].get()); + p(a); strm << ")\n"; } - for (unsigned i = 0; i < m_assumptions_star.size(); ++i) { + for (expr* a : m_assumptions_star) { smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1); strm << "(assert\n "; - p(m_assumptions_star[i].get()); + p(a); strm << ")\n"; } diff --git a/src/ast/ast_smt_pp.h b/src/ast/ast_smt_pp.h index ac2e33140..84beb34a2 100644 --- a/src/ast/ast_smt_pp.h +++ b/src/ast/ast_smt_pp.h @@ -24,8 +24,10 @@ Revision History: #include "util/map.h" class smt_renaming { + struct sym_b { symbol name; bool is_skolem; symbol name_aux; sym_b(symbol n, bool s): name(n), is_skolem(s) {} sym_b():name(),is_skolem(false) {}}; typedef map symbol2symbol; - symbol2symbol m_translate; + typedef map symbol2sym_b; + symbol2sym_b m_translate; symbol2symbol m_rev_translate; symbol fix_symbol(symbol s, int k); @@ -35,8 +37,8 @@ class smt_renaming { bool all_is_legal(char const* s); public: smt_renaming(); - symbol get_symbol(symbol s0); - symbol operator()(symbol const & s) { return get_symbol(s); } + symbol get_symbol(symbol s0, bool is_skolem = false); + symbol operator()(symbol const & s, bool is_skolem = false) { return get_symbol(s, is_skolem); } }; class ast_smt_pp { @@ -76,8 +78,8 @@ public: void set_is_declared(is_declared* id) { m_is_declared = id; } void display_smt2(std::ostream& strm, expr* n); - void display_expr_smt2(std::ostream& strm, expr* n, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = 0); - void display_ast_smt2(std::ostream& strm, ast* n, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = 0); + void display_expr_smt2(std::ostream& strm, expr* n, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = nullptr); + void display_ast_smt2(std::ostream& strm, ast* n, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = nullptr); }; @@ -87,7 +89,7 @@ struct mk_smt_pp { unsigned m_indent; unsigned m_num_var_names; char const* const* m_var_names; - mk_smt_pp(ast* e, ast_manager & m, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = 0) : + mk_smt_pp(ast* e, ast_manager & m, unsigned indent = 0, unsigned num_var_names = 0, char const* const* var_names = nullptr) : m_ast(e), m_manager(m), m_indent(indent), m_num_var_names(num_var_names), m_var_names(var_names) {} }; diff --git a/src/ast/ast_trail.h b/src/ast/ast_trail.h index d5245bd77..78ef74a47 100644 --- a/src/ast/ast_trail.h +++ b/src/ast/ast_trail.h @@ -66,7 +66,7 @@ public: m.insert(s,t); } - virtual void undo(Ctx& ctx) { + void undo(Ctx& ctx) override { m_map.pop(); } }; diff --git a/src/ast/ast_translation.cpp b/src/ast/ast_translation.cpp index 1bce4bcbe..5ab6ab351 100644 --- a/src/ast/ast_translation.cpp +++ b/src/ast/ast_translation.cpp @@ -23,6 +23,7 @@ Revision History: #include "ast/format.h" #include "ast/ast_translation.h" #include "ast/ast_ll_pp.h" +#include "ast/ast_pp.h" ast_translation::~ast_translation() { reset_cache(); @@ -47,9 +48,10 @@ void ast_translation::reset_cache() { void ast_translation::cache(ast * s, ast * t) { SASSERT(!m_cache.contains(s)); if (s->get_ref_count() > 1) { - m_cache.insert(s, t); m_from_manager.inc_ref(s); m_to_manager.inc_ref(t); + m_cache.insert(s, t); + ++m_insert_count; } } @@ -75,10 +77,16 @@ void ast_translation::push_frame(ast * n) { } bool ast_translation::visit(ast * n) { - ast * r; - if (n->get_ref_count() > 1 && m_cache.find(n, r)) { - m_result_stack.push_back(r); - return true; + if (n->get_ref_count() > 1) { + ast * r; + if (m_cache.find(n, r)) { + m_result_stack.push_back(r); + ++m_hit_count; + return true; + } + else { + ++m_miss_count; + } } push_frame(n); return false; @@ -108,7 +116,7 @@ void ast_translation::copy_params(decl * d, unsigned rpos, buffer & p void ast_translation::mk_sort(sort * s, frame & fr) { sort_info * si = s->get_info(); sort * new_s; - if (si == 0) { + if (si == nullptr) { // TODO: investigate: this branch is probably unreachable. // It became unreachable after we started using mk_uninterpreted_sort for creating uninterpreted sorts, // and mk_uninterpreted_sort actually creates a user_sort. @@ -139,7 +147,7 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) { sort ** new_domain = reinterpret_cast(m_result_stack.c_ptr() + fr.m_rpos + num_extra); sort * new_range = static_cast(m_result_stack.back()); func_decl * new_f; - if (fi == 0) { + if (fi == nullptr) { new_f = m_to_manager.mk_func_decl(f->get_name(), f->get_arity(), new_domain, @@ -160,7 +168,7 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) { new_fi.set_chainable(fi->is_chainable()); new_fi.set_pairwise(fi->is_pairwise()); new_fi.set_injective(fi->is_injective()); - new_fi.set_skolem(fi->is_skolem()); +/// TBD new_fi.set_skolem(fi->is_skolem()); new_fi.set_idempotent(fi->is_idempotent()); new_f = m_to_manager.mk_func_decl(f->get_name(), @@ -182,25 +190,37 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) { } ast * ast_translation::process(ast const * _n) { - if (!_n) return 0; + if (!_n) return nullptr; SASSERT(m_result_stack.empty()); SASSERT(m_frame_stack.empty()); SASSERT(m_extra_children_stack.empty()); + ++m_num_process; + if (m_num_process > (1 << 14)) { + reset_cache(); + m_num_process = 0; + } if (!visit(const_cast(_n))) { while (!m_frame_stack.empty()) { loop: + ++m_loop_count; frame & fr = m_frame_stack.back(); ast * n = fr.m_n; ast * r; TRACE("ast_translation", tout << mk_ll_pp(n, m_from_manager, false) << "\n";); - if (fr.m_idx == 0 && n->get_ref_count() > 1 && m_cache.find(n, r)) { - SASSERT(m_result_stack.size() == fr.m_rpos); - m_result_stack.push_back(r); - m_extra_children_stack.shrink(fr.m_cpos); - m_frame_stack.pop_back(); - TRACE("ast_translation", tout << "hit\n";); - continue; + if (fr.m_idx == 0 && n->get_ref_count() > 1) { + if (m_cache.find(n, r)) { + SASSERT(m_result_stack.size() == fr.m_rpos); + m_result_stack.push_back(r); + m_extra_children_stack.shrink(fr.m_cpos); + m_frame_stack.pop_back(); + TRACE("ast_translation", tout << "hit\n";); + m_hit_count++; + continue; + } + else { + m_miss_count++; + } } switch (n->get_kind()) { case AST_VAR: { @@ -227,7 +247,7 @@ ast * ast_translation::process(ast const * _n) { while (fr.m_idx <= num) { expr * arg = to_app(n)->get_arg(fr.m_idx - 1); fr.m_idx++; - if (!visit(arg)) + if (!visit(arg)) goto loop; } func_decl * new_f = to_func_decl(m_result_stack[fr.m_rpos]); @@ -320,7 +340,7 @@ ast * ast_translation::process(ast const * _n) { } expr_dependency * expr_dependency_translation::operator()(expr_dependency * d) { - if (d == 0) + if (d == nullptr) return d; m_buffer.reset(); m_translation.from().linearize(d, m_buffer); diff --git a/src/ast/ast_translation.h b/src/ast/ast_translation.h index b278791d7..d5e29e785 100644 --- a/src/ast/ast_translation.h +++ b/src/ast/ast_translation.h @@ -37,6 +37,11 @@ class ast_translation { ptr_vector m_extra_children_stack; // for sort and func_decl, since they have nested AST in their parameters ptr_vector m_result_stack; obj_map m_cache; + unsigned m_loop_count; + unsigned m_hit_count; + unsigned m_miss_count; + unsigned m_insert_count; + unsigned m_num_process; void cache(ast * s, ast * t); void collect_decl_extra_children(decl * d); @@ -50,25 +55,53 @@ class ast_translation { public: ast_translation(ast_manager & from, ast_manager & to, bool copy_plugins = true) : m_from_manager(from), m_to_manager(to) { - if (copy_plugins) - m_to_manager.copy_families_plugins(m_from_manager); + m_loop_count = 0; + m_hit_count = 0; + m_miss_count = 0; + m_insert_count = 0; + m_num_process = 0; + if (&from != &to) { + if (copy_plugins) + m_to_manager.copy_families_plugins(m_from_manager); + m_to_manager.update_fresh_id(m_from_manager); + } } ~ast_translation(); template T * operator()(T const * n) { + return translate(n); + } + + template + T * translate(T const * n) { + if (&from() == &to()) return const_cast(n); SASSERT(!n || from().contains(const_cast(n))); ast * r = process(n); SASSERT((!n && !r) || to().contains(const_cast(r))); return static_cast(r); } + ast_manager & from() const { return m_from_manager; } ast_manager & to() const { return m_to_manager; } + template + ref_vector operator()(ref_vector const& src) { + ref_vector dst(to()); + for (expr* v : src) dst.push_back(translate(v)); + return dst; + } + void reset_cache(); void cleanup(); + + unsigned loop_count() const { return m_loop_count; } + unsigned hit_count() const { return m_hit_count; } + unsigned miss_count() const { return m_miss_count; } + unsigned insert_count() const { return m_insert_count; } + unsigned long long get_num_collision() const { return m_cache.get_num_collision(); } }; // Translation with non-persistent cache. @@ -80,6 +113,7 @@ inline expr * translate(expr const * e, ast_manager & from, ast_manager & to) { return ast_translation(from, to)(e); } + class expr_dependency_translation { ast_translation & m_translation; ptr_vector m_buffer; diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp index 68f5b2486..42c0d698b 100644 --- a/src/ast/ast_util.cpp +++ b/src/ast/ast_util.cpp @@ -38,7 +38,7 @@ app * mk_list_assoc_app(ast_manager & m, func_decl * f, unsigned num_args, expr } app * mk_list_assoc_app(ast_manager & m, family_id fid, decl_kind k, unsigned num_args, expr * const * args) { - func_decl * decl = m.mk_func_decl(fid, k, 0, 0, num_args, args, 0); + func_decl * decl = m.mk_func_decl(fid, k, 0, nullptr, num_args, args, nullptr); SASSERT(decl != 0); SASSERT(decl->is_associative()); return mk_list_assoc_app(m, decl, num_args, args); @@ -98,7 +98,7 @@ bool is_atom(ast_manager & m, expr * n) { return true; SASSERT(is_app(n)); if (to_app(n)->get_family_id() != m.get_basic_family_id()) { - return true; + return true; } // the other operators of the basic family are not considered atomic: distinct, ite, and, or, iff, xor, not, implies. return (m.is_eq(n) && !m.is_bool(to_app(n)->get_arg(0))) || m.is_true(n) || m.is_false(n); @@ -106,7 +106,7 @@ bool is_atom(ast_manager & m, expr * n) { bool is_literal(ast_manager & m, expr * n) { - return + return is_atom(m, n) || (m.is_not(n) && is_atom(m, to_app(n)->get_arg(0))); } @@ -187,7 +187,7 @@ expr * mk_not(ast_manager & m, expr * arg) { expr * atom; if (m.is_not(arg, atom)) return atom; - else if (m.is_true(arg)) + else if (m.is_true(arg)) return m.mk_false(); else if (m.is_false(arg)) return m.mk_true(); @@ -195,6 +195,11 @@ expr * mk_not(ast_manager & m, expr * arg) { return m.mk_not(arg); } +expr_ref mk_not(const expr_ref& e) { + return expr_ref(mk_not(e.m(), e), e.m()); +} + + expr_ref push_not(const expr_ref& e) { ast_manager& m = e.get_manager(); if (!is_app(e)) { @@ -221,7 +226,7 @@ expr_ref push_not(const expr_ref& e) { } return mk_and(args); } - return expr_ref(mk_not(m, e), m); + return expr_ref(mk_not(m, e), m); } expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) { @@ -277,7 +282,7 @@ void flatten_and(expr_ref_vector& result) { } result[i] = result.back(); result.pop_back(); - --i; + --i; } else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) { result.push_back(e2); @@ -289,7 +294,7 @@ void flatten_and(expr_ref_vector& result) { m.is_false(e1))) { result[i] = result.back(); result.pop_back(); - --i; + --i; } else if (m.is_false(result[i].get()) || (m.is_not(result[i].get(), e1) && @@ -303,10 +308,17 @@ void flatten_and(expr_ref_vector& result) { void flatten_and(expr* fml, expr_ref_vector& result) { SASSERT(result.get_manager().is_bool(fml)); - result.push_back(fml); + result.push_back(fml); flatten_and(result); } +void flatten_and(expr_ref& fml) { + expr_ref_vector fmls(fml.get_manager()); + fmls.push_back(fml); + flatten_and(fmls); + fml = mk_and(fmls); +} + void flatten_or(expr_ref_vector& result) { ast_manager& m = result.get_manager(); expr* e1, *e2, *e3; @@ -333,7 +345,7 @@ void flatten_or(expr_ref_vector& result) { } result[i] = result.back(); result.pop_back(); - --i; + --i; } else if (m.is_implies(result[i].get(),e2,e3)) { result.push_back(e3); @@ -345,7 +357,7 @@ void flatten_or(expr_ref_vector& result) { m.is_true(e1))) { result[i] = result.back(); result.pop_back(); - --i; + --i; } else if (m.is_true(result[i].get()) || (m.is_not(result[i].get(), e1) && @@ -354,12 +366,12 @@ void flatten_or(expr_ref_vector& result) { result.push_back(m.mk_true()); return; } - } + } } void flatten_or(expr* fml, expr_ref_vector& result) { SASSERT(result.get_manager().is_bool(fml)); - result.push_back(fml); + result.push_back(fml); flatten_or(result); } diff --git a/src/ast/ast_util.h b/src/ast/ast_util.h index 446854f5e..23c2205bb 100644 --- a/src/ast/ast_util.h +++ b/src/ast/ast_util.h @@ -127,6 +127,8 @@ inline expr_ref mk_or(expr_ref_vector const& args) { return expr_ref(mk_or(args. */ expr * mk_not(ast_manager & m, expr * arg); +expr_ref mk_not(const expr_ref& e); + /** Negate and push over conjunction or disjunction. */ @@ -150,6 +152,8 @@ expr_ref mk_distinct(expr_ref_vector const& args); void flatten_and(expr_ref_vector& result); +void flatten_and(expr_ref& fml); + void flatten_and(expr* fml, expr_ref_vector& result); void flatten_or(expr_ref_vector& result); @@ -158,4 +162,3 @@ void flatten_or(expr* fml, expr_ref_vector& result); #endif /* AST_UTIL_H_ */ - diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp index 28c3d5f5d..4869949b7 100644 --- a/src/ast/bv_decl_plugin.cpp +++ b/src/ast/bv_decl_plugin.cpp @@ -34,11 +34,11 @@ bv_decl_plugin::bv_decl_plugin(): m_repeat_sym("repeat"), m_bit2bool_sym("bit2bool"), m_mkbv_sym("mkbv"), - m_bit0(0), - m_bit1(0), - m_carry(0), - m_xor3(0), - m_int_sort(0) { + m_bit0(nullptr), + m_bit1(nullptr), + m_carry(nullptr), + m_xor3(nullptr), + m_int_sort(nullptr) { } void bv_decl_plugin::set_manager(ast_manager * m, family_id id) { @@ -218,7 +218,7 @@ func_decl * bv_decl_plugin::mk_int2bv(unsigned bv_size, unsigned num_parameters, if (arity != 1) { m_manager->raise_exception("expecting one argument to int2bv"); - return 0; + return nullptr; } if (m_int2bv[bv_size] == 0) { @@ -237,7 +237,7 @@ func_decl * bv_decl_plugin::mk_bv2int(unsigned bv_size, unsigned num_parameters, if (arity != 1) { m_manager->raise_exception("expecting one argument to bv2int"); - return 0; + return nullptr; } if (m_bv2int[bv_size] == 0) { @@ -341,7 +341,7 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned bv_size) { case OP_EXT_ROTATE_LEFT: return mk_binary(m_ext_rotate_left, k, "ext_rotate_left", bv_size, false); case OP_EXT_ROTATE_RIGHT: return mk_binary(m_ext_rotate_right, k, "ext_rotate_right", bv_size, false); - default: return 0; + default: return nullptr; } } @@ -417,7 +417,7 @@ bool bv_decl_plugin::get_int2bv_size(unsigned num_parameters, parameter const * func_decl * bv_decl_plugin::mk_num_decl(unsigned num_parameters, parameter const * parameters, unsigned arity) { if (!(num_parameters == 2 && arity == 0 && parameters[0].is_rational() && parameters[1].is_int())) { m_manager->raise_exception("invalid bit-vector numeral declaration"); - return 0; + return nullptr; } unsigned bv_size = parameters[1].get_int(); if (bv_size == 0) { @@ -437,7 +437,7 @@ func_decl * bv_decl_plugin::mk_bit2bool(unsigned bv_size, unsigned num_parameter unsigned arity, sort * const * domain) { if (!(num_parameters == 1 && parameters[0].is_int() && arity == 1 && parameters[0].get_int() < static_cast(bv_size))) { m_manager->raise_exception("invalid bit2bool declaration"); - return 0; + return nullptr; } unsigned idx = parameters[0].get_int(); m_bit2bool.reserve(bv_size+1); @@ -455,7 +455,7 @@ func_decl * bv_decl_plugin::mk_mkbv(unsigned arity, sort * const * domain) { for (unsigned i = 0; i < arity; i++) { if (!m_manager->is_bool(domain[i])) { m_manager->raise_exception("invalid mkbv operator"); - return 0; + return nullptr; } } unsigned bv_size = arity; @@ -493,26 +493,26 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p } else if (arity == 0) { m_manager->raise_exception("no arguments supplied to bit-vector operator"); - return 0; + return nullptr; } else if (!get_bv_size(domain[0], bv_size)) { m_manager->raise_exception("could not extract bit-vector size"); - return 0; + return nullptr; } func_decl * r = mk_func_decl(k, bv_size); - if (r != 0) { + if (r != nullptr) { if (arity != r->get_arity()) { if (r->get_info()->is_associative()) arity = r->get_arity(); else { m_manager->raise_exception("declared arity mismatches supplied arity"); - return 0; + return nullptr; } } for (unsigned i = 0; i < arity; ++i) { if (domain[i] != r->get_domain(i)) { m_manager->raise_exception("declared sorts do not match supplied sorts"); - return 0; + return nullptr; } } return r; @@ -565,7 +565,7 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p return m_manager->mk_func_decl(m_repeat_sym, arity, domain, get_bv_sort(bv_size * parameters[0].get_int()), func_decl_info(m_family_id, k, num_parameters, parameters)); default: - return 0; + return nullptr; } } @@ -596,24 +596,24 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p } else if (num_args == 0 || !get_bv_size(args[0], bv_size)) { m.raise_exception("operator is applied to arguments of the wrong sort"); - return 0; + return nullptr; } func_decl * r = mk_func_decl(k, bv_size); - if (r != 0) { + if (r != nullptr) { if (num_args != r->get_arity()) { if (r->get_info()->is_associative()) { sort * fs = r->get_domain(0); for (unsigned i = 0; i < num_args; ++i) { if (m.get_sort(args[i]) != fs) { m_manager->raise_exception("declared sorts do not match supplied sorts"); - return 0; + return nullptr; } } return r; } else { m.raise_exception("declared arity mismatches supplied arity"); - return 0; + return nullptr; } } for (unsigned i = 0; i < num_args; ++i) { @@ -621,7 +621,7 @@ func_decl * bv_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p std::ostringstream buffer; buffer << "Argument " << mk_pp(args[i], m) << " at position " << i << " does not match declaration " << mk_pp(r, m); m.raise_exception(buffer.str().c_str()); - return 0; + return nullptr; } } return r; @@ -717,7 +717,7 @@ void bv_decl_plugin::get_op_names(svector & op_names, symbol const op_names.push_back(builtin_name("rotate_left",OP_ROTATE_LEFT)); op_names.push_back(builtin_name("rotate_right",OP_ROTATE_RIGHT)); - if (logic == symbol::null || logic == symbol("ALL")) { + if (logic == symbol::null || logic == symbol("ALL") || logic == "QF_FD") { op_names.push_back(builtin_name("bvumul_noovfl",OP_BUMUL_NO_OVFL)); op_names.push_back(builtin_name("bvsmul_noovfl",OP_BSMUL_NO_OVFL)); op_names.push_back(builtin_name("bvsmul_noudfl",OP_BSMUL_NO_UDFL)); @@ -747,7 +747,7 @@ expr * bv_decl_plugin::get_some_value(sort * s) { SASSERT(s->is_sort_of(m_family_id, BV_SORT)); unsigned bv_size = s->get_parameter(0).get_int(); parameter p[2] = { parameter(rational(0)), parameter(static_cast(bv_size)) }; - return m_manager->mk_app(m_family_id, OP_BV_NUM, 2, p, 0, 0); + return m_manager->mk_app(m_family_id, OP_BV_NUM, 2, p, 0, nullptr); } rational bv_recognizers::norm(rational const & val, unsigned bv_size, bool is_signed) const { @@ -856,7 +856,7 @@ bv_util::bv_util(ast_manager & m): app * bv_util::mk_numeral(rational const & val, sort* s) const { if (!is_bv_sort(s)) { - return 0; + return nullptr; } unsigned bv_size = get_bv_size(s); return mk_numeral(val, bv_size); @@ -864,7 +864,7 @@ app * bv_util::mk_numeral(rational const & val, sort* s) const { app * bv_util::mk_numeral(rational const & val, unsigned bv_size) const { parameter p[2] = { parameter(val), parameter(static_cast(bv_size)) }; - return m_manager.mk_app(get_fid(), OP_BV_NUM, 2, p, 0, 0); + return m_manager.mk_app(get_fid(), OP_BV_NUM, 2, p, 0, nullptr); } sort * bv_util::mk_sort(unsigned bv_size) { diff --git a/src/ast/bv_decl_plugin.h b/src/ast/bv_decl_plugin.h index a4ea7af80..e128f2c03 100644 --- a/src/ast/bv_decl_plugin.h +++ b/src/ast/bv_decl_plugin.h @@ -122,7 +122,7 @@ inline bv_op_kind get_div0_op(bv_op_kind k) { // models the value of "div" it is underspecified (i.e., when the denominator is zero). inline func_decl * get_div0_decl(ast_manager & m, func_decl * decl) { return m.mk_func_decl(decl->get_family_id(), get_div0_op(static_cast(decl->get_decl_kind())), - 0, 0, 1, decl->get_domain()); + 0, nullptr, 1, decl->get_domain()); } class bv_decl_plugin : public decl_plugin { @@ -204,7 +204,7 @@ protected: vector > m_bit2bool; ptr_vector m_mkbv; - virtual void set_manager(ast_manager * m, family_id id); + void set_manager(ast_manager * m, family_id id) override; void mk_bv_sort(unsigned bv_size); sort * get_bv_sort(unsigned bv_size); func_decl * mk_func_decl(decl_kind k, unsigned bv_size); @@ -241,34 +241,34 @@ protected: public: bv_decl_plugin(); - virtual ~bv_decl_plugin() {} - virtual void finalize(); + ~bv_decl_plugin() override {} + void finalize() override; - virtual decl_plugin * mk_fresh() { return alloc(bv_decl_plugin); } + decl_plugin * mk_fresh() override { return alloc(bv_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned num_args, expr * const * args, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned num_args, expr * const * args, sort * range) override; - virtual bool is_value(app * e) const; + bool is_value(app * e) const override; - virtual bool is_unique_value(app * e) const { return is_value(e); } + bool is_unique_value(app * e) const override { return is_value(e); } - virtual void get_op_names(svector & op_names, symbol const & logic); + void get_op_names(svector & op_names, symbol const & logic) override; - virtual void get_sort_names(svector & sort_names, symbol const & logic); + void get_sort_names(svector & sort_names, symbol const & logic) override; - virtual bool are_distinct(app* a, app* b) const; + bool are_distinct(app* a, app* b) const override; - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; bool get_int2bv_size(unsigned num_parameters, parameter const * parameters, int & result); - virtual bool is_considered_uninterpreted(func_decl * f) { + bool is_considered_uninterpreted(func_decl * f) override { if (f->get_family_id() != get_family_id()) return false; switch (f->get_decl_kind()) { @@ -384,7 +384,7 @@ public: app * mk_numeral(rational const & val, sort* s) const; app * mk_numeral(rational const & val, unsigned bv_size) const; - app * mk_numeral(uint64 u, unsigned bv_size) const { return mk_numeral(rational(u, rational::ui64()), bv_size); } + app * mk_numeral(uint64_t u, unsigned bv_size) const { return mk_numeral(rational(u, rational::ui64()), bv_size); } sort * mk_sort(unsigned bv_size); unsigned get_bv_size(sort const * s) const { diff --git a/src/ast/datatype_decl_plugin.cpp b/src/ast/datatype_decl_plugin.cpp index 91e636ed7..d3704a84a 100644 --- a/src/ast/datatype_decl_plugin.cpp +++ b/src/ast/datatype_decl_plugin.cpp @@ -157,13 +157,13 @@ namespace datatype { dealloc(kv.m_value); } m_defs.reset(); - m_util = 0; // force deletion + m_util = nullptr; // force deletion } util & plugin::u() const { SASSERT(m_manager); SASSERT(m_family_id != null_family_id); - if (m_util.get() == 0) { + if (m_util.get() == nullptr) { m_util = alloc(util, *m_manager); } return *(m_util.get()); @@ -215,7 +215,7 @@ namespace datatype { sort* s = m_manager->mk_sort(name.get_symbol(), sort_info(m_family_id, k, num_parameters, parameters, true)); - def* d = 0; + def* d = nullptr; if (m_defs.find(s->get_name(), d) && d->sort_size()) { obj_map S; for (unsigned i = 0; i + 1 < num_parameters; ++i) { @@ -233,7 +233,7 @@ namespace datatype { } catch (invalid_datatype) { m_manager->raise_exception("invalid datatype"); - return 0; + return nullptr; } } @@ -245,35 +245,35 @@ namespace datatype { if (num_parameters != 1 || !parameters[0].is_ast()) { m.raise_exception("invalid parameters for datatype field update"); - return 0; + return nullptr; } if (arity != 2) { m.raise_exception("invalid number of arguments for datatype field update"); - return 0; + return nullptr; } - func_decl* acc = 0; + func_decl* acc = nullptr; if (is_func_decl(parameters[0].get_ast())) { acc = to_func_decl(parameters[0].get_ast()); } if (acc && !u().is_accessor(acc)) { - acc = 0; + acc = nullptr; } if (!acc) { m.raise_exception("datatype field update requires a datatype accessor as the second argument"); - return 0; + return nullptr; } sort* dom = acc->get_domain(0); sort* rng = acc->get_range(); if (dom != domain[0]) { m.raise_exception("first argument to field update should be a data-type"); - return 0; + return nullptr; } if (rng != domain[1]) { std::ostringstream buffer; buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m) << " instead of " << mk_ismt2_pp(domain[1], m); m.raise_exception(buffer.str().c_str()); - return 0; + return nullptr; } range = domain[0]; func_decl_info info(m_family_id, k, num_parameters, parameters); @@ -345,7 +345,7 @@ namespace datatype { return mk_update_field(num_parameters, parameters, arity, domain, range); default: m_manager->raise_exception("invalid datatype operator kind"); - return 0; + return nullptr; } } @@ -386,10 +386,11 @@ namespace datatype { bool plugin::mk_datatypes(unsigned num_datatypes, def * const * datatypes, unsigned num_params, sort* const* sort_params, sort_ref_vector & new_sorts) { begin_def_block(); for (unsigned i = 0; i < num_datatypes; ++i) { - def* d = 0; + def* d = nullptr; TRACE("datatype", tout << "declaring " << datatypes[i]->name() << "\n";); if (m_defs.find(datatypes[i]->name(), d)) { TRACE("datatype", tout << "delete previous version for " << datatypes[i]->name() << "\n";); + u().reset(); dealloc(d); } m_defs.insert(datatypes[i]->name(), datatypes[i]); @@ -404,7 +405,7 @@ namespace datatype { } void plugin::remove(symbol const& s) { - def* d = 0; + def* d = nullptr; if (m_defs.find(s, d)) dealloc(d); m_defs.remove(s); } @@ -750,7 +751,7 @@ namespace datatype { ptr_vector const * util::get_datatype_constructors(sort * ty) { SASSERT(is_datatype(ty)); - ptr_vector * r = 0; + ptr_vector * r = nullptr; if (m_datatype2constructors.find(ty, r)) return r; r = alloc(ptr_vector); @@ -768,7 +769,7 @@ namespace datatype { ptr_vector const * util::get_constructor_accessors(func_decl * con) { SASSERT(is_constructor(con)); - ptr_vector * res = 0; + ptr_vector * res = nullptr; if (m_constructor2accessors.find(con, res)) { return res; } @@ -791,9 +792,17 @@ namespace datatype { return res; } + + func_decl * util::get_constructor_is(func_decl * con) { + SASSERT(is_constructor(con)); + sort * datatype = con->get_range(); + parameter ps[1] = { parameter(con)}; + return m.mk_func_decl(m_family_id, OP_DT_IS, 1, ps, 1, &datatype); + } + func_decl * util::get_constructor_recognizer(func_decl * con) { SASSERT(is_constructor(con)); - func_decl * d = 0; + func_decl * d = nullptr; if (m_constructor2recognizer.find(con, d)) return d; sort * datatype = con->get_range(); @@ -813,6 +822,10 @@ namespace datatype { return d; } + app* util::mk_is(func_decl * c, expr *f) { + return m.mk_app(get_constructor_is(c), 1, &f); + } + func_decl * util::get_recognizer_constructor(func_decl * recognizer) const { SASSERT(is_recognizer(recognizer)); return to_func_decl(recognizer->get_parameter(0).get_ast()); @@ -848,7 +861,7 @@ namespace datatype { func_decl * util::get_accessor_constructor(func_decl * accessor) { SASSERT(is_accessor(accessor)); - func_decl * r = 0; + func_decl * r = nullptr; if (m_accessor2constructor.find(accessor, r)) return r; sort * datatype = accessor->get_domain(0); @@ -892,10 +905,10 @@ namespace datatype { */ func_decl * util::get_non_rec_constructor(sort * ty) { SASSERT(is_datatype(ty)); - func_decl * r = 0; + func_decl * r = nullptr; if (m_datatype2nonrec_constructor.find(ty, r)) return r; - r = 0; + r = nullptr; ptr_vector forbidden_set; forbidden_set.push_back(ty); TRACE("util_bug", tout << "invoke get-non-rec: " << sort_ref(ty, m) << "\n";); @@ -962,14 +975,14 @@ namespace datatype { func_decl * nested_c = get_non_rec_constructor_core(T_i, forbidden_set); SASSERT(forbidden_set.back() == T_i); forbidden_set.pop_back(); - if (nested_c == 0) + if (nested_c == nullptr) break; TRACE("util_bug", tout << "nested_c: " << nested_c->get_name() << "\n";); } if (i == num_args) return c; } - return 0; + return nullptr; } unsigned util::get_constructor_idx(func_decl * f) const { @@ -1040,15 +1053,11 @@ namespace datatype { sort* s = todo.back(); todo.pop_back(); out << s->get_name() << " =\n"; - ptr_vector const& cnstrs = *get_datatype_constructors(s); - for (unsigned i = 0; i < cnstrs.size(); ++i) { - func_decl* cns = cnstrs[i]; - func_decl* rec = get_constructor_recognizer(cns); - out << " " << cns->get_name() << " :: " << rec->get_name() << " :: "; + for (func_decl * cns : cnstrs) { + out << " " << cns->get_name() << " :: "; ptr_vector const & accs = *get_constructor_accessors(cns); - for (unsigned j = 0; j < accs.size(); ++j) { - func_decl* acc = accs[j]; + for (func_decl* acc : accs) { sort* s1 = acc->get_range(); out << "(" << acc->get_name() << ": " << s1->get_name() << ") "; if (is_datatype(s1) && are_siblings(s1, s0) && !mark.is_marked(s1)) { diff --git a/src/ast/datatype_decl_plugin.h b/src/ast/datatype_decl_plugin.h index 515ca6e20..17602efcf 100644 --- a/src/ast/datatype_decl_plugin.h +++ b/src/ast/datatype_decl_plugin.h @@ -124,16 +124,16 @@ namespace datatype { struct offset : public size { sort_size m_offset; offset(sort_size const& s): m_offset(s) {} - virtual ~offset() {} - virtual size* subst(obj_map& S) { return this; } - virtual sort_size eval(obj_map const& S) { return m_offset; } + ~offset() override {} + size* subst(obj_map& S) override { return this; } + sort_size eval(obj_map const& S) override { return m_offset; } }; struct plus : public size { size* m_arg1, *m_arg2; plus(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref();} - virtual ~plus() { m_arg1->dec_ref(); m_arg2->dec_ref(); } - virtual size* subst(obj_map& S) { return mk_plus(m_arg1->subst(S), m_arg2->subst(S)); } - virtual sort_size eval(obj_map const& S) { + ~plus() override { m_arg1->dec_ref(); m_arg2->dec_ref(); } + size* subst(obj_map& S) override { return mk_plus(m_arg1->subst(S), m_arg2->subst(S)); } + sort_size eval(obj_map const& S) override { sort_size s1 = m_arg1->eval(S); sort_size s2 = m_arg2->eval(S); if (s1.is_infinite()) return s1; @@ -147,9 +147,9 @@ namespace datatype { struct times : public size { size* m_arg1, *m_arg2; times(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); } - virtual ~times() { m_arg1->dec_ref(); m_arg2->dec_ref(); } - virtual size* subst(obj_map& S) { return mk_times(m_arg1->subst(S), m_arg2->subst(S)); } - virtual sort_size eval(obj_map const& S) { + ~times() override { m_arg1->dec_ref(); m_arg2->dec_ref(); } + size* subst(obj_map& S) override { return mk_times(m_arg1->subst(S), m_arg2->subst(S)); } + sort_size eval(obj_map const& S) override { sort_size s1 = m_arg1->eval(S); sort_size s2 = m_arg2->eval(S); if (s1.is_infinite()) return s1; @@ -163,9 +163,9 @@ namespace datatype { struct power : public size { size* m_arg1, *m_arg2; power(size* a1, size* a2): m_arg1(a1), m_arg2(a2) { a1->inc_ref(); a2->inc_ref(); } - virtual ~power() { m_arg1->dec_ref(); m_arg2->dec_ref(); } - virtual size* subst(obj_map& S) { return mk_power(m_arg1->subst(S), m_arg2->subst(S)); } - virtual sort_size eval(obj_map const& S) { + ~power() override { m_arg1->dec_ref(); m_arg2->dec_ref(); } + size* subst(obj_map& S) override { return mk_power(m_arg1->subst(S), m_arg2->subst(S)); } + sort_size eval(obj_map const& S) override { sort_size s1 = m_arg1->eval(S); sort_size s2 = m_arg2->eval(S); // s1^s2 @@ -183,9 +183,9 @@ namespace datatype { struct sparam : public size { sort_ref m_param; sparam(sort_ref& p): m_param(p) {} - virtual ~sparam() {} - virtual size* subst(obj_map& S) { return S[m_param]; } - virtual sort_size eval(obj_map const& S) { return S[m_param]; } + ~sparam() override {} + size* subst(obj_map& S) override { return S[m_param]; } + sort_size eval(obj_map const& S) override { return S[m_param]; } }; }; @@ -204,7 +204,7 @@ namespace datatype { m_util(u), m_name(n), m_class_id(class_id), - m_sort_size(0), + m_sort_size(nullptr), m_params(m, num_params, params), m_sort(m) {} @@ -228,7 +228,7 @@ namespace datatype { sort_ref_vector const& params() const { return m_params; } util& u() const { return m_util; } param_size::size* sort_size() { return m_sort_size; } - void set_sort_size(param_size::size* p) { m_sort_size = p; p->inc_ref(); m_sort = 0; } + void set_sort_size(param_size::size* p) { m_sort_size = p; p->inc_ref(); m_sort = nullptr; } def* translate(ast_translation& tr, util& u); }; @@ -239,32 +239,31 @@ namespace datatype { map m_defs; svector m_def_block; unsigned m_class_id; - util & u() const; - virtual void inherit(decl_plugin* other_p, ast_translation& tr); + void inherit(decl_plugin* other_p, ast_translation& tr) override; public: plugin(): m_class_id(0) {} - virtual ~plugin(); + ~plugin() override; - virtual void finalize(); + void finalize() override; - virtual decl_plugin * mk_fresh() { return alloc(plugin); } + decl_plugin * mk_fresh() override { return alloc(plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; - virtual bool is_fully_interp(sort * s) const; + bool is_fully_interp(sort * s) const override; - virtual bool is_value(app* e) const; + bool is_value(app* e) const override; - virtual bool is_unique_value(app * e) const { return is_value(e); } + bool is_unique_value(app * e) const override { return is_value(e); } - virtual void get_op_names(svector & op_names, symbol const & logic); + void get_op_names(svector & op_names, symbol const & logic) override; void begin_def_block() { m_class_id++; m_def_block.reset(); } @@ -279,6 +278,8 @@ namespace datatype { def const& get_def(sort* s) const { return *(m_defs[datatype_name(s)]); } def& get_def(symbol const& s) { return *(m_defs[s]); } bool is_declared(sort* s) const { return m_defs.contains(datatype_name(s)); } + util & u() const; + private: bool is_value_visit(expr * arg, ptr_buffer & todo) const; @@ -357,17 +358,21 @@ namespace datatype { bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); } bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); } bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); } + bool is_constructor(expr* e) const { return is_app(e) && is_constructor(to_app(e)); } bool is_recognizer0(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER);} bool is_is(app * f) const { return is_app_of(f, m_family_id, OP_DT_IS);} + bool is_is(expr * e) const { return is_app(e) && is_is(to_app(e)); } bool is_recognizer(app * f) const { return is_recognizer0(f) || is_is(f); } bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); } bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); } + app* mk_is(func_decl * c, expr *f); ptr_vector const * get_datatype_constructors(sort * ty); unsigned get_datatype_num_constructors(sort * ty); unsigned get_datatype_num_parameter_sorts(sort * ty); sort* get_datatype_parameter_sort(sort * ty, unsigned idx); func_decl * get_non_rec_constructor(sort * ty); func_decl * get_constructor_recognizer(func_decl * constructor); + func_decl * get_constructor_is(func_decl * constructor); ptr_vector const * get_constructor_accessors(func_decl * constructor); func_decl * get_accessor_constructor(func_decl * accessor); func_decl * get_recognizer_constructor(func_decl * recognizer) const; @@ -398,7 +403,7 @@ typedef datatype::util datatype_util; class type_ref { void * m_data; public: - type_ref():m_data(TAG(void *, static_cast(0), 1)) {} + type_ref():m_data(TAG(void *, nullptr, 1)) {} type_ref(int idx):m_data(BOXINT(void *, idx)) {} type_ref(sort * s):m_data(TAG(void *, s, 1)) {} diff --git a/src/ast/decl_collector.cpp b/src/ast/decl_collector.cpp index e000f43df..bec60e61c 100644 --- a/src/ast/decl_collector.cpp +++ b/src/ast/decl_collector.cpp @@ -18,26 +18,28 @@ Revision History: --*/ #include "ast/decl_collector.h" +#include "ast/ast_pp.h" void decl_collector::visit_sort(sort * n) { + SASSERT(!m_visited.is_marked(n)); family_id fid = n->get_family_id(); if (m().is_uninterp(n)) m_sorts.push_back(n); - if (fid == m_dt_fid) { + else if (fid == m_dt_fid) { m_sorts.push_back(n); - - unsigned num_cnstr = m_dt_util.get_datatype_num_constructors(n); - for (unsigned i = 0; i < num_cnstr; i++) { - func_decl * cnstr = m_dt_util.get_datatype_constructors(n)->get(i); - m_decls.push_back(cnstr); + for (func_decl * cnstr : *m_dt_util.get_datatype_constructors(n)) { + m_todo.push_back(cnstr); ptr_vector const & cnstr_acc = *m_dt_util.get_constructor_accessors(cnstr); unsigned num_cas = cnstr_acc.size(); for (unsigned j = 0; j < num_cas; j++) { - func_decl * accsr = cnstr_acc.get(j); - m_decls.push_back(accsr); + m_todo.push_back(cnstr_acc.get(j)); } } } + for (unsigned i = n->get_num_parameters(); i-- > 0; ) { + parameter const& p = n->get_parameter(i); + if (p.is_ast()) m_todo.push_back(p.get_ast()); + } } bool decl_collector::is_bool(sort * s) { @@ -45,12 +47,15 @@ bool decl_collector::is_bool(sort * s) { } void decl_collector::visit_func(func_decl * n) { - family_id fid = n->get_family_id(); - if (fid == null_family_id) { - if (m_sep_preds && is_bool(n->get_range())) - m_preds.push_back(n); - else - m_decls.push_back(n); + if (!m_visited.is_marked(n)) { + family_id fid = n->get_family_id(); + if (fid == null_family_id) { + if (m_sep_preds && is_bool(n->get_range())) + m_preds.push_back(n); + else + m_decls.push_back(n); + } + m_visited.mark(n, true); } } @@ -63,43 +68,42 @@ decl_collector::decl_collector(ast_manager & m, bool preds): } void decl_collector::visit(ast* n) { - ptr_vector todo; - todo.push_back(n); - while (!todo.empty()) { - n = todo.back(); - todo.pop_back(); + datatype_util util(m()); + m_todo.push_back(n); + while (!m_todo.empty()) { + n = m_todo.back(); + m_todo.pop_back(); if (!m_visited.is_marked(n)) { - m_visited.mark(n, true); switch(n->get_kind()) { case AST_APP: { app * a = to_app(n); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - todo.push_back(a->get_arg(i)); + for (expr* arg : *a) { + m_todo.push_back(arg); } - todo.push_back(a->get_decl()); + m_todo.push_back(a->get_decl()); break; } case AST_QUANTIFIER: { quantifier * q = to_quantifier(n); unsigned num_decls = q->get_num_decls(); for (unsigned i = 0; i < num_decls; ++i) { - todo.push_back(q->get_decl_sort(i)); + m_todo.push_back(q->get_decl_sort(i)); } - todo.push_back(q->get_expr()); + m_todo.push_back(q->get_expr()); for (unsigned i = 0; i < q->get_num_patterns(); ++i) { - todo.push_back(q->get_pattern(i)); + m_todo.push_back(q->get_pattern(i)); } break; } - case AST_SORT: + case AST_SORT: visit_sort(to_sort(n)); break; case AST_FUNC_DECL: { func_decl * d = to_func_decl(n); - for (unsigned i = 0; i < d->get_arity(); ++i) { - todo.push_back(d->get_domain(i)); + for (sort* srt : *d) { + m_todo.push_back(srt); } - todo.push_back(d->get_range()); + m_todo.push_back(d->get_range()); visit_func(d); break; } @@ -108,9 +112,49 @@ void decl_collector::visit(ast* n) { default: UNREACHABLE(); } + m_visited.mark(n, true); + } + } +} + +void decl_collector::order_deps() { + top_sort st; + for (sort * s : m_sorts) st.insert(s, collect_deps(s)); + st.topological_sort(); + m_sorts.reset(); + for (sort* s : st.top_sorted()) m_sorts.push_back(s); +} + +decl_collector::sort_set* decl_collector::collect_deps(sort* s) { + sort_set* set = alloc(sort_set); + collect_deps(s, *set); + set->remove(s); + return set; +} + +void decl_collector::collect_deps(sort* s, sort_set& set) { + if (set.contains(s)) return; + set.insert(s); + if (s->is_sort_of(m_dt_util.get_family_id(), DATATYPE_SORT)) { + unsigned num_sorts = m_dt_util.get_datatype_num_parameter_sorts(s); + for (unsigned i = 0; i < num_sorts; ++i) { + set.insert(m_dt_util.get_datatype_parameter_sort(s, i)); + } + unsigned num_cnstr = m_dt_util.get_datatype_num_constructors(s); + for (unsigned i = 0; i < num_cnstr; i++) { + func_decl * cnstr = m_dt_util.get_datatype_constructors(s)->get(i); + set.insert(cnstr->get_range()); + for (unsigned j = 0; j < cnstr->get_arity(); ++j) + set.insert(cnstr->get_domain(j)); + } + } + + for (unsigned i = s->get_num_parameters(); i-- > 0; ) { + parameter const& p = s->get_parameter(i); + if (p.is_ast() && is_sort(p.get_ast())) { + set.insert(to_sort(p.get_ast())); } } } - diff --git a/src/ast/decl_collector.h b/src/ast/decl_collector.h index 053d6df41..07539dbd8 100644 --- a/src/ast/decl_collector.h +++ b/src/ast/decl_collector.h @@ -20,6 +20,7 @@ Revision History: #ifndef SMT_DECL_COLLECTOR_H_ #define SMT_DECL_COLLECTOR_H_ +#include "util/top_sort.h" #include "ast/ast.h" #include "ast/datatype_decl_plugin.h" @@ -33,24 +34,33 @@ class decl_collector { family_id m_basic_fid; family_id m_dt_fid; datatype_util m_dt_util; + ptr_vector m_todo; void visit_sort(sort* n); bool is_bool(sort* s); - void visit_func(func_decl* n); + + typedef obj_hashtable sort_set; + sort_set* collect_deps(sort* s); + void collect_deps(top_sort& st); + void collect_deps(sort* s, sort_set& set); public: // if preds == true, then predicates are stored in a separate collection. - decl_collector(ast_manager & m, bool preds=true); + decl_collector(ast_manager & m, bool preds = true); ast_manager & m() { return m_manager; } + void visit_func(func_decl* n); void visit(ast * n); void visit(unsigned n, expr* const* es); void visit(expr_ref_vector const& es); + void order_deps(); + unsigned get_num_sorts() const { return m_sorts.size(); } unsigned get_num_decls() const { return m_decls.size(); } unsigned get_num_preds() const { return m_preds.size(); } + sort * const * get_sorts() const { return m_sorts.c_ptr(); } func_decl * const * get_func_decls() const { return m_decls.c_ptr(); } func_decl * const * get_pred_decls() const { return m_preds.c_ptr(); } diff --git a/src/ast/dl_decl_plugin.cpp b/src/ast/dl_decl_plugin.cpp index 1be3c4756..f4a538abd 100644 --- a/src/ast/dl_decl_plugin.cpp +++ b/src/ast/dl_decl_plugin.cpp @@ -72,7 +72,7 @@ namespace datalog { for (unsigned i = 0; is_finite && i < num_parameters; ++i) { if (!parameters[i].is_ast() || !is_sort(parameters[i].get_ast())) { m_manager->raise_exception("expecting sort parameters"); - return 0; + return nullptr; } sort* s = to_sort(parameters[i].get_ast()); sort_size sz1 = s->get_num_elements(); @@ -97,16 +97,16 @@ namespace datalog { sort * dl_decl_plugin::mk_finite_sort(unsigned num_params, parameter const* params) { if (num_params != 2) { m_manager->raise_exception("expecting two parameters"); - return 0; + return nullptr; } if (!params[0].is_symbol()) { m_manager->raise_exception("expecting symbol"); - return 0; + return nullptr; } if (!params[1].is_rational() || !params[1].get_rational().is_uint64()) { m_manager->raise_exception("expecting rational"); - return 0; + return nullptr; } sort_size sz = sort_size::mk_finite(params[1].get_rational().get_uint64()); sort_info info(m_family_id, DL_FINITE_SORT, sz, num_params, params); @@ -115,7 +115,7 @@ namespace datalog { sort* dl_decl_plugin::mk_rule_sort() { sort_size sz(sort_size::mk_infinite()); - sort_info info(m_family_id, DL_RULE_SORT, sz, 0, 0); + sort_info info(m_family_id, DL_RULE_SORT, sz, 0, nullptr); return m_manager->mk_sort(m_rule_sym, info); } @@ -130,7 +130,7 @@ namespace datalog { default: UNREACHABLE(); } - return 0; + return nullptr; } bool dl_decl_plugin::is_rel_sort(sort* r) { @@ -173,11 +173,11 @@ namespace datalog { } ptr_vector sorts; if (!is_rel_sort(r, sorts)) { - return 0; + return nullptr; } if (sorts.size() + 1 != arity) { m_manager->raise_exception("wrong arity supplied to relational access"); - return 0; + return nullptr; } for (unsigned i = 0; i < sorts.size(); ++i) { if (sorts[i] != domain[i+1]) { @@ -186,10 +186,10 @@ namespace datalog { mk_pp(sorts[i], m) << "\n" << mk_pp(domain[i+1], m) << "\n";); m_manager->raise_exception("sort miss-match for relational access"); - return 0; + return nullptr; } } - func_decl_info info(m_family_id, k, 0, 0); + func_decl_info info(m_family_id, k, 0, nullptr); return m.mk_func_decl(sym, arity, domain, r, info); } @@ -197,14 +197,14 @@ namespace datalog { ast_manager& m = *m_manager; if (!p.is_ast() || !is_sort(p.get_ast())) { m_manager->raise_exception("expected sort parameter"); - return 0; + return nullptr; } sort* r = to_sort(p.get_ast()); if (!is_rel_sort(r)) { - return 0; + return nullptr; } func_decl_info info(m_family_id, OP_RA_EMPTY, 1, &p); - return m.mk_func_decl(m_empty_sym, 0, (sort*const*)0, r, info); + return m.mk_func_decl(m_empty_sym, 0, (sort*const*)nullptr, r, info); } func_decl* dl_decl_plugin::mk_project(unsigned num_params, parameter const* params, sort* r) { @@ -219,7 +219,7 @@ namespace datalog { tout << "\n"; ); if (!is_rel_sort(r, sorts)) { - return 0; + return nullptr; } SASSERT(sorts.size() >= num_params); // populate ps @@ -227,12 +227,12 @@ namespace datalog { for (; i < num_params; ++i) { if (!params[i].is_int()) { m_manager->raise_exception("expecting integer parameter"); - return 0; + return nullptr; } unsigned k = params[i].get_int(); if (j > k) { m_manager->raise_exception("arguments to projection should be increasing"); - return 0; + return nullptr; } while (j < k) { ps.push_back(parameter(sorts[j])); @@ -253,13 +253,13 @@ namespace datalog { ast_manager& m = *m_manager; if (s1 != s2) { m_manager->raise_exception("sort miss-match for arguments to union"); - return 0; + return nullptr; } if (!is_rel_sort(s1)) { - return 0; + return nullptr; } sort* domain[2] = { s1, s2 }; - func_decl_info info(m_family_id, k, 0, 0); + func_decl_info info(m_family_id, k, 0, nullptr); return m.mk_func_decl(m_union_sym, 2, domain, s1, info); } @@ -267,7 +267,7 @@ namespace datalog { ast_manager& m = *m_manager; ptr_vector sorts; if (!is_rel_sort(r, sorts)) { - return 0; + return nullptr; } if (!p.is_ast() || !is_expr(p.get_ast())) { m_manager->raise_exception("ast expression expected to filter"); @@ -277,7 +277,7 @@ namespace datalog { // 2. the free variables in f correspond to column types of r. if (!m.is_bool(f)) { m_manager->raise_exception("filter predicate should be of Boolean type"); - return 0; + return nullptr; } ptr_vector todo; todo.push_back(f); @@ -295,11 +295,11 @@ namespace datalog { idx = to_var(e)->get_idx(); if (idx >= sorts.size()) { m_manager->raise_exception("illegal index"); - return 0; + return nullptr; } if (sorts[idx] != m.get_sort(e)) { m_manager->raise_exception("sort miss-match in filter"); - return 0; + return nullptr; } break; case AST_APP: @@ -309,10 +309,10 @@ namespace datalog { break; case AST_QUANTIFIER: m_manager->raise_exception("quantifiers are not allowed in filter expressions"); - return 0; + return nullptr; default: m_manager->raise_exception("unexpected filter expression kind"); - return 0; + return nullptr; } } func_decl_info info(m_family_id, OP_RA_FILTER, 1, &p); @@ -322,23 +322,23 @@ namespace datalog { func_decl * dl_decl_plugin::mk_rename(unsigned num_params, parameter const* params, sort* r) { ptr_vector sorts; if (!is_rel_sort(r, sorts)) { - return 0; + return nullptr; } unsigned index0 = 0; - sort* last_sort = 0; + sort* last_sort = nullptr; SASSERT(num_params > 0); for (unsigned i = 0; i < num_params; ++i) { parameter const& p = params[i]; if (!p.is_int()) { m_manager->raise_exception("expected integer parameter"); - return 0; + return nullptr; } unsigned j = p.get_int(); if (j >= sorts.size()) { // We should not use ast_pp anymore on error messages. // m_manager->raise_exception("index %d out of bound %s : %d", j, ast_pp(r, *m_manager).c_str(), sorts.size()); m_manager->raise_exception("index out of bound"); - return 0; + return nullptr; } if (i == 0) { index0 = j; @@ -362,10 +362,10 @@ namespace datalog { vector params2; ptr_vector sorts1, sorts2; if (!is_rel_sort(r1, sorts1)) { - return 0; + return nullptr; } if (!is_rel_sort(r2, sorts2)) { - return 0; + return nullptr; } for (unsigned i = 0; i < sorts1.size(); ++i) { params2.push_back(parameter(sorts1[i])); @@ -375,24 +375,24 @@ namespace datalog { } if (0 != num_params % 2) { m_manager->raise_exception("expecting an even number of parameters to join"); - return 0; + return nullptr; } for (unsigned i = 0; i + 1 < num_params; i += 2) { parameter const& p1 = params[i]; parameter const& p2 = params[i+1]; if (!p1.is_int() || !p2.is_int()) { m_manager->raise_exception("encountered non-integer parameter"); - return 0; + return nullptr; } unsigned i1 = p1.get_int(); unsigned i2 = p2.get_int(); if (i1 >= sorts1.size() || i2 >= sorts2.size()) { m_manager->raise_exception("index out of bounds"); - return 0; + return nullptr; } if (sorts1[i1] != sorts2[i2]) { m_manager->raise_exception("sort miss-match in join"); - return 0; + return nullptr; } } sort* args[2] = { r1, r2 }; @@ -403,40 +403,40 @@ namespace datalog { func_decl* dl_decl_plugin::mk_complement(sort* s) { if (!is_rel_sort(s)) { - return 0; + return nullptr; } - func_decl_info info(m_family_id, OP_RA_COMPLEMENT, 0, 0); + func_decl_info info(m_family_id, OP_RA_COMPLEMENT, 0, nullptr); return m_manager->mk_func_decl(m_complement_sym, 1, &s, s, info); } func_decl * dl_decl_plugin::mk_negation_filter(unsigned num_params, parameter const* params, sort* r1, sort* r2) { ptr_vector sorts1, sorts2; if (!is_rel_sort(r1, sorts1)) { - return 0; + return nullptr; } if (!is_rel_sort(r2, sorts2)) { - return 0; + return nullptr; } if (0 != num_params % 2) { m_manager->raise_exception("expecting an even number of parameters to negation filter"); - return 0; + return nullptr; } for (unsigned i = 0; i + 1 < num_params; i += 2) { parameter const& p1 = params[i]; parameter const& p2 = params[i+1]; if (!p1.is_int() || !p2.is_int()) { m_manager->raise_exception("encountered non-integer parameter"); - return 0; + return nullptr; } unsigned i1 = p1.get_int(); unsigned i2 = p2.get_int(); if (i1 >= sorts1.size() || i2 >= sorts2.size()) { m_manager->raise_exception("index out of bounds"); - return 0; + return nullptr; } if (sorts1[i1] != sorts2[i2]) { m_manager->raise_exception("sort miss-match in join"); - return 0; + return nullptr; } } sort* args[2] = { r1, r2 }; @@ -446,9 +446,9 @@ namespace datalog { func_decl * dl_decl_plugin::mk_is_empty(sort* s) { if (!is_rel_sort(s)) { - return 0; + return nullptr; } - func_decl_info info(m_family_id, OP_RA_IS_EMPTY, 0, 0); + func_decl_info info(m_family_id, OP_RA_IS_EMPTY, 0, nullptr); sort* rng = m_manager->mk_bool_sort(); return m_manager->mk_func_decl(m_is_empty_sym, 1, &s, rng, info); } @@ -458,35 +458,35 @@ namespace datalog { parameter const& ps = params[1]; if (!p.is_rational() || !p.get_rational().is_uint64()) { m_manager->raise_exception("first parameter should be a rational"); - return 0; + return nullptr; } if (!ps.is_ast() || !is_sort(ps.get_ast()) || !is_fin_sort(to_sort(ps.get_ast()))) { m_manager->raise_exception("second parameter should be a finite domain sort"); - return 0; + return nullptr; } sort* s = to_sort(ps.get_ast()); func_decl_info info(m_family_id, OP_DL_CONSTANT, 2, params); - return m_manager->mk_func_decl(m_num_sym, 0, (sort*const*)0, s, info); + return m_manager->mk_func_decl(m_num_sym, 0, (sort*const*)nullptr, s, info); } func_decl * dl_decl_plugin::mk_compare(decl_kind k, symbol const& sym, sort *const* domain) { if (!is_sort_of(domain[0], m_family_id, DL_FINITE_SORT)) { m_manager->raise_exception("expecting finite domain sort"); - return 0; + return nullptr; } if (domain[0] != domain[1]) { m_manager->raise_exception("expecting two identical finite domain sorts"); - return 0; + return nullptr; } - func_decl_info info(m_family_id, k, 0, 0); + func_decl_info info(m_family_id, k, 0, nullptr); return m_manager->mk_func_decl(sym, 2, domain, m_manager->mk_bool_sort(), info); } func_decl * dl_decl_plugin::mk_clone(sort* s) { if (!is_rel_sort(s)) { - return 0; + return nullptr; } - func_decl_info info(m_family_id, OP_RA_CLONE, 0, 0); + func_decl_info info(m_family_id, OP_RA_CLONE, 0, nullptr); return m_manager->mk_func_decl(m_clone_sym, 1, &s, s, info); } @@ -494,14 +494,14 @@ namespace datalog { func_decl * dl_decl_plugin::mk_func_decl( decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { - func_decl* result = 0; + func_decl* result = nullptr; switch(k) { case OP_RA_STORE: case OP_RA_SELECT: if (!check_params(0, 0, num_parameters) || !check_domain(1, UINT_MAX, arity)) { - return 0; + return nullptr; } result = mk_store_select(k, arity, domain); break; @@ -509,7 +509,7 @@ namespace datalog { case OP_RA_EMPTY: if (!check_params( 1, 1, num_parameters) || !check_domain(0, 0, arity)) { - return 0; + return nullptr; } result = mk_empty(parameters[0]); break; @@ -517,7 +517,7 @@ namespace datalog { case OP_RA_JOIN: if (!check_params(0, UINT_MAX, num_parameters) || !check_domain(2, 2, arity)) { - return 0; + return nullptr; } result = mk_join(num_parameters, parameters, domain[0], domain[1]); break; @@ -526,7 +526,7 @@ namespace datalog { case OP_RA_WIDEN: if (!check_params( 0, 0, num_parameters) || !check_domain(2, 2, arity)) { - return 0; + return nullptr; } result = mk_unionw(k, domain[0], domain[1]); break; @@ -534,7 +534,7 @@ namespace datalog { case OP_RA_PROJECT: if (!check_params( 1, UINT_MAX, num_parameters) || !check_domain(1, 1, arity)) { - return 0; + return nullptr; } result = mk_project(num_parameters, parameters, domain[0]); break; @@ -542,7 +542,7 @@ namespace datalog { case OP_RA_FILTER: if (!check_params( 1, 1, num_parameters) || !check_domain(1, 1, arity)) { - return 0; + return nullptr; } result = mk_filter(parameters[0], domain[0]); break; @@ -550,7 +550,7 @@ namespace datalog { case OP_RA_IS_EMPTY: if (!check_params( 0, 0, num_parameters) || !check_domain(1, 1, arity)) { - return 0; + return nullptr; } result = mk_is_empty(domain[0]); break; @@ -558,7 +558,7 @@ namespace datalog { case OP_RA_RENAME: if (!check_params( 2, UINT_MAX, num_parameters) || !check_domain(1, 1, arity)) { - return 0; + return nullptr; } result = mk_rename(num_parameters, parameters, domain[0]); break; @@ -566,7 +566,7 @@ namespace datalog { case OP_RA_COMPLEMENT: if (!check_params( 0, 0, num_parameters) || !check_domain(1, 1, arity)) { - return 0; + return nullptr; } result = mk_complement(domain[0]); break; @@ -574,14 +574,14 @@ namespace datalog { case OP_RA_NEGATION_FILTER: if (!check_params(1, UINT_MAX, num_parameters) || !check_domain(2, 2, arity)) { - return 0; + return nullptr; } result = mk_negation_filter(num_parameters, parameters, domain[0], domain[1]); break; case OP_RA_CLONE: if (!check_params(0, 0, num_parameters) || !check_domain(1, 1, arity)) { - return 0; + return nullptr; } result = mk_clone(domain[0]); break; @@ -589,7 +589,7 @@ namespace datalog { case OP_DL_CONSTANT: if (!check_params( 2, 2, num_parameters) || !check_domain(0, 0, arity)) { - return 0; + return nullptr; } result = mk_constant(parameters); break; @@ -597,23 +597,23 @@ namespace datalog { case OP_DL_LT: if (!check_params( 0, 0, num_parameters) || !check_domain(2, 2, arity)) { - return 0; + return nullptr; } result = mk_compare(OP_DL_LT, m_lt_sym, domain); break; case OP_DL_REP: { if (!check_domain(0, 0, num_parameters) || - !check_domain(1, 1, arity)) return 0; - func_decl_info info(m_family_id, k, 0, 0); + !check_domain(1, 1, arity)) return nullptr; + func_decl_info info(m_family_id, k, 0, nullptr); result = m_manager->mk_func_decl(symbol("rep"), 1, domain, range, info); break; } case OP_DL_ABS: { if (!check_domain(0, 0, num_parameters) || - !check_domain(1, 1, arity)) return 0; - func_decl_info info(m_family_id, k, 0, 0); + !check_domain(1, 1, arity)) return nullptr; + func_decl_info info(m_family_id, k, 0, nullptr); result = m_manager->mk_func_decl(symbol("abs"), 1, domain, range, info); break; } @@ -621,7 +621,7 @@ namespace datalog { default: m_manager->raise_exception("operator not recognized"); - return 0; + return nullptr; } TRACE("dl_decl_plugin", tout << mk_pp(result, *m_manager) << "\n";); @@ -652,14 +652,14 @@ namespace datalog { // create a constant belonging to a given finite domain. - app* dl_decl_util::mk_numeral(uint64 value, sort* s) { + app* dl_decl_util::mk_numeral(uint64_t value, sort* s) { if (is_finite_sort(s)) { - uint64 sz = 0; + uint64_t sz = 0; if (try_get_size(s, sz) && sz <= value) { m.raise_exception("value is out of bounds"); } parameter params[2] = { parameter(rational(value, rational::ui64())), parameter(s) }; - return m.mk_const(m.mk_func_decl(m_fid, OP_DL_CONSTANT, 2, params, 0, (sort*const*)0)); + return m.mk_const(m.mk_func_decl(m_fid, OP_DL_CONSTANT, 2, params, 0, (sort*const*)nullptr)); } if (m_arith.is_int(s) || m_arith.is_real(s)) { return m_arith.mk_numeral(rational(value, rational::ui64()), s); @@ -677,10 +677,10 @@ namespace datalog { std::stringstream strm; strm << "sort '" << mk_pp(s, m) << "' is not recognized as a sort that contains numeric values.\nUse Bool, BitVec, Int, Real, or a Finite domain sort"; m.raise_exception(strm.str().c_str()); - return 0; + return nullptr; } - bool dl_decl_util::is_numeral(const expr* e, uint64& v) const { + bool dl_decl_util::is_numeral(const expr* e, uint64_t& v) const { if (is_numeral(e)) { const app* c = to_app(e); SASSERT(c->get_decl()->get_num_parameters() == 2); @@ -693,7 +693,7 @@ namespace datalog { return false; } - bool dl_decl_util::is_numeral_ext(expr* e, uint64& v) const { + bool dl_decl_util::is_numeral_ext(expr* e, uint64_t& v) const { if (is_numeral(e, v)) { return true; } @@ -724,7 +724,7 @@ namespace datalog { return m.is_true(c) || m.is_false(c); } - sort* dl_decl_util::mk_sort(const symbol& name, uint64 domain_size) { + sort* dl_decl_util::mk_sort(const symbol& name, uint64_t domain_size) { if (domain_size == 0) { std::stringstream sstm; sstm << "Domain size of sort '" << name << "' may not be 0"; @@ -734,7 +734,7 @@ namespace datalog { return m.mk_sort(m_fid, DL_FINITE_SORT, 2, params); } - bool dl_decl_util::try_get_size(const sort * s, uint64& size) const { + bool dl_decl_util::try_get_size(const sort * s, uint64_t& size) const { sort_size sz = s->get_info()->get_num_elements(); if (sz.is_finite()) { size = sz.size(); @@ -745,12 +745,12 @@ namespace datalog { app* dl_decl_util::mk_lt(expr* a, expr* b) { expr* args[2] = { a, b }; - return m.mk_app(m_fid, OP_DL_LT, 0, 0, 2, args); + return m.mk_app(m_fid, OP_DL_LT, 0, nullptr, 2, args); } app* dl_decl_util::mk_le(expr* a, expr* b) { expr* args[2] = { b, a }; - return m.mk_not(m.mk_app(m_fid, OP_DL_LT, 0, 0, 2, args)); + return m.mk_not(m.mk_app(m_fid, OP_DL_LT, 0, nullptr, 2, args)); } sort* dl_decl_util::mk_rule_sort() { diff --git a/src/ast/dl_decl_plugin.h b/src/ast/dl_decl_plugin.h index 75969d385..257404f70 100644 --- a/src/ast/dl_decl_plugin.h +++ b/src/ast/dl_decl_plugin.h @@ -102,9 +102,9 @@ namespace datalog { public: dl_decl_plugin(); - virtual ~dl_decl_plugin() {} + ~dl_decl_plugin() override {} - virtual decl_plugin * mk_fresh() { return alloc(dl_decl_plugin); } + decl_plugin * mk_fresh() override { return alloc(dl_decl_plugin); } // // Contract for sort DL_RELATION_SORT @@ -116,25 +116,25 @@ namespace datalog { // parameters[0] - name // parameters[1] - uint64 // - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; // // Contract for func_decl: // parameters[0] - array sort // Contract for OP_DL_CONSTANT: - // parameters[0] - rational containing uint64 with constant value + // parameters[0] - rational containing uint64_t with constant value // parameters[1] - a DL_FINITE_SORT sort of the constant // Contract for others: // no parameters - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; - virtual void get_op_names(svector & op_names, symbol const & logic); + void get_op_names(svector & op_names, symbol const & logic) override; - virtual void get_sort_names(svector & sort_names, symbol const & logic); + void get_sort_names(svector & sort_names, symbol const & logic) override; - virtual bool is_value(app * e) const { return is_app_of(e, m_family_id, OP_DL_CONSTANT); } - virtual bool is_unique_value(app * e) const { return is_value(e); } + bool is_value(app * e) const override { return is_app_of(e, m_family_id, OP_DL_CONSTANT); } + bool is_unique_value(app * e) const override { return is_value(e); } }; @@ -166,7 +166,7 @@ namespace datalog { dl_decl_util(ast_manager& m); // create a constant belonging to a given finite domain. // the options include the DL_FINITE_SORT, BV_SORT, and BOOL_SORT - app* mk_numeral(uint64 value, sort* s); + app* mk_numeral(uint64_t value, sort* s); app* mk_lt(expr* a, expr* b); @@ -176,19 +176,19 @@ namespace datalog { bool is_numeral(const expr* c) const { return is_app_of(c, m_fid, OP_DL_CONSTANT); } - bool is_numeral(const expr* e, uint64& v) const; + bool is_numeral(const expr* e, uint64_t& v) const; // // Utilities for extracting constants // from bit-vectors and finite domains. // - bool is_numeral_ext(expr* c, uint64& v) const; + bool is_numeral_ext(expr* c, uint64_t& v) const; bool is_numeral_ext(expr* c) const; - sort* mk_sort(const symbol& name, uint64 domain_size); + sort* mk_sort(const symbol& name, uint64_t domain_size); - bool try_get_size(const sort *, uint64& size) const; + bool try_get_size(const sort *, uint64_t& size) const; bool is_finite_sort(sort* s) const { return is_sort_of(s, m_fid, DL_FINITE_SORT); @@ -204,9 +204,9 @@ namespace datalog { sort* mk_rule_sort(); - app* mk_rule(symbol const& name, unsigned num_args = 0, expr* const* args = 0); + app* mk_rule(symbol const& name, unsigned num_args = 0, expr* const* args = nullptr); - app* mk_fact(symbol const& name) { return mk_rule(name, 0, 0); } + app* mk_fact(symbol const& name) { return mk_rule(name, 0, nullptr); } ast_manager& get_manager() const { return m; } diff --git a/src/ast/expr2polynomial.cpp b/src/ast/expr2polynomial.cpp index 7413624cd..280d7487a 100644 --- a/src/ast/expr2polynomial.cpp +++ b/src/ast/expr2polynomial.cpp @@ -29,7 +29,7 @@ struct expr2polynomial::imp { struct frame { app * m_curr; unsigned m_idx; - frame():m_curr(0), m_idx(0) {} + frame():m_curr(nullptr), m_idx(0) {} frame(app * t):m_curr(t), m_idx(0) {} }; @@ -59,8 +59,8 @@ struct expr2polynomial::imp { m_am(am), m_autil(am), m_pm(pm), - m_expr2var(e2v == 0 && !use_var_idxs ? alloc(expr2var, am) : e2v), - m_expr2var_owner(e2v == 0 && !use_var_idxs), + m_expr2var(e2v == nullptr && !use_var_idxs ? alloc(expr2var, am) : e2v), + m_expr2var_owner(e2v == nullptr && !use_var_idxs), m_var2expr(am), m_cached_domain(am), m_cached_polynomials(pm), @@ -156,7 +156,7 @@ struct expr2polynomial::imp { x = m_wrapper.mk_var(is_int); m_expr2var->insert(t, x); if (x >= m_var2expr.size()) - m_var2expr.resize(x+1, 0); + m_var2expr.resize(x+1, nullptr); m_var2expr.set(x, t); } } @@ -502,7 +502,7 @@ void expr2polynomial::set_cancel(bool f) { } default_expr2polynomial::default_expr2polynomial(ast_manager & am, polynomial::manager & pm): - expr2polynomial(am, pm, 0) { + expr2polynomial(am, pm, nullptr) { } default_expr2polynomial::~default_expr2polynomial() { diff --git a/src/ast/expr2polynomial.h b/src/ast/expr2polynomial.h index e7cbac6e5..8becf55dd 100644 --- a/src/ast/expr2polynomial.h +++ b/src/ast/expr2polynomial.h @@ -103,10 +103,10 @@ class default_expr2polynomial : public expr2polynomial { svector m_is_int; public: default_expr2polynomial(ast_manager & am, polynomial::manager & pm); - virtual ~default_expr2polynomial(); - virtual bool is_int(polynomial::var x) const; + ~default_expr2polynomial() override; + bool is_int(polynomial::var x) const override; protected: - virtual polynomial::var mk_var(bool is_int); + polynomial::var mk_var(bool is_int) override; }; #endif diff --git a/src/ast/expr2var.cpp b/src/ast/expr2var.cpp index 2f850d645..b1fdba2b5 100644 --- a/src/ast/expr2var.cpp +++ b/src/ast/expr2var.cpp @@ -58,13 +58,11 @@ void expr2var::display(std::ostream & out) const { } void expr2var::mk_inv(expr_ref_vector & var2expr) const { - obj_map::iterator it = m_mapping.begin(); - obj_map::iterator end = m_mapping.end(); - for (; it != end; ++it) { - expr * t = it->m_key; - var x = it->m_value; + for (auto & kv : m_mapping) { + expr * t = kv.m_key; + var x = kv.m_value; if (x >= var2expr.size()) - var2expr.resize(x+1, 0); + var2expr.resize(x+1, nullptr); var2expr.set(x, t); } } diff --git a/src/ast/expr_abstract.cpp b/src/ast/expr_abstract.cpp index 43035e203..c45e445fd 100644 --- a/src/ast/expr_abstract.cpp +++ b/src/ast/expr_abstract.cpp @@ -27,7 +27,7 @@ void expr_abstractor::operator()(unsigned base, unsigned num_bound, expr* const* result = n; return; } - expr * curr = 0, *b = 0; + expr * curr = nullptr, *b = nullptr; SASSERT(n->get_ref_count() > 0); m_stack.push_back(n); diff --git a/src/ast/expr_delta_pair.h b/src/ast/expr_delta_pair.h index f4cc40ae4..f40f4cbe8 100644 --- a/src/ast/expr_delta_pair.h +++ b/src/ast/expr_delta_pair.h @@ -26,7 +26,7 @@ struct expr_delta_pair { expr * m_node; unsigned m_delta; - expr_delta_pair():m_node(0), m_delta(0) {} + expr_delta_pair():m_node(nullptr), m_delta(0) {} expr_delta_pair(expr * n, unsigned d):m_node(n), m_delta(d) {} unsigned hash() const { return hash_u_u(m_node->hash(), m_delta); } bool operator==(const expr_delta_pair & e) const { return m_node == e.m_node && m_delta == e.m_delta; } diff --git a/src/ast/expr_functors.cpp b/src/ast/expr_functors.cpp index 4906ed3d7..cada71ac9 100644 --- a/src/ast/expr_functors.cpp +++ b/src/ast/expr_functors.cpp @@ -121,22 +121,22 @@ void map_proc::reconstruct(app* a) { } if (is_new) { expr* b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr()); - m_map.insert(a, b, 0); + m_map.insert(a, b, nullptr); } else { - m_map.insert(a, a, 0); + m_map.insert(a, a, nullptr); } } void map_proc::visit(quantifier* e) { expr_ref q(m); q = m.update_quantifier(e, get_expr(e->get_expr())); - m_map.insert(e, q, 0); + m_map.insert(e, q, nullptr); } expr* map_proc::get_expr(expr* e) { - expr* result = 0; - proof* p = 0; + expr* result = nullptr; + proof* p = nullptr; m_map.get(e, result, p); return result; } diff --git a/src/ast/expr_functors.h b/src/ast/expr_functors.h index 322b9402a..5a4cac477 100644 --- a/src/ast/expr_functors.h +++ b/src/ast/expr_functors.h @@ -73,7 +73,7 @@ class contains_app { app* m_x; public: pred(app* x) : m_x(x) {} - virtual bool operator()(expr* e) { + bool operator()(expr* e) override { return m_x == e; } }; @@ -115,7 +115,7 @@ public: void reset() { m_map.reset(); } - void visit(var* e) { m_map.insert(e, e, 0); } + void visit(var* e) { m_map.insert(e, e, nullptr); } void visit(quantifier* e); diff --git a/src/ast/expr_map.cpp b/src/ast/expr_map.cpp index 89fcccb00..a9b32f906 100644 --- a/src/ast/expr_map.cpp +++ b/src/ast/expr_map.cpp @@ -38,7 +38,7 @@ expr_map::~expr_map() { void expr_map::insert(expr * k, expr * d, proof * p) { m_manager.inc_ref(d); obj_map::obj_map_entry * entry = m_expr2expr.find_core(k); - if (entry != 0) { + if (entry != nullptr) { m_manager.dec_ref(entry->get_data().m_value); entry->get_data().m_value = d; if (m_store_proofs) { @@ -61,7 +61,7 @@ void expr_map::insert(expr * k, expr * d, proof * p) { void expr_map::get(expr * k, expr * & d, proof * & p) const { if (m_expr2expr.find(k, d)) { - p = 0; + p = nullptr; if (m_store_proofs) m_expr2pr.find(k, p); } @@ -73,7 +73,7 @@ void expr_map::erase(expr * k) { m_expr2expr.erase(k); m_manager.dec_ref(v); if (m_store_proofs) { - proof * pr = 0; + proof * pr = nullptr; m_expr2pr.find(k, pr); m_expr2pr.erase(k); m_manager.dec_ref(pr); diff --git a/src/ast/expr_substitution.cpp b/src/ast/expr_substitution.cpp index f9333c443..e8d75f461 100644 --- a/src/ast/expr_substitution.cpp +++ b/src/ast/expr_substitution.cpp @@ -106,20 +106,20 @@ void expr_substitution::insert(expr * c, expr * def, proof * def_pr, expr_depend void expr_substitution::erase(expr * c) { if (proofs_enabled()) { - proof * pr = 0; + proof * pr = nullptr; if (m_subst_pr->find(c, pr)) { m_manager.dec_ref(pr); m_subst_pr->erase(c); } } if (unsat_core_enabled()) { - expr_dependency * dep = 0; + expr_dependency * dep = nullptr; if (m_subst_dep->find(c, dep)) { m_manager.dec_ref(dep); m_subst_dep->erase(c); } } - expr * def = 0; + expr * def = nullptr; if (m_subst.find(c, def)) { m_manager.dec_ref(c); m_manager.dec_ref(def); diff --git a/src/ast/expr_substitution.h b/src/ast/expr_substitution.h index 1590f888c..cbe00433c 100644 --- a/src/ast/expr_substitution.h +++ b/src/ast/expr_substitution.h @@ -43,7 +43,7 @@ public: bool unsat_core_enabled() const { return m_cores_enabled; } bool empty() const { return m_subst.empty(); } - void insert(expr * s, expr * def, proof * def_pr = 0, expr_dependency * def_dep = 0); + void insert(expr * s, expr * def, proof * def_pr = nullptr, expr_dependency * def_dep = nullptr); void erase(expr * s); bool find(expr * s, expr * & def, proof * & def_pr); bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep); @@ -63,7 +63,7 @@ public: scoped_expr_substitution(expr_substitution& s): m_subst(s), m_trail(s.m()) {} ~scoped_expr_substitution() {} - void insert(expr * s, expr * def, proof * def_pr = 0, expr_dependency * def_dep = 0) { + void insert(expr * s, expr * def, proof * def_pr = nullptr, expr_dependency * def_dep = nullptr) { if (!m_subst.contains(s)) { m_subst.insert(s, def, def_pr, def_dep); m_trail.push_back(s); @@ -82,7 +82,7 @@ public: } unsigned scope_level() const { return m_trail_lim.size(); } bool empty() const { return m_subst.empty(); } - expr* find(expr * e) { proof* pr; expr* d = 0; if (find(e, d, pr)) return d; else return e; } + expr* find(expr * e) { proof* pr; expr* d = nullptr; if (find(e, d, pr)) return d; else return e; } bool find(expr * s, expr * & def, proof * & def_pr) { return m_subst.find(s, def, def_pr); } bool find(expr * s, expr * & def, proof * & def_pr, expr_dependency * & def_dep) { return m_subst.find(s, def, def_pr, def_dep); } bool contains(expr * s) { return m_subst.contains(s); } diff --git a/src/ast/for_each_expr.h b/src/ast/for_each_expr.h index 50a4089dc..6e203ccee 100644 --- a/src/ast/for_each_expr.h +++ b/src/ast/for_each_expr.h @@ -128,6 +128,20 @@ void for_each_expr(ForEachProc & proc, expr * n) { for_each_expr_core(proc, visited, n); } +template +void for_each_expr(ForEachProc & proc, unsigned n, expr * const* es) { + expr_mark visited; + for (unsigned i = 0; i < n; ++i) + for_each_expr_core(proc, visited, es[i]); +} + +template +void for_each_expr(ForEachProc & proc, expr_ref_vector const& es) { + expr_mark visited; + for (expr* e : es) + for_each_expr_core(proc, visited, e); +} + template void quick_for_each_expr(ForEachProc & proc, expr_fast_mark1 & visited, expr * n) { for_each_expr_core(proc, visited, n); diff --git a/src/ast/format.cpp b/src/ast/format.cpp index 835892121..40df437ea 100644 --- a/src/ast/format.cpp +++ b/src/ast/format.cpp @@ -32,7 +32,7 @@ namespace format_ns { symbol m_line_break; symbol m_line_break_ext; - virtual void set_manager(ast_manager * m, family_id id) { + void set_manager(ast_manager * m, family_id id) override { SASSERT(m->is_format_manager()); decl_plugin::set_manager(m, id); @@ -42,7 +42,7 @@ namespace format_ns { public: format_decl_plugin(): - m_format_sort(0), + m_format_sort(nullptr), m_nil("nil"), m_string("string"), m_indent("indent"), @@ -52,24 +52,24 @@ namespace format_ns { m_line_break_ext("cr++") { } - virtual ~format_decl_plugin() {} + ~format_decl_plugin() override {} - virtual void finalize() { + void finalize() override { if (m_format_sort) m_manager->dec_ref(m_format_sort); } - virtual decl_plugin * mk_fresh() { + decl_plugin * mk_fresh() override { return alloc(format_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) { + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) override { SASSERT(k == FORMAT_SORT); return m_format_sort; } - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range) { + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override { switch (k) { case OP_NIL: return m_manager->mk_func_decl(m_nil, arity, domain, m_format_sort, @@ -94,7 +94,7 @@ namespace format_ns { return m_manager->mk_func_decl(m_line_break_ext, arity, domain, m_format_sort, func_decl_info(m_family_id, OP_LINE_BREAK_EXT, num_parameters, parameters)); default: - return 0; + return nullptr; } } }; @@ -124,8 +124,8 @@ namespace format_ns { SASSERT(m_manager.is_format_manager()); } - format * visit(var *) { UNREACHABLE(); return 0; } - format * visit(quantifier * q, format *, format * const *, format * const *) { UNREACHABLE(); return 0; } + format * visit(var *) { UNREACHABLE(); return nullptr; } + format * visit(quantifier * q, format *, format * const *, format * const *) { UNREACHABLE(); return nullptr; } format * visit(format * n, format * const * children) { if (is_app_of(n, m_fid, OP_LINE_BREAK)) return mk_string(m_manager, " "); @@ -147,11 +147,11 @@ namespace format_ns { format * mk_string(ast_manager & m, char const * str) { symbol s(str); parameter p(s); - return fm(m).mk_app(fid(m), OP_STRING, 1, &p, 0, 0); + return fm(m).mk_app(fid(m), OP_STRING, 1, &p, 0, nullptr); } format * mk_int(ast_manager & m, int i) { - static char buffer[128]; + char buffer[128]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "%d", i); #else @@ -161,7 +161,7 @@ namespace format_ns { } format * mk_unsigned(ast_manager & m, unsigned u) { - static char buffer[128]; + char buffer[128]; #ifdef _WINDOWS sprintf_s(buffer, ARRAYSIZE(buffer), "%u", u); #else diff --git a/src/ast/fpa/bv2fpa_converter.cpp b/src/ast/fpa/bv2fpa_converter.cpp index d87929864..f1c2b1a14 100644 --- a/src/ast/fpa/bv2fpa_converter.cpp +++ b/src/ast/fpa/bv2fpa_converter.cpp @@ -198,7 +198,7 @@ expr_ref bv2fpa_converter::rebuild_floats(model_core * mc, sort * s, app * e) { tout << std::endl; ); if (m_fpa_util.is_float(s)) { - if (e == 0) + if (e == nullptr) result = m_fpa_util.mk_pzero(s); else if (m_fpa_util.is_numeral(e)) result = e; @@ -208,7 +208,7 @@ expr_ref bv2fpa_converter::rebuild_floats(model_core * mc, sort * s, app * e) { } } else if (m_fpa_util.is_rm(s)) { - if (e == 0) + if (e == nullptr) result = m_fpa_util.mk_round_toward_zero(); else if (m_fpa_util.is_rm_numeral(e)) result = e; @@ -256,17 +256,17 @@ bv2fpa_converter::array_model bv2fpa_converter::convert_array_func_interp(model_ func_interp * bv2fpa_converter::convert_func_interp(model_core * mc, func_decl * f, func_decl * bv_f) { SASSERT(f->get_arity() > 0); - func_interp * result = 0; + func_interp * result = nullptr; sort * rng = f->get_range(); sort * const * dmn = f->get_domain(); unsigned arity = bv_f->get_arity(); func_interp * bv_fi = mc->get_func_interp(bv_f); + result = alloc(func_interp, m, arity); if (bv_fi) { fpa_rewriter rw(m); expr_ref ai(m); - result = alloc(func_interp, m, arity); for (unsigned i = 0; i < bv_fi->num_entries(); i++) { func_entry const * bv_fe = bv_fi->get_entry(i); @@ -291,7 +291,7 @@ func_interp * bv2fpa_converter::convert_func_interp(model_core * mc, func_decl * mk_ismt2_pp(new_args[i], m) << std::endl; tout << mk_ismt2_pp(bv_fres, m) << " == " << mk_ismt2_pp(ft_fres, m) << std::endl;); func_entry * fe = result->get_entry(new_args.c_ptr()); - if (fe == 0) + if (fe == nullptr) result->insert_new_entry(new_args.c_ptr(), ft_fres); else { // The BV model may have multiple equivalent entries using different @@ -338,7 +338,7 @@ void bv2fpa_converter::convert_consts(model_core * mc, model_core * target_model v2 = mc->get_const_interp(a2->get_decl()); #else expr * bv = mc->get_const_interp(to_app(to_app(a0)->get_arg(0))->get_decl()); - if (bv == 0) { + if (bv == nullptr) { v0 = m_bv_util.mk_numeral(0, 1); v1 = m_bv_util.mk_numeral(0, ebits); v2 = m_bv_util.mk_numeral(0, sbits-1); @@ -465,26 +465,7 @@ void bv2fpa_converter::convert_uf2bvuf(model_core * mc, model_core * target_mode else { if (it->get_key().get_family_id() == m_fpa_util.get_fid()) { // it->m_value contains the model for the unspecified cases of it->m_key. - - func_interp * fmv = convert_func_interp(mc, f, it->m_value); - if (fmv) { -#if 0 - // Upon request, add this 'recursive' definition? - unsigned n = fmv->get_arity(); - expr_ref_vector args(m); - for (unsigned i = 0; i < n; i++) - args.push_back(m.mk_var(i, f->get_domain()[i])); - fmv->set_else(m.mk_app(it->m_key, n, args.c_ptr())); -#else - - fmv->set_else(0); -#endif - target_model->register_decl(f, fmv); - } - } - else { - func_interp * fmv = convert_func_interp(mc, f, it->m_value); - if (fmv) target_model->register_decl(f, fmv); + target_model->register_decl(f, convert_func_interp(mc, f, it->m_value)); } } } diff --git a/src/ast/fpa/bv2fpa_converter.h b/src/ast/fpa/bv2fpa_converter.h index caf36c1fc..3cb7f4c63 100644 --- a/src/ast/fpa/bv2fpa_converter.h +++ b/src/ast/fpa/bv2fpa_converter.h @@ -64,7 +64,7 @@ public: func_interp * new_float_fi; func_decl * bv_fd; expr_ref result; - array_model(ast_manager & m) : new_float_fd(0), new_float_fi(0), bv_fd(0), result(m) {} + array_model(ast_manager & m) : new_float_fd(nullptr), new_float_fi(nullptr), bv_fd(nullptr), result(m) {} }; array_model convert_array_func_interp(model_core * mc, func_decl * f, func_decl * bv_f); diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index d2a946e0c..b079fc2ca 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -25,7 +25,7 @@ Notes: #include "ast/fpa/fpa2bv_converter.h" #include "ast/rewriter/fpa_rewriter.h" -#define BVULT(X,Y,R) { expr_ref bvult_eq(m), bvult_not(m); m_simp.mk_eq(X, Y, bvult_eq); m_simp.mk_not(bvult_eq, bvult_not); expr_ref t(m); t = m_bv_util.mk_ule(X,Y); m_simp.mk_and(t, bvult_not, R); } +#define BVULT(X,Y,R) { expr_ref t(m); t = m_bv_util.mk_ule(Y,X); m_simp.mk_not(t, R); } fpa2bv_converter::fpa2bv_converter(ast_manager & m) : m(m), @@ -197,7 +197,7 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { #else app_ref bv(m); unsigned bv_sz = 1 + ebits + (sbits - 1); - bv = mk_fresh_const(0, bv_sz); + bv = mk_fresh_const(nullptr, bv_sz); sgn = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, bv); e = m_bv_util.mk_extract(bv_sz - 2, sbits - 1, bv); @@ -288,7 +288,7 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { #ifdef Z3DEBUG "fpa2bv_rm" #else - 0 + nullptr #endif , m_bv_util.mk_sort(3)); @@ -1071,7 +1071,7 @@ void fpa2bv_converter::mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & r SASSERT(m_mpz_manager.is_int64(max_exp_diff)); SASSERT(m_mpz_manager.get_uint64(max_exp_diff) <= UINT_MAX); - uint64 max_exp_diff_ui64 = m_mpz_manager.get_uint64(max_exp_diff); + uint64_t max_exp_diff_ui64 = m_mpz_manager.get_uint64(max_exp_diff); SASSERT(max_exp_diff_ui64 <= UINT_MAX); unsigned max_exp_diff_ui = (unsigned)max_exp_diff_ui64; m_mpz_manager.del(max_exp_diff); @@ -1274,8 +1274,8 @@ expr_ref fpa2bv_converter::mk_min_max_unspecified(func_decl * f, expr * x, expr std::pair decls(0, 0); if (!m_min_max_ufs.find(f, decls)) { - decls.first = m.mk_fresh_const(0, m_bv_util.mk_sort(1)); - decls.second = m.mk_fresh_const(0, m_bv_util.mk_sort(1)); + decls.first = m.mk_fresh_const(nullptr, m_bv_util.mk_sort(1)); + decls.second = m.mk_fresh_const(nullptr, m_bv_util.mk_sort(1)); m_min_max_ufs.insert(f, decls); m.inc_ref(f); m.inc_ref(decls.first); @@ -1919,7 +1919,7 @@ void fpa2bv_converter::mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & expr_ref pow_2_sbitsm1(m), m1(m); pow_2_sbitsm1 = m_bv_util.mk_numeral(fu().fm().m_powers2(sbits - 1), sbits); - m1 = m_bv_util.mk_numeral(-1, ebits); + m1 = m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, ebits)); m_simp.mk_eq(a_sig, pow_2_sbitsm1, t1); m_simp.mk_eq(a_exp, m1, t2); m_simp.mk_and(t1, t2, tie); @@ -1927,7 +1927,7 @@ void fpa2bv_converter::mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & m_simp.mk_and(tie, rm_is_rte, c421); m_simp.mk_and(tie, rm_is_rta, c422); - c423 = m_bv_util.mk_sle(a_exp, m_bv_util.mk_numeral(-2, ebits)); + c423 = m_bv_util.mk_sle(a_exp, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(2, ebits))); dbg_decouple("fpa2bv_r2i_c421", c421); dbg_decouple("fpa2bv_r2i_c422", c422); @@ -2452,7 +2452,7 @@ void fpa2bv_converter::mk_to_fp_float(sort * to_srt, expr * rm, expr * x, expr_r const mpz & ovft = m_mpf_manager.m_powers2.m1(to_ebits+1, false); first_ovf_exp = m_bv_util.mk_numeral(ovft, from_ebits+2); first_udf_exp = m_bv_util.mk_concat( - m_bv_util.mk_numeral(-1, ebits_diff + 3), + m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, ebits_diff + 3)), m_bv_util.mk_numeral(1, to_ebits + 1)); dbg_decouple("fpa2bv_to_float_first_ovf_exp", first_ovf_exp); dbg_decouple("fpa2bv_to_float_first_udf_exp", first_udf_exp); @@ -2681,11 +2681,11 @@ void fpa2bv_converter::mk_to_fp_real_int(func_decl * f, unsigned num, expr * con a_tz = m_plugin->mk_numeral(tz); expr_ref bv_nte(m), bv_nta(m), bv_tp(m), bv_tn(m), bv_tz(m); - mk_numeral(a_nte->get_decl(), 0, 0, bv_nte); - mk_numeral(a_nta->get_decl(), 0, 0, bv_nta); - mk_numeral(a_tp->get_decl(), 0, 0, bv_tp); - mk_numeral(a_tn->get_decl(), 0, 0, bv_tn); - mk_numeral(a_tz->get_decl(), 0, 0, bv_tz); + mk_numeral(a_nte->get_decl(), 0, nullptr, bv_nte); + mk_numeral(a_nta->get_decl(), 0, nullptr, bv_nta); + mk_numeral(a_tp->get_decl(), 0, nullptr, bv_tp); + mk_numeral(a_tn->get_decl(), 0, nullptr, bv_tn); + mk_numeral(a_tz->get_decl(), 0, nullptr, bv_tz); expr_ref c1(m), c2(m), c3(m), c4(m); c1 = m.mk_eq(bv_rm, m_bv_util.mk_numeral(BV_RM_TO_POSITIVE, 3)); @@ -2845,7 +2845,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const expr_ref is_neg(m), x_abs(m), neg_x(m); is_neg_bit = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, x); is_neg = m.mk_eq(is_neg_bit, bv1_1); - neg_x = m_bv_util.mk_bv_neg(x); + neg_x = m_bv_util.mk_bv_neg(x); // overflow problem? x_abs = m.mk_ite(is_neg, neg_x, x); dbg_decouple("fpa2bv_to_fp_signed_is_neg", is_neg); // x_abs has an extra bit in the front. @@ -2882,11 +2882,13 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const SASSERT(is_well_sorted(m, lz)); } SASSERT(m_bv_util.get_bv_size(sig_4) == sig_sz); + dbg_decouple("fpa2bv_to_fp_signed_sig_4", sig_4); expr_ref s_exp(m), exp_rest(m); s_exp = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(bv_sz - 2, bv_sz), lz); // s_exp = (bv_sz-2) + (-lz) signed SASSERT(m_bv_util.get_bv_size(s_exp) == bv_sz); + dbg_decouple("fpa2bv_to_fp_signed_s_exp", s_exp); unsigned exp_sz = ebits + 2; // (+2 for rounder) exp_2 = m_bv_util.mk_extract(exp_sz - 1, 0, s_exp); @@ -2907,10 +2909,9 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); - exp_too_large = m_bv_util.mk_ule(m_bv_util.mk_bv_add( - max_exp_bvsz, - m_bv_util.mk_numeral(1, bv_sz)), - s_exp); + exp_too_large = m_bv_util.mk_sle( + m_bv_util.mk_bv_add(max_exp_bvsz, m_bv_util.mk_numeral(1, bv_sz)), + s_exp); zero_sig_sz = m_bv_util.mk_numeral(0, sig_sz); sig_4 = m.mk_ite(exp_too_large, zero_sig_sz, sig_4); exp_2 = m.mk_ite(exp_too_large, max_exp, exp_2); @@ -3040,7 +3041,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); - exp_too_large = m_bv_util.mk_ule(m_bv_util.mk_bv_add( + exp_too_large = m_bv_util.mk_sle(m_bv_util.mk_bv_add( max_exp_bvsz, m_bv_util.mk_numeral(1, bv_sz)), s_exp); @@ -3098,17 +3099,15 @@ void fpa2bv_converter::mk_to_ieee_bv_unspecified(func_decl * f, unsigned num, ex join_fp(result, result); } else { - expr * n = args[0]; - expr_ref n_bv(m); - join_fp(n, n_bv); + expr_ref nw = nan_wrap(args[0]); - sort * domain[1] = { m.get_sort(n_bv) }; + sort * domain[1] = { m.get_sort(nw) }; func_decl * f_bv = mk_bv_uf(f, domain, f->get_range()); - result = m.mk_app(f_bv, n_bv); + result = m.mk_app(f_bv, nw); expr_ref exp_bv(m), exp_all_ones(m); exp_bv = m_bv_util.mk_extract(ebits+sbits-2, sbits-1, result); - exp_all_ones = m.mk_eq(exp_bv, m_bv_util.mk_numeral(-1, ebits)); + exp_all_ones = m.mk_eq(exp_bv, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, ebits))); m_extra_assertions.push_back(exp_all_ones); expr_ref sig_bv(m), sig_is_non_zero(m); @@ -3139,6 +3138,7 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args unsigned bv_sz = (unsigned)f->get_parameter(0).get_int(); expr_ref bv0(m), bv1(m); + bv0 = m_bv_util.mk_numeral(0, 1); bv1 = m_bv_util.mk_numeral(1, 1); expr_ref x_is_nan(m), x_is_inf(m), x_is_zero(m), x_is_neg(m), x_is_nzero(m); @@ -3188,9 +3188,9 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args exp_m_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), m_bv_util.mk_zero_extend(2, lz)); - // big_sig is +- [... bv_sz+2 bits ...].[r][g][ ... sbits-1 ... ] - big_sig = m_bv_util.mk_zero_extend(bv_sz+2, sig); - unsigned big_sig_sz = sig_sz+bv_sz+2; + // big_sig is +- [... bv_sz+2 bits ...][1].[r][ ... sbits-1 ... ] + big_sig = m_bv_util.mk_concat(m_bv_util.mk_zero_extend(bv_sz + 2, sig), bv0); + unsigned big_sig_sz = sig_sz+1+bv_sz+2; SASSERT(m_bv_util.get_bv_size(big_sig) == big_sig_sz); is_neg_shift = m_bv_util.mk_sle(exp_m_lz, m_bv_util.mk_numeral(0, ebits+2)); @@ -3242,18 +3242,34 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args dbg_decouple("fpa2bv_to_bv_inc", inc); dbg_decouple("fpa2bv_to_bv_pre_rounded", pre_rounded); - pre_rounded = m.mk_ite(x_is_neg, m_bv_util.mk_bv_neg(pre_rounded), pre_rounded); + expr_ref incd(m), pr_is_zero(m), ovfl(m); + incd = m.mk_eq(rounding_decision, bv1); + pr_is_zero = m.mk_eq(pre_rounded, m_bv_util.mk_numeral(0, bv_sz + 3)); + ovfl = m.mk_and(incd, pr_is_zero); + dbg_decouple("fpa2bv_to_bv_incd", incd); + dbg_decouple("fpa2bv_to_bv_ovfl", ovfl); - expr_ref ll(m), ul(m), in_range(m); + expr_ref ul(m), in_range(m); if (!is_signed) { - ll = m_bv_util.mk_numeral(0, bv_sz+3); - ul = m_bv_util.mk_zero_extend(3, m_bv_util.mk_numeral(-1, bv_sz)); + ul = m_bv_util.mk_zero_extend(3, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, bv_sz))); + in_range = m.mk_and(m.mk_or(m.mk_not(x_is_neg), + m.mk_eq(pre_rounded, m_bv_util.mk_numeral(0, bv_sz+3))), + m.mk_not(ovfl), + m_bv_util.mk_ule(pre_rounded, ul)); } else { + expr_ref ll(m); ll = m_bv_util.mk_sign_extend(3, m_bv_util.mk_concat(bv1, m_bv_util.mk_numeral(0, bv_sz-1))); - ul = m_bv_util.mk_zero_extend(4, m_bv_util.mk_numeral(-1, bv_sz-1)); + ul = m_bv_util.mk_zero_extend(4, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, bv_sz-1))); + ovfl = m.mk_or(ovfl, m_bv_util.mk_sle(pre_rounded, m_bv_util.mk_bv_neg(m_bv_util.mk_numeral(1, bv_sz + 3)))); + in_range = m.mk_and(m.mk_not(ovfl), + m_bv_util.mk_sle(ll, pre_rounded), + m_bv_util.mk_sle(pre_rounded, ul)); + dbg_decouple("fpa2bv_to_bv_in_range_ll", ll); + pre_rounded = m.mk_ite(x_is_neg, m_bv_util.mk_bv_neg(pre_rounded), pre_rounded); } - in_range = m.mk_and(m_bv_util.mk_sle(ll, pre_rounded), m_bv_util.mk_sle(pre_rounded, ul)); + dbg_decouple("fpa2bv_to_bv_in_range_ovfl", ovfl); + dbg_decouple("fpa2bv_to_bv_in_range_ul", ul); dbg_decouple("fpa2bv_to_bv_in_range", in_range); expr_ref rounded(m); @@ -3279,6 +3295,17 @@ void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * arg mk_to_bv(f, num, args, true, result); } +expr_ref fpa2bv_converter::nan_wrap(expr * n) { + expr_ref n_bv(m), arg_is_nan(m), nan(m), nan_bv(m), res(m); + mk_is_nan(n, arg_is_nan); + mk_nan(m.get_sort(n), nan); + join_fp(nan, nan_bv); + join_fp(n, n_bv); + res = expr_ref(m.mk_ite(arg_is_nan, nan_bv, n_bv), m); + SASSERT(is_well_sorted(m, res)); + return res; +} + void fpa2bv_converter::mk_to_bv_unspecified(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); SASSERT(m_util.is_bv2rm(args[0])); @@ -3288,13 +3315,10 @@ void fpa2bv_converter::mk_to_bv_unspecified(func_decl * f, unsigned num, expr * result = m_bv_util.mk_numeral(0, m_bv_util.get_bv_size(f->get_range())); else { expr * rm_bv = to_app(args[0])->get_arg(0); - expr * n = args[1]; - expr_ref n_bv(m); - join_fp(n, n_bv); - - sort * domain[2] = { m.get_sort(rm_bv), m.get_sort(n_bv) }; + expr_ref nw = nan_wrap(args[1]); + sort * domain[2] = { m.get_sort(rm_bv), m.get_sort(nw) }; func_decl * f_bv = mk_bv_uf(f, domain, f->get_range()); - result = m.mk_app(f_bv, rm_bv, n_bv); + result = m.mk_app(f_bv, rm_bv, nw); } TRACE("fpa2bv_to_bv_unspecified", tout << "result=" << mk_ismt2_pp(result, m) << std::endl;); @@ -3308,12 +3332,10 @@ void fpa2bv_converter::mk_to_real_unspecified(func_decl * f, unsigned num, expr result = m_arith_util.mk_numeral(rational(0), false); else { expr * n = args[0]; - expr_ref n_bv(m); - join_fp(n, n_bv); - - sort * domain[1] = { m.get_sort(n_bv) }; + expr_ref nw = nan_wrap(n); + sort * domain[1] = { m.get_sort(nw) }; func_decl * f_bv = mk_bv_uf(f, domain, f->get_range()); - result = m.mk_app(f_bv, n_bv); + result = m.mk_app(f_bv, nw); } } @@ -3745,14 +3767,10 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * rm, expr * sgn, expr * la expr * nround_lors[2] = { not_round, not_lors }; expr * pos_args[2] = { sgn, not_rors }; expr * neg_args[2] = { not_sgn, not_rors }; - expr * nl_r[2] = { last, not_round }; - expr * nl_nr_sn[3] = { not_last, not_round, not_sticky }; expr_ref inc_teven(m), inc_taway(m), inc_pos(m), inc_neg(m); inc_teven = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, nround_lors)); - expr *taway_args[2] = { m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, nl_r)), - m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(3, nl_nr_sn)) }; - inc_taway = m_bv_util.mk_bv_or(2, taway_args); + inc_taway = round; inc_pos = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, pos_args)); inc_neg = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, neg_args)); @@ -4084,7 +4102,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & TRACE("fpa2bv_round", tout << "ROUND = " << mk_ismt2_pp(result, m) << std::endl; ); } -void fpa2bv_converter::reset(void) { +void fpa2bv_converter::reset() { dec_ref_map_key_values(m, m_const2bv); dec_ref_map_key_values(m, m_rm_const2bv); dec_ref_map_key_values(m, m_uf2bvuf); @@ -4102,7 +4120,7 @@ void fpa2bv_converter::reset(void) { func_decl * fpa2bv_converter::mk_bv_uf(func_decl * f, sort * const * domain, sort * range) { func_decl * res; if (!m_uf2bvuf.find(f, res)) { - res = m.mk_fresh_func_decl(0, f->get_arity(), domain, range); + res = m.mk_fresh_func_decl(nullptr, f->get_arity(), domain, range); m_uf2bvuf.insert(f, res); m.inc_ref(f); m.inc_ref(res); diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index f0e50ba2d..7637317b0 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -150,7 +150,7 @@ public: void mk_max(func_decl * f, unsigned num, expr * const * args, expr_ref & result); expr_ref mk_min_max_unspecified(func_decl * f, expr * x, expr * y); - void reset(void); + void reset(); void dbg_decouple(const char * prefix, expr_ref & e); expr_ref_vector m_extra_assertions; @@ -219,6 +219,7 @@ private: void mk_to_fp_float(sort * s, expr * rm, expr * x, expr_ref & result); func_decl * mk_bv_uf(func_decl * f, sort * const * domain, sort * range); + expr_ref nan_wrap(expr * n); }; #endif diff --git a/src/ast/fpa/fpa2bv_rewriter.cpp b/src/ast/fpa/fpa2bv_rewriter.cpp index 6c96d92c1..2ddc69aaf 100644 --- a/src/ast/fpa/fpa2bv_rewriter.cpp +++ b/src/ast/fpa/fpa2bv_rewriter.cpp @@ -220,7 +220,7 @@ bool fpa2bv_rewriter_cfg::reduce_quantifier(quantifier * old_q, result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); - result_pr = 0; + result_pr = nullptr; m_bindings.shrink(old_sz); TRACE("fpa2bv", tout << "reduce_quantifier[" << old_q->get_depth() << "]: " << mk_ismt2_pp(old_q->get_expr(), m()) << std::endl << @@ -249,7 +249,7 @@ bool fpa2bv_rewriter_cfg::reduce_var(var * t, expr_ref & result, proof_ref & res new_exp = m().mk_var(t->get_idx(), s); result = new_exp; - result_pr = 0; + result_pr = nullptr; TRACE("fpa2bv", tout << "reduce_var: " << mk_ismt2_pp(t, m()) << " -> " << mk_ismt2_pp(result, m()) << std::endl;); return true; } diff --git a/src/ast/fpa_decl_plugin.cpp b/src/ast/fpa_decl_plugin.cpp index 9d298b413..c0caaa3ca 100644 --- a/src/ast/fpa_decl_plugin.cpp +++ b/src/ast/fpa_decl_plugin.cpp @@ -23,9 +23,9 @@ Revision History: fpa_decl_plugin::fpa_decl_plugin(): m_values(m_fm), m_value_table(mpf_hash_proc(m_values), mpf_eq_proc(m_values)) { - m_real_sort = 0; - m_int_sort = 0; - m_bv_plugin = 0; + m_real_sort = nullptr; + m_int_sort = nullptr; + m_bv_plugin = nullptr; } void fpa_decl_plugin::set_manager(ast_manager * m, family_id id) { @@ -70,7 +70,7 @@ void fpa_decl_plugin::recycled_id(unsigned id) { func_decl * fpa_decl_plugin::mk_numeral_decl(mpf const & v) { sort * s = mk_float_sort(v.get_ebits(), v.get_sbits()); - func_decl * r = 0; + func_decl * r = nullptr; if (m_fm.is_nan(v)) r = m_manager->mk_const_decl(symbol("NaN"), s, func_decl_info(m_family_id, OP_FPA_NAN)); else if (m_fm.is_pinf(v)) @@ -160,7 +160,7 @@ bool fpa_decl_plugin::is_rm_numeral(expr * n, mpf_rounding_mode & val) { return true; } - return 0; + return false; } bool fpa_decl_plugin::is_rm_numeral(expr * n) { @@ -223,7 +223,7 @@ sort * fpa_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter return mk_float_sort(15, 113); default: m_manager->raise_exception("unknown floating point theory sort"); - return 0; + return nullptr; } } @@ -248,20 +248,20 @@ func_decl * fpa_decl_plugin::mk_rm_const_decl(decl_kind k, unsigned num_paramete return m_manager->mk_const_decl(symbol("roundTowardZero"), s, finfo); default: UNREACHABLE(); - return 0; + return nullptr; } } func_decl * fpa_decl_plugin::mk_float_const_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { - sort * s = 0; + sort * s = nullptr; if (num_parameters == 1 && parameters[0].is_ast() && is_sort(parameters[0].get_ast()) && is_float_sort(to_sort(parameters[0].get_ast()))) { s = to_sort(parameters[0].get_ast()); } else if (num_parameters == 2 && parameters[0].is_int() && parameters[1].is_int()) { s = mk_float_sort(parameters[0].get_int(), parameters[1].get_int()); } - else if (range != 0 && is_float_sort(range)) { + else if (range != nullptr && is_float_sort(range)) { s = range; } else { @@ -561,7 +561,7 @@ func_decl * fpa_decl_plugin::mk_to_fp(decl_kind k, unsigned num_parameters, para ); } - return 0; + return nullptr; } func_decl * fpa_decl_plugin::mk_to_fp_unsigned(decl_kind k, unsigned num_parameters, parameter const * parameters, @@ -779,7 +779,7 @@ func_decl * fpa_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, default: m_manager->raise_exception("unsupported floating point operator"); - return 0; + return nullptr; } } @@ -862,12 +862,12 @@ expr * fpa_decl_plugin::get_some_value(sort * s) { return res; } else if (s->is_sort_of(m_family_id, ROUNDING_MODE_SORT)) { - func_decl * f = mk_rm_const_decl(OP_FPA_RM_TOWARD_ZERO, 0, 0, 0, 0, s); + func_decl * f = mk_rm_const_decl(OP_FPA_RM_TOWARD_ZERO, 0, nullptr, 0, nullptr, s); return m_manager->mk_const(f); } UNREACHABLE(); - return 0; + return nullptr; } bool fpa_decl_plugin::is_value(app * e) const { diff --git a/src/ast/fpa_decl_plugin.h b/src/ast/fpa_decl_plugin.h index 4e86c9d3f..c914bb6c7 100644 --- a/src/ast/fpa_decl_plugin.h +++ b/src/ast/fpa_decl_plugin.h @@ -159,11 +159,11 @@ class fpa_decl_plugin : public decl_plugin { func_decl * mk_bv_wrap(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); - virtual void set_manager(ast_manager * m, family_id id); + void set_manager(ast_manager * m, family_id id) override; unsigned mk_id(mpf const & v); void recycled_id(unsigned id); - virtual bool is_considered_uninterpreted(func_decl * f) { return false; } + bool is_considered_uninterpreted(func_decl * f) override { return false; } public: fpa_decl_plugin(); @@ -171,18 +171,18 @@ public: bool is_float_sort(sort * s) const { return is_sort_of(s, m_family_id, FLOATING_POINT_SORT); } bool is_rm_sort(sort * s) const { return is_sort_of(s, m_family_id, ROUNDING_MODE_SORT); } - virtual ~fpa_decl_plugin(); - virtual void finalize(); + ~fpa_decl_plugin() override; + void finalize() override; - virtual decl_plugin * mk_fresh(); - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); - virtual void get_op_names(svector & op_names, symbol const & logic); - virtual void get_sort_names(svector & sort_names, symbol const & logic); - virtual expr * get_some_value(sort * s); - virtual bool is_value(app* e) const; - virtual bool is_unique_value(app* e) const; + decl_plugin * mk_fresh() override; + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; + void get_op_names(svector & op_names, symbol const & logic) override; + void get_sort_names(svector & sort_names, symbol const & logic) override; + expr * get_some_value(sort * s) override; + bool is_value(app* e) const override; + bool is_unique_value(app* e) const override; mpf_manager & fm() { return m_fm; } func_decl * mk_numeral_decl(mpf const & v); @@ -197,8 +197,8 @@ public: return m_values[id]; } - virtual void del(parameter const & p); - virtual parameter translate(parameter const & p, decl_plugin & target); + void del(parameter const & p) override; + parameter translate(parameter const & p, decl_plugin & target) override; }; class fpa_util { @@ -342,7 +342,7 @@ public: app * mk_bv2rm(expr * bv3) { SASSERT(m_bv_util.is_bv(bv3) && m_bv_util.get_bv_size(bv3) == 3); - return m().mk_app(m_fid, OP_FPA_BV2RM, 0, 0, 1, &bv3, mk_rm_sort()); + return m().mk_app(m_fid, OP_FPA_BV2RM, 0, nullptr, 1, &bv3, mk_rm_sort()); } bool is_bvwrap(expr const * e) const { return is_app_of(e, get_family_id(), OP_FPA_BVWRAP); } diff --git a/src/ast/func_decl_dependencies.cpp b/src/ast/func_decl_dependencies.cpp index a5412f206..2a69f4514 100644 --- a/src/ast/func_decl_dependencies.cpp +++ b/src/ast/func_decl_dependencies.cpp @@ -86,7 +86,7 @@ class func_decl_dependencies::top_sort { ptr_vector m_todo; func_decl_set * definition(func_decl * f) const { - func_decl_set * r = 0; + func_decl_set * r = nullptr; m_deps.find(f, r); return r; } @@ -210,7 +210,7 @@ bool func_decl_dependencies::insert(func_decl * f, func_decl_set * s) { } void func_decl_dependencies::erase(func_decl * f) { - func_decl_set * s = 0; + func_decl_set * s = nullptr; if (m_deps.find(f, s)) { m_manager.dec_ref(f); dec_ref(m_manager, *s); diff --git a/src/ast/func_decl_dependencies.h b/src/ast/func_decl_dependencies.h index 0a3c1892f..b813dc31f 100644 --- a/src/ast/func_decl_dependencies.h +++ b/src/ast/func_decl_dependencies.h @@ -96,7 +96,7 @@ public: */ bool contains(func_decl * f) const { return m_deps.contains(f); } - func_decl_set * get_dependencies(func_decl * f) const { func_decl_set * r = 0; m_deps.find(f, r); return r; } + func_decl_set * get_dependencies(func_decl * f) const { func_decl_set * r = nullptr; m_deps.find(f, r); return r; } /** \brief Erase \c f (and its dependencies) from the manager. diff --git a/src/ast/macro_substitution.cpp b/src/ast/macro_substitution.cpp index 7b4cd6244..2868a1876 100644 --- a/src/ast/macro_substitution.cpp +++ b/src/ast/macro_substitution.cpp @@ -77,7 +77,7 @@ void macro_substitution::cleanup() { void macro_substitution::insert(func_decl * f, quantifier * q, proof * pr, expr_dependency * dep) { DEBUG_CODE({ app * body = to_app(q->get_expr()); - SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body)); + SASSERT(m_manager.is_eq(body)); expr * lhs = body->get_arg(0); expr * rhs = body->get_arg(1); SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f)); @@ -123,20 +123,20 @@ void macro_substitution::insert(func_decl * f, quantifier * q, proof * pr, expr_ void macro_substitution::erase(func_decl * f) { if (proofs_enabled()) { - proof * pr = 0; + proof * pr = nullptr; if (m_decl2macro_pr->find(f, pr)) { m_manager.dec_ref(pr); m_decl2macro_pr->erase(f); } } if (unsat_core_enabled()) { - expr_dependency * dep = 0; + expr_dependency * dep = nullptr; if (m_decl2macro_dep->find(f, dep)) { m_manager.dec_ref(dep); m_decl2macro_dep->erase(f); } } - quantifier * q = 0; + quantifier * q = nullptr; if (m_decl2macro.find(f, q)) { m_manager.dec_ref(f); m_manager.dec_ref(q); @@ -146,7 +146,7 @@ void macro_substitution::erase(func_decl * f) { void macro_substitution::get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def) { app * body = to_app(q->get_expr()); - SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body)); + SASSERT(m_manager.is_eq(body)); expr * lhs = to_app(body)->get_arg(0); expr * rhs = to_app(body)->get_arg(1); SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f)); diff --git a/src/ast/macro_substitution.h b/src/ast/macro_substitution.h index 7c65421fd..e028dcef8 100644 --- a/src/ast/macro_substitution.h +++ b/src/ast/macro_substitution.h @@ -45,7 +45,7 @@ public: bool empty() const { return m_decl2macro.empty(); } - void insert(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep = 0); + void insert(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep = nullptr); void erase(func_decl * f); bool contains(func_decl * f) { return m_decl2macro.contains(f); } bool find(func_decl * f, quantifier * & q, proof * & pr); diff --git a/src/ast/macros/macro_finder.cpp b/src/ast/macros/macro_finder.cpp index ed067f331..5a46da65a 100644 --- a/src/ast/macros/macro_finder.cpp +++ b/src/ast/macros/macro_finder.cpp @@ -71,7 +71,7 @@ bool macro_finder::is_arith_macro(expr * n, proof * pr, expr_dependency * dep, e quantifier_ref new_q(m); new_q = m.update_quantifier(to_quantifier(n), new_body); - proof * new_pr = 0; + proof * new_pr = nullptr; if (m.proofs_enabled()) { proof * rw = m.mk_rewrite(n, new_q); new_pr = m.mk_modus_ponens(pr, rw); @@ -142,7 +142,7 @@ bool macro_finder::is_arith_macro(expr * n, proof * pr, vector& quantifier_ref new_q(m); new_q = m.update_quantifier(to_quantifier(n), new_body); - proof * new_pr = 0; + proof * new_pr = nullptr; if (m.proofs_enabled()) { proof * rw = m.mk_rewrite(n, new_q); new_pr = m.mk_modus_ponens(pr, rw); @@ -163,7 +163,7 @@ bool macro_finder::is_arith_macro(expr * n, proof * pr, vector& quantifier * q1 = m.update_quantifier(new_q, body1); expr * patterns[1] = { m.mk_pattern(k_app) }; quantifier * q2 = m.update_quantifier(new_q, 1, patterns, body2); - proof* pr1 = 0, *pr2 = 0; + proof* pr1 = nullptr, *pr2 = nullptr; if (m.proofs_enabled()) { // new_pr : new_q // rw : [rewrite] new_q ~ q1 & q2 @@ -233,7 +233,7 @@ static void pseudo_predicate_macro2macro(ast_manager & m, app * head, app * t, e app * body_1 = m.mk_eq(head, ite); app * body_2 = m.mk_not(m.mk_eq(k_app, t)); quantifier * q1 = m.update_quantifier(q, body_1); - proof * pr1 = 0, *pr2 = 0; + proof * pr1 = nullptr, *pr2 = nullptr; expr * pats[1] = { m.mk_pattern(k_app) }; quantifier * q2 = m.update_quantifier(q, 1, pats, body_2); // erase patterns if (m.proofs_enabled()) { @@ -268,8 +268,8 @@ bool macro_finder::expand_macros(unsigned num, expr * const * exprs, proof * con bool found_new_macro = false; for (unsigned i = 0; i < num; i++) { expr * n = exprs[i]; - proof * pr = m.proofs_enabled() ? prs[i] : 0; - expr_dependency * depi = deps != 0 ? deps[i] : 0; + proof * pr = m.proofs_enabled() ? prs[i] : nullptr; + expr_dependency * depi = deps != nullptr ? deps[i] : nullptr; expr_ref new_n(m), def(m); proof_ref new_pr(m); expr_dependency_ref new_dep(m); @@ -292,7 +292,7 @@ bool macro_finder::expand_macros(unsigned num, expr * const * exprs, proof * con new_exprs.push_back(new_n); if (m.proofs_enabled()) new_prs.push_back(new_pr); - if (deps != 0) + if (deps != nullptr) new_deps.push_back(new_dep); } } @@ -333,11 +333,11 @@ bool macro_finder::expand_macros(unsigned num, justified_expr const * fmls, vect bool found_new_macro = false; for (unsigned i = 0; i < num; i++) { expr * n = fmls[i].get_fml(); - proof * pr = m.proofs_enabled() ? fmls[i].get_proof() : 0; + proof * pr = m.proofs_enabled() ? fmls[i].get_proof() : nullptr; expr_ref new_n(m), def(m); proof_ref new_pr(m); expr_dependency_ref new_dep(m); - m_macro_manager.expand_macros(n, pr, 0, new_n, new_pr, new_dep); + m_macro_manager.expand_macros(n, pr, nullptr, new_n, new_pr, new_dep); app_ref head(m), t(m); if (is_macro(new_n, head, def) && m_macro_manager.insert(head->get_decl(), to_quantifier(new_n.get()), new_pr)) { TRACE("macro_finder_found", tout << "found new macro: " << head->get_decl()->get_name() << "\n" << new_n << "\n";); diff --git a/src/ast/macros/macro_manager.cpp b/src/ast/macros/macro_manager.cpp index 855cae107..2f429ccf7 100644 --- a/src/ast/macros/macro_manager.cpp +++ b/src/ast/macros/macro_manager.cpp @@ -169,9 +169,8 @@ void macro_manager::mark_forbidden(unsigned n, justified_expr const * exprs) { void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const { app * body = to_app(q->get_expr()); - SASSERT(m.is_eq(body) || m.is_iff(body)); - expr * lhs = to_app(body)->get_arg(0); - expr * rhs = to_app(body)->get_arg(1); + expr * lhs = nullptr, *rhs = nullptr; + VERIFY(m.is_eq(body, lhs, rhs)); SASSERT(is_app_of(lhs, d) || is_app_of(rhs, d)); SASSERT(!is_app_of(lhs, d) || !is_app_of(rhs, d)); if (is_app_of(lhs, d)) { @@ -188,7 +187,7 @@ void macro_manager::display(std::ostream & out) { unsigned sz = m_decls.size(); for (unsigned i = 0; i < sz; i++) { func_decl * f = m_decls.get(i); - quantifier * q = 0; + quantifier * q = nullptr; m_decl2macro.find(f, q); app * head; expr * def; @@ -226,7 +225,7 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg { bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return BR_FAILED; } @@ -255,7 +254,7 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg { erase_patterns = true; } if (erase_patterns) { - result = m.update_quantifier(old_q, 0, 0, 0, 0, new_body); + result = m.update_quantifier(old_q, 0, nullptr, 0, nullptr, new_body); } return erase_patterns; } @@ -264,13 +263,13 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg { if (!is_app(_n)) return false; app * n = to_app(_n); - quantifier * q = 0; + quantifier * q = nullptr; func_decl * d = n->get_decl(); TRACE("macro_manager", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";); if (mm.m_decl2macro.find(d, q)) { TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";); - app * head = 0; - expr * def = 0; + app * head = nullptr; + expr * def = nullptr; mm.get_head_def(q, d, head, def); unsigned num = n->get_num_args(); SASSERT(head && def); @@ -292,14 +291,14 @@ struct macro_manager::macro_expander_cfg : public default_rewriter_cfg { expr_ref instance(m); s(q->get_expr(), num, subst_args.c_ptr(), instance); proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr()); - proof * q_pr = 0; + proof * q_pr = nullptr; mm.m_decl2macro_pr.find(d, q_pr); SASSERT(q_pr != 0); proof * prs[2] = { qi_pr, q_pr }; p = m.mk_unit_resolution(2, prs); } else { - p = 0; + p = nullptr; } expr_dependency * ed = mm.m_decl2macro_dep.find(d); m_used_macro_dependencies = m.mk_join(m_used_macro_dependencies, ed); diff --git a/src/ast/macros/macro_manager.h b/src/ast/macros/macro_manager.h index 0205fb891..4fcb29f5d 100644 --- a/src/ast/macros/macro_manager.h +++ b/src/ast/macros/macro_manager.h @@ -67,7 +67,7 @@ public: ~macro_manager(); ast_manager & get_manager() const { return m; } macro_util & get_util() { return m_util; } - bool insert(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep = 0); + bool insert(func_decl * f, quantifier * m, proof * pr, expr_dependency * dep = nullptr); bool has_macros() const { return !m_macros.empty(); } void push_scope(); void pop_scope(unsigned num_scopes); @@ -82,7 +82,7 @@ public: unsigned get_first_macro_last_level() const { return m_scopes.empty() ? 0 : m_scopes.back().m_decls_lim; } func_decl * get_macro_func_decl(unsigned i) const { return m_decls.get(i); } func_decl * get_macro_interpretation(unsigned i, expr_ref & interp) const; - quantifier * get_macro_quantifier(func_decl * f) const { quantifier * q = 0; m_decl2macro.find(f, q); return q; } + quantifier * get_macro_quantifier(func_decl * f) const { quantifier * q = nullptr; m_decl2macro.find(f, q); return q; } void get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const; void expand_macros(expr * n, proof * pr, expr_dependency * dep, expr_ref & r, proof_ref & new_pr, expr_dependency_ref & new_dep); diff --git a/src/ast/macros/macro_util.cpp b/src/ast/macros/macro_util.cpp index 1ab54d0b5..b2f31e374 100644 --- a/src/ast/macros/macro_util.cpp +++ b/src/ast/macros/macro_util.cpp @@ -33,8 +33,8 @@ macro_util::macro_util(ast_manager & m): m_arith(m), m_arith_rw(m), m_bv_rw(m), - m_forbidden_set(0), - m_curr_clause(0) { + m_forbidden_set(nullptr), + m_curr_clause(nullptr) { } @@ -175,7 +175,7 @@ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const { */ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const { - if (m_manager.is_eq(n) || m_manager.is_iff(n)) { + if (m_manager.is_eq(n)) { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (is_macro_head(lhs, num_decls) && !is_forbidden(to_app(lhs)->get_decl()) && @@ -207,7 +207,7 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & he */ bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const { - if (m_manager.is_eq(n) || m_manager.is_iff(n)) { + if (m_manager.is_eq(n)) { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); if (is_macro_head(rhs, num_decls) && !is_forbidden(to_app(rhs)->get_decl()) && @@ -256,7 +256,7 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex inv = false; ptr_buffer args; - expr * h = 0; + expr * h = nullptr; unsigned lhs_num_args; expr * const * lhs_args; if (is_add(lhs)) { @@ -270,13 +270,13 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex for (unsigned i = 0; i < lhs_num_args; i++) { expr * arg = lhs_args[i]; expr * neg_arg; - if (h == 0 && + if (h == nullptr && is_macro_head(arg, num_decls) && !is_forbidden(to_app(arg)->get_decl()) && !poly_contains_head(lhs, to_app(arg)->get_decl(), arg)) { h = arg; } - else if (h == 0 && m_arith_rw.is_times_minus_one(arg, neg_arg) && + else if (h == nullptr && m_arith_rw.is_times_minus_one(arg, neg_arg) && is_macro_head(neg_arg, num_decls) && !is_forbidden(to_app(neg_arg)->get_decl()) && !poly_contains_head(lhs, to_app(neg_arg)->get_decl(), arg)) { @@ -287,7 +287,7 @@ bool macro_util::is_arith_macro(expr * n, unsigned num_decls, app_ref & head, ex args.push_back(arg); } } - if (h == 0) + if (h == nullptr) return false; head = to_app(h); expr_ref tmp(m_manager); @@ -339,10 +339,9 @@ bool macro_util::is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";); expr * body = to_quantifier(n)->get_expr(); unsigned num_decls = to_quantifier(n)->get_num_decls(); - if (!m_manager.is_iff(body)) + expr * lhs, *rhs; + if (!m_manager.is_iff(body, lhs, rhs)) return false; - expr * lhs = to_app(body)->get_arg(0); - expr * rhs = to_app(body)->get_arg(1); if (is_pseudo_head(lhs, num_decls, head, t) && !is_forbidden(head->get_decl()) && !occurs(head->get_decl(), rhs)) { @@ -666,7 +665,7 @@ void macro_util::insert_macro(app * head, unsigned num_decls, expr * def, expr * expr_ref norm_def(m_manager); expr_ref norm_cond(m_manager); normalize_expr(head, num_decls, def, norm_def); - if (cond != 0) + if (cond != nullptr) normalize_expr(head, num_decls, cond, norm_cond); else if (!hint) norm_cond = m_manager.mk_true(); @@ -682,7 +681,7 @@ void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, expr_ref new_cond(m_manager); if (!hint) { quasi_macro_head_to_macro_head(head, num_decls, new_head, extra_cond); - if (cond == 0) + if (cond == nullptr) new_cond = extra_cond; else bool_rewriter(m_manager).mk_and(cond, extra_cond, new_cond); @@ -701,7 +700,7 @@ void macro_util::insert_quasi_macro(app * head, unsigned num_decls, expr * def, } bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) { - if (m_curr_clause == 0) + if (m_curr_clause == nullptr) return false; SASSERT(is_clause(m_manager, m_curr_clause)); unsigned num_lits = get_clause_num_literals(m_manager, m_curr_clause); @@ -714,7 +713,7 @@ bool macro_util::rest_contains_decl(func_decl * f, expr * except_lit) { } void macro_util::get_rest_clause_as_cond(expr * except_lit, expr_ref & extra_cond) { - if (m_curr_clause == 0) + if (m_curr_clause == nullptr) return; SASSERT(is_clause(m_manager, m_curr_clause)); expr_ref_buffer neg_other_lits(m_manager); @@ -795,7 +794,7 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a mk_sub(rhs, rest, def); // If is_poly_hint, rhs may contain variables that do not occur in to_app(arg). // So, we should re-check. - if (!_is_poly_hint || is_poly_hint(def, to_app(arg), 0)) + if (!_is_poly_hint || is_poly_hint(def, to_app(arg), nullptr)) add_arith_macro_candidate(to_app(arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } else if (is_times_minus_one(arg, neg_arg) && is_app(neg_arg)) { @@ -816,7 +815,7 @@ void macro_util::collect_arith_macro_candidates(expr * lhs, expr * rhs, expr * a mk_sub(rest, rhs, def); // If is_poly_hint, rhs may contain variables that do not occur in to_app(neg_arg). // So, we should re-check. - if (!_is_poly_hint || is_poly_hint(def, to_app(neg_arg), 0)) + if (!_is_poly_hint || is_poly_hint(def, to_app(neg_arg), nullptr)) add_arith_macro_candidate(to_app(neg_arg), num_decls, def, atom, is_ineq, _is_poly_hint, r); } } @@ -885,7 +884,7 @@ void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, insert_quasi_macro(to_app(lhs), num_decls, rhs, cond, false, true, false, r); } else if (is_hint_atom(lhs, rhs)) { - insert_quasi_macro(to_app(lhs), num_decls, rhs, 0, false, true, true, r); + insert_quasi_macro(to_app(lhs), num_decls, rhs, nullptr, false, true, true, r); } if (is_quasi_macro_head(rhs, num_decls) && @@ -897,7 +896,7 @@ void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, insert_quasi_macro(to_app(rhs), num_decls, lhs, cond, false, true, false, r); } else if (is_hint_atom(rhs, lhs)) { - insert_quasi_macro(to_app(rhs), num_decls, lhs, 0, false, true, true, r); + insert_quasi_macro(to_app(rhs), num_decls, lhs, nullptr, false, true, true, r); } } @@ -905,7 +904,7 @@ void macro_util::collect_macro_candidates_core(expr * atom, unsigned num_decls, } void macro_util::collect_macro_candidates(expr * atom, unsigned num_decls, macro_candidates & r) { - m_curr_clause = 0; + m_curr_clause = nullptr; r.reset(); collect_macro_candidates_core(atom, num_decls, r); } @@ -922,7 +921,7 @@ void macro_util::collect_macro_candidates(quantifier * q, macro_candidates & r) unsigned num_lits = get_clause_num_literals(m_manager, n); for (unsigned i = 0; i < num_lits; i++) collect_macro_candidates_core(get_clause_literal(m_manager, n, i), num_decls, r); - m_curr_clause = 0; + m_curr_clause = nullptr; } else { collect_macro_candidates_core(n, num_decls, r); diff --git a/src/ast/macros/macro_util.h b/src/ast/macros/macro_util.h index 3ab00df2a..fecdeb97f 100644 --- a/src/ast/macros/macro_util.h +++ b/src/ast/macros/macro_util.h @@ -64,7 +64,7 @@ private: mutable bv_rewriter m_bv_rw; obj_hashtable * m_forbidden_set; - bool is_forbidden(func_decl * f) const { return m_forbidden_set != 0 && m_forbidden_set->contains(f); } + bool is_forbidden(func_decl * f) const { return m_forbidden_set != nullptr && m_forbidden_set->contains(f); } bool poly_contains_head(expr * n, func_decl * f, expr * exception) const; void collect_arith_macros(expr * n, unsigned num_decls, unsigned max_macros, bool allow_cond_macros, diff --git a/src/ast/macros/quasi_macros.cpp b/src/ast/macros/quasi_macros.cpp index 7d5e7c3db..3a0735e25 100644 --- a/src/ast/macros/quasi_macros.cpp +++ b/src/ast/macros/quasi_macros.cpp @@ -91,7 +91,7 @@ public: void operator()(var * n) { m_bitset.set(n->get_idx(), true); } void operator()(quantifier * n) {} void operator()(app * n) {} - bool all_used(void) { + bool all_used() { for (unsigned i = 0; i < m_bitset.size() ; i++) if (!m_bitset.get(i)) return false; @@ -158,7 +158,7 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const { if (is_quantifier(e) && to_quantifier(e)->is_forall()) { quantifier * q = to_quantifier(e); expr * qe = q->get_expr(); - if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) { + if ((m_manager.is_eq(qe))) { expr * lhs = to_app(qe)->get_arg(0); expr * rhs = to_app(qe)->get_arg(1); @@ -281,10 +281,10 @@ bool quasi_macros::find_macros(unsigned n, expr * const * exprs) { quasi_macro_to_macro(to_quantifier(exprs[i]), a, t, macro); TRACE("quasi_macros", tout << "Found quasi macro: " << mk_pp(exprs[i], m_manager) << std::endl; tout << "Macro: " << mk_pp(macro, m_manager) << std::endl; ); - proof * pr = 0; + proof * pr = nullptr; if (m_manager.proofs_enabled()) pr = m_manager.mk_def_axiom(macro); - expr_dependency * dep = 0; + expr_dependency * dep = nullptr; if (m_macro_manager.insert(a->get_decl(), macro, pr, dep)) res = true; } @@ -320,7 +320,7 @@ bool quasi_macros::find_macros(unsigned n, justified_expr const * exprs) { quasi_macro_to_macro(to_quantifier(exprs[i].get_fml()), a, t, macro); TRACE("quasi_macros", tout << "Found quasi macro: " << mk_pp(exprs[i].get_fml(), m_manager) << std::endl; tout << "Macro: " << mk_pp(macro, m_manager) << std::endl; ); - proof * pr = 0; + proof * pr = nullptr; if (m_manager.proofs_enabled()) pr = m_manager.mk_def_axiom(macro); if (m_macro_manager.insert(a->get_decl(), macro, pr)) @@ -336,7 +336,7 @@ void quasi_macros::apply_macros(unsigned n, expr * const * exprs, proof * const expr_ref r(m_manager), rs(m_manager); proof_ref pr(m_manager), ps(m_manager); expr_dependency_ref dep(m_manager); - proof * p = m_manager.proofs_enabled() ? prs[i] : 0; + proof * p = m_manager.proofs_enabled() ? prs[i] : nullptr; m_macro_manager.expand_macros(exprs[i], p, deps[i], r, pr, dep); m_rewriter(r); @@ -366,9 +366,9 @@ void quasi_macros::apply_macros(unsigned n, justified_expr const* fmls, vector const & var_names, expr_ref & new_def); + void mk_definition(expr * e, app * n, sort_ref_buffer & var_sorts, buffer const & var_names, expr_ref & new_def) override; }; @@ -210,7 +210,7 @@ bool defined_names::impl::mk_name(expr * e, expr_ref & new_def, proof_ref & new_ TRACE("mk_definition_bug", tout << "name for expression is already cached..., returning false...\n";); n = n_ptr; if (m_manager.proofs_enabled()) { - proof * pr_ptr = 0; + proof * pr_ptr = nullptr; m_expr2proof.find(e, pr_ptr); SASSERT(pr_ptr); pr = pr_ptr; diff --git a/src/ast/normal_forms/name_exprs.cpp b/src/ast/normal_forms/name_exprs.cpp index 7cc2719c9..3e5503ea7 100644 --- a/src/ast/normal_forms/name_exprs.cpp +++ b/src/ast/normal_forms/name_exprs.cpp @@ -38,8 +38,8 @@ class name_exprs_core : public name_exprs { m_pred(pred), m_r(m), m_pr(m), - m_def_exprs(0), - m_def_proofs(0) { + m_def_exprs(nullptr), + m_def_proofs(nullptr) { } void gen_name_for_expr(expr * n, expr * & t, proof * & t_pr) { @@ -77,10 +77,10 @@ public: m_rw(m, m.proofs_enabled(), m_cfg) { } - virtual ~name_exprs_core() { + ~name_exprs_core() override { } - virtual void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) { + void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) override { m_cfg.m_def_exprs = &new_defs; m_cfg.m_def_proofs = &new_def_proofs; m_rw(n, r, p); @@ -88,7 +88,7 @@ public: } - virtual void reset() { + void reset() override { m_rw.reset(); } }; @@ -102,7 +102,7 @@ class name_quantifier_labels : public name_exprs_core { ast_manager & m_manager; public: pred(ast_manager & m):m_manager(m) {} - virtual bool operator()(expr * t) { + bool operator()(expr * t) override { return is_quantifier(t) || m_manager.is_label(t); } }; @@ -114,7 +114,7 @@ public: m_pred(m) { } - virtual ~name_quantifier_labels() { + ~name_quantifier_labels() override { } }; @@ -127,9 +127,9 @@ class name_nested_formulas : public name_exprs_core { ast_manager & m_manager; expr * m_root; - pred(ast_manager & m):m_manager(m), m_root(0) {} + pred(ast_manager & m):m_manager(m), m_root(nullptr) {} - virtual bool operator()(expr * t) { + bool operator()(expr * t) override { TRACE("name_exprs", tout << "name_nested_formulas::pred:\n" << mk_ismt2_pp(t, m_manager) << "\n";); if (is_app(t)) return to_app(t)->get_family_id() == m_manager.get_basic_family_id() && to_app(t)->get_num_args() > 0 && t != m_root; @@ -145,10 +145,10 @@ public: m_pred(m) { } - virtual ~name_nested_formulas() { + ~name_nested_formulas() override { } - virtual void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) { + void operator()(expr * n, expr_ref_vector & new_defs, proof_ref_vector & new_def_proofs, expr_ref & r, proof_ref & p) override { m_pred.m_root = n; TRACE("name_exprs", tout << "operator()\n";); name_exprs_core::operator()(n, new_defs, new_def_proofs, r, p); diff --git a/src/ast/normal_forms/nnf.cpp b/src/ast/normal_forms/nnf.cpp index 6fc65543d..c7f20ced6 100644 --- a/src/ast/normal_forms/nnf.cpp +++ b/src/ast/normal_forms/nnf.cpp @@ -82,7 +82,7 @@ class skolemizer { expr_ref_vector args(m()); for (unsigned i = 0; i < sz; i++) { sort * s = uv.get(i); - if (s != 0) { + if (s != nullptr) { sorts.push_back(s); args.push_back(m().mk_var(i, s)); } @@ -105,10 +105,10 @@ class skolemizer { // for (unsigned i = 0; i < sz; i++) { sort * s = uv.get(i); - if (s != 0) + if (s != nullptr) substitution.push_back(m().mk_var(i, s)); else - substitution.push_back(0); + substitution.push_back(nullptr); } // // (VAR num_decls) ... (VAR num_decls+sz-1) @@ -137,7 +137,7 @@ class skolemizer { } } s(body, substitution.size(), substitution.c_ptr(), r); - p = 0; + p = nullptr; if (m().proofs_enabled()) { if (q->is_forall()) p = m().mk_skolemization(m().mk_not(q), m().mk_not(r)); @@ -163,8 +163,8 @@ public: void operator()(quantifier * q, expr_ref & r, proof_ref & p) { r = m_cache.find(q); - if (r.get() != 0) { - p = 0; + if (r.get() != nullptr) { + p = nullptr; if (m().proofs_enabled()) p = static_cast(m_cache_pr.find(q)); } @@ -496,7 +496,7 @@ struct nnf::imp { return false; } expr * r = m_result_stack.back(); - proof * pr = 0; + proof * pr = nullptr; if (proofs_enabled()) { pr = m_result_pr_stack.back(); if (!fr.m_pol) { @@ -582,7 +582,7 @@ struct nnf::imp { return true; } - bool is_eq(app * t) const { return m().is_eq(t) || m().is_iff(t); } + bool is_eq(app * t) const { return m().is_eq(t); } bool process_iff_xor(app * t, frame & fr) { SASSERT(t->get_num_args() == 2); @@ -630,7 +630,7 @@ struct nnf::imp { } bool process_eq(app * t, frame & fr) { - if (m().is_bool(t->get_arg(0))) + if (m().is_iff(t)) return process_iff_xor(t, fr); else return process_default(t, fr); @@ -673,7 +673,7 @@ struct nnf::imp { } expr * arg = m_result_stack.back(); - proof * arg_pr = proofs_enabled() ? m_result_pr_stack.back() : 0; + proof * arg_pr = proofs_enabled() ? m_result_pr_stack.back() : nullptr; if (m_ignore_labels && !proofs_enabled()) return true; // the result is already on the stack @@ -725,7 +725,6 @@ struct nnf::imp { return process_implies(t, fr); case OP_ITE: return process_ite(t, fr); - case OP_IFF: case OP_XOR: return process_iff_xor(t, fr); case OP_EQ: @@ -765,7 +764,7 @@ struct nnf::imp { if (q->is_forall() == fr.m_pol || !m_skolemize) { expr * new_expr = m_result_stack.back(); - proof * new_expr_pr = proofs_enabled() ? m_result_pr_stack.back() : 0; + proof * new_expr_pr = proofs_enabled() ? m_result_pr_stack.back() : nullptr; ptr_buffer new_patterns; @@ -783,8 +782,8 @@ struct nnf::imp { // So, ignore patterns } - quantifier * new_q = 0; - proof * new_q_pr = 0; + quantifier * new_q = nullptr; + proof * new_q_pr = nullptr; if (fr.m_pol) { new_q = m().update_quantifier(q, new_patterns.size(), new_patterns.c_ptr(), new_expr); if (proofs_enabled()) @@ -827,7 +826,7 @@ struct nnf::imp { if (proofs_enabled()) { result_pr = m_result_pr_stack.back(); m_result_pr_stack.pop_back(); - if (result_pr.get() == 0) + if (result_pr.get() == nullptr) result_pr = m().mk_reflexivity(t); SASSERT(m_result_pr_stack.empty()); } @@ -870,7 +869,7 @@ struct nnf::imp { if (status) { if (fr.m_cache_result) - cache_result(fr.m_curr, fr.m_pol, fr.m_in_q, m_result_stack.back(), proofs_enabled() ? m_result_pr_stack.back() : 0); + cache_result(fr.m_curr, fr.m_pol, fr.m_in_q, m_result_stack.back(), proofs_enabled() ? m_result_pr_stack.back() : nullptr); m_frame_stack.pop_back(); } } diff --git a/src/ast/normal_forms/pull_quant.cpp b/src/ast/normal_forms/pull_quant.cpp index ee618c747..06881f97b 100644 --- a/src/ast/normal_forms/pull_quant.cpp +++ b/src/ast/normal_forms/pull_quant.cpp @@ -85,7 +85,7 @@ struct pull_quant::imp { var_sorts.push_back(nested_q->get_decl_sort(j)); symbol s = nested_q->get_decl_name(j); if (std::find(var_names.begin(), var_names.end(), s) != var_names.end()) - var_names.push_back(m_manager.mk_fresh_var_name(s.is_numerical() ? 0 : s.bare_str())); + var_names.push_back(m_manager.mk_fresh_var_name(s.is_numerical() ? nullptr : s.bare_str())); else var_names.push_back(s); } @@ -215,7 +215,7 @@ struct pull_quant::imp { // Code for proof generation... void pull_quant2(expr * n, expr_ref & r, proof_ref & pr) { - pr = 0; + pr = nullptr; if (is_app(n)) { expr_ref_buffer new_args(m_manager); expr_ref new_arg(m_manager); @@ -231,8 +231,8 @@ struct pull_quant::imp { pull_quant1(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr(), r); if (m_manager.proofs_enabled()) { app * r1 = m_manager.mk_app(to_app(n)->get_decl(), new_args.size(), new_args.c_ptr()); - proof * p1 = proofs.empty() ? 0 : m_manager.mk_congruence(to_app(n), r1, proofs.size(), proofs.c_ptr()); - proof * p2 = r1 == r ? 0 : m_manager.mk_pull_quant(r1, to_quantifier(r)); + proof * p1 = proofs.empty() ? nullptr : m_manager.mk_congruence(to_app(n), r1, proofs.size(), proofs.c_ptr()); + proof * p2 = r1 == r ? nullptr : m_manager.mk_pull_quant(r1, to_quantifier(r)); pr = m_manager.mk_transitivity(p1, p2); } } @@ -242,12 +242,12 @@ struct pull_quant::imp { pull_quant1(to_quantifier(n), new_expr, r); if (m_manager.proofs_enabled()) { quantifier * q1 = m_manager.update_quantifier(to_quantifier(n), new_expr); - proof * p1 = 0; + proof * p1 = nullptr; if (n != q1) { proof * p0 = m_manager.mk_pull_quant(n, to_quantifier(new_expr)); p1 = m_manager.mk_quant_intro(to_quantifier(n), q1, p0); } - proof * p2 = q1 == r ? 0 : m_manager.mk_pull_quant(q1, to_quantifier(r)); + proof * p2 = q1 == r ? nullptr : m_manager.mk_pull_quant(q1, to_quantifier(r)); pr = m_manager.mk_transitivity(p1, p2); } } diff --git a/src/ast/pattern/expr_pattern_match.cpp b/src/ast/pattern/expr_pattern_match.cpp index 628c777d3..c441fb4ce 100644 --- a/src/ast/pattern/expr_pattern_match.cpp +++ b/src/ast/pattern/expr_pattern_match.cpp @@ -83,7 +83,7 @@ expr_pattern_match::instantiate(expr* a, unsigned num_bound, subst& s, expr_ref& inst_proc proc(m_manager, s, b, m_regs); for_each_ast(proc, a); - expr* v = 0; + expr* v = nullptr; proc.m_memoize.find(a, v); SASSERT(v); result = v; @@ -387,7 +387,7 @@ expr_pattern_match::initialize(char const * spec_string) { m_instrs.push_back(instr(BACKTRACK)); std::istringstream is(spec_string); - cmd_context ctx(true, &m_manager); + cmd_context ctx(true, &m_manager); bool ps = ctx.print_success_enabled(); ctx.set_print_success(false); VERIFY(parse_smt2_commands(ctx, is)); diff --git a/src/ast/pattern/expr_pattern_match.h b/src/ast/pattern/expr_pattern_match.h index 6d9d47e1e..d1388b43f 100644 --- a/src/ast/pattern/expr_pattern_match.h +++ b/src/ast/pattern/expr_pattern_match.h @@ -80,7 +80,7 @@ class expr_pattern_match { } void operator()(var* v) { - var* b = 0; + var* b = nullptr; if (m_bound.find(v, b)) { m_memoize.insert(v, b); } @@ -99,7 +99,7 @@ class expr_pattern_match { decl = to_app(m_regs[r])->get_decl(); } for (unsigned i = 0; i < num_args; ++i) { - expr* arg = 0; + expr* arg = nullptr; if (m_memoize.find(n->get_arg(i), arg)) { SASSERT(arg); args.push_back(arg); diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp index 17d8747f2..3208a50fe 100644 --- a/src/ast/pattern/pattern_inference.cpp +++ b/src/ast/pattern/pattern_inference.cpp @@ -168,7 +168,7 @@ bool pattern_inference_cfg::collect::visit_children(expr * n, unsigned delta) { inline void pattern_inference_cfg::collect::save(expr * n, unsigned delta, info * i) { m_cache.insert(entry(n, delta), i); - if (i != 0) + if (i != nullptr) m_info.push_back(i); } @@ -181,7 +181,7 @@ void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) { uint_set free_vars; if (idx < m_num_bindings) free_vars.insert(idx); - info * i = 0; + info * i = nullptr; if (delta == 0) i = alloc(info, m, n, free_vars, 1); else @@ -189,7 +189,7 @@ void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) { save(n, delta, i); } else { - save(n, delta, 0); + save(n, delta, nullptr); } return; } @@ -197,7 +197,7 @@ void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) { app * c = to_app(n); func_decl * decl = c->get_decl(); if (m_owner.is_forbidden(c)) { - save(n, delta, 0); + save(n, delta, nullptr); return; } @@ -213,14 +213,14 @@ void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) { unsigned num = c->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * child = c->get_arg(i); - info * child_info = 0; + info * child_info = nullptr; #ifdef Z3DEBUG bool found = #endif m_cache.find(entry(child, delta), child_info); SASSERT(found); - if (child_info == 0) { - save(n, delta, 0); + if (child_info == nullptr) { + save(n, delta, nullptr); return; } buffer.push_back(child_info->m_node.get()); @@ -230,7 +230,7 @@ void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) { changed = true; } - app * new_node = 0; + app * new_node = nullptr; if (changed) new_node = m.mk_app(decl, buffer.size(), buffer.c_ptr()); else @@ -254,7 +254,7 @@ void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) { return; } default: - save(n, delta, 0); + save(n, delta, nullptr); return; } } @@ -630,7 +630,7 @@ bool pattern_inference_cfg::reduce_quantifier( if (new_patterns.empty() && num_no_patterns > 0) { if (new_patterns.empty()) { - mk_patterns(q->get_num_decls(), new_body, 0, 0, new_patterns); + mk_patterns(q->get_num_decls(), new_body, 0, nullptr, new_patterns); if (m_params.m_pi_warnings && !new_patterns.empty()) { warning_msg("ignoring nopats annotation because Z3 couldn't find any other pattern (quantifier id: %s)", q->get_qid().str().c_str()); } @@ -683,7 +683,7 @@ bool pattern_inference_cfg::reduce_quantifier( pull(new_q, new_expr, new_pr); quantifier * result2 = to_quantifier(new_expr); if (result2 != new_q) { - mk_patterns(result2->get_num_decls(), result2->get_expr(), 0, 0, new_patterns); + mk_patterns(result2->get_num_decls(), result2->get_expr(), 0, nullptr, new_patterns); if (!new_patterns.empty()) { if (m_params.m_pi_warnings) { warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str()); @@ -710,7 +710,7 @@ bool pattern_inference_cfg::reduce_quantifier( result = new_q; - IF_IVERBOSE(10, + IF_VERBOSE(10, verbose_stream() << "(smt.inferred-patterns :qid " << q->get_qid() << "\n"; for (unsigned i = 0; i < new_patterns.size(); i++) verbose_stream() << " " << mk_ismt2_pp(new_patterns[i], m, 2) << "\n"; diff --git a/src/ast/pattern/pattern_inference.h b/src/ast/pattern/pattern_inference.h index 905662477..f32dcecd0 100644 --- a/src/ast/pattern/pattern_inference.h +++ b/src/ast/pattern/pattern_inference.h @@ -118,7 +118,7 @@ class pattern_inference_cfg : public default_rewriter_cfg { struct entry { expr * m_node; unsigned m_delta; - entry():m_node(0), m_delta(0) {} + entry():m_node(nullptr), m_delta(0) {} entry(expr * n, unsigned d):m_node(n), m_delta(d) {} unsigned hash() const { return hash_u_u(m_node->get_id(), m_delta); diff --git a/src/ast/pb_decl_plugin.cpp b/src/ast/pb_decl_plugin.cpp index 06c6aac48..c8e707cee 100644 --- a/src/ast/pb_decl_plugin.cpp +++ b/src/ast/pb_decl_plugin.cpp @@ -19,6 +19,7 @@ Revision History: #include "ast/pb_decl_plugin.h" #include "ast/ast_util.h" +#include "ast/ast_pp.h" pb_decl_plugin::pb_decl_plugin(): m_at_most_sym("at-most"), @@ -54,7 +55,7 @@ func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p } func_decl_info info(m_family_id, k, 1, parameters); return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info); - } + } case OP_PB_GE: case OP_PB_LE: case OP_PB_EQ: { @@ -86,7 +87,7 @@ func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, p } default: UNREACHABLE(); - return 0; + return nullptr; } } @@ -128,9 +129,15 @@ app * pb_util::mk_le(unsigned num_args, rational const * coeffs, expr * const * normalize(num_args, coeffs, k); m_params.reset(); m_params.push_back(parameter(floor(m_k))); + bool all_ones = true; for (unsigned i = 0; i < num_args; ++i) { + all_ones &= m_coeffs[i].is_one(); m_params.push_back(parameter(m_coeffs[i])); } + if (all_ones && k.is_unsigned()) { + m_params[0] = parameter(floor(m_k).get_unsigned()); + return m.mk_app(m_fid, OP_AT_MOST_K, 1, m_params.c_ptr(), num_args, args, m.mk_bool_sort()); + } return m.mk_app(m_fid, OP_PB_LE, m_params.size(), m_params.c_ptr(), num_args, args, m.mk_bool_sort()); } @@ -138,9 +145,15 @@ app * pb_util::mk_ge(unsigned num_args, rational const * coeffs, expr * const * normalize(num_args, coeffs, k); m_params.reset(); m_params.push_back(parameter(ceil(m_k))); + bool all_ones = true; for (unsigned i = 0; i < num_args; ++i) { + all_ones &= m_coeffs[i].is_one(); m_params.push_back(parameter(m_coeffs[i])); } + if (all_ones && k.is_unsigned()) { + m_params[0] = parameter(ceil(m_k).get_unsigned()); + return m.mk_app(m_fid, OP_AT_LEAST_K, 1, m_params.c_ptr(), num_args, args, m.mk_bool_sort()); + } return m.mk_app(m_fid, OP_PB_GE, m_params.size(), m_params.c_ptr(), num_args, args, m.mk_bool_sort()); } @@ -149,6 +162,9 @@ app * pb_util::mk_eq(unsigned num_args, rational const * coeffs, expr * const * if (!m_k.is_int()) { return m.mk_false(); } + if (num_args == 0) { + return m_k.is_zero() ? m.mk_true() : m.mk_false(); + } m_params.reset(); m_params.push_back(parameter(m_k)); for (unsigned i = 0; i < num_args; ++i) { @@ -299,6 +315,6 @@ bool pb_util::has_unit_coefficients(func_decl* f) const { app* pb_util::mk_fresh_bool() { symbol name = m.mk_fresh_var_name("pb"); - func_decl_info info(m_fid, OP_PB_AUX_BOOL, 0, 0); - return m.mk_const(m.mk_func_decl(name, 0, (sort *const*)0, m.mk_bool_sort(), info)); + func_decl_info info(m_fid, OP_PB_AUX_BOOL, 0, nullptr); + return m.mk_const(m.mk_func_decl(name, 0, (sort *const*)nullptr, m.mk_bool_sort(), info)); } diff --git a/src/ast/pb_decl_plugin.h b/src/ast/pb_decl_plugin.h index 7fdb592aa..aec14a64a 100644 --- a/src/ast/pb_decl_plugin.h +++ b/src/ast/pb_decl_plugin.h @@ -53,14 +53,14 @@ class pb_decl_plugin : public decl_plugin { func_decl * mk_eq(unsigned arity, rational const* coeffs, int k); public: pb_decl_plugin(); - virtual ~pb_decl_plugin() {} + ~pb_decl_plugin() override {} - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) { + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override { UNREACHABLE(); - return 0; + return nullptr; } - virtual decl_plugin * mk_fresh() { + decl_plugin * mk_fresh() override { return alloc(pb_decl_plugin); } @@ -69,11 +69,11 @@ public: // parameters[0] - integer (at most k elements) // all sorts are Booleans // parameters[1] .. parameters[arity] - coefficients - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); - virtual void get_op_names(svector & op_names, symbol const & logic); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; + void get_op_names(svector & op_names, symbol const & logic) override; - virtual bool is_considered_uninterpreted(func_decl * f) { return false; } + bool is_considered_uninterpreted(func_decl * f) override { return false; } }; @@ -123,7 +123,6 @@ public: app* mk_fresh_bool(); - private: rational to_rational(parameter const& p) const; }; diff --git a/src/ast/proofs/proof_checker.cpp b/src/ast/proofs/proof_checker.cpp index 6fd876efc..e148299be 100644 --- a/src/ast/proofs/proof_checker.cpp +++ b/src/ast/proofs/proof_checker.cpp @@ -1,4 +1,3 @@ - /*++ Copyright (c) 2015 Microsoft Corporation @@ -12,15 +11,15 @@ Copyright (c) 2015 Microsoft Corporation #include "ast/rewriter/th_rewriter.h" #include "ast/rewriter/var_subst.h" -#define IS_EQUIV(_e_) (m.is_eq(_e_) || m.is_iff(_e_)) +#define IS_EQUIV(_e_) m.is_eq(_e_) #define SAME_OP(_d1_, _d2_) ((_d1_ == _d2_) || (IS_EQUIV(_d1_) && IS_EQUIV(_d2_))) -proof_checker::hyp_decl_plugin::hyp_decl_plugin() : - m_cons(0), - m_atom(0), - m_nil(0), - m_cell(0) { +proof_checker::hyp_decl_plugin::hyp_decl_plugin() : + m_cons(nullptr), + m_atom(nullptr), + m_nil(nullptr), + m_cell(nullptr) { } void proof_checker::hyp_decl_plugin::finalize() { @@ -54,18 +53,18 @@ func_decl * proof_checker::hyp_decl_plugin::mk_func_decl(decl_kind k) { case OP_NIL: return m_nil; default: UNREACHABLE(); - return 0; + return nullptr; } } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl( - decl_kind k, unsigned num_parameters, parameter const * parameters, + decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { return mk_func_decl(k); } func_decl * proof_checker::hyp_decl_plugin::mk_func_decl( - decl_kind k, unsigned num_parameters, parameter const * parameters, + decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { return mk_func_decl(k); } @@ -84,8 +83,8 @@ void proof_checker::hyp_decl_plugin::get_sort_names(svector & sort } } -proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pinned(m), m_nil(m), - m_dump_lemmas(false), m_logic("AUFLIA"), m_proof_lemma_id(0) { +proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pinned(m), m_nil(m), + m_dump_lemmas(false), m_logic("AUFLIRA"), m_proof_lemma_id(0) { symbol fam_name("proof_hypothesis"); if (!m.has_plugin(fam_name)) { m.register_plugin(fam_name, alloc(hyp_decl_plugin)); @@ -98,16 +97,16 @@ proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pi bool proof_checker::check(proof* p, expr_ref_vector& side_conditions) { proof_ref curr(m); m_todo.push_back(p); - + bool result = true; while (result && !m_todo.empty()) { curr = m_todo.back(); m_todo.pop_back(); result = check1(curr.get(), side_conditions); if (!result) { - IF_VERBOSE(0, ast_ll_pp(verbose_stream() << "Proof check failed\n", m, curr.get());); + IF_VERBOSE(0, ast_ll_pp(verbose_stream() << "Proof check failed\n", m, curr.get());); UNREACHABLE(); - } + } } m_hypotheses.reset(); @@ -157,10 +156,10 @@ bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) { } return false; } - case PR_SPC_REWRITE: - case PR_SUPERPOSITION: + case PR_SPC_REWRITE: + case PR_SUPERPOSITION: case PR_EQUALITY_RESOLUTION: - case PR_SPC_RESOLUTION: + case PR_SPC_RESOLUTION: case PR_FACTORING: case PR_SPC_DER: { if (match_fact(p, fact)) { @@ -176,7 +175,7 @@ bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) { side_conditions.push_back(rewrite_cond.get()); return true; } - return false; + return false; } default: UNREACHABLE(); @@ -201,7 +200,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { func_decl_ref f1(m), f2(m); expr_ref_vector terms1(m), terms2(m), terms(m); sort_ref_vector decls1(m), decls2(m); - + if (match_proof(p, proofs)) { for (unsigned i = 0; i < proofs.size(); ++i) { add_premise(proofs.get(i)); @@ -211,6 +210,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { switch(k) { case PR_UNDEF: return true; + case PR_TRUE: + return true; case PR_ASSERTED: return true; case PR_GOAL: @@ -229,8 +230,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { return false; } case PR_REFLEXIVITY: { - if (match_fact(p, fact) && - match_proof(p) && + if (match_fact(p, fact) && + match_proof(p) && (match_equiv(fact, t1, t2) || match_oeq(fact, t1, t2)) && (t1.get() == t2.get())) { return true; @@ -243,7 +244,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { match_proof(p, p1) && match_fact(p1.get(), fml) && match_binary(fact.get(), d1, l1, r1) && - match_binary(fml.get(), d2, l2, r2) && + match_binary(fml.get(), d2, l2, r2) && SAME_OP(d1.get(), d2.get()) && l1.get() == r2.get() && r1.get() == l2.get()) { @@ -271,7 +272,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } UNREACHABLE(); return false; - } + } case PR_TRANSITIVITY_STAR: { if (match_fact(p, fact) && match_binary(fact.get(), d1, t1, t2)) { @@ -292,14 +293,14 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { return false; } } - return + return vertices.size() == 2 && vertices.contains(t1->get_id()) && vertices.contains(t2->get_id()); } UNREACHABLE(); return false; - } + } case PR_MONOTONICITY: { TRACE("proof_checker", tout << mk_bounded_pp(p, m, 3) << "\n";); if (match_fact(p, fact) && @@ -315,7 +316,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { if (term1 != term2) { bool found = false; for(unsigned j = 0; j < proofs.size() && !found; ++j) { - found = + found = match_fact(proofs[j].get(), fml) && match_binary(fml.get(), d2, s1, s2) && SAME_OP(d1.get(), d2.get()) && @@ -351,7 +352,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { for (unsigned i = 0; i < q1->get_num_decls(); ++i) { if (q1->get_decl_sort(i) != q2->get_decl_sort(i)) { // term is not well-typed. - UNREACHABLE(); + UNREACHABLE(); return false; } } @@ -408,7 +409,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { side_conditions.push_back(fact.get()); return true; } - IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); + IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); return false; } case PR_REWRITE_STAR: { @@ -426,8 +427,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { side_conditions.push_back(rewrite_cond.get()); return true; } - IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); - return false; + IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m);); + return false; } case PR_PULL_QUANT: { if (match_proof(p) && @@ -437,17 +438,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // TBD: check the enchilada. return true; } - IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence with a quantifier:\n" << mk_bounded_pp(p, m);); - return false; - } - case PR_PULL_QUANT_STAR: { - if (match_proof(p) && - match_fact(p, fact) && - match_iff(fact.get(), t1, t2)) { - // TBD: check the enchilada. - return true; - } - IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence:\n" << mk_bounded_pp(p, m);); + IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence with a quantifier:\n" << mk_bounded_pp(p, m);); return false; } case PR_PUSH_QUANT: { @@ -467,7 +458,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } else { return false; - } + } } } UNREACHABLE(); @@ -478,7 +469,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { match_fact(p, fact) && match_iff(fact.get(), t1, t2)) { // TBD: - // match_quantifier(t1.get(), is_forall1, decls1, body1) + // match_quantifier(t1.get(), is_forall1, decls1, body1) // filter out decls1 that occur in body1. // if list is empty, then t2 could be just body1. // otherwise t2 is also a quantifier. @@ -497,12 +488,12 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // TBD: check that terms are set of equalities. // t2 is an instance of a predicate in terms1 return true; - } + } IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";); return false; } case PR_HYPOTHESIS: { - // TBD all branches with hyptheses must be closed by a later lemma. + // TBD all branches with hypotheses must be closed by a later lemma. if (match_proof(p) && match_fact(p, fml)) { return true; @@ -519,7 +510,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { get_hypotheses(p1.get(), hypotheses); if (hypotheses.size() == 1 && match_negated(hypotheses.get(0), fact)) { // Suppose fact is (or a b c) and hypothesis is (not (or a b c)) - // That is, (or a b c) should be viewed as a 'quoted expression' and a unary clause, + // That is, (or a b c) should be viewed as a 'quoted expression' and a unary clause, // instead of a clause with three literals. return true; } @@ -543,7 +534,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { }); UNREACHABLE(); return false; - } + } TRACE("proof_checker", tout << "Matched:\n"; ast_ll_pp(tout, m, hypotheses[i].get()); ast_ll_pp(tout, m, ors[j-1].get());); @@ -558,7 +549,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { proofs.size() == 2 && match_fact(proofs[0].get(), fml1) && match_fact(proofs[1].get(), fml2) && - match_negated(fml1.get(), fml2.get()) && + m.is_complement(fml1.get(), fml2.get()) && m.is_false(fact.get())) { return true; } @@ -572,7 +563,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } bool found = false; for (unsigned j = 0; !found && j < terms1.size(); ++j) { - if (match_negated(terms1.get(j), fml2)) { + if (m.is_complement(terms1.get(j), fml2)) { found = true; if (j + 1 < terms1.size()) { terms1[j] = terms1.get(terms1.size()-1); @@ -581,7 +572,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } } if (!found) { - TRACE("pr_unit_bug", + TRACE("pr_unit_bug", tout << "Parents:\n"; for (unsigned i = 0; i < proofs.size(); i++) { expr_ref p(m); @@ -600,9 +591,9 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } } switch(terms1.size()) { - case 0: + case 0: return m.is_false(fact.get()); - case 1: + case 1: return fact.get() == terms1[0].get(); default: { if (match_or(fact.get(), terms2)) { @@ -625,7 +616,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { return false; } - + } } UNREACHABLE(); @@ -643,7 +634,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } UNREACHABLE(); return false; - } + } case PR_IFF_FALSE: { // iff_false(?rule(?p1, (not ?fml)), (iff ?fml false)) if (match_proof(p, p1) && @@ -686,11 +677,11 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { } case PR_DEF_INTRO: { // def_intro(?fml) - // + // // ?fml: forall x . ~p(x) or e(x) and forall x . ~e(x) or p(x) // : forall x . ~cond(x) or f(x) = then(x) and forall x . cond(x) or f(x) = else(x) // : forall x . f(x) = e(x) - // + // if (match_fact(p, fact) && match_proof(p) && m.is_bool(fact.get())) { @@ -720,7 +711,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { return true; } UNREACHABLE(); - return false; + return false; } case PR_NNF_POS: { // TBD: @@ -730,16 +721,12 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // TBD: return true; } - case PR_NNF_STAR: { - // TBD: - return true; - } case PR_SKOLEMIZE: { // (exists ?x (p ?x y)) -> (p (sk y) y) // (not (forall ?x (p ?x y))) -> (not (p (sk y) y)) if (match_fact(p, fact) && match_oeq(fact.get(), t1, t2)) { - quantifier* q = 0; + quantifier* q = nullptr; expr* e = t1.get(); bool is_forall = false; if (match_not(t1.get(), s1)) { @@ -747,27 +734,14 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { is_forall = true; } if (is_quantifier(e)) { - q = to_quantifier(e); + q = to_quantifier(e); // TBD check that quantifier is properly instantiated - return is_forall == q->is_forall(); + return is_forall == q->is_forall(); } } UNREACHABLE(); return false; } - case PR_CNF_STAR: { - for (unsigned i = 0; i < proofs.size(); ++i) { - if (match_op(proofs[i].get(), PR_DEF_INTRO, terms)) { - // ok - } - else { - UNREACHABLE(); - return false; - } - } - // coarse grain CNF conversion. - return true; - } case PR_MODUS_PONENS_OEQ: { if (match_fact(p, fact) && match_proof(p, p0, p1) && @@ -813,7 +787,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { vs(premise, sub.size(), sub.c_ptr(), premise); } fmls.push_back(premise.get()); - TRACE("proof_checker", + TRACE("proof_checker", tout << mk_pp(premise.get(), m) << "\n"; for (unsigned j = 0; j < sub.size(); ++j) { tout << mk_pp(sub[j], m) << " "; @@ -823,7 +797,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { premise0 = fmls[0].get(); for (unsigned i = 1; i < fmls.size(); ++i) { expr_ref lit1(m), lit2(m); - expr* lit3 = 0; + expr* lit3 = nullptr; std::pair pos = positions[i-1]; premise1 = fmls[i].get(); set_false(premise0, pos.first, lit1); @@ -835,7 +809,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { // ok } else { - IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" << + IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" << mk_pp(lit1, m) << "\n" << mk_pp(lit2, m) << "\n" << mk_pp(p, m) << "\n";); } fmls[i] = premise1; @@ -850,7 +824,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { else { premise0 = m.mk_iff(premise0, conclusion); } - side_conditions.push_back(premise0); + side_conditions.push_back(premise0); return true; } default: @@ -866,7 +840,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) { (=> (and ln+1 ln+2 .. ln+m) l0) or in the most general (ground) form: (=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln-1)) - In other words we use the following (Prolog style) convention for Horn + In other words we use the following (Prolog style) convention for Horn implications: The head of a Horn implication is position 0, the first conjunct in the body of an implication is position 1 @@ -878,7 +852,7 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { app* a = to_app(e); expr* head, *body; expr_ref_vector args(m); - if (m.is_or(e)) { + if (m.is_or(e)) { SASSERT(position < a->get_num_args()); args.append(a->get_num_args(), a->get_args()); lit = args[position].get(); @@ -890,13 +864,13 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { unsigned num_heads = 1; if (m.is_or(head)) { num_heads = to_app(head)->get_num_args(); - heads = to_app(head)->get_args(); + heads = to_app(head)->get_args(); } expr*const* bodies = &body; unsigned num_bodies = 1; if (m.is_and(body)) { num_bodies = to_app(body)->get_num_args(); - bodies = to_app(body)->get_args(); + bodies = to_app(body)->get_args(); } if (position < num_heads) { args.append(num_heads, heads); @@ -909,7 +883,7 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { args.append(num_bodies, bodies); lit = m.mk_not(args[position].get()); args[position] = m.mk_true(); - e = m.mk_implies(m.mk_and(args.size(), args.c_ptr()), head); + e = m.mk_implies(m.mk_and(args.size(), args.c_ptr()), head); } } else if (position == 0) { @@ -922,7 +896,7 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) { } } -bool proof_checker::match_fact(proof* p, expr_ref& fact) { +bool proof_checker::match_fact(proof const* p, expr_ref& fact) const { if (m.is_proof(p) && m.has_fact(p)) { fact = m.get_fact(p); @@ -938,13 +912,13 @@ void proof_checker::add_premise(proof* p) { } } -bool proof_checker::match_proof(proof* p) { - return +bool proof_checker::match_proof(proof const* p) const { + return m.is_proof(p) && m.get_num_parents(p) == 0; } -bool proof_checker::match_proof(proof* p, proof_ref& p0) { +bool proof_checker::match_proof(proof const* p, proof_ref& p0) const { if (m.is_proof(p) && m.get_num_parents(p) == 1) { p0 = m.get_parent(p, 0); @@ -952,8 +926,8 @@ bool proof_checker::match_proof(proof* p, proof_ref& p0) { } return false; } - -bool proof_checker::match_proof(proof* p, proof_ref& p0, proof_ref& p1) { + +bool proof_checker::match_proof(proof const* p, proof_ref& p0, proof_ref& p1) const { if (m.is_proof(p) && m.get_num_parents(p) == 2) { p0 = m.get_parent(p, 0); @@ -963,7 +937,7 @@ bool proof_checker::match_proof(proof* p, proof_ref& p0, proof_ref& p1) { return false; } -bool proof_checker::match_proof(proof* p, proof_ref_vector& parents) { +bool proof_checker::match_proof(proof const* p, proof_ref_vector& parents) const { if (m.is_proof(p)) { for (unsigned i = 0; i < m.get_num_parents(p); ++i) { parents.push_back(m.get_parent(p, i)); @@ -974,7 +948,7 @@ bool proof_checker::match_proof(proof* p, proof_ref_vector& parents) { } -bool proof_checker::match_binary(expr* e, func_decl_ref& d, expr_ref& t1, expr_ref& t2) { +bool proof_checker::match_binary(expr const* e, func_decl_ref& d, expr_ref& t1, expr_ref& t2) const { if (e->get_kind() == AST_APP && to_app(e)->get_num_args() == 2) { d = to_app(e)->get_decl(); @@ -986,7 +960,7 @@ bool proof_checker::match_binary(expr* e, func_decl_ref& d, expr_ref& t1, expr_r } -bool proof_checker::match_app(expr* e, func_decl_ref& d, expr_ref_vector& terms) { +bool proof_checker::match_app(expr const* e, func_decl_ref& d, expr_ref_vector& terms) const { if (e->get_kind() == AST_APP) { d = to_app(e)->get_decl(); for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { @@ -997,9 +971,9 @@ bool proof_checker::match_app(expr* e, func_decl_ref& d, expr_ref_vector& terms) return false; } -bool proof_checker::match_quantifier(expr* e, bool& is_univ, sort_ref_vector& sorts, expr_ref& body) { +bool proof_checker::match_quantifier(expr const* e, bool& is_univ, sort_ref_vector& sorts, expr_ref& body) const { if (is_quantifier(e)) { - quantifier* q = to_quantifier(e); + quantifier const* q = to_quantifier(e); is_univ = q->is_forall(); body = q->get_expr(); for (unsigned i = 0; i < q->get_num_decls(); ++i) { @@ -1010,7 +984,7 @@ bool proof_checker::match_quantifier(expr* e, bool& is_univ, sort_ref_vector& so return false; } -bool proof_checker::match_op(expr* e, decl_kind k, expr_ref& t1, expr_ref& t2) { +bool proof_checker::match_op(expr const* e, decl_kind k, expr_ref& t1, expr_ref& t2) const { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k && @@ -1022,7 +996,7 @@ bool proof_checker::match_op(expr* e, decl_kind k, expr_ref& t1, expr_ref& t2) { return false; } -bool proof_checker::match_op(expr* e, decl_kind k, expr_ref_vector& terms) { +bool proof_checker::match_op(expr const* e, decl_kind k, expr_ref_vector& terms) const { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k) { @@ -1035,7 +1009,7 @@ bool proof_checker::match_op(expr* e, decl_kind k, expr_ref_vector& terms) { } -bool proof_checker::match_op(expr* e, decl_kind k, expr_ref& t) { +bool proof_checker::match_op(expr const* e, decl_kind k, expr_ref& t) const { if (e->get_kind() == AST_APP && to_app(e)->get_family_id() == m.get_basic_family_id() && to_app(e)->get_decl_kind() == k && @@ -1046,41 +1020,41 @@ bool proof_checker::match_op(expr* e, decl_kind k, expr_ref& t) { return false; } -bool proof_checker::match_not(expr* e, expr_ref& t) { +bool proof_checker::match_not(expr const* e, expr_ref& t) const { return match_op(e, OP_NOT, t); } -bool proof_checker::match_or(expr* e, expr_ref_vector& terms) { +bool proof_checker::match_or(expr const* e, expr_ref_vector& terms) const { return match_op(e, OP_OR, terms); } -bool proof_checker::match_and(expr* e, expr_ref_vector& terms) { +bool proof_checker::match_and(expr const* e, expr_ref_vector& terms) const { return match_op(e, OP_AND, terms); } -bool proof_checker::match_iff(expr* e, expr_ref& t1, expr_ref& t2) { - return match_op(e, OP_IFF, t1, t2); +bool proof_checker::match_iff(expr const* e, expr_ref& t1, expr_ref& t2) const { + return match_op(e, OP_EQ, t1, t2) && m.is_bool(t1); } -bool proof_checker::match_equiv(expr* e, expr_ref& t1, expr_ref& t2) { +bool proof_checker::match_equiv(expr const* e, expr_ref& t1, expr_ref& t2) const { return match_oeq(e, t1, t2) || match_eq(e, t1, t2); } -bool proof_checker::match_implies(expr* e, expr_ref& t1, expr_ref& t2) { +bool proof_checker::match_implies(expr const* e, expr_ref& t1, expr_ref& t2) const { return match_op(e, OP_IMPLIES, t1, t2); } -bool proof_checker::match_eq(expr* e, expr_ref& t1, expr_ref& t2) { - return match_op(e, OP_EQ, t1, t2) || match_iff(e, t1, t2); +bool proof_checker::match_eq(expr const* e, expr_ref& t1, expr_ref& t2) const { + return match_op(e, OP_EQ, t1, t2); } -bool proof_checker::match_oeq(expr* e, expr_ref& t1, expr_ref& t2) { +bool proof_checker::match_oeq(expr const* e, expr_ref& t1, expr_ref& t2) const { return match_op(e, OP_OEQ, t1, t2); } -bool proof_checker::match_negated(expr* a, expr* b) { +bool proof_checker::match_negated(expr const* a, expr* b) const { expr_ref t(m); - return + return (match_not(a, t) && t.get() == b) || (match_not(b, t) && t.get() == a); } @@ -1099,7 +1073,7 @@ void proof_checker::get_ors(expr* e, expr_ref_vector& ors) { void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { ptr_vector stack; - expr* h = 0; + expr* h = nullptr; expr_ref hyp(m); stack.push_back(p); @@ -1107,7 +1081,7 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { p = stack.back(); SASSERT(m.is_proof(p)); if (m_hypotheses.contains(p)) { - stack.pop_back(); + stack.pop_back(); continue; } if (is_hypothesis(p) && match_fact(p, hyp)) { @@ -1149,13 +1123,13 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { if (!m_hypotheses.find(p, h)) { UNREACHABLE(); } - + ptr_buffer hyps; ptr_buffer todo; expr_mark mark; todo.push_back(h); expr_ref a(m), b(m); - + while (!todo.empty()) { h = todo.back(); @@ -1180,20 +1154,20 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) { ast_ll_pp(tout << "Getting hypotheses from: ", m, p); tout << "Found hypotheses:\n"; for (unsigned i = 0; i < ante.size(); ++i) { - ast_ll_pp(tout, m, ante[i].get()); + ast_ll_pp(tout, m, ante[i].get()); } }); } -bool proof_checker::match_nil(expr* e) const { - return +bool proof_checker::match_nil(expr const* e) const { + return is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_NIL; } -bool proof_checker::match_cons(expr* e, expr_ref& a, expr_ref& b) const { +bool proof_checker::match_cons(expr const* e, expr_ref& a, expr_ref& b) const { if (is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_CONS) { @@ -1205,7 +1179,7 @@ bool proof_checker::match_cons(expr* e, expr_ref& a, expr_ref& b) const { } -bool proof_checker::match_atom(expr* e, expr_ref& a) const { +bool proof_checker::match_atom(expr const* e, expr_ref& a) const { if (is_app(e) && to_app(e)->get_family_id() == m_hyp_fid && to_app(e)->get_decl_kind() == OP_ATOM) { @@ -1227,14 +1201,14 @@ expr* proof_checker::mk_nil() { return m_nil.get(); } -bool proof_checker::is_hypothesis(proof* p) const { - return +bool proof_checker::is_hypothesis(proof const* p) const { + return m.is_proof(p) && p->get_decl_kind() == PR_HYPOTHESIS; } expr* proof_checker::mk_hyp(unsigned num_hyps, expr * const * hyps) { - expr* result = 0; + expr* result = nullptr; for (unsigned i = 0; i < num_hyps; ++i) { if (!match_nil(hyps[i])) { if (result) { @@ -1245,15 +1219,15 @@ expr* proof_checker::mk_hyp(unsigned num_hyps, expr * const * hyps) { } } } - if (result == 0) { + if (result == nullptr) { return mk_nil(); } else { return result; - } + } } -void proof_checker::dump_proof(proof * pr) { +void proof_checker::dump_proof(proof const* pr) { if (!m_dump_lemmas) return; SASSERT(m.has_fact(pr)); @@ -1271,9 +1245,9 @@ void proof_checker::dump_proof(proof * 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.smt", m_proof_lemma_id); + sprintf_s(buffer, ARRAYSIZE(buffer), "proof_lemma_%d.smt2", m_proof_lemma_id); #else - sprintf(buffer, "proof_lemma_%d.smt", m_proof_lemma_id); + sprintf(buffer, "proof_lemma_%d.smt2", m_proof_lemma_id); #endif std::ofstream out(buffer); ast_smt_pp pp(m); @@ -1292,7 +1266,7 @@ void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecede bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const& coeff, expr_ref& sum, bool& is_strict) { arith_util a(m); app* lit = lit0; - + if (m.is_not(lit)) { lit = to_app(lit->get_arg(0)); is_pos = !is_pos; @@ -1304,6 +1278,10 @@ bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const& SASSERT(lit->get_num_args() == 2); sort* s = m.get_sort(lit->get_arg(0)); bool is_int = a.is_int(s); + if (!is_int && a.is_int_expr(lit->get_arg(0))) { + is_int = true; + s = a.mk_int(); + } if (!is_int && is_pos && (a.is_gt(lit) || a.is_lt(lit))) { is_strict = true; @@ -1332,25 +1310,25 @@ bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const& } // - // Multiplying by coefficients over strict + // Multiplying by coefficients over strict // and non-strict inequalities: // // (a <= b) * 2 // (a - b <= 0) * 2 // (2a - 2b <= 0) - + // (a < b) * 2 <=> // (a +1 <= b) * 2 <=> // 2a + 2 <= 2b <=> // 2a+2-2b <= 0 - + bool strict_ineq = is_pos?(a.is_gt(lit) || a.is_lt(lit)):(a.is_ge(lit) || a.is_le(lit)); - + if (is_int && strict_ineq) { sum = a.mk_add(sum, sign1); } - + term = a.mk_mul(sign1, a0); sum = a.mk_add(sum, term); term = a.mk_mul(sign2, a1); @@ -1382,7 +1360,7 @@ bool proof_checker::check_arith_proof(proof* p) { } expr_ref fact(m); proof_ref_vector proofs(m); - + if (!match_fact(p, fact)) { UNREACHABLE(); return false; @@ -1411,7 +1389,7 @@ bool proof_checker::check_arith_proof(proof* p) { coeffs[i] = lc*coeffs[i]; } } - + unsigned num_parents = m.get_num_parents(p); for (unsigned i = 0; i < num_parents; i++) { proof * a = m.get_parent(p, i); @@ -1420,11 +1398,10 @@ bool proof_checker::check_arith_proof(proof* p) { return false; } } - - if (m.is_or(fact)) { + if (m.is_or(fact)) { app* disj = to_app(fact); unsigned num_args = disj->get_num_args(); - for (unsigned i = 0; i < num_args; ++i) { + for (unsigned i = 0; i < num_args; ++i) { app* lit = to_app(disj->get_arg(i)); if (!check_arith_literal(false, lit, coeffs[offset++], sum, is_strict)) { return false; @@ -1436,13 +1413,13 @@ bool proof_checker::check_arith_proof(proof* p) { return false; } } - + if (!sum.get()) { return false; } - + sort* s = m.get_sort(sum); - + if (is_strict) { sum = autil.mk_lt(sum, autil.mk_numeral(rational(0), s)); @@ -1460,6 +1437,6 @@ bool proof_checker::check_arith_proof(proof* p) { dump_proof(p); return false; } - + return true; } diff --git a/src/ast/proofs/proof_checker.h b/src/ast/proofs/proof_checker.h index ce2684c51..ac0e31dbd 100644 --- a/src/ast/proofs/proof_checker.h +++ b/src/ast/proofs/proof_checker.h @@ -48,24 +48,24 @@ class proof_checker { func_decl* m_atom; func_decl* m_nil; sort* m_cell; - virtual void set_manager(ast_manager * m, family_id id); + void set_manager(ast_manager * m, family_id id) override; func_decl * mk_func_decl(decl_kind k); public: hyp_decl_plugin(); - virtual ~hyp_decl_plugin() {} + ~hyp_decl_plugin() override {} - virtual void finalize(); + void finalize() override; - virtual decl_plugin * mk_fresh() { return alloc(hyp_decl_plugin); } + decl_plugin * mk_fresh() override { return alloc(hyp_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters); - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned num_args, expr * const * args, sort * range); - virtual void get_op_names(svector & op_names, symbol const & logic); - virtual void get_sort_names(svector & sort_names, symbol const & logic); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const* parameters) override; + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned num_args, expr * const * args, sort * range) override; + void get_op_names(svector & op_names, symbol const & logic) override; + void get_sort_names(svector & sort_names, symbol const & logic) override; }; public: proof_checker(ast_manager& m); @@ -77,39 +77,39 @@ private: bool check1_spc(proof* p, expr_ref_vector& side_conditions); bool check_arith_proof(proof* p); bool check_arith_literal(bool is_pos, app* lit, rational const& coeff, expr_ref& sum, bool& is_strict); - bool match_fact(proof* p, expr_ref& fact); + bool match_fact(proof const* p, expr_ref& fact) const; void add_premise(proof* p); - bool match_proof(proof* p); - bool match_proof(proof* p, proof_ref& p0); - bool match_proof(proof* p, proof_ref& p0, proof_ref& p1); - bool match_proof(proof* p, proof_ref_vector& parents); - bool match_binary(expr* e, func_decl_ref& d, expr_ref& t1, expr_ref& t2); - bool match_op(expr* e, decl_kind k, expr_ref& t1, expr_ref& t2); - bool match_op(expr* e, decl_kind k, expr_ref& t); - bool match_op(expr* e, decl_kind k, expr_ref_vector& terms); - bool match_iff(expr* e, expr_ref& t1, expr_ref& t2); - bool match_implies(expr* e, expr_ref& t1, expr_ref& t2); - bool match_eq(expr* e, expr_ref& t1, expr_ref& t2); - bool match_oeq(expr* e, expr_ref& t1, expr_ref& t2); - bool match_not(expr* e, expr_ref& t); - bool match_or(expr* e, expr_ref_vector& terms); - bool match_and(expr* e, expr_ref_vector& terms); - bool match_app(expr* e, func_decl_ref& d, expr_ref_vector& terms); - bool match_quantifier(expr*, bool& is_univ, sort_ref_vector&, expr_ref& body); - bool match_negated(expr* a, expr* b); - bool match_equiv(expr* a, expr_ref& t1, expr_ref& t2); + bool match_proof(proof const* p) const; + bool match_proof(proof const* p, proof_ref& p0) const; + bool match_proof(proof const* p, proof_ref& p0, proof_ref& p1) const; + bool match_proof(proof const* p, proof_ref_vector& parents) const; + bool match_binary(expr const* e, func_decl_ref& d, expr_ref& t1, expr_ref& t2) const; + bool match_op(expr const* e, decl_kind k, expr_ref& t1, expr_ref& t2) const; + bool match_op(expr const* e, decl_kind k, expr_ref& t) const; + bool match_op(expr const* e, decl_kind k, expr_ref_vector& terms) const; + bool match_iff(expr const* e, expr_ref& t1, expr_ref& t2) const; + bool match_implies(expr const* e, expr_ref& t1, expr_ref& t2) const; + bool match_eq(expr const* e, expr_ref& t1, expr_ref& t2) const; + bool match_oeq(expr const* e, expr_ref& t1, expr_ref& t2) const; + bool match_not(expr const* e, expr_ref& t) const; + bool match_or(expr const* e, expr_ref_vector& terms) const; + bool match_and(expr const* e, expr_ref_vector& terms) const; + bool match_app(expr const* e, func_decl_ref& d, expr_ref_vector& terms) const; + bool match_quantifier(expr const*, bool& is_univ, sort_ref_vector&, expr_ref& body) const; + bool match_negated(expr const* a, expr* b) const; + bool match_equiv(expr const* a, expr_ref& t1, expr_ref& t2) const; void get_ors(expr* e, expr_ref_vector& ors); void get_hypotheses(proof* p, expr_ref_vector& ante); - bool match_nil(expr* e) const; - bool match_cons(expr* e, expr_ref& a, expr_ref& b) const; - bool match_atom(expr* e, expr_ref& a) const; + bool match_nil(expr const* e) const; + bool match_cons(expr const* e, expr_ref& a, expr_ref& b) const; + bool match_atom(expr const* e, expr_ref& a) const; expr* mk_nil(); expr* mk_cons(expr* a, expr* b); expr* mk_atom(expr* e); - bool is_hypothesis(proof* p) const; + bool is_hypothesis(proof const* p) const; expr* mk_hyp(unsigned num_hyps, expr * const * hyps); - void dump_proof(proof * pr); + void dump_proof(proof const* pr); void dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent); void set_false(expr_ref& e, unsigned idx, expr_ref& lit); diff --git a/src/ast/proofs/proof_utils.cpp b/src/ast/proofs/proof_utils.cpp index 58500cb79..97213c235 100644 --- a/src/ast/proofs/proof_utils.cpp +++ b/src/ast/proofs/proof_utils.cpp @@ -83,7 +83,7 @@ class reduce_hypotheses { // map from unit literals to their hypotheses-free derivations obj_map m_units; - // -- all hypotheses in the the proof + // -- all hypotheses in the proof obj_hashtable m_hyps; // marks hypothetical proofs @@ -143,7 +143,7 @@ class reduce_hypotheses { void reduce(proof* pf, proof_ref &out) { - proof *res = NULL; + proof *res = nullptr; m_todo.reset(); m_todo.push_back(pf); @@ -192,7 +192,7 @@ class reduce_hypotheses { res = mk_lemma_core(args.get(0), m.get_fact(p)); compute_mark1(res); } else if (m.is_unit_resolution(p)) { - // unit: reduce untis; reduce the first premise; rebuild unit resolution + // unit: reduce units; reduce the first premise; rebuild unit resolution res = mk_unit_resolution_core(args.size(), args.c_ptr()); compute_mark1(res); } else { @@ -340,7 +340,7 @@ void reduce_hypotheses(proof_ref &pr) { class reduce_hypotheses0 { typedef obj_hashtable expr_set; ast_manager& m; - // reference for any expression created by the tranformation + // reference for any expression created by the transformation expr_ref_vector m_refs; // currently computed result obj_map m_cache; @@ -352,7 +352,7 @@ class reduce_hypotheses0 { unsigned_vector m_limits; // map from proofs to active hypotheses obj_map m_hypmap; - // refernce train for hypotheses sets + // reference train for hypotheses sets ptr_vector m_hyprefs; ptr_vector m_literals; @@ -392,7 +392,7 @@ class reduce_hypotheses0 { } void add_hypotheses(proof* p) { - expr_set* hyps = 0; + expr_set* hyps = nullptr; bool inherited = false; if (p->get_decl_kind() == PR_HYPOTHESIS) { hyps = alloc(expr_set); @@ -492,7 +492,7 @@ public: // replace result by m_units[m.get_fact (p)] if defined // AG: This is the main step. Replace a hypothesis by a derivation of its consequence if (!m_units.find(m.get_fact(p), result)) { - // restore ther result back to p + // restore the result back to p result = p.get(); } // compute hypothesis of the result @@ -509,7 +509,7 @@ public: // eliminate hypothesis recursively in the proof of the lemma elim(tmp); expr_set* hyps = m_hypmap.find(tmp); - expr_set* new_hyps = 0; + expr_set* new_hyps = nullptr; // XXX if the proof is correct, the hypotheses of the tmp // XXX should be exactly those of the consequence of the lemma // XXX but if this code actually eliminates hypotheses, the set might be a subset @@ -567,7 +567,7 @@ public: } if (new_hyps && new_hyps->empty()) { dealloc(new_hyps); - new_hyps = 0; + new_hyps = nullptr; } m_hypmap.insert(result, new_hyps); // might push 0 into m_hyprefs. No reason for that @@ -822,7 +822,7 @@ bool proof_utils::is_closed(ast_manager& m, proof* p) { static void permute_unit_resolution(expr_ref_vector& refs, obj_map& cache, proof_ref& pr) { ast_manager& m = pr.get_manager(); - proof* pr2 = 0; + proof* pr2 = nullptr; proof_ref_vector parents(m); proof_ref prNew(pr); if (cache.find(pr, pr2)) { diff --git a/src/ast/proofs/proof_utils.h b/src/ast/proofs/proof_utils.h index b953c834d..729a30eb0 100644 --- a/src/ast/proofs/proof_utils.h +++ b/src/ast/proofs/proof_utils.h @@ -88,7 +88,7 @@ class elim_aux_assertions { app_ref m_aux; public: - elim_aux_assertions(app_ref aux) : m_aux(aux) {} + elim_aux_assertions(app_ref const& aux) : m_aux(aux) {} void mk_or_core(expr_ref_vector &args, expr_ref &res) { @@ -110,10 +110,10 @@ public: if (m.is_or(decl)) { mk_or_core(args, res); } - else if (m.is_iff(decl) && args.size() == 2) + else if (m.is_eq(decl) && args.size() == 2) // avoiding simplifying equalities. In particular, // we don't want (= (not a) (not b)) to be reduced to (= a b) - { res = m.mk_iff(args.get(0), args.get(1)); } + { res = m.mk_eq(args.get(0), args.get(1)); } else { brwr.mk_app(decl, args.size(), args.c_ptr(), res); } } diff --git a/src/ast/recurse_expr_def.h b/src/ast/recurse_expr_def.h index d4f608c5a..b4a83c082 100644 --- a/src/ast/recurse_expr_def.h +++ b/src/ast/recurse_expr_def.h @@ -72,7 +72,7 @@ void recurse_expr::process(expr * n break; case AST_QUANTIFIER: if (IgnorePatterns) { - cache_result(n, this->Visitor::visit(to_quantifier(n), get_cached(to_quantifier(n)->get_expr()), 0, 0)); + cache_result(n, this->Visitor::visit(to_quantifier(n), get_cached(to_quantifier(n)->get_expr()), nullptr, nullptr)); } else { m_results1.reset(); diff --git a/src/ast/rewriter/CMakeLists.txt b/src/ast/rewriter/CMakeLists.txt index 57924b48a..9d80fd5ac 100644 --- a/src/ast/rewriter/CMakeLists.txt +++ b/src/ast/rewriter/CMakeLists.txt @@ -16,6 +16,7 @@ z3_add_component(rewriter enum2bv_rewriter.cpp expr_replacer.cpp expr_safe_replace.cpp + factor_equivs.cpp factor_rewriter.cpp fpa_rewriter.cpp inj_axiom.cpp @@ -25,7 +26,6 @@ z3_add_component(rewriter pb_rewriter.cpp pb2bv_rewriter.cpp push_app_ite.cpp - pull_ite_tree.cpp quant_hoist.cpp rewriter.cpp seq_rewriter.cpp diff --git a/src/ast/rewriter/arith_rewriter.cpp b/src/ast/rewriter/arith_rewriter.cpp index acc3b889f..379020331 100644 --- a/src/ast/rewriter/arith_rewriter.cpp +++ b/src/ast/rewriter/arith_rewriter.cpp @@ -800,9 +800,107 @@ br_status arith_rewriter::mk_idiv_core(expr * arg1, expr * arg2, expr_ref & resu result = m_util.mk_numeral(div(v1, v2), is_int); return BR_DONE; } + if (m_util.is_numeral(arg2, v2, is_int) && v2.is_one()) { + result = arg1; + return BR_DONE; + } + if (m_util.is_numeral(arg2, v2, is_int) && v2.is_zero()) { + return BR_FAILED; + } + if (arg1 == arg2) { + expr_ref zero(m_util.mk_int(0), m()); + result = m().mk_ite(m().mk_eq(arg1, zero), m_util.mk_idiv(zero, zero), m_util.mk_int(1)); + return BR_REWRITE3; + } + if (divides(arg1, arg2, result)) { + return BR_REWRITE_FULL; + } return BR_FAILED; } + +// +// implement div ab ac = floor( ab / ac) = floor (b / c) = div b c +// +bool arith_rewriter::divides(expr* num, expr* den, expr_ref& result) { + expr_fast_mark1 mark; + rational num_r(1), den_r(1); + expr* num_e = nullptr, *den_e = nullptr; + ptr_buffer args1, args2; + flat_mul(num, args1); + flat_mul(den, args2); + for (expr * arg : args1) { + mark.mark(arg); + if (m_util.is_numeral(arg, num_r)) num_e = arg; + } + for (expr* arg : args2) { + if (mark.is_marked(arg)) { + result = remove_divisor(arg, num, den); + return true; + } + if (m_util.is_numeral(arg, den_r)) den_e = arg; + } + rational g = gcd(num_r, den_r); + if (!g.is_one()) { + SASSERT(g.is_pos()); + // replace num_e, den_e by their gcd reduction. + for (unsigned i = 0; i < args1.size(); ++i) { + if (args1[i] == num_e) { + args1[i] = m_util.mk_numeral(num_r / g, true); + break; + } + } + for (unsigned i = 0; i < args2.size(); ++i) { + if (args2[i] == den_e) { + args2[i] = m_util.mk_numeral(den_r / g, true); + break; + } + } + + num = m_util.mk_mul(args1.size(), args1.c_ptr()); + den = m_util.mk_mul(args2.size(), args2.c_ptr()); + result = m_util.mk_idiv(num, den); + return true; + } + return false; +} + +expr_ref arith_rewriter::remove_divisor(expr* arg, expr* num, expr* den) { + ptr_buffer args1, args2; + flat_mul(num, args1); + flat_mul(den, args2); + remove_divisor(arg, args1); + remove_divisor(arg, args2); + expr_ref zero(m_util.mk_int(0), m()); + num = args1.empty() ? m_util.mk_int(1) : m_util.mk_mul(args1.size(), args1.c_ptr()); + den = args2.empty() ? m_util.mk_int(1) : m_util.mk_mul(args2.size(), args2.c_ptr()); + return expr_ref(m().mk_ite(m().mk_eq(zero, arg), m_util.mk_idiv(zero, zero), m_util.mk_idiv(num, den)), m()); +} + +void arith_rewriter::flat_mul(expr* e, ptr_buffer& args) { + args.push_back(e); + for (unsigned i = 0; i < args.size(); ++i) { + e = args[i]; + if (m_util.is_mul(e)) { + args.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + args[i] = args.back(); + args.shrink(args.size()-1); + --i; + } + } +} + +void arith_rewriter::remove_divisor(expr* d, ptr_buffer& args) { + for (unsigned i = 0; i < args.size(); ++i) { + if (args[i] == d) { + args[i] = args.back(); + args.shrink(args.size()-1); + return; + } + } + UNREACHABLE(); +} + br_status arith_rewriter::mk_mod_core(expr * arg1, expr * arg2, expr_ref & result) { set_curr_sort(m().get_sort(arg1)); numeral v1, v2; @@ -1056,7 +1154,7 @@ br_status arith_rewriter::mk_power_core(expr * arg1, expr * arg2, expr_ref & res br_status arith_rewriter::mk_to_int_core(expr * arg, expr_ref & result) { numeral a; - expr* x = 0; + expr* x = nullptr; if (m_util.is_numeral(arg, a)) { result = m_util.mk_numeral(floor(a), true); return BR_DONE; @@ -1303,7 +1401,7 @@ expr * arith_rewriter::mk_sin_value(rational const & k) { expr * result = m_util.mk_div(m_util.mk_add(mk_sqrt(rational(6)), mk_sqrt(rational(2))), m_util.mk_numeral(rational(4), false)); return neg ? m_util.mk_uminus(result) : result; } - return 0; + return nullptr; } br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) { @@ -1327,7 +1425,7 @@ br_status arith_rewriter::mk_sin_core(expr * arg, expr_ref & result) { if (is_pi_multiple(arg, k)) { result = mk_sin_value(k); - if (result.get() != 0) + if (result.get() != nullptr) return BR_REWRITE_FULL; } @@ -1386,7 +1484,7 @@ br_status arith_rewriter::mk_cos_core(expr * arg, expr_ref & result) { if (is_pi_multiple(arg, k)) { k = k + rational(1, 2); result = mk_sin_value(k); - if (result.get() != 0) + if (result.get() != nullptr) return BR_REWRITE_FULL; } @@ -1443,7 +1541,7 @@ br_status arith_rewriter::mk_tan_core(expr * arg, expr_ref & result) { if (is_pi_multiple(arg, k)) { expr_ref n(m()), d(m()); n = mk_sin_value(k); - if (n.get() == 0) + if (n.get() == nullptr) goto end; if (is_zero(n)) { result = n; diff --git a/src/ast/rewriter/arith_rewriter.h b/src/ast/rewriter/arith_rewriter.h index 95668ea44..fa8677941 100644 --- a/src/ast/rewriter/arith_rewriter.h +++ b/src/ast/rewriter/arith_rewriter.h @@ -95,6 +95,10 @@ class arith_rewriter : public poly_rewriter { expr_ref neg_monomial(expr * e) const; expr * mk_sin_value(rational const & k); app * mk_sqrt(rational const & k); + bool divides(expr* d, expr* n, expr_ref& result); + expr_ref remove_divisor(expr* arg, expr* num, expr* den); + void flat_mul(expr* e, ptr_buffer& args); + void remove_divisor(expr* d, ptr_buffer& args); public: arith_rewriter(ast_manager & m, params_ref const & p = params_ref()): diff --git a/src/ast/rewriter/bit2int.cpp b/src/ast/rewriter/bit2int.cpp index 257740412..a72b73c4d 100644 --- a/src/ast/rewriter/bit2int.cpp +++ b/src/ast/rewriter/bit2int.cpp @@ -273,7 +273,7 @@ void bit2int::visit(app* n) { // bv2int(x) <= z - bv2int(y) -> bv2int(x) + bv2int(y) <= z // - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; expr_ref tmp1(m_manager), tmp2(m_manager); expr_ref tmp3(m_manager); expr_ref pos1(m_manager), neg1(m_manager); diff --git a/src/ast/rewriter/bit2int.h b/src/ast/rewriter/bit2int.h index fbbf2e6d1..048884a94 100644 --- a/src/ast/rewriter/bit2int.h +++ b/src/ast/rewriter/bit2int.h @@ -77,7 +77,7 @@ protected: bool mk_add(expr* e1, expr* e2, expr_ref& result); expr * get_cached(expr * n) const; - bool is_cached(expr * n) const { return get_cached(n) != 0; } + bool is_cached(expr * n) const { return get_cached(n) != nullptr; } void cache_result(expr * n, expr * r); void reset_cache() { m_cache.reset(); } void flush_cache() { m_cache.cleanup(); } diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp index c47dacc96..ba2c6547d 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.cpp @@ -92,6 +92,8 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { func_decl_ref_vector m_keys; expr_ref_vector m_values; unsigned_vector m_keyval_lim; + func_decl_ref_vector m_newbits; + unsigned_vector m_newbits_lim; bool m_blast_mul; bool m_blast_add; @@ -118,7 +120,8 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { m_out(m), m_bindings(m), m_keys(m), - m_values(m) { + m_values(m), + m_newbits(m) { updt_params(p); } @@ -160,6 +163,7 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { void push() { m_keyval_lim.push_back(m_keys.size()); + m_newbits_lim.push_back(m_newbits.size()); } unsigned get_num_scopes() const { @@ -178,9 +182,27 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { m_keys.resize(lim); m_values.resize(lim); m_keyval_lim.resize(new_sz); + + lim = m_newbits_lim[new_sz]; + m_newbits.shrink(lim); + m_newbits_lim.shrink(new_sz); } } + unsigned m_keypos; + + void start_rewrite() { + m_keypos = m_keys.size(); + } + + void end_rewrite(obj_map& const2bits, ptr_vector & newbits) { + for (unsigned i = m_keypos; i < m_keys.size(); ++i) { + const2bits.insert(m_keys[i].get(), m_values[i].get()); + } + for (func_decl* f : m_newbits) newbits.push_back(f); + + } + template app * mk_mkbv(V const & bits) { return m().mk_app(butil().get_family_id(), OP_MKBV, bits.size(), bits.c_ptr()); @@ -200,7 +222,8 @@ struct blaster_rewriter_cfg : public default_rewriter_cfg { sort * b = m().mk_bool_sort(); m_out.reset(); for (unsigned i = 0; i < bv_size; i++) { - m_out.push_back(m().mk_fresh_const(0, b)); + m_out.push_back(m().mk_fresh_const(nullptr, b)); + m_newbits.push_back(to_app(m_out.back())->get_decl()); } r = mk_mkbv(m_out); m_const2bits.insert(f, r); @@ -342,11 +365,11 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); bits.push_back(m().mk_app(butil().get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t)); } result = mk_mkbv(bits); - result_pr = 0; + result_pr = nullptr; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; TRACE("bit_blaster", tout << f->get_name() << " "; for (unsigned i = 0; i < num; ++i) tout << mk_pp(args[i], m()) << " "; tout << "\n";); @@ -569,7 +592,7 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); if (t->get_idx() >= m_bindings.size()) return false; result = m_bindings.get(m_bindings.size() - t->get_idx() - 1); - result_pr = 0; + result_pr = nullptr; return true; } @@ -616,7 +639,7 @@ MK_PARAMETRIC_UNARY_REDUCE(reduce_sign_extend, mk_sign_extend); result = m().mk_quantifier(old_q->is_forall(), new_decl_sorts.size(), new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), new_body, old_q->get_weight(), old_q->get_qid(), old_q->get_skid(), old_q->get_num_patterns(), new_patterns, old_q->get_num_no_patterns(), new_no_patterns); - result_pr = 0; + result_pr = nullptr; m_bindings.shrink(old_sz); return true; } @@ -635,6 +658,8 @@ struct bit_blaster_rewriter::imp : public rewriter_tpl { } void push() { m_cfg.push(); } void pop(unsigned s) { m_cfg.pop(s); } + void start_rewrite() { m_cfg.start_rewrite(); } + void end_rewrite(obj_map& const2bits, ptr_vector & newbits) { m_cfg.end_rewrite(const2bits, newbits); } unsigned get_num_scopes() const { return m_cfg.get_num_scopes(); } }; @@ -683,3 +708,10 @@ unsigned bit_blaster_rewriter::get_num_scopes() const { return m_imp->get_num_scopes(); } +void bit_blaster_rewriter::start_rewrite() { + m_imp->start_rewrite(); +} + +void bit_blaster_rewriter::end_rewrite(obj_map& const2bits, ptr_vector & newbits) { + m_imp->end_rewrite(const2bits, newbits); +} diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h index 6ffed00ae..24b6b0c0a 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_rewriter.h @@ -33,11 +33,15 @@ public: ast_manager & m() const; unsigned get_num_steps() const; void cleanup(); - obj_map const& const2bits() const; + void start_rewrite(); + void end_rewrite(obj_map& const2bits, ptr_vector & newbits); void operator()(expr * e, expr_ref & result, proof_ref & result_proof); void push(); void pop(unsigned num_scopes); unsigned get_num_scopes() const; +private: + obj_map const& const2bits() const; + }; #endif diff --git a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h index a80994f6c..cf7e7c951 100644 --- a/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h +++ b/src/ast/rewriter/bit_blaster/bit_blaster_tpl_def.h @@ -1193,7 +1193,6 @@ bool bit_blaster_tpl::mk_const_case_multiplier(unsigned sz, expr * const * return false; } SASSERT(out_bits.empty()); - ptr_buffer na_bits; na_bits.append(sz, a_bits); ptr_buffer nb_bits; diff --git a/src/ast/rewriter/bool_rewriter.cpp b/src/ast/rewriter/bool_rewriter.cpp index 6e99cb23e..46613a12e 100644 --- a/src/ast/rewriter/bool_rewriter.cpp +++ b/src/ast/rewriter/bool_rewriter.cpp @@ -39,7 +39,6 @@ br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * co SASSERT(f->get_family_id() == m().get_basic_family_id()); switch (f->get_decl_kind()) { case OP_EQ: - case OP_IFF: SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result); case OP_DISTINCT: @@ -428,7 +427,7 @@ bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, exp neg = true; t = to_app(t)->get_arg(0); } - if (m().is_iff(t) || m().is_eq(t)) { + if (m().is_eq(t)) { bool modified = false; expr * new_lhs = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified); expr * new_rhs = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified); @@ -585,7 +584,7 @@ bool bool_rewriter::local_ctx_simp(unsigned num_args, expr * const * args, expr_ */ br_status bool_rewriter::try_ite_value(app * ite, app * val, expr_ref & result) { - expr* cond = 0, *t = 0, *e = 0; + expr* cond = nullptr, *t = nullptr, *e = nullptr; VERIFY(m().is_ite(ite, cond, t, e)); SASSERT(m().is_value(val)); @@ -708,7 +707,7 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) { expr *la, *lb, *ra, *rb; // fold (iff (iff a b) (iff (not a) b)) to false - if (m().is_iff(lhs, la, lb) && m().is_iff(rhs, ra, rb)) { + if (m().is_eq(lhs, la, lb) && m().is_eq(rhs, ra, rb)) { expr *n; if ((la == ra && ((m().is_not(rb, n) && n == lb) || (m().is_not(lb, n) && n == rb))) || diff --git a/src/ast/rewriter/bool_rewriter.h b/src/ast/rewriter/bool_rewriter.h index be9799c13..83ece2aae 100644 --- a/src/ast/rewriter/bool_rewriter.h +++ b/src/ast/rewriter/bool_rewriter.h @@ -81,7 +81,7 @@ public: bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); } ast_manager & m() const { return m_manager; } family_id get_fid() const { return m().get_basic_family_id(); } - bool is_eq(expr * t) const { return m().is_eq(t) || m().is_iff(t); } + bool is_eq(expr * t) const { return m().is_eq(t); } bool flat() const { return m_flat; } void set_flat(bool f) { m_flat = f; } @@ -183,7 +183,7 @@ struct bool_rewriter_cfg : public default_rewriter_cfg { bool flat_assoc(func_decl * f) const { return m_r.flat() && (m_r.m().is_and(f) || m_r.m().is_or(f)); } bool rewrite_patterns() const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; if (f->get_family_id() != m_r.get_fid()) return BR_FAILED; return m_r.mk_app_core(f, num, args, result); diff --git a/src/ast/rewriter/bv_bounds.cpp b/src/ast/rewriter/bv_bounds.cpp index fd263efb2..f337ca638 100644 --- a/src/ast/rewriter/bv_bounds.cpp +++ b/src/ast/rewriter/bv_bounds.cpp @@ -200,7 +200,7 @@ bv_bounds::conv_res bv_bounds::convert(expr * e, vector& nis, bool ne } // v + c1 <= v + c2 - app * v1(NULL), *v2(NULL); + app * v1(nullptr), *v2(nullptr); numeral val1, val2; if (is_constant_add(bv_sz, lhs, v1, val1) && is_constant_add(bv_sz, rhs, v2, val2) @@ -412,7 +412,7 @@ bool bv_bounds::add_constraint(expr* e) { } // v + c1 <= v + c2 - app * v1(NULL), *v2(NULL); + app * v1(nullptr), *v2(nullptr); numeral val1, val2; if (is_constant_add(bv_sz, lhs, v1, val1) && is_constant_add(bv_sz, rhs, v2, val2) @@ -449,7 +449,7 @@ bool bv_bounds::add_constraint(expr* e) { return m_okay; } -bool bv_bounds::add_bound_unsigned(app * v, numeral a, numeral b, bool negate) { +bool bv_bounds::add_bound_unsigned(app * v, const numeral& a, const numeral& b, bool negate) { TRACE("bv_bounds", tout << "bound_unsigned " << mk_ismt2_pp(v, m_m) << ": " << (negate ? "~[" : "[") << a << ";" << b << "]" << std::endl;); const unsigned bv_sz = m_bv_util.get_bv_size(v); const numeral& zero = numeral::zero(); @@ -472,7 +472,7 @@ bool bv_bounds::add_bound_unsigned(app * v, numeral a, numeral b, bool negate) { } } -bv_bounds::conv_res bv_bounds::convert_signed(app * v, numeral a, numeral b, bool negate, vector& nis) { +bv_bounds::conv_res bv_bounds::convert_signed(app * v, const numeral& a, const numeral& b, bool negate, vector& nis) { TRACE("bv_bounds", tout << "convert_signed " << mk_ismt2_pp(v, m_m) << ":" << (negate ? "~[" : "[") << a << ";" << b << "]" << std::endl;); const unsigned bv_sz = m_bv_util.get_bv_size(v); SASSERT(a <= b); @@ -496,7 +496,7 @@ bv_bounds::conv_res bv_bounds::convert_signed(app * v, numeral a, numeral b, boo } } -bool bv_bounds::add_bound_signed(app * v, numeral a, numeral b, bool negate) { +bool bv_bounds::add_bound_signed(app * v, const numeral& a, const numeral& b, bool negate) { TRACE("bv_bounds", tout << "bound_signed " << mk_ismt2_pp(v, m_m) << ":" << (negate ? "~" : " ") << a << ";" << b << std::endl;); const unsigned bv_sz = m_bv_util.get_bv_size(v); SASSERT(a <= b); @@ -519,7 +519,7 @@ bool bv_bounds::add_bound_signed(app * v, numeral a, numeral b, bool negate) { } } -bool bv_bounds::bound_lo(app * v, numeral l) { +bool bv_bounds::bound_lo(app * v, const numeral& l) { SASSERT(in_range(v, l)); TRACE("bv_bounds", tout << "lower " << mk_ismt2_pp(v, m_m) << ":" << l << std::endl;); // l <= v @@ -530,7 +530,7 @@ bool bv_bounds::bound_lo(app * v, numeral l) { return m_okay; } -bool bv_bounds::bound_up(app * v, numeral u) { +bool bv_bounds::bound_up(app * v, const numeral& u) { SASSERT(in_range(v, u)); TRACE("bv_bounds", tout << "upper " << mk_ismt2_pp(v, m_m) << ":" << u << std::endl;); // v <= u @@ -541,7 +541,7 @@ bool bv_bounds::bound_up(app * v, numeral u) { return m_okay; } -bool bv_bounds::add_neg_bound(app * v, numeral a, numeral b) { +bool bv_bounds::add_neg_bound(app * v, const numeral& a, const numeral& b) { TRACE("bv_bounds", tout << "negative bound " << mk_ismt2_pp(v, m_m) << ":" << a << ";" << b << std::endl;); bv_bounds::interval negative_interval(a, b); SASSERT(m_bv_util.is_bv(v)); @@ -550,8 +550,8 @@ bool bv_bounds::add_neg_bound(app * v, numeral a, numeral b) { SASSERT(a <= b); intervals_map::obj_map_entry * const e = m_negative_intervals.find_core(v); - intervals * ivs(NULL); - if (e == 0) { + intervals * ivs(nullptr); + if (e == nullptr) { ivs = alloc(intervals); m_negative_intervals.insert(v, ivs); } @@ -621,7 +621,7 @@ bool bv_bounds::is_sat_core(app * v) { if (!has_lower) lower = numeral::zero(); if (!has_upper) upper = (numeral::power_of_two(bv_sz) - one); TRACE("bv_bounds", tout << "is_sat bound:" << lower << "-" << upper << std::endl;); - intervals * negative_intervals(NULL); + intervals * negative_intervals(nullptr); const bool has_neg_intervals = m_negative_intervals.find(v, negative_intervals); bool is_sat(false); numeral new_lo = lower; diff --git a/src/ast/rewriter/bv_bounds.h b/src/ast/rewriter/bv_bounds.h index 4a7226fa7..fcd173a12 100644 --- a/src/ast/rewriter/bv_bounds.h +++ b/src/ast/rewriter/bv_bounds.h @@ -49,11 +49,11 @@ public: // bounds addition methods **/ bool add_constraint(expr* e); - bool bound_up(app * v, numeral u); // v <= u - bool bound_lo(app * v, numeral l); // l <= v - inline bool add_neg_bound(app * v, numeral a, numeral b); // not (a<=v<=b) - bool add_bound_signed(app * v, numeral a, numeral b, bool negate); - bool add_bound_unsigned(app * v, numeral a, numeral b, bool negate); + bool bound_up(app * v, const numeral& u); // v <= u + bool bound_lo(app * v, const numeral& l); // l <= v + inline bool add_neg_bound(app * v, const numeral& a, const numeral& b); // not (a<=v<=b) + bool add_bound_signed(app * v, const numeral& a, const numeral& b, bool negate); + bool add_bound_unsigned(app * v, const numeral& a, const numeral& b, bool negate); public: bool is_sat(); ///< Determine if the set of considered constraints is satisfiable. bool is_okay(); @@ -70,7 +70,7 @@ protected: enum conv_res { CONVERTED, UNSAT, UNDEF }; conv_res convert(expr * e, vector& nis, bool negated); conv_res record(app * v, numeral lo, numeral hi, bool negated, vector& nis); - conv_res convert_signed(app * v, numeral a, numeral b, bool negate, vector& nis); + conv_res convert_signed(app * v, const numeral& a, const numeral& b, bool negate, vector& nis); typedef vector intervals; typedef obj_map intervals_map; @@ -83,7 +83,7 @@ protected: bool m_okay; bool is_sat(app * v); bool is_sat_core(app * v); - inline bool in_range(app *v, numeral l); + inline bool in_range(app *v, const numeral& l); inline bool is_constant_add(unsigned bv_sz, expr * e, app*& v, numeral& val); void record_singleton(app * v, numeral& singleton_value); inline bool to_bound(const expr * e) const; @@ -99,7 +99,7 @@ inline bool bv_bounds::to_bound(const expr * e) const { && !m_bv_util.is_numeral(e); } -inline bool bv_bounds::in_range(app *v, bv_bounds::numeral n) { +inline bool bv_bounds::in_range(app *v, const bv_bounds::numeral& n) { const unsigned bv_sz = m_bv_util.get_bv_size(v); const bv_bounds::numeral zero(0); const bv_bounds::numeral mod(rational::power_of_two(bv_sz)); @@ -109,7 +109,7 @@ inline bool bv_bounds::in_range(app *v, bv_bounds::numeral n) { inline bool bv_bounds::is_constant_add(unsigned bv_sz, expr * e, app*& v, numeral& val) { SASSERT(e && !v); SASSERT(m_bv_util.get_bv_size(e) == bv_sz); - expr *lhs(NULL), *rhs(NULL); + expr *lhs(nullptr), *rhs(nullptr); if (!m_bv_util.is_bv_add(e, lhs, rhs)) { v = to_app(e); val = rational(0); diff --git a/src/ast/rewriter/bv_elim.cpp b/src/ast/rewriter/bv_elim.cpp index 270d7deb8..0c6484ed6 100644 --- a/src/ast/rewriter/bv_elim.cpp +++ b/src/ast/rewriter/bv_elim.cpp @@ -58,7 +58,7 @@ bool bv_elim_cfg::reduce_quantifier(quantifier * q, _sorts.push_back(m.mk_bool_sort()); _names.push_back(symbol(new_name.str().c_str())); } - bv = m.mk_app(bfid, OP_MKBV, 0, 0, args.size(), args.c_ptr()); + bv = m.mk_app(bfid, OP_MKBV, 0, nullptr, args.size(), args.c_ptr()); _subst_map.push_back(bv.get()); } else { diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index ce35300ca..a0adb6398 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -630,13 +630,13 @@ unsigned bv_rewriter::propagate_extract(unsigned high, expr * arg, expr_ref & re const bool curr_is_conc = m_util.is_concat(curr); if (curr_is_conc && to_app(curr)->get_num_args() == 0) continue; expr * const curr_first = curr_is_conc ? to_app(curr)->get_arg(0) : curr; - expr * new_first = NULL; + expr * new_first = nullptr; if (is_numeral(curr_first, val, curr_first_sz)) { SASSERT(curr_first_sz >= removable); const unsigned new_num_sz = curr_first_sz - removable; - new_first = new_num_sz ? mk_numeral(val, new_num_sz) : NULL; + new_first = new_num_sz ? mk_numeral(val, new_num_sz) : nullptr; } - expr * new_arg = NULL; + expr * new_arg = nullptr; if (curr_is_conc) { const unsigned conc_num = to_app(curr)->get_num_args(); if (new_first) { @@ -650,7 +650,7 @@ unsigned bv_rewriter::propagate_extract(unsigned high, expr * arg, expr_ref & re expr * const * const old_conc_args = to_app(curr)->get_args(); switch (conc_num) { case 0: UNREACHABLE(); break; - case 1: new_arg = NULL; break; + case 1: new_arg = nullptr; break; case 2: new_arg = to_app(curr)->get_arg(1); break; default: new_arg = m_util.mk_concat(conc_num - 1, old_conc_args + 1); } @@ -680,8 +680,8 @@ br_status bv_rewriter::mk_extract(unsigned high, unsigned low, expr * arg, expr_ if (v.is_neg()) mod(v, rational::power_of_two(sz), v); if (v.is_uint64()) { - uint64 u = v.get_uint64(); - uint64 e = shift_right(u, low) & (shift_left(1ull, sz) - 1ull); + uint64_t u = v.get_uint64(); + uint64_t e = shift_right(u, low) & (shift_left(1ull, sz) - 1ull); result = mk_numeral(numeral(e, numeral::ui64()), sz); return BR_DONE; } @@ -811,7 +811,7 @@ br_status bv_rewriter::mk_bv_shl(expr * arg1, expr * arg2, expr_ref & result) { SASSERT(r1.is_uint64() && r2.is_uint64()); SASSERT(r2.get_uint64() < bv_size); - uint64 r = shift_left(r1.get_uint64(), r2.get_uint64()); + uint64_t r = shift_left(r1.get_uint64(), r2.get_uint64()); numeral rn(r, numeral::ui64()); rn = m_util.norm(rn, bv_size); result = mk_numeral(rn, bv_size); @@ -860,7 +860,7 @@ br_status bv_rewriter::mk_bv_lshr(expr * arg1, expr * arg2, expr_ref & result) { if (bv_size <= 64) { SASSERT(r1.is_uint64()); SASSERT(r2.is_uint64()); - uint64 r = shift_right(r1.get_uint64(), r2.get_uint64()); + uint64_t r = shift_right(r1.get_uint64(), r2.get_uint64()); numeral rn(r, numeral::ui64()); rn = m_util.norm(rn, bv_size); result = mk_numeral(rn, bv_size); @@ -902,11 +902,11 @@ br_status bv_rewriter::mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result) { bool is_num1 = is_numeral(arg1, r1, bv_size); if (bv_size <= 64 && is_num1 && is_num2) { - uint64 n1 = r1.get_uint64(); - uint64 n2_orig = r2.get_uint64(); - uint64 n2 = n2_orig % bv_size; + uint64_t n1 = r1.get_uint64(); + uint64_t n2_orig = r2.get_uint64(); + uint64_t n2 = n2_orig % bv_size; SASSERT(n2 < bv_size); - uint64 r = shift_right(n1, n2); + uint64_t r = shift_right(n1, n2); bool sign = (n1 & shift_left(1ull, bv_size - 1ull)) != 0; if (n2_orig > n2) { if (sign) { @@ -917,9 +917,9 @@ br_status bv_rewriter::mk_bv_ashr(expr * arg1, expr * arg2, expr_ref & result) { } } else if (sign) { - uint64 allone = shift_left(1ull, bv_size) - 1ull; - uint64 mask = ~(shift_left(1ull, bv_size - n2) - 1ull); - mask &= allone; + uint64_t allone = shift_left(1ull, bv_size) - 1ull; + uint64_t mask = ~(shift_left(1ull, bv_size - n2) - 1ull); + mask &= allone; r |= mask; } result = mk_numeral(numeral(r, numeral::ui64()), bv_size); @@ -1458,10 +1458,10 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re bool fused_extract = false; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; - expr * prev = 0; + expr * prev = nullptr; if (i > 0) prev = new_args.back(); - if (is_numeral(arg, v1, sz1) && prev != 0 && is_numeral(prev, v2, sz2)) { + if (is_numeral(arg, v1, sz1) && prev != nullptr && is_numeral(prev, v2, sz2)) { v2 *= rational::power_of_two(sz1); v2 += v1; new_args.pop_back(); @@ -1476,7 +1476,7 @@ br_status bv_rewriter::mk_concat(unsigned num_args, expr * const * args, expr_re expanded = true; } else if (m_util.is_extract(arg) && - prev != 0 && + prev != nullptr && m_util.is_extract(prev) && to_app(arg)->get_arg(0) == to_app(prev)->get_arg(0) && m_util.get_extract_low(prev) == m_util.get_extract_high(arg) + 1) { @@ -1814,7 +1814,7 @@ br_status bv_rewriter::mk_bv_xor(unsigned num, expr * const * args, expr_ref & r // if (!v1.is_zero() && num_coeffs == num - 1) { // find argument that is not a numeral - expr * t = 0; + expr * t = nullptr; for (unsigned i = 0; i < num; i++) { t = args[i]; if (!is_numeral(t)) @@ -1937,7 +1937,7 @@ br_status bv_rewriter::mk_bv_not(expr * arg, expr_ref & result) { } if (m_bvnot_simpl) { - expr *s(0), *t(0); + expr *s(nullptr), *t(nullptr); if (m_util.is_bv_mul(arg, s, t)) { // ~(-1 * x) --> (x - 1) bv_size = m_util.get_bv_size(s); @@ -2021,7 +2021,7 @@ br_status bv_rewriter::mk_bv_ext_rotate_left(expr * arg1, expr * arg2, expr_ref numeral r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { - unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); + unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); return mk_bv_rotate_left(shift, arg1, result); } return BR_FAILED; @@ -2031,7 +2031,7 @@ br_status bv_rewriter::mk_bv_ext_rotate_right(expr * arg1, expr * arg2, expr_ref numeral r2; unsigned bv_size; if (is_numeral(arg2, r2, bv_size)) { - unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); + unsigned shift = static_cast((r2 % numeral(bv_size)).get_uint64() % static_cast(bv_size)); return mk_bv_rotate_right(shift, arg1, result); } return BR_FAILED; diff --git a/src/ast/rewriter/bv_trailing.cpp b/src/ast/rewriter/bv_trailing.cpp index 5cdf27574..ad9350eca 100644 --- a/src/ast/rewriter/bv_trailing.cpp +++ b/src/ast/rewriter/bv_trailing.cpp @@ -39,7 +39,7 @@ struct bv_trailing::imp { TRACE("bv-trailing", tout << "ctor\n";); for (unsigned i = 0; i <= TRAILING_DEPTH; ++i) - m_count_cache[i] = NULL; + m_count_cache[i] = nullptr; } virtual ~imp() { @@ -116,7 +116,7 @@ struct bv_trailing::imp { const unsigned sz = m_util.get_bv_size(a); if (to_rm == sz) { - result = NULL; + result = nullptr; return sz; } @@ -155,7 +155,7 @@ struct bv_trailing::imp { const unsigned new_sz = sz - retv; if (!new_sz) { - result = NULL; + result = nullptr; return retv; } @@ -181,7 +181,7 @@ struct bv_trailing::imp { const unsigned num = a->get_num_args(); unsigned retv = 0; unsigned i = num; - expr_ref new_last(NULL, m); + expr_ref new_last(nullptr, m); while (i && retv < n) { i--; expr * const curr = a->get_arg(i); @@ -197,7 +197,7 @@ struct bv_trailing::imp { if (!i && !new_last) {// all args eaten completely SASSERT(retv == m_util.get_bv_size(a)); - result = NULL; + result = nullptr; return retv; } @@ -230,7 +230,7 @@ struct bv_trailing::imp { if (m_util.is_numeral(e, e_val, sz)) { retv = remove_trailing(std::min(n, sz), e_val); const unsigned new_sz = sz - retv; - result = new_sz ? (retv ? m_util.mk_numeral(e_val, new_sz) : e) : NULL; + result = new_sz ? (retv ? m_util.mk_numeral(e_val, new_sz) : e) : nullptr; return retv; } if (m_util.is_bv_mul(e)) @@ -338,7 +338,7 @@ struct bv_trailing::imp { void cache(unsigned depth, expr * e, unsigned min, unsigned max) { SASSERT(depth <= TRAILING_DEPTH); if (depth == 0) return; - if (m_count_cache[depth] == NULL) + if (m_count_cache[depth] == nullptr) m_count_cache[depth] = alloc(map); SASSERT(!m_count_cache[depth]->contains(e)); m.inc_ref(e); @@ -353,10 +353,10 @@ struct bv_trailing::imp { max = m_util.get_bv_size(e); return true; } - if (m_count_cache[depth] == NULL) + if (m_count_cache[depth] == nullptr) return false; const map::obj_map_entry * const oe = m_count_cache[depth]->find_core(e); - if (oe == NULL) return false; + if (oe == nullptr) return false; min = oe->get_data().m_value.first; max = oe->get_data().m_value.second; TRACE("bv-trailing", tout << "cached@" << depth << ": " << mk_ismt2_pp(e, m) << '[' << m_util.get_bv_size(e) << "]\n: " << min << '-' << max << "\n";); @@ -366,7 +366,7 @@ struct bv_trailing::imp { void reset_cache(const unsigned condition) { SASSERT(m_count_cache[0] == NULL); for (unsigned i = 1; i <= TRAILING_DEPTH; ++i) { - if (m_count_cache[i] == NULL) continue; + if (m_count_cache[i] == nullptr) continue; TRACE("bv-trailing", tout << "may reset cache " << i << " " << condition << "\n";); if (condition && m_count_cache[i]->size() < condition) continue; TRACE("bv-trailing", tout << "reset cache " << i << "\n";); @@ -374,7 +374,7 @@ struct bv_trailing::imp { map::iterator end = m_count_cache[i]->end(); for (; it != end; ++it) m.dec_ref(it->m_key); dealloc(m_count_cache[i]); - m_count_cache[i] = NULL; + m_count_cache[i] = nullptr; } } diff --git a/src/ast/rewriter/datatype_rewriter.cpp b/src/ast/rewriter/datatype_rewriter.cpp index f0a95929b..194668b9c 100644 --- a/src/ast/rewriter/datatype_rewriter.cpp +++ b/src/ast/rewriter/datatype_rewriter.cpp @@ -23,6 +23,9 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr switch(f->get_decl_kind()) { case OP_DT_CONSTRUCTOR: return BR_FAILED; case OP_DT_RECOGNISER: + SASSERT(num_args == 1); + result = m_util.mk_is(m_util.get_recognizer_constructor(f), args[0]); + return BR_REWRITE1; case OP_DT_IS: // // simplify is_cons(cons(x,y)) -> true diff --git a/src/ast/rewriter/der.cpp b/src/ast/rewriter/der.cpp index 56f895b8a..54409e9c2 100644 --- a/src/ast/rewriter/der.cpp +++ b/src/ast/rewriter/der.cpp @@ -40,11 +40,8 @@ static bool is_neg_var(ast_manager & m, expr * e, unsigned num_decls) { */ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { // (not (= VAR t)) and (not (iff VAR t)) cases - if (m_manager.is_not(e) && (m_manager.is_eq(to_app(e)->get_arg(0)) || m_manager.is_iff(to_app(e)->get_arg(0)))) { - app * eq = to_app(to_app(e)->get_arg(0)); - SASSERT(m_manager.is_eq(eq) || m_manager.is_iff(eq)); - expr * lhs = eq->get_arg(0); - expr * rhs = eq->get_arg(1); + expr *eq, * lhs, *rhs; + if (m_manager.is_not(e, eq) && m_manager.is_eq(eq, lhs, rhs)) { if (!is_var(lhs, num_decls) && !is_var(rhs, num_decls)) return false; if (!is_var(lhs, num_decls)) @@ -60,9 +57,7 @@ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { return true; } // (iff VAR t) and (iff (not VAR) t) cases - else if (m_manager.is_iff(e)) { - expr * lhs = to_app(e)->get_arg(0); - expr * rhs = to_app(e)->get_arg(1); + else if (m_manager.is_eq(e, lhs, rhs) && m_manager.is_bool(lhs)) { // (iff VAR t) case if (is_var(lhs, num_decls) || is_var(rhs, num_decls)) { if (!is_var(lhs, num_decls)) @@ -118,7 +113,7 @@ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) { void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) { bool reduced = false; - pr = 0; + pr = nullptr; r = q; TRACE("der", tout << mk_pp(q, m_manager) << "\n";); @@ -149,14 +144,14 @@ void der::operator()(quantifier * q, expr_ref & r, proof_ref & pr) { void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { if (!is_forall(q)) { - pr = 0; + pr = nullptr; r = q; return; } expr * e = q->get_expr(); unsigned num_decls = q->get_num_decls(); - var * v = 0; + var * v = nullptr; expr_ref t(m_manager); if (m_manager.is_or(e)) { @@ -211,7 +206,7 @@ void der::reduce1(quantifier * q, expr_ref & r, proof_ref & pr) { r = q; if (m_manager.proofs_enabled()) { - pr = r == q ? 0 : m_manager.mk_der(q, r); + pr = r == q ? nullptr : m_manager.mk_der(q, r); } } @@ -223,7 +218,7 @@ void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsig for (unsigned i = 0; i < definitions.size(); i++) { var * v = vars[i]; expr * t = definitions[i]; - if (t == 0 || has_quantifiers(t) || occurs(v, t)) + if (t == nullptr || has_quantifiers(t) || occurs(v, t)) definitions[i] = 0; else found = true; // found at least one candidate @@ -260,7 +255,7 @@ void der_sort_vars(ptr_vector & vars, ptr_vector & definitions, unsig vidx = to_var(t)->get_idx(); if (fr.second == 0) { CTRACE("der_bug", vidx >= definitions.size(), tout << "vidx: " << vidx << "\n";); - // Remark: The size of definitions may be smaller than the number of variables occuring in the quantified formula. + // Remark: The size of definitions may be smaller than the number of variables occurring in the quantified formula. if (definitions.get(vidx, 0) != 0) { if (visiting.is_marked(t)) { // cycle detected: remove t @@ -342,7 +337,7 @@ void der::get_elimination_order() { void der::create_substitution(unsigned sz) { m_subst_map.reset(); - m_subst_map.resize(sz, 0); + m_subst_map.resize(sz, nullptr); for(unsigned i = 0; i < m_order.size(); i++) { expr_ref cur(m_map[m_order[i]], m_manager); diff --git a/src/ast/rewriter/distribute_forall.h b/src/ast/rewriter/distribute_forall.h index ab0976945..8bb77d70d 100644 --- a/src/ast/rewriter/distribute_forall.h +++ b/src/ast/rewriter/distribute_forall.h @@ -71,7 +71,7 @@ protected: void reduce1_app(app * a); expr * get_cached(expr * n) const; - bool is_cached(expr * n) const { return get_cached(n) != 0; } + bool is_cached(expr * n) const { return get_cached(n) != nullptr; } void cache_result(expr * n, expr * r); void reset_cache() { m_cache.reset(); } void flush_cache() { m_cache.cleanup(); } diff --git a/src/ast/rewriter/dl_rewriter.cpp b/src/ast/rewriter/dl_rewriter.cpp index 74ea7814e..73944085b 100644 --- a/src/ast/rewriter/dl_rewriter.cpp +++ b/src/ast/rewriter/dl_rewriter.cpp @@ -22,7 +22,7 @@ Revision History: br_status dl_rewriter::mk_app_core( func_decl * f, unsigned num_args, expr* const* args, expr_ref& result) { ast_manager& m = result.get_manager(); - uint64 v1, v2; + uint64_t v1, v2; switch(f->get_decl_kind()) { case datalog::OP_DL_LT: if (m_util.is_numeral_ext(args[0], v1) && diff --git a/src/ast/rewriter/elim_bounds.cpp b/src/ast/rewriter/elim_bounds.cpp index d3240e511..a640081ba 100644 --- a/src/ast/rewriter/elim_bounds.cpp +++ b/src/ast/rewriter/elim_bounds.cpp @@ -44,15 +44,15 @@ elim_bounds_cfg::elim_bounds_cfg(ast_manager & m): It also detects >=, and the atom can be negated. */ bool elim_bounds_cfg::is_bound(expr * n, var * & lower, var * & upper) { - upper = 0; - lower = 0; + upper = nullptr; + lower = nullptr; bool neg = false; if (m.is_not(n)) { n = to_app(n)->get_arg(0); neg = true; } - expr* l = 0, *r = 0; + expr* l = nullptr, *r = nullptr; bool le = false; if (m_util.is_le(n, l, r) && m_util.is_numeral(r)) { n = l; @@ -139,14 +139,14 @@ bool elim_bounds_cfg::reduce_quantifier(quantifier * q, ptr_buffer candidates; #define ADD_CANDIDATE(V) if (!lowers.contains(V) && !uppers.contains(V)) { candidate_set.insert(V); candidates.push_back(V); } for (expr * a : atoms) { - var * lower = 0; - var * upper = 0; + var * lower = nullptr; + var * upper = nullptr; if (is_bound(a, lower, upper)) { - if (lower != 0 && !used_vars.contains(lower->get_idx()) && lower->get_idx() < num_vars) { + if (lower != nullptr && !used_vars.contains(lower->get_idx()) && lower->get_idx() < num_vars) { ADD_CANDIDATE(lower); lowers.insert(lower); } - if (upper != 0 && !used_vars.contains(upper->get_idx()) && upper->get_idx() < num_vars) { + if (upper != nullptr && !used_vars.contains(upper->get_idx()) && upper->get_idx() < num_vars) { ADD_CANDIDATE(upper); uppers.insert(upper); } @@ -167,9 +167,9 @@ bool elim_bounds_cfg::reduce_quantifier(quantifier * q, unsigned j = 0; for (unsigned i = 0; i < atoms.size(); ++i) { expr * a = atoms[i]; - var * lower = 0; - var * upper = 0; - if (is_bound(a, lower, upper) && ((lower != 0 && candidate_set.contains(lower)) || (upper != 0 && candidate_set.contains(upper)))) + var * lower = nullptr; + var * upper = nullptr; + if (is_bound(a, lower, upper) && ((lower != nullptr && candidate_set.contains(lower)) || (upper != nullptr && candidate_set.contains(upper)))) continue; atoms[j] = a; j++; @@ -178,7 +178,7 @@ bool elim_bounds_cfg::reduce_quantifier(quantifier * q, return false; } atoms.resize(j); - expr * new_body = 0; + expr * new_body = nullptr; switch (atoms.size()) { case 0: result = m.mk_false(); diff --git a/src/ast/rewriter/elim_bounds.h b/src/ast/rewriter/elim_bounds.h index e0bba4e60..4dcb24173 100644 --- a/src/ast/rewriter/elim_bounds.h +++ b/src/ast/rewriter/elim_bounds.h @@ -70,7 +70,7 @@ public: m_cfg(m) {} - virtual ~elim_bounds_rw() {} + ~elim_bounds_rw() override {} }; #endif /* ELIM_BOUNDS2_H_ */ diff --git a/src/ast/rewriter/enum2bv_rewriter.cpp b/src/ast/rewriter/enum2bv_rewriter.cpp index eb6b195f0..814a25ee9 100644 --- a/src/ast/rewriter/enum2bv_rewriter.cpp +++ b/src/ast/rewriter/enum2bv_rewriter.cpp @@ -194,7 +194,7 @@ struct enum2bv_rewriter::imp { q->get_weight(), q->get_qid(), q->get_skid(), q->get_num_patterns(), new_patterns, q->get_num_no_patterns(), new_no_patterns); - result_pr = 0; + result_pr = nullptr; return true; } @@ -226,7 +226,7 @@ struct enum2bv_rewriter::imp { m_enum_bvs(m), m_enum_defs(m), m_num_translated(0), - m_sort_pred(0), + m_sort_pred(nullptr), m_rw(*this, m, p) { } diff --git a/src/ast/rewriter/expr_replacer.cpp b/src/ast/rewriter/expr_replacer.cpp index 70e12dced..c9bf834fd 100644 --- a/src/ast/rewriter/expr_replacer.cpp +++ b/src/ast/rewriter/expr_replacer.cpp @@ -34,7 +34,7 @@ void expr_replacer::operator()(expr * t, expr_ref & result) { struct expr_replacer::scoped_set_subst { expr_replacer & m_r; scoped_set_subst(expr_replacer & r, expr_substitution & s):m_r(r) { m_r.set_substitution(&s); } - ~scoped_set_subst() { m_r.set_substitution(0); } + ~scoped_set_subst() { m_r.set_substitution(nullptr); } }; void expr_replacer::apply_substitution(expr * s, expr * def, proof * def_pr, expr_ref & t) { @@ -58,14 +58,14 @@ struct default_expr_replacer_cfg : public default_rewriter_cfg { default_expr_replacer_cfg(ast_manager & _m): m(_m), - m_subst(0), + m_subst(nullptr), m_used_dependencies(_m) { } bool get_subst(expr * s, expr * & t, proof * & pr) { - if (m_subst == 0) + if (m_subst == nullptr) return false; - expr_dependency * d = 0; + expr_dependency * d = nullptr; if (m_subst->find(s, t, pr, d)) { m_used_dependencies = m.mk_join(m_used_dependencies, d); return true; @@ -90,29 +90,29 @@ public: m_replacer(m, m.proofs_enabled(), m_cfg) { } - virtual ast_manager & m() const { return m_replacer.m(); } + ast_manager & m() const override { return m_replacer.m(); } - virtual void set_substitution(expr_substitution * s) { + void set_substitution(expr_substitution * s) override { m_replacer.cleanup(); m_replacer.cfg().m_subst = s; } - virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) { - result_dep = 0; + void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) override { + result_dep = nullptr; m_replacer.operator()(t, result, result_pr); if (m_cfg.m_used_dependencies != 0) { result_dep = m_cfg.m_used_dependencies; m_replacer.reset(); // reset cache - m_cfg.m_used_dependencies = 0; + m_cfg.m_used_dependencies = nullptr; } } - virtual unsigned get_num_steps() const { + unsigned get_num_steps() const override { return m_replacer.get_num_steps(); } - virtual void reset() { + void reset() override { m_replacer.reset(); } }; @@ -131,23 +131,23 @@ public: m_r(m, p) { } - virtual ~th_rewriter2expr_replacer() {} + ~th_rewriter2expr_replacer() override {} - virtual ast_manager & m() const { return m_r.m(); } + ast_manager & m() const override { return m_r.m(); } - virtual void set_substitution(expr_substitution * s) { m_r.set_substitution(s); } + void set_substitution(expr_substitution * s) override { m_r.set_substitution(s); } - virtual void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) { + void operator()(expr * t, expr_ref & result, proof_ref & result_pr, expr_dependency_ref & result_dep) override { m_r(t, result, result_pr); result_dep = m_r.get_used_dependencies(); m_r.reset_used_dependencies(); } - virtual unsigned get_num_steps() const { + unsigned get_num_steps() const override { return m_r.get_num_steps(); } - virtual void reset() { + void reset() override { m_r.reset(); } diff --git a/src/ast/rewriter/expr_safe_replace.cpp b/src/ast/rewriter/expr_safe_replace.cpp index 31453691e..0ed0efc00 100644 --- a/src/ast/rewriter/expr_safe_replace.cpp +++ b/src/ast/rewriter/expr_safe_replace.cpp @@ -61,7 +61,7 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) { m_args.reset(); bool arg_differs = false; for (unsigned i = 0; i < n; ++i) { - expr* d = 0, *arg = c->get_arg(i); + expr* d = nullptr, *arg = c->get_arg(i); if (m_cache.find(arg, d)) { m_args.push_back(d); arg_differs |= arg != d; diff --git a/src/ast/factor_equivs.cpp b/src/ast/rewriter/factor_equivs.cpp similarity index 66% rename from src/ast/factor_equivs.cpp rename to src/ast/rewriter/factor_equivs.cpp index bbb21c1b2..6384ad8ce 100644 --- a/src/ast/factor_equivs.cpp +++ b/src/ast/rewriter/factor_equivs.cpp @@ -25,8 +25,11 @@ Revision History: */ -#include "ast/factor_equivs.h" #include "ast/arith_decl_plugin.h" +#include "ast/for_each_expr.h" +#include "ast/ast_pp.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/rewriter/factor_equivs.h" /** Factors input vector v into equivalence classes and the rest @@ -34,7 +37,7 @@ Revision History: void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) { ast_manager &m = v.get_manager(); arith_util arith(m); - expr *e1 = 0, *e2 = 0; + expr *e1 = nullptr, *e2 = nullptr; flatten_and(v); unsigned j = 0; @@ -45,7 +48,7 @@ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) { } // y + -1*x == 0 - expr* a0 = 0, *a1 = 0, *x = 0; + expr* a0 = nullptr, *a1 = nullptr, *x = nullptr; if (arith.is_zero(e2) && arith.is_add(e1, a0, a1)) { if (arith.is_times_minus_one(a1, x)) { e1 = a0; @@ -59,8 +62,8 @@ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) { equiv.merge(e1, e2); } else { - if (j < i) { - v[j] = v.get(i); + if (j < i) { + v[j] = v.get(i); } j++; } @@ -68,19 +71,52 @@ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) { v.shrink(j); } +/** + * Chooses a representative of an equivalence class + */ +expr *choose_rep(expr_equiv_class::eq_class &clazz, ast_manager &m) { + expr *rep = nullptr; + unsigned rep_sz, elem_sz; + for (expr *elem : clazz) { + if (!m.is_value(elem)) { + elem_sz = get_num_exprs(elem); + if (!rep || (rep && rep_sz > elem_sz)) { + rep = elem; + rep_sz = elem_sz; + } + } + } + TRACE("equiv", + tout << "Rep: " << mk_pp(rep, m) << "\n"; + for (expr *el : clazz) + tout << mk_pp(el, m) << "\n"; + tout << "RepEnd\n";); + + return rep; +} + +void rewrite_eqs (expr_ref_vector &v, expr_equiv_class &equiv) { + ast_manager &m = v.m(); + expr_safe_replace sub(m); + for (auto eq_class : equiv) { + expr *rep = choose_rep(eq_class, m); + for (expr *el : eq_class) { + if (el != rep) { + sub.insert (el, rep); + } + } + } + sub(v); +} + + /** * converts equivalence classes to equalities */ void equiv_to_expr(expr_equiv_class &equiv, expr_ref_vector &out) { ast_manager &m = out.get_manager(); for (auto eq_class : equiv) { - expr *rep = nullptr; - for (expr *elem : eq_class) { - if (!m.is_value(elem)) { - rep = elem; - break; - } - } + expr *rep = choose_rep(eq_class, m); SASSERT(rep); for (expr *elem : eq_class) { if (rep != elem) { diff --git a/src/ast/factor_equivs.h b/src/ast/rewriter/factor_equivs.h similarity index 96% rename from src/ast/factor_equivs.h rename to src/ast/rewriter/factor_equivs.h index f0ce1608d..5d306bad8 100644 --- a/src/ast/factor_equivs.h +++ b/src/ast/rewriter/factor_equivs.h @@ -60,6 +60,8 @@ public: obj_equiv_class(Manager& m) : m_to_obj(m) {} + Manager &m() const {return m_to_obj.m();} + void add_elem(OBJ*o) { SASSERT(!m_to_int.find(o)); add_elem_impl(o); @@ -169,6 +171,11 @@ typedef obj_equiv_class expr_equiv_class; * Factors input vector v into equivalence classes and the rest */ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv); +/** + * Rewrite expressions in v by choosing a representative from the + * equivalence class. + */ +void rewrite_eqs(expr_ref_vector &v, expr_equiv_class &equiv); /** * converts equivalence classes to equalities */ diff --git a/src/ast/rewriter/factor_rewriter.h b/src/ast/rewriter/factor_rewriter.h index aae9b05b3..823a8ae69 100644 --- a/src/ast/rewriter/factor_rewriter.h +++ b/src/ast/rewriter/factor_rewriter.h @@ -61,7 +61,7 @@ struct factor_rewriter_cfg : public default_rewriter_cfg { bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return m_r.mk_app_core(f, num, args, result); } factor_rewriter_cfg(ast_manager & m):m_r(m) {} diff --git a/src/ast/rewriter/fpa_rewriter.cpp b/src/ast/rewriter/fpa_rewriter.cpp index e62c9346f..d978779aa 100644 --- a/src/ast/rewriter/fpa_rewriter.cpp +++ b/src/ast/rewriter/fpa_rewriter.cpp @@ -48,17 +48,17 @@ br_status fpa_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con case OP_FPA_RM_TOWARD_POSITIVE: case OP_FPA_RM_TOWARD_NEGATIVE: case OP_FPA_RM_TOWARD_ZERO: - SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)0); st = BR_DONE; break; + SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)nullptr); st = BR_DONE; break; case OP_FPA_PLUS_INF: case OP_FPA_MINUS_INF: case OP_FPA_NAN: case OP_FPA_PLUS_ZERO: case OP_FPA_MINUS_ZERO: - SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)0); st = BR_DONE; break; + SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)nullptr); st = BR_DONE; break; case OP_FPA_NUM: - SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)0); st = BR_DONE; break; + SASSERT(num_args == 0); result = m().mk_app(f, (expr * const *)nullptr); st = BR_DONE; break; case OP_FPA_ADD: SASSERT(num_args == 3); st = mk_add(args[0], args[1], args[2], result); break; case OP_FPA_SUB: SASSERT(num_args == 3); st = mk_sub(args[0], args[1], args[2], result); break; @@ -773,7 +773,7 @@ br_status fpa_rewriter::mk_to_ieee_bv(func_decl * f, expr * arg, expr_ref & resu if (m_fm.is_nan(v)) { if (m_hi_fp_unspecified) { expr * args[4] = { bu.mk_numeral(0, 1), - bu.mk_numeral(-1, x.get_ebits()), + bu.mk_bv_neg(bu.mk_numeral(1, x.get_ebits())), bu.mk_numeral(0, x.get_sbits() - 2), bu.mk_numeral(1, 1) }; result = bu.mk_concat(4, args); diff --git a/src/ast/rewriter/inj_axiom.cpp b/src/ast/rewriter/inj_axiom.cpp index d322f3228..184c84144 100644 --- a/src/ast/rewriter/inj_axiom.cpp +++ b/src/ast/rewriter/inj_axiom.cpp @@ -29,9 +29,9 @@ Revision History: */ bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) { expr * n = q->get_expr(); - expr* arg1 = 0, * arg2 = 0, *narg = 0; - expr* app1 = 0, * app2 = 0; - expr* var1 = 0, * var2 = 0; + expr* arg1 = nullptr, * arg2 = nullptr, *narg = nullptr; + expr* app1 = nullptr, * app2 = nullptr; + expr* var1 = nullptr, * var2 = nullptr; if (q->is_forall() && m.is_or(n, arg1, arg2)) { if (m.is_not(arg2)) std::swap(arg1, arg2); @@ -84,7 +84,7 @@ bool simplify_inj_axiom(ast_manager & m, quantifier * q, expr_ref & result) { ptr_buffer decls; buffer names; - expr * var = 0; + expr * var = nullptr; for (unsigned i = 0; i < num; i++) { expr * c = f1->get_arg(i); if (is_var(c)) { diff --git a/src/ast/rewriter/maximize_ac_sharing.cpp b/src/ast/rewriter/maximize_ac_sharing.cpp index a838f59fa..a8bee62d5 100644 --- a/src/ast/rewriter/maximize_ac_sharing.cpp +++ b/src/ast/rewriter/maximize_ac_sharing.cpp @@ -35,7 +35,7 @@ br_status maximize_ac_sharing::reduce_app(func_decl * f, unsigned num_args, expr if (std::find(m_kinds.begin(), m_kinds.end(), k) == m_kinds.end()) return BR_FAILED; ptr_buffer _args; - expr * numeral = 0; + expr * numeral = nullptr; if (is_numeral(args[0])) { numeral = args[0]; for (unsigned i = 1; i < num_args; i++) @@ -86,7 +86,7 @@ br_status maximize_ac_sharing::reduce_app(func_decl * f, unsigned num_args, expr } num_args = j; if (num_args == 1) { - if (numeral == 0) { + if (numeral == nullptr) { result = _args[0]; } else { diff --git a/src/ast/rewriter/maximize_ac_sharing.h b/src/ast/rewriter/maximize_ac_sharing.h index c0ee0d09f..a36a31101 100644 --- a/src/ast/rewriter/maximize_ac_sharing.h +++ b/src/ast/rewriter/maximize_ac_sharing.h @@ -45,7 +45,7 @@ class maximize_ac_sharing : public default_rewriter_cfg { expr * m_arg1; expr * m_arg2; - entry(func_decl * d = 0, expr * arg1 = 0, expr * arg2 = 0):m_decl(d), m_arg1(arg1), m_arg2(arg2) { + entry(func_decl * d = nullptr, expr * arg1 = nullptr, expr * arg2 = nullptr):m_decl(d), m_arg1(arg1), m_arg2(arg2) { SASSERT((d == 0 && arg1 == 0 && arg2 == 0) || (d != 0 && arg1 != 0 && arg2 != 0)); if (arg1->get_id() > arg2->get_id()) std::swap(m_arg1, m_arg2); @@ -103,8 +103,8 @@ public: class maximize_bv_sharing : public maximize_ac_sharing { bv_util m_util; protected: - virtual void init_core(); - virtual bool is_numeral(expr * n) const; + void init_core() override; + bool is_numeral(expr * n) const override; public: maximize_bv_sharing(ast_manager & m); }; diff --git a/src/ast/rewriter/mk_extract_proc.cpp b/src/ast/rewriter/mk_extract_proc.cpp index da22c3b80..e245f3a3d 100644 --- a/src/ast/rewriter/mk_extract_proc.cpp +++ b/src/ast/rewriter/mk_extract_proc.cpp @@ -19,8 +19,8 @@ mk_extract_proc::mk_extract_proc(bv_util & u): m_util(u), m_high(0), m_low(UINT_MAX), - m_domain(0), - m_f_cached(0) { + m_domain(nullptr), + m_f_cached(nullptr) { } mk_extract_proc::~mk_extract_proc() { diff --git a/src/ast/rewriter/mk_simplified_app.cpp b/src/ast/rewriter/mk_simplified_app.cpp index eac26ddc3..9bf460aa9 100644 --- a/src/ast/rewriter/mk_simplified_app.cpp +++ b/src/ast/rewriter/mk_simplified_app.cpp @@ -98,7 +98,7 @@ br_status mk_simplified_app::mk_core(func_decl * decl, unsigned num, expr * cons } void mk_simplified_app::operator()(func_decl * decl, unsigned num, expr * const * args, expr_ref & result) { - result = 0; + result = nullptr; mk_core(decl, num, args, result); if (!result) result = m_imp->m.mk_app(decl, num, args); diff --git a/src/ast/rewriter/pb2bv_rewriter.cpp b/src/ast/rewriter/pb2bv_rewriter.cpp index 39d6e2143..3862aecae 100644 --- a/src/ast/rewriter/pb2bv_rewriter.cpp +++ b/src/ast/rewriter/pb2bv_rewriter.cpp @@ -25,6 +25,10 @@ Notes: #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "util/lbool.h" +#include "util/uint_set.h" +#include "util/gparams.h" + +const unsigned g_primes[7] = { 2, 3, 5, 7, 11, 13, 17}; struct pb2bv_rewriter::imp { @@ -35,10 +39,12 @@ struct pb2bv_rewriter::imp { func_decl_ref_vector m_fresh; // all fresh variables unsigned_vector m_fresh_lim; unsigned m_num_translated; + unsigned m_compile_bv; + unsigned m_compile_card; struct card2bv_rewriter { - typedef expr* literal; - typedef ptr_vector literal_vector; + typedef expr* pliteral; + typedef ptr_vector pliteral_vector; psort_nw m_sort; ast_manager& m; imp& m_imp; @@ -49,6 +55,9 @@ struct pb2bv_rewriter::imp { expr_ref_vector m_args; rational m_k; vector m_coeffs; + bool m_keep_cardinality_constraints; + symbol m_pb_solver; + unsigned m_min_arity; template expr_ref mk_le_ge(expr_ref_vector& fmls, expr* a, expr* b, expr* bound) { @@ -68,7 +77,28 @@ struct pb2bv_rewriter::imp { fmls.push_back(bv.mk_ule(bound, result)); } return result; + } + typedef std::pair ca; + + struct compare_coeffs { + bool operator()(ca const& x, ca const& y) const { + return x.first > y.first; + } + }; + + void sort_args() { + vector cas; + for (unsigned i = 0; i < m_args.size(); ++i) { + cas.push_back(std::make_pair(m_coeffs[i], expr_ref(m_args[i].get(), m))); + } + std::sort(cas.begin(), cas.end(), compare_coeffs()); + m_coeffs.reset(); + m_args.reset(); + for (ca const& ca : cas) { + m_coeffs.push_back(ca.first); + m_args.push_back(ca.second); + } } // @@ -84,21 +114,25 @@ struct pb2bv_rewriter::imp { // is_le = l_false - >= // template - expr_ref mk_le_ge(unsigned sz, expr * const* args, rational const & k) { + expr_ref mk_le_ge(rational const & k) { + //sort_args(); + unsigned sz = m_args.size(); + expr * const* args = m_args.c_ptr(); TRACE("pb", for (unsigned i = 0; i < sz; ++i) { tout << m_coeffs[i] << "*" << mk_pp(args[i], m) << " "; if (i + 1 < sz && !m_coeffs[i+1].is_neg()) tout << "+ "; } switch (is_le) { - case l_true: tout << "<= "; break; + case l_true: tout << "<= "; break; case l_undef: tout << "= "; break; case l_false: tout << ">= "; break; } - tout << m_k << "\n";); + tout << k << "\n";); + if (k.is_zero()) { if (is_le != l_false) { - return expr_ref(m.mk_not(mk_or(m, sz, args)), m); + return expr_ref(m.mk_not(::mk_or(m_args)), m); } else { return expr_ref(m.mk_true(), m); @@ -107,6 +141,35 @@ struct pb2bv_rewriter::imp { if (k.is_neg()) { return expr_ref((is_le == l_false)?m.mk_true():m.mk_false(), m); } + + if (m_pb_solver == "totalizer") { + expr_ref result(m); + switch (is_le) { + case l_true: if (mk_le_tot(sz, args, k, result)) return result; else break; + case l_false: if (mk_ge_tot(sz, args, k, result)) return result; else break; + case l_undef: break; + } + } + + if (m_pb_solver == "sorting") { + expr_ref result(m); + switch (is_le) { + case l_true: if (mk_le(sz, args, k, result)) return result; else break; + case l_false: if (mk_ge(sz, args, k, result)) return result; else break; + case l_undef: if (mk_eq(sz, args, k, result)) return result; else break; + } + } + + if (m_pb_solver == "segmented") { + expr_ref result(m); + switch (is_le) { + case l_true: return mk_seg_le(k); + case l_false: return mk_seg_ge(k); + case l_undef: break; + } + } + + // fall back to divide and conquer encoding. SASSERT(k.is_pos()); expr_ref zero(m), bound(m); expr_ref_vector es(m), fmls(m); @@ -138,12 +201,12 @@ struct pb2bv_rewriter::imp { } switch (is_le) { case l_true: - return mk_and(fmls); + return ::mk_and(fmls); case l_false: if (!es.empty()) { fmls.push_back(bv.mk_ule(bound, es.back())); } - return mk_or(fmls); + return ::mk_or(fmls); case l_undef: if (es.empty()) { fmls.push_back(m.mk_bool_val(k.is_zero())); @@ -151,35 +214,415 @@ struct pb2bv_rewriter::imp { else { fmls.push_back(m.mk_eq(bound, es.back())); } - return mk_and(fmls); + return ::mk_and(fmls); default: UNREACHABLE(); return expr_ref(m.mk_true(), m); } } + /** + \brief Totalizer encoding. Based on a version by Miguel. + */ + + bool mk_le_tot(unsigned sz, expr * const * args, rational const& _k, expr_ref& result) { + SASSERT(sz == m_coeffs.size()); + if (!_k.is_unsigned() || sz == 0) return false; + unsigned k = _k.get_unsigned(); + expr_ref_vector args1(m); + rational bound; + flip(sz, args, args1, _k, bound); + if (bound.get_unsigned() < k) { + return mk_ge_tot(sz, args1.c_ptr(), bound, result); + } + if (k > 20) { + return false; + } + result = m.mk_not(bounded_addition(sz, args, k + 1)); + TRACE("pb", tout << result << "\n";); + return true; + } + + bool mk_ge_tot(unsigned sz, expr * const * args, rational const& _k, expr_ref& result) { + SASSERT(sz == m_coeffs.size()); + if (!_k.is_unsigned() || sz == 0) return false; + unsigned k = _k.get_unsigned(); + expr_ref_vector args1(m); + rational bound; + flip(sz, args, args1, _k, bound); + if (bound.get_unsigned() < k) { + return mk_le_tot(sz, args1.c_ptr(), bound, result); + } + if (k > 20) { + return false; + } + result = bounded_addition(sz, args, k); + TRACE("pb", tout << result << "\n";); + return true; + } + + void flip(unsigned sz, expr* const* args, expr_ref_vector& args1, rational const& k, rational& bound) { + bound = -k; + for (unsigned i = 0; i < sz; ++i) { + args1.push_back(mk_not(args[i])); + bound += m_coeffs[i]; + } + } + + expr_ref bounded_addition(unsigned sz, expr * const * args, unsigned k) { + SASSERT(sz > 0); + expr_ref result(m); + vector es; + vector coeffs; + for (unsigned i = 0; i < m_coeffs.size(); ++i) { + unsigned_vector v; + expr_ref_vector e(m); + unsigned c = m_coeffs[i].get_unsigned(); + v.push_back(c >= k ? k : c); + e.push_back(args[i]); + es.push_back(e); + coeffs.push_back(v); + } + while (es.size() > 1) { + for (unsigned i = 0; i + 1 < es.size(); i += 2) { + expr_ref_vector o(m); + unsigned_vector oc; + tot_adder(es[i], coeffs[i], es[i + 1], coeffs[i + 1], k, o, oc); + es[i / 2].set(o); + coeffs[i / 2] = oc; + } + if ((es.size() % 2) == 1) { + es[es.size() / 2].set(es.back()); + coeffs[es.size() / 2] = coeffs.back(); + } + es.shrink((1 + es.size())/2); + coeffs.shrink((1 + coeffs.size())/2); + } + SASSERT(coeffs.size() == 1); + SASSERT(coeffs[0].back() <= k); + if (coeffs[0].back() == k) { + result = es[0].back(); + } + else { + result = m.mk_false(); + } + return result; + } + + void tot_adder(expr_ref_vector const& l, unsigned_vector const& lc, + expr_ref_vector const& r, unsigned_vector const& rc, + unsigned k, + expr_ref_vector& o, unsigned_vector & oc) { + SASSERT(l.size() == lc.size()); + SASSERT(r.size() == rc.size()); + uint_set sums; + vector trail; + u_map sum2def; + for (unsigned i = 0; i <= l.size(); ++i) { + for (unsigned j = (i == 0) ? 1 : 0; j <= r.size(); ++j) { + unsigned sum = std::min(k, ((i == 0) ? 0 : lc[i - 1]) + ((j == 0) ? 0 : rc[j - 1])); + sums.insert(sum); + } + } + for (unsigned u : sums) { + oc.push_back(u); + } + std::sort(oc.begin(), oc.end()); + DEBUG_CODE( + for (unsigned i = 0; i + 1 < oc.size(); ++i) { + SASSERT(oc[i] < oc[i+1]); + }); + for (unsigned i = 0; i < oc.size(); ++i) { + sum2def.insert(oc[i], i); + trail.push_back(expr_ref_vector(m)); + } + for (unsigned i = 0; i <= l.size(); ++i) { + for (unsigned j = (i == 0) ? 1 : 0; j <= r.size(); ++j) { + if (i != 0 && j != 0 && (lc[i - 1] >= k || rc[j - 1] >= k)) continue; + unsigned sum = std::min(k, ((i == 0) ? 0 : lc[i - 1]) + ((j == 0) ? 0 : rc[j - 1])); + expr_ref_vector ands(m); + if (i != 0) { + ands.push_back(l[i - 1]); + } + if (j != 0) { + ands.push_back(r[j - 1]); + } + trail[sum2def.find(sum)].push_back(::mk_and(ands)); + } + } + for (unsigned i = 0; i < oc.size(); ++i) { + o.push_back(::mk_or(trail[sum2def.find(oc[i])])); + } + } + + /** + \brief MiniSat+ based encoding of PB constraints. + Translating Pseudo-Boolean Constraints into SAT, + Niklas Een, Niklas Soerensson, JSAT 2006. + */ + + + vector m_min_base; + rational m_min_cost; + vector m_base; + + void create_basis(vector const& seq, rational carry_in, rational cost) { + if (cost >= m_min_cost) { + return; + } + rational delta_cost(0); + for (unsigned i = 0; i < seq.size(); ++i) { + delta_cost += seq[i]; + } + if (cost + delta_cost < m_min_cost) { + m_min_cost = cost + delta_cost; + m_min_base = m_base; + m_min_base.push_back(delta_cost + rational::one()); + } + + for (unsigned i = 0; i < sizeof(g_primes)/sizeof(*g_primes); ++i) { + vector seq1; + rational p(g_primes[i]); + rational rest = carry_in; + // create seq1 + for (unsigned j = 0; j < seq.size(); ++j) { + rest += seq[j] % p; + if (seq[j] >= p) { + seq1.push_back(div(seq[j], p)); + } + } + + m_base.push_back(p); + create_basis(seq1, div(rest, p), cost + rest); + m_base.pop_back(); + } + } + + bool create_basis() { + m_base.reset(); + m_min_cost = rational(INT_MAX); + m_min_base.reset(); + rational cost(0); + create_basis(m_coeffs, rational::zero(), cost); + m_base = m_min_base; + TRACE("pb", + tout << "Base: "; + for (unsigned i = 0; i < m_base.size(); ++i) { + tout << m_base[i] << " "; + } + tout << "\n";); + return + !m_base.empty() && + m_base.back().is_unsigned() && + m_base.back().get_unsigned() <= 20*m_base.size(); + } + + /** + \brief Check if 'out mod n >= lim'. + */ + expr_ref mod_ge(ptr_vector const& out, unsigned n, unsigned lim) { + TRACE("pb", for (unsigned i = 0; i < out.size(); ++i) tout << mk_pp(out[i], m) << " "; tout << "\n"; + tout << "n:" << n << " lim: " << lim << "\n";); + if (lim == n) { + return expr_ref(m.mk_false(), m); + } + if (lim == 0) { + return expr_ref(m.mk_true(), m); + } + SASSERT(0 < lim && lim < n); + expr_ref_vector ors(m); + for (unsigned j = 0; j + lim - 1 < out.size(); j += n) { + expr_ref tmp(m); + tmp = out[j + lim - 1]; + if (j + n - 1 < out.size()) { + tmp = m.mk_and(tmp, m.mk_not(out[j + n - 1])); + } + ors.push_back(tmp); + } + return ::mk_or(ors); + } + + // x0 + 5x1 + 3x2 >= k + // x0 x1 x1 -> s0 s1 s2 + // s2 x1 x2 -> s3 s4 s5 + // k = 7: s5 or (s4 & not s2 & s0) + // k = 6: s4 + // k = 5: s4 or (s3 & not s2 & s1) + // k = 4: s4 or (s3 & not s2 & s0) + // k = 3: s3 + // + bool mk_ge(unsigned sz, expr * const* args, rational bound, expr_ref& result) { + if (!create_basis()) return false; + if (!bound.is_unsigned()) return false; + vector coeffs(m_coeffs); + 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) { + unsigned B = b_i.get_unsigned(); + unsigned d_i = (bound % b_i).get_unsigned(); + bound = div(bound, b_i); + for (unsigned j = 0; j < coeffs.size(); ++j) { + rational c = coeffs[j] % b_i; + SASSERT(c.is_unsigned()); + for (unsigned k = 0; k < c.get_unsigned(); ++k) { + carry.push_back(args[j]); + } + coeffs[j] = div(coeffs[j], b_i); + } + TRACE("pb", tout << "Carry: " << carry << "\n"; + for (auto c : coeffs) tout << c << " "; + tout << "\n"; + ); + ptr_vector out; + m_sort.sorting(carry.size(), carry.c_ptr(), out); + + expr_ref gt = mod_ge(out, B, d_i + 1); + expr_ref ge = mod_ge(out, B, d_i); + result = mk_and(ge, result); + result = mk_or(gt, result); + TRACE("pb", tout << "b: " << b_i << " d: " << d_i << " gt: " << gt << " ge: " << ge << " " << result << "\n";); + + new_carry.reset(); + for (unsigned j = B - 1; j < out.size(); j += B) { + new_carry.push_back(out[j]); + } + carry.reset(); + carry.append(new_carry); + } + TRACE("pb", tout << "bound: " << bound << " Carry: " << carry << " result: " << result << "\n";); + return true; + } + + /** + \brief Segment based encoding. + The PB terms are partitoned into segments, such that each segment contains arguments with the same cofficient. + The segments are sorted, such that the segment with highest coefficient is first. + Then for each segment create circuits based on sorting networks the arguments of the segment. + */ + + expr_ref mk_seg_ge(rational const& k) { + rational bound(-k); + for (unsigned i = 0; i < m_args.size(); ++i) { + m_args[i] = mk_not(m_args[i].get()); + bound += m_coeffs[i]; + } + return mk_seg_le(bound); + } + + expr_ref mk_seg_le(rational const& k) { + sort_args(); + unsigned sz = m_args.size(); + expr* const* args = m_args.c_ptr(); + + // Create sorted entries. + vector> outs; + vector coeffs; + for (unsigned i = 0, seg_size = 0; i < sz; i += seg_size) { + seg_size = segment_size(i); + ptr_vector out; + m_sort.sorting(seg_size, args + i, out); + out.push_back(m.mk_false()); + outs.push_back(out); + coeffs.push_back(m_coeffs[i]); + } + return mk_seg_le_rec(outs, coeffs, 0, k); + } + + expr_ref mk_seg_le_rec(vector> const& outs, vector const& coeffs, unsigned i, rational const& k) { + rational const& c = coeffs[i]; + ptr_vector const& out = outs[i]; + if (k.is_neg()) { + return expr_ref(m.mk_false(), m); + } + if (i == outs.size()) { + return expr_ref(m.mk_true(), m); + } + if (i + 1 == outs.size() && k >= rational(out.size()-1)*c) { + return expr_ref(m.mk_true(), m); + } + expr_ref_vector fmls(m); + fmls.push_back(m.mk_implies(m.mk_not(out[0]), mk_seg_le_rec(outs, coeffs, i + 1, k))); + rational k1; + for (unsigned j = 0; j + 1 < out.size(); ++j) { + k1 = k - rational(j+1)*c; + if (k1.is_neg()) { + fmls.push_back(m.mk_not(out[j])); + break; + } + fmls.push_back(m.mk_implies(m.mk_and(out[j], m.mk_not(out[j+1])), mk_seg_le_rec(outs, coeffs, i + 1, k1))); + } + return ::mk_and(fmls); + } + + // The number of arguments with the same coefficient. + unsigned segment_size(unsigned start) const { + unsigned i = start; + while (i < m_args.size() && m_coeffs[i] == m_coeffs[start]) ++i; + return i - start; + } + + expr_ref mk_and(expr_ref& a, expr_ref& b) { + if (m.is_true(a)) return b; + if (m.is_true(b)) return a; + if (m.is_false(a)) return a; + if (m.is_false(b)) return b; + return expr_ref(m.mk_and(a, b), m); + } + + expr_ref mk_or(expr_ref& a, expr_ref& b) { + if (m.is_true(a)) return a; + if (m.is_true(b)) return b; + if (m.is_false(a)) return b; + if (m.is_false(b)) return a; + return expr_ref(m.mk_or(a, b), m); + } + + bool mk_le(unsigned sz, expr * const* args, rational const& k, expr_ref& result) { + expr_ref_vector args1(m); + rational bound(-k); + for (unsigned i = 0; i < sz; ++i) { + args1.push_back(mk_not(args[i])); + bound += m_coeffs[i]; + } + return mk_ge(sz, args1.c_ptr(), bound, result); + } + + bool mk_eq(unsigned sz, expr * const* args, rational const& k, expr_ref& result) { + expr_ref r1(m), r2(m); + if (mk_ge(sz, args, k, r1) && mk_le(sz, args, k, r2)) { + result = m.mk_and(r1, r2); + return true; + } + else { + return false; + } + } + expr_ref mk_bv(func_decl * f, unsigned sz, expr * const* args) { + ++m_imp.m_compile_bv; decl_kind kind = f->get_decl_kind(); rational k = pb.get_k(f); m_coeffs.reset(); + m_args.reset(); for (unsigned i = 0; i < sz; ++i) { m_coeffs.push_back(pb.get_coeff(f, i)); + m_args.push_back(args[i]); } + CTRACE("pb", k.is_neg(), tout << expr_ref(m.mk_app(f, sz, args), m) << "\n";); SASSERT(!k.is_neg()); switch (kind) { case OP_PB_GE: case OP_AT_LEAST_K: { - expr_ref_vector nargs(m); - nargs.append(sz, args); - dualize(f, nargs, k); + dualize(f, m_args, k); SASSERT(!k.is_neg()); - return mk_le_ge(sz, nargs.c_ptr(), k); + return mk_le_ge(k); } case OP_PB_LE: case OP_AT_MOST_K: - return mk_le_ge(sz, args, k); + return mk_le_ge(k); case OP_PB_EQ: - return mk_le_ge(sz, args, k); + return mk_le_ge(k); default: UNREACHABLE(); return expr_ref(m.mk_true(), m); @@ -230,7 +673,7 @@ struct pb2bv_rewriter::imp { public: - card2bv_rewriter(imp& i, ast_manager& m): + card2bv_rewriter(imp& i, ast_manager& m): m_sort(*this), m(m), m_imp(i), @@ -238,29 +681,34 @@ struct pb2bv_rewriter::imp { pb(m), bv(m), m_trail(m), - m_args(m) + m_args(m), + m_keep_cardinality_constraints(false), + m_pb_solver(symbol("solver")), + m_min_arity(9) {} + void set_pb_solver(symbol const& s) { m_pb_solver = s; } + bool mk_app(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { - if (f->get_family_id() == pb.get_family_id()) { - mk_pb(full, f, sz, args, result); + if (f->get_family_id() == pb.get_family_id() && mk_pb(full, f, sz, args, result)) { + // skip } else if (au.is_le(f) && is_pb(args[0], args[1])) { - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else if (au.is_lt(f) && is_pb(args[0], args[1])) { ++m_k; - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else if (au.is_ge(f) && is_pb(args[1], args[0])) { - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else if (au.is_gt(f) && is_pb(args[1], args[0])) { ++m_k; - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else if (m.is_eq(f) && is_pb(args[0], args[1])) { - result = mk_le_ge(m_args.size(), m_args.c_ptr(), m_k); + result = mk_le_ge(m_k); } else { return false; @@ -278,6 +726,11 @@ struct pb2bv_rewriter::imp { } } + bool mk_app(bool full, expr* e, expr_ref& r) { + app* a; + return (is_app(e) && (a = to_app(e), mk_app(full, a->get_decl(), a->get_num_args(), a->get_args(), r))); + } + bool is_pb(expr* x, expr* y) { m_args.reset(); m_coeffs.reset(); @@ -349,53 +802,95 @@ struct pb2bv_rewriter::imp { return false; } - void mk_pb(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { + bool mk_pb(bool full, func_decl * f, unsigned sz, expr * const* args, expr_ref & result) { SASSERT(f->get_family_id() == pb.get_family_id()); if (is_or(f)) { result = m.mk_or(sz, args); } else if (pb.is_at_most_k(f) && pb.get_k(f).is_unsigned()) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; } else if (pb.is_at_least_k(f) && pb.get_k(f).is_unsigned()) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; } else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.eq(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; } else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.le(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; } else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && pb.has_unit_coefficients(f)) { + if (m_keep_cardinality_constraints && f->get_arity() >= m_min_arity) return false; result = m_sort.ge(full, pb.get_k(f).get_unsigned(), sz, args); + ++m_imp.m_compile_card; + } + else if (pb.is_eq(f) && pb.get_k(f).is_unsigned() && has_small_coefficients(f) && m_pb_solver == "solver") { + return false; + } + else if (pb.is_le(f) && pb.get_k(f).is_unsigned() && has_small_coefficients(f) && m_pb_solver == "solver") { + return false; + } + else if (pb.is_ge(f) && pb.get_k(f).is_unsigned() && has_small_coefficients(f) && m_pb_solver == "solver") { + return false; } else { result = mk_bv(f, sz, args); } + TRACE("pb", tout << "full: " << full << " " << expr_ref(m.mk_app(f, sz, args), m) << " " << result << "\n"; + ); + return true; + } + + bool has_small_coefficients(func_decl* f) { + unsigned sz = f->get_arity(); + unsigned sum = 0; + for (unsigned i = 0; i < sz; ++i) { + rational c = pb.get_coeff(f, i); + if (!c.is_unsigned()) return false; + unsigned sum1 = sum + c.get_unsigned(); + if (sum1 < sum) return false; + sum = sum1; + } + return true; } // definitions used for sorting network - literal mk_false() { return m.mk_false(); } - literal mk_true() { return m.mk_true(); } - literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } - literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } - literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } + pliteral mk_false() { return m.mk_false(); } + pliteral mk_true() { return m.mk_true(); } + pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } + pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } + pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } - std::ostream& pp(std::ostream& out, literal lit) { return out << mk_ismt2_pp(lit, m); } + std::ostream& pp(std::ostream& out, pliteral lit) { return out << mk_ismt2_pp(lit, m); } - literal trail(literal l) { + pliteral trail(pliteral l) { m_trail.push_back(l); return l; } - literal fresh() { - expr_ref fr(m.mk_fresh_const("sn", m.mk_bool_sort()), m); + pliteral fresh(char const* n) { + expr_ref fr(m.mk_fresh_const(n, m.mk_bool_sort()), m); m_imp.m_fresh.push_back(to_app(fr)->get_decl()); return trail(fr); } - void mk_clause(unsigned n, literal const* lits) { - m_imp.m_lemmas.push_back(mk_or(m, n, lits)); + void mk_clause(unsigned n, pliteral const* lits) { + m_imp.m_lemmas.push_back(::mk_or(m, n, lits)); } + + void keep_cardinality_constraints(bool f) { + m_keep_cardinality_constraints = f; + } + + void set_at_most1(sorting_network_encoding enc) { m_sort.cfg().m_encoding = enc; } + }; struct card2bv_rewriter_cfg : public default_rewriter_cfg { @@ -403,10 +898,14 @@ struct pb2bv_rewriter::imp { bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return m_r.mk_app_core(f, num, args, result); } card2bv_rewriter_cfg(imp& i, ast_manager & m):m_r(i, m) {} + void keep_cardinality_constraints(bool f) { m_r.keep_cardinality_constraints(f); } + void set_pb_solver(symbol const& s) { m_r.set_pb_solver(s); } + void set_at_most1(sorting_network_encoding enc) { m_r.set_at_most1(enc); } + }; class card_pb_rewriter : public rewriter_tpl { @@ -415,22 +914,77 @@ struct pb2bv_rewriter::imp { card_pb_rewriter(imp& i, ast_manager & m): rewriter_tpl(m, false, m_cfg), m_cfg(i, m) {} + void keep_cardinality_constraints(bool f) { m_cfg.keep_cardinality_constraints(f); } + void set_pb_solver(symbol const& s) { m_cfg.set_pb_solver(s); } + void set_at_most1(sorting_network_encoding e) { m_cfg.set_at_most1(e); } + void rewrite(bool full, expr* e, expr_ref& r, proof_ref& p) { + expr_ref ee(e, m()); + if (m_cfg.m_r.mk_app(full, e, r)) { + ee = r; + // mp proof? + } + (*this)(ee, r, p); + } }; card_pb_rewriter m_rw; + bool keep_cardinality() const { + params_ref const& p = m_params; + return + p.get_bool("keep_cardinality_constraints", false) || + p.get_bool("sat.cardinality.solver", false) || + p.get_bool("cardinality.solver", false) || + gparams::get_module("sat").get_bool("cardinality.solver", false); + } + + symbol pb_solver() const { + params_ref const& p = m_params; + symbol s = p.get_sym("sat.pb.solver", symbol()); + if (s != symbol()) return s; + s = p.get_sym("pb.solver", symbol()); + if (s != symbol()) return s; + return gparams::get_module("sat").get_sym("pb.solver", symbol("solver")); + } + + sorting_network_encoding atmost1_encoding() const { + symbol enc = m_params.get_sym("atmost1_encoding", symbol()); + if (enc == symbol()) { + enc = gparams::get_module("sat").get_sym("atmost1_encoding", symbol()); + } + if (enc == symbol("grouped")) return sorting_network_encoding::grouped_at_most_1; + if (enc == symbol("bimander")) return sorting_network_encoding::bimander_at_most_1; + if (enc == symbol("ordered")) return sorting_network_encoding::ordered_at_most_1; + return grouped_at_most_1; + } + + imp(ast_manager& m, params_ref const& p): m(m), m_params(p), m_lemmas(m), m_fresh(m), m_num_translated(0), m_rw(*this, m) { + updt_params(p); + m_compile_bv = 0; + m_compile_card = 0; + } + + void updt_params(params_ref const & p) { + m_params.append(p); + m_rw.keep_cardinality_constraints(keep_cardinality()); + m_rw.set_pb_solver(pb_solver()); + m_rw.set_at_most1(atmost1_encoding()); + } + void collect_param_descrs(param_descrs& r) const { + r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints (don't bit-blast them) and use built-in cardinality solver"); + r.insert("pb.solver", CPK_SYMBOL, "(default: solver) retain pb constraints (don't bit-blast them) and use built-in pb solver"); } - void updt_params(params_ref const & p) {} unsigned get_num_steps() const { return m_rw.get_num_steps(); } void cleanup() { m_rw.cleanup(); } - void operator()(expr * e, expr_ref & result, proof_ref & result_proof) { - m_rw(e, result, result_proof); + void operator()(bool full, expr * e, expr_ref & result, proof_ref & result_proof) { + // m_rw(e, result, result_proof); + m_rw.rewrite(full, e, result, result_proof); } void push() { m_fresh_lim.push_back(m_fresh.size()); @@ -453,6 +1007,8 @@ struct pb2bv_rewriter::imp { } void collect_statistics(statistics & st) const { + st.update("pb-compile-bv", m_compile_bv); + st.update("pb-compile-card", m_compile_card); st.update("pb-aux-variables", m_fresh.size()); st.update("pb-aux-clauses", m_rw.m_cfg.m_r.m_sort.m_stats.m_num_compiled_clauses); } @@ -463,11 +1019,13 @@ struct pb2bv_rewriter::imp { pb2bv_rewriter::pb2bv_rewriter(ast_manager & m, params_ref const& p) { m_imp = alloc(imp, m, p); } pb2bv_rewriter::~pb2bv_rewriter() { dealloc(m_imp); } void pb2bv_rewriter::updt_params(params_ref const & p) { m_imp->updt_params(p); } +void pb2bv_rewriter::collect_param_descrs(param_descrs& r) const { m_imp->collect_param_descrs(r); } + ast_manager & pb2bv_rewriter::m() const { return m_imp->m; } unsigned pb2bv_rewriter::get_num_steps() const { return m_imp->get_num_steps(); } void pb2bv_rewriter::cleanup() { ast_manager& mgr = m(); params_ref p = m_imp->m_params; dealloc(m_imp); m_imp = alloc(imp, mgr, p); } func_decl_ref_vector const& pb2bv_rewriter::fresh_constants() const { return m_imp->m_fresh; } -void pb2bv_rewriter::operator()(expr * e, expr_ref & result, proof_ref & result_proof) { (*m_imp)(e, result, result_proof); } +void pb2bv_rewriter::operator()(bool full, expr * e, expr_ref & result, proof_ref & result_proof) { (*m_imp)(full, e, result, result_proof); } void pb2bv_rewriter::push() { m_imp->push(); } void pb2bv_rewriter::pop(unsigned num_scopes) { m_imp->pop(num_scopes); } void pb2bv_rewriter::flush_side_constraints(expr_ref_vector& side_constraints) { m_imp->flush_side_constraints(side_constraints); } diff --git a/src/ast/rewriter/pb2bv_rewriter.h b/src/ast/rewriter/pb2bv_rewriter.h index a4176922a..3460f08ab 100644 --- a/src/ast/rewriter/pb2bv_rewriter.h +++ b/src/ast/rewriter/pb2bv_rewriter.h @@ -31,11 +31,12 @@ public: ~pb2bv_rewriter(); void updt_params(params_ref const & p); + void collect_param_descrs(param_descrs& r) const; ast_manager & m() const; unsigned get_num_steps() const; void cleanup(); func_decl_ref_vector const& fresh_constants() const; - void operator()(expr * e, expr_ref & result, proof_ref & result_proof); + void operator()(bool full, expr * e, expr_ref & result, proof_ref & result_proof); void push(); void pop(unsigned num_scopes); void flush_side_constraints(expr_ref_vector& side_constraints); diff --git a/src/ast/rewriter/pb_rewriter.cpp b/src/ast/rewriter/pb_rewriter.cpp index 5660f9d65..cb42052b9 100644 --- a/src/ast/rewriter/pb_rewriter.cpp +++ b/src/ast/rewriter/pb_rewriter.cpp @@ -115,14 +115,15 @@ expr_ref pb_rewriter::translate_pb2lia(obj_map& vars, expr* fml) { else { tmp = a.mk_add(es.size(), es.c_ptr()); } + rational k = util.get_k(fml); if (util.is_le(fml)) { - result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false)); + result = a.mk_le(tmp, a.mk_numeral(k, false)); } else if (util.is_ge(fml)) { - result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false)); + result = a.mk_ge(tmp, a.mk_numeral(k, false)); } else { - result = m().mk_eq(tmp, a.mk_numeral(util.get_k(fml), false)); + result = m().mk_eq(tmp, a.mk_numeral(k, false)); } } else { @@ -233,6 +234,7 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons } bool is_eq = f->get_decl_kind() == OP_PB_EQ; + br_status st = BR_DONE; pb_ast_rewriter_util pbu(m); pb_rewriter_util util(pbu); @@ -250,29 +252,73 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons default: { bool all_unit = true; unsigned sz = vec.size(); + rational slack(0); m_args.reset(); m_coeffs.reset(); - for (unsigned i = 0; i < sz; ++i) { - m_args.push_back(vec[i].first); - m_coeffs.push_back(vec[i].second); + for (auto const& kv : vec) { + m_args.push_back(kv.first); + m_coeffs.push_back(kv.second); + SASSERT(kv.second.is_pos()); + slack += kv.second; all_unit &= m_coeffs.back().is_one(); } if (is_eq) { if (sz == 0) { result = k.is_zero()?m.mk_true():m.mk_false(); } + else if (k.is_zero()) { + result = mk_not(m, mk_or(m, sz, m_args.c_ptr())); + } + else if (k.is_one() && all_unit && m_args.size() == 1) { + result = m_args.back(); + } + else if (slack == k) { + result = mk_and(m, sz, m_args.c_ptr()); + } else { result = m_util.mk_eq(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); } } - else if (all_unit && k.is_one()) { + else if (all_unit && k.is_one() && sz < 10) { result = mk_or(m, sz, m_args.c_ptr()); } else if (all_unit && k == rational(sz)) { result = mk_and(m, sz, m_args.c_ptr()); } else { - result = m_util.mk_ge(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k); + expr_ref_vector conj(m), disj(m); + unsigned j = 0; + sz = m_args.size(); + for (unsigned i = 0; i < sz; ++i) { + rational& c = m_coeffs[i]; + if (slack < c + k) { + conj.push_back(m_args[i]); + slack -= c; + k -= c; + } + else if (c >= k && k.is_pos()) { + disj.push_back(m_args[i]); + } + else { + m_args[j] = m_args[i]; + m_coeffs[j] = m_coeffs[i]; + ++j; + } + } + m_args.shrink(j); + m_coeffs.shrink(j); + sz = j; + if (sz > 0) { + disj.push_back(m_util.mk_ge(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k)); + } + if (!disj.empty()) { + conj.push_back(mk_or(disj)); + } + result = mk_and(conj); + + if (disj.size() > 1 || conj.size() > 1) { + st = BR_REWRITE3; + } } break; } @@ -283,11 +329,11 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons tout << tmp << "\n"; tout << result << "\n"; ); - + TRACE("pb_validate", validate_rewrite(f, num_args, args, result);); - return BR_DONE; + return st; } diff --git a/src/ast/rewriter/pb_rewriter_def.h b/src/ast/rewriter/pb_rewriter_def.h index aa2c2a61f..e4a7e012d 100644 --- a/src/ast/rewriter/pb_rewriter_def.h +++ b/src/ast/rewriter/pb_rewriter_def.h @@ -45,25 +45,25 @@ void pb_rewriter_util::unique(typename PBU::args_t& args, typename PBU::num } } // remove constants - for (unsigned i = 0; i < args.size(); ++i) { + unsigned j = 0, sz = args.size(); + for (unsigned i = 0; i < sz; ++i) { if (m_util.is_true(args[i].first)) { k -= args[i].second; - std::swap(args[i], args[args.size()-1]); - args.pop_back(); - --i; } else if (m_util.is_false(args[i].first)) { - std::swap(args[i], args[args.size()-1]); - args.pop_back(); - --i; + // no-op + } + else { + args[j++] = args[i]; } } + args.shrink(j); // sort and coalesce arguments: typename PBU::compare cmp; std::sort(args.begin(), args.end(), cmp); // coallesce - unsigned i, j; + unsigned i; for (i = 0, j = 1; j < args.size(); ++j) { if (args[i].first == args[j].first) { args[i].second += args[j].second; diff --git a/src/ast/rewriter/poly_rewriter.h b/src/ast/rewriter/poly_rewriter.h index 23743399e..c4b120ae5 100644 --- a/src/ast/rewriter/poly_rewriter.h +++ b/src/ast/rewriter/poly_rewriter.h @@ -96,7 +96,7 @@ protected: public: poly_rewriter(ast_manager & m, params_ref const & p = params_ref()): Config(m), - m_curr_sort(0), + m_curr_sort(nullptr), m_sort_sums(false) { updt_params(p); SASSERT(!m_som || m_flat); // som of monomials form requires flattening to be enabled. diff --git a/src/ast/rewriter/poly_rewriter_def.h b/src/ast/rewriter/poly_rewriter_def.h index 71a9079a0..d65960857 100644 --- a/src/ast/rewriter/poly_rewriter_def.h +++ b/src/ast/rewriter/poly_rewriter_def.h @@ -206,7 +206,7 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con numeral c(1); unsigned num_coeffs = 0; unsigned num_add = 0; - expr * var = 0; + expr * var = nullptr; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; if (is_numeral(arg, a)) { @@ -290,13 +290,13 @@ br_status poly_rewriter::mk_nflat_mul_core(unsigned num_args, expr * con if (!m_som || num_add == 0) { ptr_buffer new_args; - expr * prev = 0; + expr * prev = nullptr; bool ordered = true; for (unsigned i = 0; i < num_args; i++) { expr * curr = args[i]; if (is_numeral(curr)) continue; - if (prev != 0 && lt(curr, prev)) + if (prev != nullptr && lt(curr, prev)) ordered = false; new_args.push_back(curr); prev = curr; @@ -431,7 +431,8 @@ struct poly_rewriter::hoist_cmul_lt { hoist_cmul_lt(poly_rewriter & r):m_r(r) {} bool operator()(expr * t1, expr * t2) const { - expr * pp1, * pp2; + expr * pp1 = nullptr; + expr * pp2 = nullptr; numeral c1, c2; bool is_mul1 = m_r.is_mul(t1, c1, pp1); bool is_mul2 = m_r.is_mul(t2, c2, pp2); @@ -532,7 +533,7 @@ br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * con expr_fast_mark1 visited; // visited.is_marked(power_product) if the power_product occurs in args expr_fast_mark2 multiple; // multiple.is_marked(power_product) if power_product occurs more than once bool has_multiple = false; - expr * prev = 0; + expr * prev = nullptr; bool ordered = true; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; @@ -543,7 +544,7 @@ br_status poly_rewriter::mk_nflat_add_core(unsigned num_args, expr * con ordered = !m_sort_sums || i == 0; } else if (m_sort_sums && ordered) { - if (prev != 0 && lt(arg, prev)) + if (prev != nullptr && lt(arg, prev)) ordered = false; prev = arg; } @@ -874,8 +875,8 @@ br_status poly_rewriter::cancel_monomials(expr * lhs, expr * rhs, bool m const bool insert_c_rhs = c_at_rhs && (new_rhs_monomials.size() == 1 || !c.is_zero()); const unsigned lhs_offset = insert_c_lhs ? 0 : 1; const unsigned rhs_offset = insert_c_rhs ? 0 : 1; - new_rhs_monomials[0] = insert_c_rhs ? mk_numeral(c) : NULL; - new_lhs_monomials[0] = insert_c_lhs ? mk_numeral(c) : NULL; + new_rhs_monomials[0] = insert_c_rhs ? mk_numeral(c) : nullptr; + new_lhs_monomials[0] = insert_c_lhs ? mk_numeral(c) : nullptr; lhs_result = mk_add_app(new_lhs_monomials.size() - lhs_offset, new_lhs_monomials.c_ptr() + lhs_offset); rhs_result = mk_add_app(new_rhs_monomials.size() - rhs_offset, new_rhs_monomials.c_ptr() + rhs_offset); TRACE("mk_le_bug", tout << lhs_result << " " << rhs_result << "\n";); @@ -994,7 +995,7 @@ bool poly_rewriter::is_var_plus_ground(expr * n, bool & inv, var * & v, return false; ptr_buffer args; - v = 0; + v = nullptr; expr * curr = to_app(n); bool stop = false; inv = false; @@ -1013,12 +1014,12 @@ bool poly_rewriter::is_var_plus_ground(expr * n, bool & inv, var * & v, args.push_back(arg); } else if (is_var(arg)) { - if (v != 0) + if (v != nullptr) return false; // already found variable v = to_var(arg); } else if (is_times_minus_one(arg, neg_arg) && is_var(neg_arg)) { - if (v != 0) + if (v != nullptr) return false; // already found variable v = to_var(neg_arg); inv = true; @@ -1027,7 +1028,7 @@ bool poly_rewriter::is_var_plus_ground(expr * n, bool & inv, var * & v, return false; // non ground term. } } - if (v == 0) + if (v == nullptr) return false; // did not find variable SASSERT(!args.empty()); mk_add(args.size(), args.c_ptr(), t); diff --git a/src/ast/rewriter/pull_ite_tree.cpp b/src/ast/rewriter/pull_ite_tree.cpp deleted file mode 100644 index 651744bf9..000000000 --- a/src/ast/rewriter/pull_ite_tree.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - pull_ite_tree.cpp - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2008-06-22. - -Revision History: - ---*/ -#include "ast/rewriter/pull_ite_tree.h" -#include "ast/recurse_expr_def.h" -#include "ast/for_each_expr.h" -#include "ast/ast_pp.h" - -pull_ite_tree::pull_ite_tree(ast_manager & m): - m_manager(m), - m_rewriter(m), - m_cache(m) { -} - -void pull_ite_tree::cache_result(expr * n, expr * r, proof * pr) { - m_cache.insert(n, r, pr); -} - -void pull_ite_tree::visit(expr * n, bool & visited) { - if (!is_cached(n)) { - m_todo.push_back(n); - visited = false; - } -} - -bool pull_ite_tree::visit_children(expr * n) { - if (m_manager.is_ite(n)) { - bool visited = true; - visit(to_app(n)->get_arg(1), visited); - visit(to_app(n)->get_arg(2), visited); - return visited; - } - else { - return true; - } -} - -void pull_ite_tree::reduce(expr * n) { - // Remark: invoking the simplifier to build the new expression saves a lot of memory. - if (m_manager.is_ite(n)) { - expr * c = to_app(n)->get_arg(0); - expr * t_old = to_app(n)->get_arg(1); - expr * e_old = to_app(n)->get_arg(2); - expr * t = 0; - proof * t_pr = 0; - expr * e = 0; - proof * e_pr = 0; - get_cached(t_old, t, t_pr); - get_cached(e_old, e, e_pr); - expr_ref r(m_manager); - expr * args[3] = {c, t, e}; - r = m_rewriter.mk_app(to_app(n)->get_decl(), 3, args); - if (!m_manager.proofs_enabled()) { - // expr * r = m_manager.mk_ite(c, t, e); - cache_result(n, r, 0); - } - else { - // t_pr is a proof for (m_p ... t_old ...) == t - // e_pr is a proof for (m_p ... e_old ...) == e - expr_ref old(m_manager); - expr_ref p_t_old(m_manager); - expr_ref p_e_old(m_manager); - old = mk_p_arg(n); // (m_p ... n ...) where n is (ite c t_old e_old) - p_t_old = mk_p_arg(t_old); // (m_p ... t_old ...) - p_e_old = mk_p_arg(e_old); // (m_p ... e_old ...) - expr_ref tmp1(m_manager); - tmp1 = m_manager.mk_ite(c, p_t_old, p_e_old); // (ite c (m_p ... t_old ...) (m_p ... e_old ...)) - proof * pr1 = m_manager.mk_rewrite(old, tmp1); // proof for (m_p ... (ite c t_old e_old) ...) = (ite c (m_p ... t_old ...) (m_p ... e_old ...)) - expr_ref tmp2(m_manager); - tmp2 = m_manager.mk_ite(c, t, e); // (ite c t e) - proof * pr2 = 0; // it will contain a proof for (ite c (m_p ... t_old ...) (m_p ... e_old ...)) = (ite c t e) - proof * pr3 = 0; // it will contain a proof for (m_p ... (ite c t_old e_old) ...) = (ite c t e) - proof * proofs[2]; - unsigned num_proofs = 0; - if (t_pr != 0) { - proofs[num_proofs] = t_pr; - num_proofs++; - } - if (e_pr != 0) { - proofs[num_proofs] = e_pr; - num_proofs++; - } - if (num_proofs > 0) { - pr2 = m_manager.mk_congruence(to_app(tmp1), to_app(tmp2), num_proofs, proofs); - pr3 = m_manager.mk_transitivity(pr1, pr2); - } - else { - pr3 = pr1; - } - proof * pr4 = 0; // it will contain a proof for (ite c t e) = r - proof * pr5 = 0; // it will contain a proof for (m_p ... (ite c t_old e_old) ...) = r - if (tmp2 != r) { - pr4 = m_manager.mk_rewrite(tmp2, r); - pr5 = m_manager.mk_transitivity(pr3, pr4); - } - else { - pr5 = pr3; - } - cache_result(n, r, pr5); - } - } - else { - expr_ref r(m_manager); - m_args[m_arg_idx] = n; - r = m_rewriter.mk_app(m_p, m_args.size(), m_args.c_ptr()); - if (!m_manager.proofs_enabled()) { - // expr * r = m_manager.mk_app(m_p, m_args.size(), m_args.c_ptr()); - cache_result(n, r, 0); - } - else { - expr_ref old(m_manager); - proof * p; - old = mk_p_arg(n); - if (old == r) - p = 0; - else - p = m_manager.mk_rewrite(old, r); - cache_result(n, r, p); - } - } -} - -void pull_ite_tree::operator()(app * n, app_ref & r, proof_ref & pr) { - unsigned num_args = n->get_num_args(); - m_args.resize(num_args); - m_p = n->get_decl(); - expr * ite = 0; - for (unsigned i = 0; i < num_args; i++) { - expr * arg = n->get_arg(i); - if (ite) { - m_args[i] = arg; - } - else if (m_manager.is_ite(arg)) { - m_arg_idx = i; - m_args[i] = 0; - ite = arg; - } - else { - m_args[i] = arg; - } - } - if (!ite) { - r = n; - pr = 0; - return; - } - m_todo.push_back(ite); - while (!m_todo.empty()) { - expr * n = m_todo.back(); - if (is_cached(n)) - m_todo.pop_back(); - else if (visit_children(n)) { - m_todo.pop_back(); - reduce(n); - } - } - SASSERT(is_cached(ite)); - expr * _r = 0; - proof * _pr = 0; - get_cached(ite, _r, _pr); - r = to_app(_r); - pr = _pr; - m_cache.reset(); - m_todo.reset(); -} - - - -pull_ite_tree_cfg::pull_ite_tree_cfg(ast_manager & m): - m(m), - m_trail(m), - m_proc(m) { -} - -bool pull_ite_tree_cfg::get_subst(expr * n, expr* & r, proof* & p) { - if (is_app(n) && is_target(to_app(n))) { - app_ref tmp(m); - proof_ref pr(m); - m_proc(to_app(n), tmp, pr); - if (tmp != n) { - r = tmp; - p = pr; - m_trail.push_back(r); - m_trail.push_back(p); - return true; - } - } - return false; -} - -bool pull_cheap_ite_tree_cfg::is_target(app * n) const { - bool r = - n->get_num_args() == 2 && - n->get_family_id() != null_family_id && - m.is_bool(n) && - (m.is_value(n->get_arg(0)) || m.is_value(n->get_arg(1))) && - (m.is_term_ite(n->get_arg(0)) || m.is_term_ite(n->get_arg(1))); - TRACE("pull_ite_target", tout << mk_pp(n, m) << "\nresult: " << r << "\n";); - return r; -} - - - - - - diff --git a/src/ast/rewriter/pull_ite_tree.h b/src/ast/rewriter/pull_ite_tree.h deleted file mode 100644 index 3ff0a716d..000000000 --- a/src/ast/rewriter/pull_ite_tree.h +++ /dev/null @@ -1,113 +0,0 @@ -/*++ -Copyright (c) 2006 Microsoft Corporation - -Module Name: - - pull_ite_tree.h - -Abstract: - - - -Author: - - Leonardo de Moura (leonardo) 2008-06-22. - -Revision History: - ---*/ -#ifndef PULL_ITE_TREE_H_ -#define PULL_ITE_TREE_H_ - -#include "ast/ast.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/th_rewriter.h" -#include "ast/expr_map.h" -#include "ast/recurse_expr.h" -#include "util/obj_hashtable.h" - -/** - \brief Functor for applying the following transformation - F[(p (ite c t1 t2) args)] = F'[(ite c t1 t2), p, args] - - F'[(ite c t1 t2), p, args] = (ite c F'[t1, p, args] F'[t2, p, args]) - F'[t, p, args] = (p t args) -*/ -class pull_ite_tree { - ast_manager & m_manager; - th_rewriter m_rewriter; - func_decl * m_p; - ptr_vector m_args; - unsigned m_arg_idx; //!< position of the ite argument - expr_map m_cache; - ptr_vector m_todo; - - bool is_cached(expr * n) const { return m_cache.contains(n); } - void get_cached(expr * n, expr * & r, proof * & p) const { m_cache.get(n, r, p); } - void cache_result(expr * n, expr * r, proof * pr); - void visit(expr * n, bool & visited); - bool visit_children(expr * n); - void reduce(expr * n); - /** - \brief Creante an application (m_p ... n ...) where n is the argument m_arg_idx and the other arguments - are in m_args. - */ - expr * mk_p_arg(expr * n) { - m_args[m_arg_idx] = n; - return m_manager.mk_app(m_p, m_args.size(), m_args.c_ptr()); - } -public: - pull_ite_tree(ast_manager & m); - /** - \brief Apply the transformation above if n contains an ite-expression. - Store the result in r. If n does not contain an ite-expression, then - store n in r. - - When proof generation is enabled, pr is a proof for n = r. - */ - void operator()(app * n, app_ref & r, proof_ref & pr); -}; - -/** - \brief Functor for applying the pull_ite_tree on subexpressions n that - satisfy the is_target virtual predicate. -*/ -class pull_ite_tree_cfg : public default_rewriter_cfg { -protected: - ast_manager& m; - expr_ref_vector m_trail; - pull_ite_tree m_proc; -public: - pull_ite_tree_cfg(ast_manager & m); - virtual ~pull_ite_tree_cfg() {} - virtual bool is_target(app * n) const = 0; - bool get_subst(expr * n, expr* & r, proof* & p); -}; - -/** - \brief Apply pull_ite_tree on predicates of the form - (p ite v) and (p v ite) - - where: - - p is an interpreted predicate - - ite is an ite-term expression - - v is a value -*/ -class pull_cheap_ite_tree_cfg : public pull_ite_tree_cfg { -public: - pull_cheap_ite_tree_cfg(ast_manager & m):pull_ite_tree_cfg(m) {} - virtual ~pull_cheap_ite_tree_cfg() {} - virtual bool is_target(app * n) const; -}; - -class pull_cheap_ite_tree_rw : public rewriter_tpl { - pull_cheap_ite_tree_cfg m_cfg; -public: - pull_cheap_ite_tree_rw(ast_manager& m): - rewriter_tpl(m, m.proofs_enabled(), m_cfg), - m_cfg(m) - {} -}; - -#endif /* PULL_ITE_TREE_H_ */ - diff --git a/src/ast/rewriter/push_app_ite.cpp b/src/ast/rewriter/push_app_ite.cpp index f3df4d711..411c61d8e 100644 --- a/src/ast/rewriter/push_app_ite.cpp +++ b/src/ast/rewriter/push_app_ite.cpp @@ -63,7 +63,7 @@ br_status push_app_ite_cfg::reduce_app(func_decl * f, unsigned num, expr * const return BR_FAILED; } app * ite = to_app(args[ite_arg_idx]); - expr * c = 0, * t = 0, * e = 0; + expr * c = nullptr, * t = nullptr, * e = nullptr; VERIFY(m.is_ite(ite, c, t, e)); expr ** args_prime = const_cast(args); expr * old = args_prime[ite_arg_idx]; diff --git a/src/ast/rewriter/push_app_ite.h b/src/ast/rewriter/push_app_ite.h index ae06aad30..8f737ea4d 100644 --- a/src/ast/rewriter/push_app_ite.h +++ b/src/ast/rewriter/push_app_ite.h @@ -45,7 +45,7 @@ struct push_app_ite_cfg : public default_rewriter_cfg { */ class ng_push_app_ite_cfg : public push_app_ite_cfg { protected: - virtual bool is_target(func_decl * decl, unsigned num_args, expr * const * args); + bool is_target(func_decl * decl, unsigned num_args, expr * const * args) override; public: ng_push_app_ite_cfg(ast_manager& m, bool conservative = true): push_app_ite_cfg(m, conservative) {} virtual ~ng_push_app_ite_cfg() {} diff --git a/src/ast/rewriter/quant_hoist.cpp b/src/ast/rewriter/quant_hoist.cpp index 3592f84cd..2f1116299 100644 --- a/src/ast/rewriter/quant_hoist.cpp +++ b/src/ast/rewriter/quant_hoist.cpp @@ -259,7 +259,7 @@ private: result = m.mk_ite(t1, tt2, tt3); } } - else if ((m.is_eq(fml, t1, t2) && m.is_bool(t1)) || m.is_iff(fml, t1, t2)) { + else if (m.is_eq(fml, t1, t2) && m.is_bool(t1)) { expr_ref tt1(m), tt2(m), ntt1(m), ntt2(m), nt1(m), nt2(m); pull_quantifier(t1, qt, vars, tt1, use_fresh, rewrite_ok); pull_quantifier(t2, qt, vars, tt2, use_fresh, rewrite_ok); diff --git a/src/ast/rewriter/rewriter.cpp b/src/ast/rewriter/rewriter.cpp index 36ad2dec4..4356f8f45 100644 --- a/src/ast/rewriter/rewriter.cpp +++ b/src/ast/rewriter/rewriter.cpp @@ -34,11 +34,11 @@ void rewriter_core::init_cache_stack() { void rewriter_core::del_cache_stack() { std::for_each(m_cache_stack.begin(), m_cache_stack.end(), delete_proc()); m_cache_stack.finalize(); - m_cache = 0; + m_cache = nullptr; if (m_proof_gen) { std::for_each(m_cache_pr_stack.begin(), m_cache_pr_stack.end(), delete_proc()); m_cache_pr_stack.finalize(); - m_cache_pr = 0; + m_cache_pr = nullptr; } } @@ -161,7 +161,7 @@ void rewriter_core::elim_reflex_prs(unsigned spos) { unsigned j = spos; for (unsigned i = spos; i < sz; i++) { proof * pr = m_result_pr_stack.get(i); - if (pr != 0) { + if (pr != nullptr) { if (i != j) m_result_pr_stack.set(j, pr); j++; @@ -192,7 +192,7 @@ void rewriter_core::reset() { m_result_stack.reset(); if (m_proof_gen) m_result_pr_stack.reset(); - m_root = 0; + m_root = nullptr; m_num_qvars = 0; m_scopes.reset(); } @@ -201,7 +201,7 @@ void rewriter_core::reset() { void rewriter_core::cleanup() { free_memory(); init_cache_stack(); - m_root = 0; + m_root = nullptr; m_num_qvars = 0; } diff --git a/src/ast/rewriter/rewriter.h b/src/ast/rewriter/rewriter.h index 17742f670..c761b5ca3 100644 --- a/src/ast/rewriter/rewriter.h +++ b/src/ast/rewriter/rewriter.h @@ -150,7 +150,7 @@ class var_shifter : public var_shifter_core { unsigned m_bound; unsigned m_shift1; unsigned m_shift2; - virtual void process_var(var * v); + void process_var(var * v) override; public: var_shifter(ast_manager & m):var_shifter_core(m) {} void operator()(expr * t, unsigned bound, unsigned shift1, unsigned shift2, expr_ref & r); @@ -183,7 +183,7 @@ public: class inv_var_shifter : public var_shifter_core { protected: unsigned m_shift; - virtual void process_var(var * v); + void process_var(var * v) override; public: inv_var_shifter(ast_manager & m):var_shifter_core(m) {} void operator()(expr * t, unsigned shift, expr_ref & r); @@ -339,13 +339,15 @@ public: Config & cfg() { return m_cfg; } Config const & cfg() const { return m_cfg; } - ~rewriter_tpl(); + ~rewriter_tpl() override; void reset(); void cleanup(); void set_bindings(unsigned num_bindings, expr * const * bindings); void set_inv_bindings(unsigned num_bindings, expr * const * bindings); + void update_binding_at(unsigned i, expr* binding); + void update_inv_binding_at(unsigned i, expr* binding); void operator()(expr * t, expr_ref & result, proof_ref & result_pr); void operator()(expr * t, expr_ref & result) { operator()(t, result, m_pr); } void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) { diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h index 2abd6d467..e8a14b953 100644 --- a/src/ast/rewriter/rewriter_def.h +++ b/src/ast/rewriter/rewriter_def.h @@ -28,11 +28,11 @@ void rewriter_tpl::process_var(var * v) { SASSERT(v->get_sort() == m().get_sort(m_r)); if (ProofGen) { result_pr_stack().push_back(m_pr); - m_pr = 0; + m_pr = nullptr; } set_new_child_flag(v); TRACE("rewriter", tout << mk_ismt2_pp(v, m()) << " -> " << m_r << "\n";); - m_r = 0; + m_r = nullptr; return; } if (!ProofGen) { @@ -41,7 +41,7 @@ void rewriter_tpl::process_var(var * v) { if (idx < m_bindings.size()) { unsigned index = m_bindings.size() - idx - 1; var * r = (var*)(m_bindings[index]); - if (r != 0) { + if (r != nullptr) { CTRACE("rewriter", v->get_sort() != m().get_sort(r), tout << expr_ref(v, m()) << ":" << sort_ref(v->get_sort(), m()) << " != " << expr_ref(r, m()) << ":" << sort_ref(m().get_sort(r), m()); tout << "index " << index << " bindings " << m_bindings.size() << "\n"; @@ -67,14 +67,14 @@ void rewriter_tpl::process_var(var * v) { } result_stack().push_back(v); if (ProofGen) - result_pr_stack().push_back(0); // implicit reflexivity + result_pr_stack().push_back(nullptr); // implicit reflexivity } template template void rewriter_tpl::process_const(app * t) { SASSERT(t->get_num_args() == 0); - br_status st = m_cfg.reduce_app(t->get_decl(), 0, 0, m_r, m_pr); + br_status st = m_cfg.reduce_app(t->get_decl(), 0, nullptr, m_r, m_pr); SASSERT(st != BR_DONE || m().get_sort(m_r) == m().get_sort(t)); SASSERT(st == BR_FAILED || st == BR_DONE); if (st == BR_DONE) { @@ -84,15 +84,15 @@ void rewriter_tpl::process_const(app * t) { result_pr_stack().push_back(m_pr); else result_pr_stack().push_back(m().mk_rewrite(t, m_r)); - m_pr = 0; + m_pr = nullptr; } - m_r = 0; + m_r = nullptr; set_new_child_flag(t); } else { result_stack().push_back(t); if (ProofGen) - result_pr_stack().push_back(0); // implicit reflexivity + result_pr_stack().push_back(nullptr); // implicit reflexivity } } @@ -108,8 +108,8 @@ template template bool rewriter_tpl::visit(expr * t, unsigned max_depth) { TRACE("rewriter_visit", tout << "visiting\n" << mk_ismt2_pp(t, m()) << "\n";); - expr * new_t = 0; - proof * new_t_pr = 0; + expr * new_t = nullptr; + proof * new_t_pr = nullptr; if (m_cfg.get_subst(t, new_t, new_t_pr)) { TRACE("rewriter_subst", tout << "subst\n" << mk_ismt2_pp(t, m()) << "\n---->\n" << mk_ismt2_pp(new_t, m()) << "\n";); SASSERT(m().get_sort(t) == m().get_sort(new_t)); @@ -122,7 +122,7 @@ bool rewriter_tpl::visit(expr * t, unsigned max_depth) { if (max_depth == 0) { result_stack().push_back(t); if (ProofGen) - result_pr_stack().push_back(0); // implicit reflexivity + result_pr_stack().push_back(nullptr); // implicit reflexivity return true; // t is not going to be processed } SASSERT(max_depth > 0); @@ -150,7 +150,7 @@ bool rewriter_tpl::visit(expr * t, unsigned max_depth) { if (!pre_visit(t)) { result_stack().push_back(t); if (ProofGen) - result_pr_stack().push_back(0); // implicit reflexivity + result_pr_stack().push_back(nullptr); // implicit reflexivity return true; // t is not going to be processed } switch (t->get_kind()) { @@ -183,7 +183,7 @@ template bool rewriter_tpl::constant_fold(app * t, frame & fr) { if (fr.m_i == 1 && m().is_ite(t)) { expr * cond = result_stack()[fr.m_spos].get(); - expr* arg = 0; + expr* arg = nullptr; if (m().is_true(cond)) { arg = t->get_arg(1); } @@ -194,16 +194,18 @@ bool rewriter_tpl::constant_fold(app * t, frame & fr) { result_stack().shrink(fr.m_spos); result_stack().push_back(arg); fr.m_state = REWRITE_BUILTIN; + TRACE("rewriter_step", tout << "step\n" << mk_ismt2_pp(t, m()) << "\n";); if (visit(arg, fr.m_max_depth)) { m_r = result_stack().back(); result_stack().pop_back(); result_stack().pop_back(); result_stack().push_back(m_r); cache_result(t, m_r, m_pr, fr.m_cache_result); + TRACE("rewriter_step", tout << "step 1\n" << mk_ismt2_pp(m_r, m()) << "\n";); frame_stack().pop_back(); set_new_child_flag(t); } - m_r = 0; + m_r = nullptr; return true; } } @@ -255,7 +257,7 @@ void rewriter_tpl::process_app(app * t, frame & fr) { unsigned num_prs = result_pr_stack().size() - fr.m_spos; if (num_prs == 0) { new_t = t; - m_pr = 0; + m_pr = nullptr; } else { new_t = m().mk_app(f, new_num_args, new_args); @@ -278,16 +280,16 @@ void rewriter_tpl::process_app(app * t, frame & fr) { if (!m_pr2) m_pr2 = m().mk_rewrite(new_t, m_r); m_pr = m().mk_transitivity(m_pr, m_pr2); - m_pr2 = 0; + m_pr2 = nullptr; result_pr_stack().push_back(m_pr); } if (st == BR_DONE) { cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); - m_r = 0; + m_r = nullptr; if (ProofGen) - m_pr = 0; + m_pr = nullptr; return; } else { @@ -318,25 +320,25 @@ void rewriter_tpl::process_app(app * t, frame & fr) { cache_result(t, m_r, m_pr, fr.m_cache_result); frame_stack().pop_back(); set_new_child_flag(t); - m_r = 0; + m_r = nullptr; if (ProofGen) - m_pr = 0; + m_pr = nullptr; return; } else { // frame was created for processing m_r - m_r = 0; + m_r = nullptr; if (ProofGen) - m_pr = 0; + m_pr = nullptr; return; } } UNREACHABLE(); } // TODO: add rewrite rules support - expr * def; - proof * def_pr; - quantifier * def_q; + expr * def = nullptr; + proof * def_pr = nullptr; + quantifier * def_q = nullptr; // When get_macro succeeds, then // we know that: // forall X. f(X) = def[X] @@ -358,7 +360,7 @@ void rewriter_tpl::process_app(app * t, frame & fr) { if (ProofGen) { NOT_IMPLEMENTED_YET(); // We do not support the use of bindings in proof generation mode. - // Thus we have to apply the subsitution here, and + // Thus we have to apply the substitution here, and // beta_reducer subst(m()); // subst.set_bindings(new_num_args, new_args); // expr_ref r2(m()); @@ -407,11 +409,11 @@ void rewriter_tpl::process_app(app * t, frame & fr) { if (ProofGen) { result_pr_stack().shrink(fr.m_spos); result_pr_stack().push_back(m_pr); - m_pr = 0; + m_pr = nullptr; } frame_stack().pop_back(); set_new_child_flag(t, m_r); - m_r = 0; + m_r = nullptr; return; } case REWRITE_BUILTIN: @@ -497,23 +499,27 @@ void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) { SASSERT(fr.m_spos + num_children == result_stack().size()); expr * const * it = result_stack().c_ptr() + fr.m_spos; expr * new_body = *it; - expr * const * new_pats; - expr * const * new_no_pats; + unsigned num_pats = q->get_num_patterns(); + unsigned num_no_pats = q->get_num_no_patterns(); + expr_ref_vector new_pats(m_manager, num_pats, q->get_patterns()); + expr_ref_vector new_no_pats(m_manager, num_no_pats, q->get_no_patterns()); if (rewrite_patterns()) { TRACE("reduce_quantifier_bug", tout << "rewrite patterns\n";); - new_pats = it + 1; - new_no_pats = new_pats + q->get_num_patterns(); - } - else { - new_pats = q->get_patterns(); - new_no_pats = q->get_no_patterns(); + expr * const * np = it + 1; + expr * const * nnp = np + num_pats; + for (unsigned i = 0; i < num_pats; i++) + if (m_manager.is_pattern(np[i])) + new_pats[i] = np[i]; + for (unsigned i = 0; i < num_no_pats; i++) + if (m_manager.is_pattern(nnp[i])) + new_no_pats[i] = nnp[i]; } if (ProofGen) { - quantifier_ref new_q(m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body), m()); - m_pr = q == new_q ? 0 : m().mk_quant_intro(q, new_q, result_pr_stack().get(fr.m_spos)); + quantifier_ref new_q(m().update_quantifier(q, num_pats, new_pats.c_ptr(), num_no_pats, new_no_pats.c_ptr(), new_body), m()); + m_pr = q == new_q ? nullptr : m().mk_quant_intro(q, new_q, result_pr_stack().get(fr.m_spos)); m_r = new_q; proof_ref pr2(m()); - if (m_cfg.reduce_quantifier(new_q, new_body, new_pats, new_no_pats, m_r, pr2)) { + if (m_cfg.reduce_quantifier(new_q, new_body, new_pats.c_ptr(), new_no_pats.c_ptr(), m_r, pr2)) { m_pr = m().mk_transitivity(m_pr, pr2); } TRACE("reduce_quantifier_bug", tout << "m_pr is_null: " << (m_pr.get() == 0) << "\n"; @@ -524,9 +530,9 @@ void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) { else { expr_ref tmp(m()); TRACE("reduce_quantifier_bug", tout << mk_ismt2_pp(q, m()) << " " << mk_ismt2_pp(new_body, m()) << "\n";); - if (!m_cfg.reduce_quantifier(q, new_body, new_pats, new_no_pats, m_r, m_pr)) { + if (!m_cfg.reduce_quantifier(q, new_body, new_pats.c_ptr(), new_no_pats.c_ptr(), m_r, m_pr)) { if (fr.m_new_child) { - m_r = m().update_quantifier(q, q->get_num_patterns(), new_pats, q->get_num_no_patterns(), new_no_pats, new_body); + m_r = m().update_quantifier(q, num_pats, new_pats.c_ptr(), num_no_pats, new_no_pats.c_ptr(), new_body); } else { TRACE("rewriter_reuse", tout << "reusing:\n" << mk_ismt2_pp(q, m()) << "\n";); @@ -546,9 +552,9 @@ void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) { } else { cache_result(q, m_r, m_pr, fr.m_cache_result); - m_pr = 0; + m_pr = nullptr; } - m_r = 0; + m_r = nullptr; frame_stack().pop_back(); set_new_child_flag(q, m_r); } @@ -633,6 +639,17 @@ void rewriter_tpl::set_inv_bindings(unsigned num_bindings, expr * const TRACE("rewriter", display_bindings(tout);); } +template +void rewriter_tpl::update_inv_binding_at(unsigned i, expr* binding) { + m_bindings[i] = binding; +} + +template +void rewriter_tpl::update_binding_at(unsigned i, expr* binding) { + m_bindings[m_bindings.size() - i - 1] = binding; +} + + template template void rewriter_tpl::main_loop(expr * t, expr_ref & result, proof_ref & result_pr) { @@ -652,7 +669,7 @@ void rewriter_tpl::main_loop(expr * t, expr_ref & result, proof_ref & re if (ProofGen) { result_pr = result_pr_stack().back(); result_pr_stack().pop_back(); - if (result_pr.get() == 0) + if (result_pr.get() == nullptr) result_pr = m().mk_reflexivity(t); SASSERT(result_pr_stack().empty()); } @@ -717,7 +734,7 @@ void rewriter_tpl::resume_core(expr_ref & result, proof_ref & result_pr) if (ProofGen) { result_pr = result_pr_stack().back(); result_pr_stack().pop_back(); - if (result_pr.get() == 0) + if (result_pr.get() == nullptr) result_pr = m().mk_reflexivity(m_root); SASSERT(result_pr_stack().empty()); } diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 2f9f3f9ce..111e834fa 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -85,15 +85,15 @@ public: sym_expr_boolean_algebra(ast_manager& m, expr_solver& s): m(m), m_solver(s) {} - virtual T mk_false() { + T mk_false() override { expr_ref fml(m.mk_false(), m); return sym_expr::mk_pred(fml, m.mk_bool_sort()); // use of Bool sort for bound variable is arbitrary } - virtual T mk_true() { + T mk_true() override { expr_ref fml(m.mk_true(), m); return sym_expr::mk_pred(fml, m.mk_bool_sort()); } - virtual T mk_and(T x, T y) { + T mk_and(T x, T y) override { if (x->is_char() && y->is_char()) { if (x->get_char() == y->get_char()) { return x; @@ -118,7 +118,7 @@ public: br.mk_and(fml1, fml2, fml); return sym_expr::mk_pred(fml, x->get_sort()); } - virtual T mk_or(T x, T y) { + T mk_or(T x, T y) override { if (x->is_char() && y->is_char() && x->get_char() == y->get_char()) { return x; @@ -135,7 +135,7 @@ public: return sym_expr::mk_pred(fml, x->get_sort()); } - virtual T mk_and(unsigned sz, T const* ts) { + T mk_and(unsigned sz, T const* ts) override { switch (sz) { case 0: return mk_true(); case 1: return ts[0]; @@ -148,7 +148,7 @@ public: } } } - virtual T mk_or(unsigned sz, T const* ts) { + T mk_or(unsigned sz, T const* ts) override { switch (sz) { case 0: return mk_false(); case 1: return ts[0]; @@ -161,7 +161,7 @@ public: } } } - virtual lbool is_sat(T x) { + lbool is_sat(T x) override { if (x->is_char()) { return l_true; } @@ -178,7 +178,7 @@ public: } return m_solver.check_sat(fml); } - virtual T mk_not(T x) { + T mk_not(T x) override { var_ref v(m.mk_var(0, x->get_sort()), m); expr_ref fml(m.mk_not(x->accept(v)), m); return sym_expr::mk_pred(fml, x->get_sort()); @@ -190,7 +190,7 @@ public: }*/ }; -re2automaton::re2automaton(ast_manager& m): m(m), u(m), bv(m), m_ba(0), m_sa(0) {} +re2automaton::re2automaton(ast_manager& m): m(m), u(m), bv(m), m_ba(nullptr), m_sa(nullptr) {} re2automaton::~re2automaton() {} @@ -200,6 +200,9 @@ void re2automaton::set_solver(expr_solver* solver) { m_sa = alloc(symbolic_automata_t, sm, *m_ba.get()); } +eautomaton* re2automaton::mk_product(eautomaton* a1, eautomaton* a2) { + return m_sa->mk_product(*a1, *a2); +} eautomaton* re2automaton::operator()(expr* e) { eautomaton* r = re2aut(e); @@ -286,19 +289,28 @@ eautomaton* re2automaton::re2aut(expr* e) { else if (u.re.is_empty(e)) { return alloc(eautomaton, sm); } - else if (u.re.is_full(e)) { + else if (u.re.is_full_seq(e)) { expr_ref tt(m.mk_true(), m); - sort *seq_s = 0, *char_s = 0; + sort *seq_s = nullptr, *char_s = nullptr; VERIFY (u.is_re(m.get_sort(e), seq_s)); VERIFY (u.is_seq(seq_s, char_s)); sym_expr* _true = sym_expr::mk_pred(tt, char_s); return eautomaton::mk_loop(sm, _true); } + else if (u.re.is_full_char(e)) { + expr_ref tt(m.mk_true(), m); + sort *seq_s = nullptr, *char_s = nullptr; + VERIFY (u.is_re(m.get_sort(e), seq_s)); + VERIFY (u.is_seq(seq_s, char_s)); + sym_expr* _true = sym_expr::mk_pred(tt, char_s); + a = alloc(eautomaton, sm, _true); + return a.detach(); + } else if (u.re.is_intersection(e, e1, e2) && m_sa && (a = re2aut(e1)) && (b = re2aut(e2))) { return m_sa->mk_product(*a, *b); } - return 0; + return nullptr; } eautomaton* re2automaton::seq2aut(expr* e) { @@ -326,7 +338,7 @@ eautomaton* re2automaton::seq2aut(expr* e) { } return alloc(eautomaton, sm, init, final, mvs); } - return 0; + return nullptr; } br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) { @@ -355,6 +367,9 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con SASSERT(num_args == 2); return mk_re_concat(args[0], args[1], result); case OP_RE_UNION: + if (num_args == 1) { + result = args[0]; return BR_DONE; + } SASSERT(num_args == 2); return mk_re_union(args[0], args[1], result); case OP_RE_RANGE: @@ -370,7 +385,9 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con return mk_re_loop(num_args, args, result); case OP_RE_EMPTY_SET: return BR_FAILED; - case OP_RE_FULL_SET: + case OP_RE_FULL_SEQ_SET: + return BR_FAILED; + case OP_RE_FULL_CHAR_SET: return BR_FAILED; case OP_RE_OF_PRED: return BR_FAILED; @@ -839,7 +856,7 @@ br_status seq_rewriter::mk_seq_replace(expr* a, expr* b, expr* c, expr_ref& resu return BR_DONE; } if (m_util.str.is_string(b, s2) && s2.length() == 0) { - result = m_util.str.mk_concat(a, c); + result = m_util.str.mk_concat(c, a); return BR_REWRITE1; } if (m_util.str.is_string(a, s1) && s1.length() == 0) { @@ -982,14 +999,14 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { bool isc1 = false; bool isc2 = false; - expr *a1 = 0, *a2 = 0, *b1 = 0, *b2 = 0; + expr *a1 = nullptr, *a2 = nullptr, *b1 = nullptr, *b2 = nullptr; if (m_util.str.is_concat(a, a1, a2) && m_util.str.is_string(a2, s1)) { isc1 = true; } else if (m_util.str.is_string(a, s1)) { isc1 = true; a2 = a; - a1 = 0; + a1 = nullptr; } if (m_util.str.is_concat(b, b1, b2) && m_util.str.is_string(b2, s2)) { @@ -998,7 +1015,7 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { else if (m_util.str.is_string(b, s2)) { isc2 = true; b2 = b; - b1 = 0; + b1 = nullptr; } if (isc1 && isc2) { if (s1.length() == s2.length()) { @@ -1008,7 +1025,7 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { } else if (s1.length() < s2.length()) { bool suffix = s1.suffixof(s2); - if (suffix && a1 == 0) { + if (suffix && a1 == nullptr) { result = m().mk_true(); return BR_DONE; } @@ -1025,7 +1042,7 @@ br_status seq_rewriter::mk_seq_suffix(expr* a, expr* b, expr_ref& result) { } else { SASSERT(s1.length() > s2.length()); - if (b1 == 0) { + if (b1 == nullptr) { result = m().mk_false(); return BR_DONE; } @@ -1189,8 +1206,7 @@ bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) { e = todo.back(); todo.pop_back(); if (m_util.str.is_string(e, s)) { - for (unsigned i = s.length(); i > 0; ) { - --i; + for (unsigned i = 0; i < s.length(); ++i) { seq.push_back(m_util.str.mk_char(s, i)); } } @@ -1201,14 +1217,13 @@ bool seq_rewriter::is_sequence(expr* e, expr_ref_vector& seq) { seq.push_back(e1); } else if (m_util.str.is_concat(e, e1, e2)) { - todo.push_back(e1); todo.push_back(e2); + todo.push_back(e1); } else { return false; } } - seq.reverse(); return true; } @@ -1217,7 +1232,7 @@ br_status seq_rewriter::mk_str_in_regexp(expr* a, expr* b, expr_ref& result) { result = m().mk_false(); return BR_DONE; } - if (m_util.re.is_full(b)) { + if (m_util.re.is_full_seq(b)) { result = m().mk_true(); return BR_DONE; } @@ -1312,7 +1327,7 @@ br_status seq_rewriter::mk_str_to_regexp(expr* a, expr_ref& result) { return BR_FAILED; } br_status seq_rewriter::mk_re_concat(expr* a, expr* b, expr_ref& result) { - if (m_util.re.is_full(a) && m_util.re.is_full(b)) { + if (m_util.re.is_full_seq(a) && m_util.re.is_full_seq(b)) { result = a; return BR_DONE; } @@ -1352,11 +1367,11 @@ br_status seq_rewriter::mk_re_union(expr* a, expr* b, expr_ref& result) { result = a; return BR_DONE; } - if (m_util.re.is_full(a)) { + if (m_util.re.is_full_seq(a)) { result = a; return BR_DONE; } - if (m_util.re.is_full(b)) { + if (m_util.re.is_full_seq(b)) { result = b; return BR_DONE; } @@ -1382,10 +1397,10 @@ br_status seq_rewriter::mk_re_complement(expr* a, expr_ref& result) { return BR_REWRITE2; } if (m_util.re.is_empty(a)) { - result = m_util.re.mk_full(m().get_sort(a)); + result = m_util.re.mk_full_seq(m().get_sort(a)); return BR_DONE; } - if (m_util.re.is_full(a)) { + if (m_util.re.is_full_seq(a)) { result = m_util.re.mk_empty(m().get_sort(a)); return BR_DONE; } @@ -1412,11 +1427,11 @@ br_status seq_rewriter::mk_re_inter(expr* a, expr* b, expr_ref& result) { result = b; return BR_DONE; } - if (m_util.re.is_full(a)) { + if (m_util.re.is_full_seq(a)) { result = b; return BR_DONE; } - if (m_util.re.is_full(b)) { + if (m_util.re.is_full_seq(b)) { result = a; return BR_DONE; } @@ -1459,12 +1474,16 @@ br_status seq_rewriter::mk_re_loop(unsigned num_args, expr* const* args, expr_re */ br_status seq_rewriter::mk_re_star(expr* a, expr_ref& result) { expr* b, *c, *b1, *c1; - if (m_util.re.is_star(a) || m_util.re.is_full(a)) { + if (m_util.re.is_star(a) || m_util.re.is_full_seq(a)) { result = a; return BR_DONE; } + if (m_util.re.is_full_char(a)) { + result = m_util.re.mk_full_seq(m().get_sort(a)); + return BR_DONE; + } if (m_util.re.is_empty(a)) { - sort* seq_sort = 0; + sort* seq_sort = nullptr; VERIFY(m_util.is_re(a, seq_sort)); result = m_util.re.mk_to_re(m_util.str.mk_empty(seq_sort)); return BR_DONE; @@ -1519,7 +1538,7 @@ br_status seq_rewriter::mk_re_plus(expr* a, expr_ref& result) { result = a; return BR_DONE; } - if (m_util.re.is_full(a)) { + if (m_util.re.is_full_seq(a)) { result = a; return BR_DONE; } @@ -1542,7 +1561,7 @@ br_status seq_rewriter::mk_re_plus(expr* a, expr_ref& result) { } br_status seq_rewriter::mk_re_opt(expr* a, expr_ref& result) { - sort* s = 0; + sort* s = nullptr; VERIFY(m_util.is_re(a, s)); result = m_util.re.mk_union(m_util.re.mk_to_re(m_util.str.mk_empty(s)), a); return BR_REWRITE1; @@ -1866,7 +1885,7 @@ expr* seq_rewriter::concat_non_empty(unsigned n, expr* const* as) { bool seq_rewriter::set_empty(unsigned sz, expr* const* es, bool all, expr_ref_vector& lhs, expr_ref_vector& rhs) { zstring s; - expr* emp = 0; + expr* emp = nullptr; for (unsigned i = 0; i < sz; ++i) { if (m_util.str.is_unit(es[i])) { if (all) return false; diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 69f319168..c96096c65 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -53,7 +53,9 @@ public: bool is_range() const { return m_ty == t_range; } sort* get_sort() const { return m_sort; } expr* get_char() const { SASSERT(is_char()); return m_t; } - + expr* get_pred() const { SASSERT(is_pred()); return m_t; } + expr* get_lo() const { SASSERT(is_range()); return m_t; } + expr* get_hi() const { SASSERT(is_range()); return m_s; } }; class sym_expr_manager { @@ -87,6 +89,8 @@ public: ~re2automaton(); eautomaton* operator()(expr* e); void set_solver(expr_solver* solver); + bool has_solver() const { return m_solver; } + eautomaton* mk_product(eautomaton *a1, eautomaton *a2); }; /** diff --git a/src/ast/rewriter/th_rewriter.cpp b/src/ast/rewriter/th_rewriter.cpp index a2ca12b24..912df0fc9 100644 --- a/src/ast/rewriter/th_rewriter.cpp +++ b/src/ast/rewriter/th_rewriter.cpp @@ -187,7 +187,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { if (st != BR_FAILED) return st; } - if (k == OP_EQ || k == OP_IFF) { + if (k == OP_EQ) { SASSERT(num == 2); st = apply_tamagotchi(args[0], args[1], result); if (st != BR_FAILED) @@ -440,8 +440,8 @@ struct th_rewriter_cfg : public default_rewriter_cfg { } if (num1 != num2 && num1 != num2 + 1 && num1 != num2 - 1) return false; - new_t1 = 0; - new_t2 = 0; + new_t1 = nullptr; + new_t2 = nullptr; expr_fast_mark1 visited1; expr_fast_mark2 visited2; for (unsigned i = 0; i < num1; i++) { @@ -533,7 +533,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr * c = args[0]; expr * t = args[1]; expr * e = args[2]; - func_decl * f_prime = 0; + func_decl * f_prime = nullptr; expr_ref new_t(m()), new_e(m()), common(m()); bool first; TRACE("push_ite", tout << "unifying:\n" << mk_ismt2_pp(t, m()) << "\n" << mk_ismt2_pp(e, m()) << "\n";); @@ -559,7 +559,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; br_status st = reduce_app_core(f, num, args, result); if (st != BR_DONE && st != BR_FAILED) { CTRACE("th_rewriter_step", st != BR_FAILED, @@ -604,7 +604,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { expr_ref & result, proof_ref & result_pr) { quantifier_ref q1(m()); - proof * p1 = 0; + proof * p1 = nullptr; if (is_quantifier(new_body) && to_quantifier(new_body)->is_forall() == old_q->is_forall() && !old_q->has_patterns() && @@ -627,7 +627,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { std::min(old_q->get_weight(), nested_q->get_weight()), old_q->get_qid(), old_q->get_skid(), - 0, 0, 0, 0); + 0, nullptr, 0, nullptr); SASSERT(is_well_sorted(m(), q1)); @@ -657,9 +657,9 @@ struct th_rewriter_cfg : public default_rewriter_cfg { TRACE("reduce_quantifier", tout << "after elim_unused_vars:\n" << mk_ismt2_pp(result, m()) << "\n";); - result_pr = 0; + result_pr = nullptr; if (m().proofs_enabled()) { - proof * p2 = 0; + proof * p2 = nullptr; if (q1.get() != result.get()) p2 = m().mk_elim_unused_vars(q1, result); result_pr = m().mk_transitivity(p1, p2); @@ -680,7 +680,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg { m_a_util(m), m_bv_util(m), m_used_dependencies(m), - m_subst(0) { + m_subst(nullptr) { updt_local_params(p); } @@ -690,13 +690,13 @@ struct th_rewriter_cfg : public default_rewriter_cfg { } void reset() { - m_subst = 0; + m_subst = nullptr; } bool get_subst(expr * s, expr * & t, proof * & pr) { - if (m_subst == 0) + if (m_subst == nullptr) return false; - expr_dependency * d = 0; + expr_dependency * d = nullptr; if (m_subst->find(s, t, pr, d)) { m_used_dependencies = m().mk_join(m_used_dependencies, d); return true; @@ -798,9 +798,9 @@ expr_dependency * th_rewriter::get_used_dependencies() { } void th_rewriter::reset_used_dependencies() { - if (get_used_dependencies() != 0) { + if (get_used_dependencies() != nullptr) { set_substitution(m_imp->cfg().m_subst); // reset cache preserving subst - m_imp->cfg().m_used_dependencies = 0; + m_imp->cfg().m_used_dependencies = nullptr; } } diff --git a/src/ast/rewriter/var_subst.cpp b/src/ast/rewriter/var_subst.cpp index 756e62a5f..7877cf1d2 100644 --- a/src/ast/rewriter/var_subst.cpp +++ b/src/ast/rewriter/var_subst.cpp @@ -24,6 +24,10 @@ Notes: #include "ast/for_each_expr.h" void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result) { + if (is_ground(n)) { + result = n; + return; + } SASSERT(is_well_sorted(result.m(), n)); m_reducer.reset(); if (m_std_order) @@ -94,7 +98,7 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { } else { num_removed++; - var_mapping.push_back(0); + var_mapping.push_back(nullptr); } } // (VAR 0) is in the first position of var_mapping. @@ -104,7 +108,7 @@ void unused_vars_eliminator::operator()(quantifier* q, expr_ref & result) { if (s) var_mapping.push_back(m.mk_var(i - num_removed, s)); else - var_mapping.push_back(0); + var_mapping.push_back(nullptr); } diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 7cc0ccb12..cd8571a95 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -177,7 +177,7 @@ zstring zstring::replace(zstring const& src, zstring const& dst) const { return zstring(*this); } if (src.length() == 0) { - return zstring(*this); + return dst + zstring(*this); } bool found = false; for (unsigned i = 0; i < length(); ++i) { @@ -213,6 +213,9 @@ std::string zstring::encode() const { else if (ch == '\\') { strm << "\\\\"; } + else if (ch >= 128) { + strm << "\\x" << std::hex << (unsigned)ch << std::dec; + } else { strm << (char)(ch); } @@ -253,6 +256,7 @@ bool zstring::contains(zstring const& other) const { int zstring::indexof(zstring const& other, int offset) const { SASSERT(offset >= 0); + if (static_cast(offset) <= length() && other.length() == 0) return offset; if (static_cast(offset) == length()) return -1; if (other.length() + offset > length()) return -1; unsigned last = length() - other.length(); @@ -339,9 +343,9 @@ bool operator<(const zstring& lhs, const zstring& rhs) { seq_decl_plugin::seq_decl_plugin(): m_init(false), m_stringc_sym("String"), m_charc_sym("Char"), - m_string(0), - m_char(0), - m_re(0) {} + m_string(nullptr), + m_char(nullptr), + m_re(nullptr) {} void seq_decl_plugin::finalize() { for (unsigned i = 0; i < m_sigs.size(); ++i) @@ -524,7 +528,7 @@ void seq_decl_plugin::init() { m_sigs.resize(LAST_SEQ_OP); // TBD: have (par ..) construct and load parameterized signature from premable. m_sigs[OP_SEQ_UNIT] = alloc(psig, m, "seq.unit", 1, 1, &A, seqA); - m_sigs[OP_SEQ_EMPTY] = alloc(psig, m, "seq.empty", 1, 0, 0, seqA); + m_sigs[OP_SEQ_EMPTY] = alloc(psig, m, "seq.empty", 1, 0, nullptr, seqA); m_sigs[OP_SEQ_CONCAT] = alloc(psig, m, "seq.++", 1, 2, seqAseqA, seqA); m_sigs[OP_SEQ_PREFIX] = alloc(psig, m, "seq.prefixof", 1, 2, seqAseqA, boolT); m_sigs[OP_SEQ_SUFFIX] = alloc(psig, m, "seq.suffixof", 1, 2, seqAseqA, boolT); @@ -543,8 +547,9 @@ void seq_decl_plugin::init() { m_sigs[OP_RE_INTERSECT] = alloc(psig, m, "re.inter", 1, 2, reAreA, reA); m_sigs[OP_RE_LOOP] = alloc(psig, m, "re.loop", 1, 1, &reA, reA); m_sigs[OP_RE_COMPLEMENT] = alloc(psig, m, "re.complement", 1, 1, &reA, reA); - m_sigs[OP_RE_EMPTY_SET] = alloc(psig, m, "re.empty", 1, 0, 0, reA); - m_sigs[OP_RE_FULL_SET] = alloc(psig, m, "re.all", 1, 0, 0, reA); + m_sigs[OP_RE_EMPTY_SET] = alloc(psig, m, "re.empty", 1, 0, nullptr, reA); + m_sigs[OP_RE_FULL_SEQ_SET] = alloc(psig, m, "re.all", 1, 0, nullptr, reA); + m_sigs[OP_RE_FULL_CHAR_SET] = alloc(psig, m, "re.allchar", 1, 0, nullptr, reA); m_sigs[OP_RE_OF_PRED] = alloc(psig, m, "re.of.pred", 1, 1, &predA, reA); m_sigs[OP_SEQ_TO_RE] = alloc(psig, m, "seq.to.re", 1, 1, &seqA, reA); m_sigs[OP_SEQ_IN_RE] = alloc(psig, m, "seq.in.re", 1, 2, seqAreA, boolT); @@ -561,8 +566,8 @@ void seq_decl_plugin::init() { m_sigs[_OP_STRING_SUFFIX] = alloc(psig, m, "str.suffixof", 0, 2, str2T, boolT); m_sigs[_OP_STRING_IN_REGEXP] = alloc(psig, m, "str.in.re", 0, 2, strTreT, boolT); m_sigs[_OP_STRING_TO_REGEXP] = alloc(psig, m, "str.to.re", 0, 1, &strT, reT); - m_sigs[_OP_REGEXP_EMPTY] = alloc(psig, m, "re.nostr", 0, 0, 0, reT); - m_sigs[_OP_REGEXP_FULL] = alloc(psig, m, "re.allchar", 0, 0, 0, reT); + m_sigs[_OP_REGEXP_EMPTY] = alloc(psig, m, "re.nostr", 0, 0, nullptr, reT); + m_sigs[_OP_REGEXP_FULL_CHAR] = alloc(psig, m, "re.allchar", 0, 0, nullptr, reT); m_sigs[_OP_STRING_SUBSTR] = alloc(psig, m, "str.substr", 0, 3, strTint2T, strT); m_sigs[_OP_RE_UNROLL] = alloc(psig, m, "_re.unroll", 0, 2, reTintT, strT); } @@ -608,7 +613,7 @@ sort * seq_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter return m_string; default: UNREACHABLE(); - return 0; + return nullptr; } } @@ -649,7 +654,7 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, match(*m_sigs[k], arity, domain, range, rng); if (rng == m_string) { parameter param(symbol("")); - return mk_func_decl(OP_STRING_CONST, 1, ¶m, 0, 0, m_string); + return mk_func_decl(OP_STRING_CONST, 1, ¶m, 0, nullptr, m_string); } else { parameter param(rng.get()); @@ -669,25 +674,25 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, rng, func_decl_info(m_family_id, k)); - case _OP_REGEXP_FULL: - if (!range) { - range = m_re; - } + case _OP_REGEXP_FULL_CHAR: + if (!range) range = m_re; match(*m_sigs[k], arity, domain, range, rng); - return m.mk_func_decl(symbol("re.allchar"), arity, domain, rng, func_decl_info(m_family_id, OP_RE_FULL_SET)); - case OP_RE_FULL_SET: + return m.mk_func_decl(symbol("re.allchar"), arity, domain, rng, func_decl_info(m_family_id, OP_RE_FULL_CHAR_SET)); + + case OP_RE_FULL_CHAR_SET: if (!range) range = m_re; if (range == m_re) { match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(symbol("re.allchar"), arity, domain, rng, func_decl_info(m_family_id, k)); } return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, range, func_decl_info(m_family_id, k)); - + + case OP_RE_FULL_SEQ_SET: + if (!range) range = m_re; + return m.mk_func_decl(m_sigs[k]->m_name, arity, domain, range, func_decl_info(m_family_id, k)); case _OP_REGEXP_EMPTY: - if (!range) { - range = m_re; - } + if (!range) range = m_re; match(*m_sigs[k], arity, domain, range, rng); return m.mk_func_decl(symbol("re.nostr"), arity, domain, rng, func_decl_info(m_family_id, OP_RE_EMPTY_SET)); @@ -814,7 +819,7 @@ func_decl * seq_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, } default: UNREACHABLE(); - return 0; + return nullptr; } } @@ -916,7 +921,7 @@ expr* seq_decl_plugin::get_some_value(sort* s) { return util.re.mk_to_re(util.str.mk_empty(seq)); } UNREACHABLE(); - return 0; + return nullptr; } app* seq_util::mk_skolem(symbol const& name, unsigned n, expr* const* args, sort* range) { @@ -972,12 +977,16 @@ app* seq_util::re::mk_loop(expr* r, unsigned lo, unsigned hi) { return m.mk_app(m_fid, OP_RE_LOOP, 2, params, 1, &r); } -app* seq_util::re::mk_full(sort* s) { - return m.mk_app(m_fid, OP_RE_FULL_SET, 0, 0, 0, 0, s); +app* seq_util::re::mk_full_char(sort* s) { + return m.mk_app(m_fid, OP_RE_FULL_CHAR_SET, 0, nullptr, 0, nullptr, s); +} + +app* seq_util::re::mk_full_seq(sort* s) { + return m.mk_app(m_fid, OP_RE_FULL_SEQ_SET, 0, nullptr, 0, nullptr, s); } app* seq_util::re::mk_empty(sort* s) { - return m.mk_app(m_fid, OP_RE_EMPTY_SET, 0, 0, 0, 0, s); + return m.mk_app(m_fid, OP_RE_EMPTY_SET, 0, nullptr, 0, nullptr, s); } diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 17fa50423..8bd4d2807 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -56,7 +56,8 @@ enum seq_op_kind { OP_RE_LOOP, OP_RE_COMPLEMENT, OP_RE_EMPTY_SET, - OP_RE_FULL_SET, + OP_RE_FULL_SEQ_SET, + OP_RE_FULL_CHAR_SET, OP_RE_OF_PRED, @@ -77,7 +78,7 @@ enum seq_op_kind { _OP_STRING_SUBSTR, _OP_STRING_STRIDOF, _OP_REGEXP_EMPTY, - _OP_REGEXP_FULL, + _OP_REGEXP_FULL_CHAR, _OP_SEQ_SKOLEM, _OP_RE_UNROLL, LAST_SEQ_OP @@ -161,34 +162,34 @@ class seq_decl_plugin : public decl_plugin { void init(); - virtual void set_manager(ast_manager * m, family_id id); + void set_manager(ast_manager * m, family_id id) override; public: seq_decl_plugin(); - virtual ~seq_decl_plugin() {} - virtual void finalize(); + ~seq_decl_plugin() override {} + void finalize() override; - virtual decl_plugin * mk_fresh() { return alloc(seq_decl_plugin); } + decl_plugin * mk_fresh() override { return alloc(seq_decl_plugin); } - virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters); + sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) override; - virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, - unsigned arity, sort * const * domain, sort * range); + func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, + unsigned arity, sort * const * domain, sort * range) override; - virtual void get_op_names(svector & op_names, symbol const & logic); + void get_op_names(svector & op_names, symbol const & logic) override; - virtual void get_sort_names(svector & sort_names, symbol const & logic); + void get_sort_names(svector & sort_names, symbol const & logic) override; - virtual bool is_value(app * e) const; + bool is_value(app * e) const override; - virtual bool is_unique_value(app * e) const { return false; } + bool is_unique_value(app * e) const override { return false; } - virtual bool are_equal(app* a, app* b) const; + bool are_equal(app* a, app* b) const override; - virtual bool are_distinct(app* a, app* b) const; + bool are_distinct(app* a, app* b) const override; - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; bool is_char(ast* a) const { return a == m_char; } @@ -232,8 +233,8 @@ public: str(seq_util& u): u(u), m(u.m), m_fid(u.m_fid) {} sort* mk_seq(sort* s) { parameter param(s); return m.mk_sort(m_fid, SEQ_SORT, 1, ¶m); } - sort* mk_string_sort() const { return m.mk_sort(m_fid, _STRING_SORT, 0, 0); } - app* mk_empty(sort* s) const { return m.mk_const(m.mk_func_decl(m_fid, OP_SEQ_EMPTY, 0, 0, 0, (expr*const*)0, s)); } + sort* mk_string_sort() const { return m.mk_sort(m_fid, _STRING_SORT, 0, nullptr); } + app* mk_empty(sort* s) const { return m.mk_const(m.mk_func_decl(m_fid, OP_SEQ_EMPTY, 0, nullptr, 0, (expr*const*)nullptr, s)); } app* mk_string(zstring const& s); app* mk_string(symbol const& s) { return u.seq.mk_string(s); } app* mk_char(char ch); @@ -327,7 +328,8 @@ public: app* mk_opt(expr* r) { return m.mk_app(m_fid, OP_RE_OPTION, r); } app* mk_loop(expr* r, unsigned lo); app* mk_loop(expr* r, unsigned lo, unsigned hi); - app* mk_full(sort* s); + app* mk_full_char(sort* s); + app* mk_full_seq(sort* s); app* mk_empty(sort* s); bool is_to_re(expr const* n) const { return is_app_of(n, m_fid, OP_SEQ_TO_RE); } @@ -341,7 +343,8 @@ public: bool is_range(expr const* n) const { return is_app_of(n, m_fid, OP_RE_RANGE); } bool is_loop(expr const* n) const { return is_app_of(n, m_fid, OP_RE_LOOP); } bool is_empty(expr const* n) const { return is_app_of(n, m_fid, OP_RE_EMPTY_SET); } - bool is_full(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_SET); } + bool is_full_char(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_CHAR_SET); } + bool is_full_seq(expr const* n) const { return is_app_of(n, m_fid, OP_RE_FULL_SEQ_SET); } MATCH_UNARY(is_to_re); MATCH_BINARY(is_concat); MATCH_BINARY(is_union); diff --git a/src/ast/static_features.cpp b/src/ast/static_features.cpp index 9f52ea9a5..e3530b4b5 100644 --- a/src/ast/static_features.cpp +++ b/src/ast/static_features.cpp @@ -81,6 +81,7 @@ void static_features::reset() { m_has_str = false; m_has_seq_non_str = false; m_has_arrays = false; + m_has_ext_arrays = false; m_arith_k_sum .reset(); m_num_arith_terms = 0; m_num_arith_eqs = 0; @@ -153,8 +154,10 @@ bool static_features::is_diff_atom(expr const * e) const { bool static_features::is_gate(expr const * e) const { if (is_basic_expr(e)) { switch (to_app(e)->get_decl_kind()) { - case OP_ITE: case OP_AND: case OP_OR: case OP_IFF: case OP_XOR: case OP_IMPLIES: + case OP_ITE: case OP_AND: case OP_OR: case OP_XOR: case OP_IMPLIES: return true; + case OP_EQ: + return m_manager.is_bool(e); } } return false; @@ -206,7 +209,7 @@ void static_features::update_core(expr * e) { case OP_OR: m_num_ors++; break; - case OP_IFF: + case OP_EQ: m_num_iffs++; break; } @@ -271,8 +274,11 @@ void static_features::update_core(expr * e) { m_has_bv = true; if (!m_has_fpa && (m_fpautil.is_float(e) || m_fpautil.is_rm(e))) m_has_fpa = true; - if (!m_has_arrays && m_arrayutil.is_array(e)) + if (!m_has_arrays && m_arrayutil.is_array(e)) m_has_arrays = true; + if (!m_has_ext_arrays && m_arrayutil.is_array(e) && + !m_arrayutil.is_select(e) && !m_arrayutil.is_store(e)) + m_has_ext_arrays = true; if (!m_has_str && m_sequtil.str.is_string_term(e)) m_has_str = true; if (!m_has_seq_non_str && m_sequtil.str.is_non_string_sequence(e)) { @@ -414,7 +420,7 @@ void static_features::process(expr * e, bool form_ctx, bool or_and_ctx, bool ite form_ctx_new = true; or_and_ctx_new = true; break; - case OP_IFF: + case OP_EQ: form_ctx_new = true; break; } diff --git a/src/ast/static_features.h b/src/ast/static_features.h index 5473ba0ff..197947026 100644 --- a/src/ast/static_features.h +++ b/src/ast/static_features.h @@ -82,6 +82,7 @@ struct static_features { bool m_has_str; // has String-typed terms bool m_has_seq_non_str; // has non-String-typed Sequence terms bool m_has_arrays; // + bool m_has_ext_arrays; // does this use extended array theory. rational m_arith_k_sum; // sum of the numerals in arith atoms. unsigned m_num_arith_terms; unsigned m_num_arith_eqs; // equalities of the form t = k where k is a numeral diff --git a/src/ast/substitution/expr_offset.h b/src/ast/substitution/expr_offset.h index 1f27f222a..ee098053f 100644 --- a/src/ast/substitution/expr_offset.h +++ b/src/ast/substitution/expr_offset.h @@ -30,7 +30,7 @@ class expr_offset { expr * m_expr; unsigned m_offset; public: - expr_offset():m_expr(0), m_offset(0) {} + expr_offset():m_expr(nullptr), m_offset(0) {} expr_offset(expr * e, unsigned o):m_expr(e), m_offset(o) {} expr * get_expr() const { return m_expr; } diff --git a/src/ast/substitution/substitution.cpp b/src/ast/substitution/substitution.cpp index d54a8e057..39e9e07a2 100644 --- a/src/ast/substitution/substitution.cpp +++ b/src/ast/substitution/substitution.cpp @@ -79,13 +79,13 @@ void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, e // It is incorrect to cache results between different calls if we are applying a substitution // modulo a substitution s -> t. - if (m_state == INSERT || s != expr_offset(0,0)) + if (m_state == INSERT || s != expr_offset(nullptr,0)) reset_cache(); m_state = APPLY; unsigned j; - expr * e = 0; + expr * e = nullptr; unsigned off; expr_offset n1; bool visited; @@ -146,7 +146,7 @@ void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, e bool has_new_args = false; for (unsigned i = 0; i < num_args; i++) { expr * arg = to_app(e)->get_arg(i); - expr * new_arg = 0; + expr * new_arg = nullptr; VERIFY(m_apply_cache.find(expr_offset(arg, off), new_arg)); new_args.push_back(new_arg); @@ -185,10 +185,10 @@ void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, e } expr_offset body(q->get_expr(), off); expr_ref s1_ref(m_manager), t1_ref(m_manager); - if (s.get_expr() != 0) { + if (s.get_expr() != nullptr) { var_sh(s.get_expr(), num_vars, s1_ref); } - if (t.get_expr() != 0) { + if (t.get_expr() != nullptr) { var_sh(t.get_expr(), num_vars, t1_ref); } expr_offset s1(s1_ref, s.get_offset()); @@ -218,7 +218,7 @@ void substitution::apply(unsigned num_actual_offsets, unsigned const * deltas, e m_new_exprs.push_back(e); result = e; - if (s != expr_offset(0,0)) + if (s != expr_offset(nullptr,0)) reset_cache(); TRACE("subst_bug", tout << "END substitution::apply\nresult:\n" << mk_pp(e, m_manager) << "\nref_count: " << e->get_ref_count() << "\n";); diff --git a/src/ast/substitution/substitution.h b/src/ast/substitution/substitution.h index 0d318e4fb..5f1288fd7 100644 --- a/src/ast/substitution/substitution.h +++ b/src/ast/substitution/substitution.h @@ -178,7 +178,7 @@ public: to the variable x+delta[i]. */ void apply(unsigned num_actual_offsets, unsigned const * deltas, expr_offset const & n, expr_ref & result) { - apply(num_actual_offsets, deltas, n, expr_offset(0, 0), expr_offset(0, 0), result); + apply(num_actual_offsets, deltas, n, expr_offset(nullptr, 0), expr_offset(nullptr, 0), result); } /** diff --git a/src/ast/substitution/substitution_tree.cpp b/src/ast/substitution/substitution_tree.cpp index 20d5f1590..809197c8f 100644 --- a/src/ast/substitution/substitution_tree.cpp +++ b/src/ast/substitution/substitution_tree.cpp @@ -172,7 +172,7 @@ substitution_tree::node * substitution_tree::find_best_child(node * r) { #ifdef Z3DEBUG unsigned todo_size = m_todo.size(); #endif - node * best_child = 0; + node * best_child = nullptr; unsigned max_measure = 0; node * curr_child = r->m_first_child; while (curr_child) { @@ -335,7 +335,7 @@ void substitution_tree::insert(app * new_expr) { else { mark_used_regs(r->m_subst); node * best_child = find_best_child(r); - if (best_child == 0) { + if (best_child == nullptr) { // there is no compatible child node * n = mk_node_for(new_expr); n->m_next_sibling = r->m_first_child; @@ -414,7 +414,7 @@ bool substitution_tree::is_fully_compatible(svector const & sv) { */ bool substitution_tree::find_fully_compatible_child(node * r, node * & prev, node * & child) { SASSERT(!r->m_leaf); - prev = 0; + prev = nullptr; child = r->m_first_child; while (child) { if (is_fully_compatible(child->m_subst)) @@ -462,8 +462,8 @@ void substitution_tree::erase(app * e) { m_todo.push_back(0); node * r = m_roots[id]; - node * parent = 0; - node * prev = 0; + node * parent = nullptr; + node * prev = nullptr; while (true) { svector & sv = r->m_subst; @@ -495,12 +495,12 @@ void substitution_tree::erase(app * e) { if (m_todo.empty()) { reset_registers(0); SASSERT(r->m_expr == e); - if (parent == 0) { + if (parent == nullptr) { delete_node(r); m_roots[id] = 0; } else if (at_least_3_children(parent)) { - if (prev == 0) + if (prev == nullptr) parent->m_first_child = r->m_next_sibling; else prev->m_next_sibling = r->m_next_sibling; @@ -843,7 +843,7 @@ void substitution_tree::visit(expr * e, st_visitor & st, unsigned in_offset, uns ptr_vector::iterator end = m_roots.end(); for (; it != end; ++it) { node * r = *it; - if (r != 0) { + if (r != nullptr) { var * v = r->m_subst[0].first; if (v->get_sort() == to_var(e)->get_sort()) if (!visit(e, st, r)) @@ -878,7 +878,7 @@ void substitution_tree::display(std::ostream & out) const { ptr_vector::const_iterator end2 = m_vars.end(); for (; it2 != end2; ++it2) { var_ref_vector * v = *it2; - if (v == 0) + if (v == nullptr) continue; // m_vars may contain null pointers. See substitution_tree::insert. unsigned num = v->size(); for (unsigned i = 0; i < num; i++) { diff --git a/src/ast/substitution/substitution_tree.h b/src/ast/substitution/substitution_tree.h index 7a8c30a63..018bee471 100644 --- a/src/ast/substitution/substitution_tree.h +++ b/src/ast/substitution/substitution_tree.h @@ -50,7 +50,7 @@ class substitution_tree { node * m_first_child; expr * m_expr; }; - node(bool leaf):m_leaf(leaf), m_next_sibling(0), m_first_child(0) {} + node(bool leaf):m_leaf(leaf), m_next_sibling(nullptr), m_first_child(nullptr) {} }; ast_manager & m_manager; diff --git a/src/cmd_context/CMakeLists.txt b/src/cmd_context/CMakeLists.txt index f7b888343..8da871f9a 100644 --- a/src/cmd_context/CMakeLists.txt +++ b/src/cmd_context/CMakeLists.txt @@ -8,14 +8,12 @@ z3_add_component(cmd_context context_params.cpp echo_tactic.cpp eval_cmd.cpp - interpolant_cmds.cpp parametric_cmd.cpp pdecl.cpp simplify_cmd.cpp tactic_cmds.cpp tactic_manager.cpp COMPONENT_DEPENDENCIES - interp rewriter solver EXTRA_REGISTER_MODULE_HEADERS diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 65c8860b1..846fb1ded 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -49,14 +49,14 @@ class help_cmd : public cmd { public: help_cmd():cmd("help") {} - virtual char const * get_usage() const { return "*"; } - virtual char const * get_descr(cmd_context & ctx) const { return "print this help."; } - virtual unsigned get_arity() const { return VAR_ARITY; } - virtual void prepare(cmd_context & ctx) { m_cmds.reset(); } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_SYMBOL; } - virtual void set_next_arg(cmd_context & ctx, symbol const & s) { + char const * get_usage() const override { return "*"; } + char const * get_descr(cmd_context & ctx) const override { return "print this help."; } + unsigned get_arity() const override { return VAR_ARITY; } + void prepare(cmd_context & ctx) override { m_cmds.reset(); } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_SYMBOL; } + void set_next_arg(cmd_context & ctx, symbol const & s) override { cmd * c = ctx.find_cmd(s); - if (c == 0) { + if (c == nullptr) { std::string err_msg("unknown command '"); err_msg = err_msg + s.bare_str() + "'"; throw cmd_exception(err_msg); @@ -69,7 +69,7 @@ public: bool operator()(named_cmd const & c1, named_cmd const & c2) const { return c1.first.str() < c2.first.str(); } }; - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { ctx.regular_stream() << "\""; if (m_cmds.empty()) { vector cmds; @@ -101,36 +101,35 @@ class get_model_cmd : public cmd { unsigned m_index; public: get_model_cmd(): cmd("get-model"), m_index(0) {} - virtual char const * get_usage() const { return "[]"; } - virtual char const * get_descr(cmd_context & ctx) const { + char const * get_usage() const override { return "[]"; } + char const * get_descr(cmd_context & ctx) const override { return "retrieve model for the last check-sat command.\nSupply optional index if retrieving a model corresponding to a box optimization objective"; } - virtual unsigned get_arity() const { return VAR_ARITY; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_UINT; } - virtual void set_next_arg(cmd_context & ctx, unsigned index) { m_index = index; } - virtual void execute(cmd_context & ctx) { - if (!ctx.is_model_available() || ctx.get_check_sat_result() == 0) - throw cmd_exception("model is not available"); + + unsigned get_arity() const override { return VAR_ARITY; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_UINT; } + void set_next_arg(cmd_context & ctx, unsigned index) override { m_index = index; } + void execute(cmd_context & ctx) override { model_ref m; + if (ctx.ignore_check()) + return; + if (!ctx.is_model_available(m) || !ctx.get_check_sat_result()) + throw cmd_exception("model is not available"); if (m_index > 0 && ctx.get_opt()) { ctx.get_opt()->get_box_model(m, m_index); } - else { - ctx.get_check_sat_result()->get_model(m); - } ctx.display_model(m); } - virtual void reset(cmd_context& ctx) { + void reset(cmd_context& ctx) override { m_index = 0; } }; ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", { - if (!ctx.is_model_available() || ctx.get_check_sat_result() == 0) - throw cmd_exception("model is not available"); model_ref m; - ctx.get_check_sat_result()->get_model(m); + if (!ctx.is_model_available(m) || ctx.get_check_sat_result() == 0) + throw cmd_exception("model is not available"); ctx.regular_stream() << "("; dictionary const & macros = ctx.get_macros(); bool first = true; @@ -139,8 +138,8 @@ ATOMIC_CMD(get_assignment_cmd, "get-assignment", "retrieve assignment", { macro_decls const & _m = kv.m_value; for (auto md : _m) { if (md.m_domain.size() == 0 && ctx.m().is_bool(md.m_body)) { - expr_ref val(ctx.m()); - m->eval(md.m_body, val, true); + model::scoped_model_completion _scm(*m, true); + expr_ref val = (*m)(md.m_body); if (ctx.m().is_true(val) || ctx.m().is_false(val)) { if (first) first = false; @@ -166,10 +165,10 @@ class cmd_is_declared : public ast_smt_pp::is_declared { public: cmd_is_declared(cmd_context& ctx): m_ctx(ctx) {} - virtual bool operator()(func_decl* d) const { + bool operator()(func_decl* d) const override { return m_ctx.is_func_decl(d->get_name()); } - virtual bool operator()(sort* s) const { + bool operator()(sort* s) const override { return m_ctx.is_sort_decl(s->get_name()); } }; @@ -224,7 +223,7 @@ ATOMIC_CMD(get_proof_graph_cmd, "get-proof-graph", "retrieve proof and print it }); static void print_core(cmd_context& ctx) { - ptr_vector core; + expr_ref_vector core(ctx.m()); ctx.get_check_sat_result()->get_unsat_core(core); ctx.regular_stream() << "("; bool first = true; @@ -360,7 +359,7 @@ public: m_int_real_coercions(":int-real-coercions"), m_reproducible_resource_limit(":reproducible-resource-limit") { } - virtual ~set_get_option_cmd() {} + ~set_get_option_cmd() override {} }; @@ -395,7 +394,7 @@ class set_option_cmd : public set_get_option_cmd { env_params::updt_params(); ctx.global_params_updated(); } - catch (gparams::exception ex) { + catch (const gparams::exception & ex) { throw cmd_exception(ex.msg()); } } @@ -477,19 +476,19 @@ public: m_unsupported(false) { } - virtual char const * get_usage() const { return " "; } + char const * get_usage() const override { return " "; } - virtual char const * get_descr(cmd_context & ctx) const { return "set configuration option."; } + char const * get_descr(cmd_context & ctx) const override { return "set configuration option."; } - virtual unsigned get_arity() const { return 2; } + unsigned get_arity() const override { return 2; } - virtual void prepare(cmd_context & ctx) { m_unsupported = false; m_option = symbol::null; } + void prepare(cmd_context & ctx) override { m_unsupported = false; m_option = symbol::null; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return m_option == symbol::null ? CPK_KEYWORD : CPK_OPTION_VALUE; } - virtual void set_next_arg(cmd_context & ctx, symbol const & opt) { + void set_next_arg(cmd_context & ctx, symbol const & opt) override { if (m_option == symbol::null) { m_option = opt; } @@ -498,12 +497,12 @@ public: } } - virtual void set_next_arg(cmd_context & ctx, rational const & val) { + void set_next_arg(cmd_context & ctx, rational const & val) override { if (m_option == m_random_seed) { ctx.set_random_seed(to_unsigned(val)); } else if (m_option == m_reproducible_resource_limit) { - ctx.params().m_rlimit = to_unsigned(val); + ctx.params().set_rlimit(to_unsigned(val)); } else if (m_option == m_verbosity) { set_verbosity_level(to_unsigned(val)); @@ -517,7 +516,7 @@ public: } } - virtual void set_next_arg(cmd_context & ctx, char const * value) { + void set_next_arg(cmd_context & ctx, char const * value) override { if (m_option == m_regular_output_channel) { ctx.set_regular_stream(value); } @@ -532,7 +531,7 @@ public: } } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { if (m_unsupported) ctx.print_unsupported(m_option, m_line, m_pos); else @@ -557,11 +556,11 @@ public: get_option_cmd(): set_get_option_cmd("get-option") { } - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "get configuration option."; } - virtual unsigned get_arity() const { return 1; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_KEYWORD; } - virtual void set_next_arg(cmd_context & ctx, symbol const & opt) { + char const * get_usage() const override { return ""; } + char const * get_descr(cmd_context & ctx) const override { return "get configuration option."; } + unsigned get_arity() const override { return 1; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_KEYWORD; } + void set_next_arg(cmd_context & ctx, symbol const & opt) override { if (opt == m_print_success) { print_bool(ctx, ctx.print_success_enabled()); } @@ -620,7 +619,7 @@ public: try { ctx.regular_stream() << gparams::get_value(opt) << std::endl; } - catch (gparams::exception ex) { + catch (const gparams::exception &) { ctx.print_unsupported(opt, m_line, m_pos); } } @@ -651,11 +650,11 @@ public: m_all_statistics(":all-statistics"), m_assertion_stack_levels(":assertion-stack-levels") { } - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "get information."; } - virtual unsigned get_arity() const { return 1; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_KEYWORD; } - virtual void set_next_arg(cmd_context & ctx, symbol const & opt) { + char const * get_usage() const override { return ""; } + char const * get_descr(cmd_context & ctx) const override { return "get information."; } + unsigned get_arity() const override { return 1; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_KEYWORD; } + void set_next_arg(cmd_context & ctx, symbol const & opt) override { if (opt == m_error_behavior) { if (ctx.exit_on_error()) ctx.regular_stream() << "(:error-behavior immediate-exit)" << std::endl; @@ -707,16 +706,16 @@ public: m_sat("sat"), m_unknown("unknown") { } - virtual char const * get_usage() const { return " "; } - virtual char const * get_descr(cmd_context & ctx) const { return "set information."; } - virtual unsigned get_arity() const { return 2; } - virtual void prepare(cmd_context & ctx) { m_info = symbol::null; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + char const * get_usage() const override { return " "; } + char const * get_descr(cmd_context & ctx) const override { return "set information."; } + unsigned get_arity() const override { return 2; } + void prepare(cmd_context & ctx) override { m_info = symbol::null; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return m_info == symbol::null ? CPK_KEYWORD : CPK_OPTION_VALUE; } - virtual void set_next_arg(cmd_context & ctx, rational const & val) {} - virtual void set_next_arg(cmd_context & ctx, char const * val) {} - virtual void set_next_arg(cmd_context & ctx, symbol const & s) { + void set_next_arg(cmd_context & ctx, rational const & val) override {} + void set_next_arg(cmd_context & ctx, char const * val) override {} + void set_next_arg(cmd_context & ctx, symbol const & s) override { if (m_info == symbol::null) { m_info = s; } @@ -737,7 +736,7 @@ public: } } } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { ctx.print_success(); } }; @@ -760,32 +759,32 @@ public: } return m_array_fid; } - virtual char const * get_usage() const { return " (+) "; } - virtual char const * get_descr(cmd_context & ctx) const { return "declare a new array map operator with name using the given function declaration.\n ::= \n | ( (*) )\n | ((_ +) (*) )\nThe last two cases are used to disumbiguate between declarations with the same name and/or select (indexed) builtin declarations.\nFor more details about the the array map operator, see 'Generalized and Efficient Array Decision Procedures' (FMCAD 2009).\nExample: (declare-map set-union (Int) (or (Bool Bool) Bool))\nDeclares a new function (declare-fun set-union ((Array Int Bool) (Array Int Bool)) (Array Int Bool)).\nThe instance of the map axiom for this new declaration is:\n(forall ((a1 (Array Int Bool)) (a2 (Array Int Bool)) (i Int)) (= (select (set-union a1 a2) i) (or (select a1 i) (select a2 i))))"; } - virtual unsigned get_arity() const { return 3; } - virtual void prepare(cmd_context & ctx) { m_name = symbol::null; m_domain.reset(); } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + char const * get_usage() const override { return " (+) "; } + char const * get_descr(cmd_context & ctx) const override { return "declare a new array map operator with name using the given function declaration.\n ::= \n | ( (*) )\n | ((_ +) (*) )\nThe last two cases are used to disumbiguate between declarations with the same name and/or select (indexed) builtin declarations.\nFor more details about the array map operator, see 'Generalized and Efficient Array Decision Procedures' (FMCAD 2009).\nExample: (declare-map set-union (Int) (or (Bool Bool) Bool))\nDeclares a new function (declare-fun set-union ((Array Int Bool) (Array Int Bool)) (Array Int Bool)).\nThe instance of the map axiom for this new declaration is:\n(forall ((a1 (Array Int Bool)) (a2 (Array Int Bool)) (i Int)) (= (select (set-union a1 a2) i) (or (select a1 i) (select a2 i))))"; } + unsigned get_arity() const override { return 3; } + void prepare(cmd_context & ctx) override { m_name = symbol::null; m_domain.reset(); } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_name == symbol::null) return CPK_SYMBOL; if (m_domain.empty()) return CPK_SORT_LIST; return CPK_FUNC_DECL; } - virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_name = s; } - virtual void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) { + void set_next_arg(cmd_context & ctx, symbol const & s) override { m_name = s; } + void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) override { if (num == 0) throw cmd_exception("invalid map declaration, empty sort list"); m_domain.append(num, slist); } - virtual void set_next_arg(cmd_context & ctx, func_decl * f) { + void set_next_arg(cmd_context & ctx, func_decl * f) override { m_f = f; if (m_f->get_arity() == 0) throw cmd_exception("invalid map declaration, function declaration must have arity > 0"); } - virtual void reset(cmd_context & ctx) { + void reset(cmd_context & ctx) override { m_array_fid = null_family_id; } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { psort_decl * array_sort = ctx.find_psort_decl(m_array_sort); - if (array_sort == 0) + if (array_sort == nullptr) throw cmd_exception("Array sort is not available"); ptr_vector & array_sort_args = m_domain; sort_ref_buffer domain(ctx.m()); @@ -813,11 +812,11 @@ class get_consequences_cmd : public cmd { unsigned m_count; public: get_consequences_cmd(): cmd("get-consequences"), m_count(0) {} - virtual char const * get_usage() const { return "(*) (*)"; } - virtual char const * get_descr(cmd_context & ctx) const { return "retrieve consequences that fix values for supplied variables"; } - virtual unsigned get_arity() const { return 2; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_EXPR_LIST; } - virtual void set_next_arg(cmd_context & ctx, unsigned num, expr * const * tlist) { + char const * get_usage() const override { return "(*) (*)"; } + char const * get_descr(cmd_context & ctx) const override { return "retrieve consequences that fix values for supplied variables"; } + unsigned get_arity() const override { return 2; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR_LIST; } + void set_next_arg(cmd_context & ctx, unsigned num, expr * const * tlist) override { if (m_count == 0) { m_assumptions.append(num, tlist); ++m_count; @@ -826,8 +825,8 @@ public: m_variables.append(num, tlist); } } - virtual void failure_cleanup(cmd_context & ctx) {} - virtual void execute(cmd_context & ctx) { + void failure_cleanup(cmd_context & ctx) override {} + void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); expr_ref_vector assumptions(m), variables(m), consequences(m); assumptions.append(m_assumptions.size(), m_assumptions.c_ptr()); @@ -835,12 +834,12 @@ public: ctx.get_consequences(assumptions, variables, consequences); ctx.regular_stream() << consequences << "\n"; } - virtual void prepare(cmd_context & ctx) { reset(ctx); } + void prepare(cmd_context & ctx) override { reset(ctx); } - virtual void reset(cmd_context& ctx) { + void reset(cmd_context& ctx) override { m_assumptions.reset(); m_variables.reset(); m_count = 0; } - virtual void finalize(cmd_context & ctx) {} + void finalize(cmd_context & ctx) override {} }; // provides "help" for builtin cmds @@ -850,8 +849,8 @@ class builtin_cmd : public cmd { public: builtin_cmd(char const * name, char const * usage, char const * descr): cmd(name), m_usage(usage), m_descr(descr) {} - virtual char const * get_usage() const { return m_usage; } - virtual char const * get_descr(cmd_context & ctx) const { return m_descr; } + char const * get_usage() const override { return m_usage; } + char const * get_descr(cmd_context & ctx) const override { return m_descr; } }; @@ -892,7 +891,7 @@ void install_ext_basic_cmds(cmd_context & ctx) { ctx.insert(alloc(echo_cmd)); ctx.insert(alloc(labels_cmd)); ctx.insert(alloc(declare_map_cmd)); - ctx.insert(alloc(builtin_cmd, "reset", 0, "reset the shell (all declarations and assertions will be erased)")); + ctx.insert(alloc(builtin_cmd, "reset", nullptr, "reset the shell (all declarations and assertions will be erased)")); install_simplify_cmd(ctx); install_eval_cmd(ctx); } diff --git a/src/cmd_context/check_logic.cpp b/src/cmd_context/check_logic.cpp index dd08ac9db..55be27c6d 100644 --- a/src/cmd_context/check_logic.cpp +++ b/src/cmd_context/check_logic.cpp @@ -328,17 +328,17 @@ struct check_logic::imp { bool is_offset(app * t) { while (true) { - expr * non_numeral = 0; + expr * non_numeral = nullptr; unsigned num_args = t->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = t->get_arg(i); if (is_numeral(arg)) continue; - if (non_numeral != 0) + if (non_numeral != nullptr) return false; non_numeral = arg; } - if (non_numeral == 0) + if (non_numeral == nullptr) return true; if (is_diff_var(non_numeral)) return true; @@ -501,7 +501,7 @@ struct check_logic::imp { }; check_logic::check_logic() { - m_imp = 0; + m_imp = nullptr; } check_logic::~check_logic() { @@ -512,7 +512,7 @@ check_logic::~check_logic() { void check_logic::reset() { if (m_imp) dealloc(m_imp); - m_imp = 0; + m_imp = nullptr; } void check_logic::set_logic(ast_manager & m, symbol const & logic) { diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index ec2eea032..6dd7fbcaa 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -43,9 +43,9 @@ Notes: #include "model/model_v2_pp.h" #include "model/model_params.hpp" #include "tactic/tactic_exception.h" +#include "tactic/generic_model_converter.h" #include "solver/smt_logics.h" #include "cmd_context/basic_cmds.h" -#include "cmd_context/interpolant_cmds.h" #include "cmd_context/cmd_context.h" func_decls::func_decls(ast_manager & m, func_decl * f): @@ -67,7 +67,7 @@ void func_decls::finalize(ast_manager & m) { } dealloc(fs); } - m_decls = 0; + m_decls = nullptr; } bool func_decls::signatures_collide(func_decl* f, func_decl* g) const { @@ -116,7 +116,7 @@ bool func_decls::insert(ast_manager & m, func_decl * f) { if (contains(f)) return false; m.inc_ref(f); - if (m_decls == 0) { + if (m_decls == nullptr) { m_decls = TAG(func_decl*, f, 0); } else if (GET_TAG(m_decls) == 0) { @@ -137,7 +137,7 @@ void func_decls::erase(ast_manager & m, func_decl * f) { return; if (GET_TAG(m_decls) == 0) { m.dec_ref(f); - m_decls = 0; + m_decls = nullptr; } else { func_decl_set * fs = UNTAG(func_decl_set *, m_decls); @@ -145,7 +145,7 @@ void func_decls::erase(ast_manager & m, func_decl * f) { m.dec_ref(f); if (fs->empty()) { dealloc(fs); - m_decls = 0; + m_decls = nullptr; } } } @@ -154,7 +154,7 @@ void func_decls::erase(ast_manager & m, func_decl * f) { \brief Return true if func_decls contains a declaration different from f, but with the same domain. */ bool func_decls::clash(func_decl * f) const { - if (m_decls == 0) + if (m_decls == nullptr) return false; if (GET_TAG(m_decls) == 0) return false; @@ -176,15 +176,15 @@ bool func_decls::clash(func_decl * f) const { } bool func_decls::more_than_one() const { - if (m_decls == 0 || GET_TAG(m_decls) == 0) + if (m_decls == nullptr || GET_TAG(m_decls) == 0) return false; func_decl_set * fs = UNTAG(func_decl_set *, m_decls); return fs->size() > 1; } func_decl * func_decls::first() const { - if (m_decls == 0) - return 0; + if (m_decls == nullptr) + return nullptr; if (GET_TAG(m_decls) == 0) return UNTAG(func_decl*, m_decls); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); @@ -197,7 +197,7 @@ func_decl * func_decls::find(unsigned arity, sort * const * domain, sort * range return first(); func_decl_set * fs = UNTAG(func_decl_set *, m_decls); for (func_decl * f : *fs) { - if (range != 0 && f->get_range() != range) + if (range != nullptr && f->get_range() != range) continue; if (f->get_arity() != arity) continue; @@ -209,7 +209,7 @@ func_decl * func_decls::find(unsigned arity, sort * const * domain, sort * range if (i == arity) return f; } - return 0; + return nullptr; } func_decl * func_decls::find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const { @@ -257,7 +257,7 @@ bool macro_decls::insert(ast_manager& m, unsigned arity, sort *const* domain, ex } expr* macro_decls::find(unsigned arity, sort *const* domain) const { - if (!m_decls) return 0; + if (!m_decls) return nullptr; for (auto v : *m_decls) { if (v.m_domain.size() != arity) continue; bool eq = true; @@ -266,7 +266,7 @@ expr* macro_decls::find(unsigned arity, sort *const* domain) const { } if (eq) return v.m_body; } - return 0; + return nullptr; } void macro_decls::erase_last(ast_manager& m) { @@ -291,7 +291,7 @@ bool cmd_context::contains_macro(symbol const& s, func_decl* f) const { bool cmd_context::contains_macro(symbol const& s, unsigned arity, sort *const* domain) const { macro_decls decls; - return m_macros.find(s, decls) && 0 != decls.find(arity, domain); + return m_macros.find(s, decls) && nullptr != decls.find(arity, domain); } void cmd_context::insert_macro(symbol const& s, unsigned arity, sort*const* domain, expr* t) { @@ -320,7 +320,7 @@ bool cmd_context::macros_find(symbol const& s, unsigned n, expr*const* args, exp if (d.m_domain.size() != n) continue; bool eq = true; for (unsigned i = 0; eq && i < n; ++i) { - eq = d.m_domain[i] == m().get_sort(args[i]); + eq = m().compatible_sorts(d.m_domain[i], m().get_sort(args[i])); } if (eq) { t = d.m_body; @@ -396,7 +396,7 @@ protected: datalog::dl_decl_util m_dlutil; format_ns::format * pp_fdecl_name(symbol const & s, func_decls const & fs, func_decl * f, unsigned & len) { - format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); + format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len, f->is_skolem()); if (!fs.more_than_one()) return f_name; if (!fs.clash(f)) @@ -406,7 +406,7 @@ protected: format_ns::format * pp_fdecl_ref_core(symbol const & s, func_decls const & fs, func_decl * f) { unsigned len; - format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len); + format_ns::format * f_name = smt2_pp_environment::pp_fdecl_name(s, len, f->is_skolem()); if (!fs.more_than_one()) return f_name; return pp_signature(f_name, f); @@ -414,25 +414,25 @@ protected: public: pp_env(cmd_context & o):m_owner(o), m_autil(o.m()), m_bvutil(o.m()), m_arutil(o.m()), m_futil(o.m()), m_sutil(o.m()), m_dtutil(o.m()), m_dlutil(o.m()) {} - virtual ~pp_env() {} - virtual ast_manager & get_manager() const { return m_owner.m(); } - virtual arith_util & get_autil() { return m_autil; } - virtual bv_util & get_bvutil() { return m_bvutil; } - virtual array_util & get_arutil() { return m_arutil; } - virtual fpa_util & get_futil() { return m_futil; } - virtual seq_util & get_sutil() { return m_sutil; } - virtual datatype_util & get_dtutil() { return m_dtutil; } + ~pp_env() override {} + ast_manager & get_manager() const override { return m_owner.m(); } + arith_util & get_autil() override { return m_autil; } + bv_util & get_bvutil() override { return m_bvutil; } + array_util & get_arutil() override { return m_arutil; } + fpa_util & get_futil() override { return m_futil; } + seq_util & get_sutil() override { return m_sutil; } + datatype_util & get_dtutil() override { return m_dtutil; } - virtual datalog::dl_decl_util& get_dlutil() { return m_dlutil; } - virtual bool uses(symbol const & s) const { + datalog::dl_decl_util& get_dlutil() override { return m_dlutil; } + bool uses(symbol const & s) const override { return m_owner.m_builtin_decls.contains(s) || m_owner.m_func_decls.contains(s); } - virtual format_ns::format * pp_sort(sort * s) { + format_ns::format * pp_sort(sort * s) override { return m_owner.pp(s); } - virtual format_ns::format * pp_fdecl(func_decl * f, unsigned & len) { + format_ns::format * pp_fdecl(func_decl * f, unsigned & len) override { symbol s = f->get_name(); func_decls fs; if (m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { @@ -443,7 +443,7 @@ public: } return smt2_pp_environment::pp_fdecl(f, len); } - virtual format_ns::format * pp_fdecl_ref(func_decl * f) { + format_ns::format * pp_fdecl_ref(func_decl * f) override { symbol s = f->get_name(); func_decls fs; if (m_owner.m_func_decls.find(s, fs) && fs.contains(f)) { @@ -472,18 +472,17 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l): m_processing_pareto(false), m_exit_on_error(false), m_manager(m), - m_own_manager(m == 0), + m_own_manager(m == nullptr), m_manager_initialized(false), m_rec_fun_declared(false), - m_pmanager(0), - m_sexpr_manager(0), + m_pmanager(nullptr), + m_sexpr_manager(nullptr), m_regular("stdout", std::cout), m_diagnostic("stderr", std::cerr) { SASSERT(m != 0 || !has_manager()); install_basic_cmds(*this); install_ext_basic_cmds(*this); install_core_tactic_cmds(*this); - install_interpolant_cmds(*this); SASSERT(m != 0 || !has_manager()); if (m_main_ctx) { set_verbose_stream(diagnostic_stream()); @@ -498,8 +497,9 @@ cmd_context::~cmd_context() { finalize_tactic_cmds(); finalize_probes(); reset(true); - m_solver = 0; - m_check_sat_result = 0; + m_mc0 = nullptr; + m_solver = nullptr; + m_check_sat_result = nullptr; } void cmd_context::set_cancel(bool f) { @@ -593,7 +593,7 @@ bool cmd_context::validate_model_enabled() const { } cmd_context::check_sat_state cmd_context::cs_state() const { - if (m_check_sat_result.get() == 0) + if (m_check_sat_result.get() == nullptr) return css_clear; switch (m_check_sat_result->status()) { case l_true: return css_sat; @@ -718,7 +718,8 @@ void cmd_context::init_manager_core(bool new_manager) { } m_dt_eh = alloc(dt_eh, *this); m_pmanager->set_new_datatype_eh(m_dt_eh.get()); - if (!has_logic()) { + if (!has_logic() && new_manager) { + TRACE("cmd_context", tout << "init manager " << m_logic << "\n";); // add list type only if the logic is not specified. // it prevents clashes with builtin types. insert(pm().mk_plist_decl()); @@ -741,7 +742,7 @@ void cmd_context::init_manager() { else { m_manager_initialized = true; SASSERT(m_pmanager == 0); - m_check_sat_result = 0; + m_check_sat_result = nullptr; m_manager = m_params.mk_ast_manager(); m_pmanager = alloc(pdecl_manager, *m_manager); init_manager_core(true); @@ -756,6 +757,7 @@ void cmd_context::init_external_manager() { } bool cmd_context::set_logic(symbol const & s) { + TRACE("cmd_context", tout << s << "\n";); if (has_logic()) throw cmd_exception("the logic has already been set"); if (has_manager() && m_main_ctx) @@ -771,7 +773,7 @@ bool cmd_context::set_logic(symbol const & s) { } std::string cmd_context::reason_unknown() const { - if (m_check_sat_result.get() == 0) + if (m_check_sat_result.get() == nullptr) return "state of the most recent check-sat command is not known"; return m_check_sat_result->reason_unknown(); } @@ -861,13 +863,30 @@ void cmd_context::insert_user_tactic(symbol const & s, sexpr * d) { void cmd_context::insert(symbol const & s, object_ref * r) { r->inc_ref(*this); - object_ref * old_r = 0; + object_ref * old_r = nullptr; if (m_object_refs.find(s, old_r)) { old_r->dec_ref(*this); } m_object_refs.insert(s, r); } +void cmd_context::model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t) { + if (!m_mc0.get()) m_mc0 = alloc(generic_model_converter, m(), "cmd_context"); + if (m_solver.get() && !m_solver->mc0()) m_solver->set_model_converter(m_mc0.get()); + func_decl_ref fn(m().mk_func_decl(s, arity, domain, m().get_sort(t)), m()); + dictionary::entry * e = m_func_decls.insert_if_not_there2(s, func_decls()); + func_decls & fs = e->get_data().m_value; + fs.insert(m(), fn); + VERIFY(fn->get_range() == m().get_sort(t)); + m_mc0->add(fn, t); +} + +void cmd_context::model_del(func_decl* f) { + if (!m_mc0.get()) m_mc0 = alloc(generic_model_converter, m(), "cmd_context"); + if (m_solver.get() && !m_solver->mc0()) m_solver->set_model_converter(m_mc0.get()); + m_mc0->hide(f); +} + void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e) { expr_ref eq(m()); app_ref lhs(m()); @@ -905,8 +924,8 @@ func_decl * cmd_context::find_func_decl(symbol const & s) const { try { // Remark: ignoring m_next of d. We do not allow two different theories to define the same constant name. func_decl * f; - f = m().mk_func_decl(d.m_fid, d.m_decl, 0, 0, 0, static_cast(0), 0); - if (f != 0) + f = m().mk_func_decl(d.m_fid, d.m_decl, 0, nullptr, 0, static_cast(nullptr), nullptr); + if (f != nullptr) return f; } catch (ast_exception &) { @@ -923,7 +942,7 @@ func_decl * cmd_context::find_func_decl(symbol const & s) const { return fs.first(); } throw cmd_exception("invalid function declaration reference, unknown function ", s); - return 0; + return nullptr; } /** @@ -936,7 +955,7 @@ func_decl * cmd_context::find_func_decl(symbol const & s) const { */ static builtin_decl const & peek_builtin_decl(builtin_decl const & first, family_id target_id) { builtin_decl const * curr = &first; - while (curr != 0) { + while (curr != nullptr) { if (curr->m_fid == target_id) return *curr; curr = curr->m_next; @@ -958,7 +977,7 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, } func_decl * f; if (num_indices == 0) { - f = m().mk_func_decl(fid, k, 0, 0, arity, domain, range); + f = m().mk_func_decl(fid, k, 0, nullptr, arity, domain, range); } else { buffer ps; @@ -966,7 +985,7 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, ps.push_back(parameter(indices[i])); f = m().mk_func_decl(fid, k, num_indices, ps.c_ptr(), arity, domain, range); } - if (f == 0) + if (f == nullptr) throw cmd_exception("invalid function declaration reference, invalid builtin reference ", s); return f; } @@ -977,46 +996,46 @@ func_decl * cmd_context::find_func_decl(symbol const & s, unsigned num_indices, if (num_indices > 0) throw cmd_exception("invalid indexed function declaration reference, unknown builtin function ", s); - func_decl * f = 0; + func_decl * f = nullptr; func_decls fs; if (m_func_decls.find(s, fs)) { f = fs.find(arity, domain, range); } - if (f == 0) + if (f == nullptr) throw cmd_exception("invalid function declaration reference, unknown function ", s); return f; } psort_decl * cmd_context::find_psort_decl(symbol const & s) const { - psort_decl * p = 0; + psort_decl * p = nullptr; m_psort_decls.find(s, p); return p; } cmd * cmd_context::find_cmd(symbol const & s) const { - cmd * c = 0; + cmd * c = nullptr; m_cmds.find(s, c); return c; } sexpr * cmd_context::find_user_tactic(symbol const & s) const { - sexpr * n = 0; + sexpr * n = nullptr; m_user_tactic_decls.find(s, n); return n; } object_ref * cmd_context::find_object_ref(symbol const & s) const { - object_ref * r = 0; + object_ref * r = nullptr; m_object_refs.find(s, r); - if (r == 0) throw cmd_exception("unknown global variable ", s); + if (r == nullptr) throw cmd_exception("unknown global variable ", s); return r; } #define CHECK_SORT(T) if (well_sorted_check_enabled()) m().check_sorts_core(T) void cmd_context::mk_const(symbol const & s, expr_ref & result) const { - mk_app(s, 0, 0, 0, 0, 0, result); + mk_app(s, 0, nullptr, 0, nullptr, nullptr, result); } void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * args, unsigned num_indices, parameter const * indices, sort * range, @@ -1032,12 +1051,12 @@ void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * arg k = d2.m_decl; } if (num_indices == 0) { - result = m().mk_app(fid, k, 0, 0, num_args, args, range); + result = m().mk_app(fid, k, 0, nullptr, num_args, args, range); } else { result = m().mk_app(fid, k, num_indices, indices, num_args, args, range); } - if (result.get() == 0) + if (result.get() == nullptr) throw cmd_exception("invalid builtin application ", s); CHECK_SORT(result.get()); return; @@ -1066,11 +1085,11 @@ void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * arg throw cmd_exception("unknown function/constant ", s); } - if (num_args == 0 && range == 0) { + if (num_args == 0 && range == nullptr) { if (fs.more_than_one()) throw cmd_exception("ambiguous constant reference, more than one constant with the same sort, use a qualified expression (as ) to disumbiguate ", s); func_decl * f = fs.first(); - if (f == 0) { + if (f == nullptr) { throw cmd_exception("unknown constant ", s); } if (f->get_arity() != 0) @@ -1079,7 +1098,7 @@ void cmd_context::mk_app(symbol const & s, unsigned num_args, expr * const * arg } else { func_decl * f = fs.find(m(), num_args, args, range); - if (f == 0) { + if (f == nullptr) { std::ostringstream buffer; buffer << "unknown constant " << s << " "; buffer << " ("; @@ -1173,7 +1192,7 @@ void cmd_context::erase_user_tactic(symbol const & s) { } void cmd_context::erase_object_ref(symbol const & s) { - object_ref * r = 0; + object_ref * r = nullptr; if (m_object_refs.find(s, r)) { r->dec_ref(*this); m_object_refs.erase(s); @@ -1239,10 +1258,10 @@ void cmd_context::insert_aux_pdecl(pdecl * p) { m_aux_pdecls.push_back(p); } -void cmd_context::reset(bool finalize) { +void cmd_context::reset(bool finalize) { m_processing_pareto = false; m_logic = symbol::null; - m_check_sat_result = 0; + m_check_sat_result = nullptr; m_numeral_as_real = false; m_builtin_decls.reset(); m_extra_builtin_decls.reset(); @@ -1254,18 +1273,18 @@ void cmd_context::reset(bool finalize) { reset_macros(); reset_func_decls(); restore_assertions(0); - if (m_solver) - m_solver = 0; + m_solver = nullptr; + m_mc0 = nullptr; m_scopes.reset(); - m_opt = 0; - m_pp_env = 0; - m_dt_eh = 0; + m_opt = nullptr; + m_pp_env = nullptr; + m_dt_eh = nullptr; if (m_manager) { dealloc(m_pmanager); - m_pmanager = 0; + m_pmanager = nullptr; if (m_own_manager) { dealloc(m_manager); - m_manager = 0; + m_manager = nullptr; m_manager_initialized = false; } else { @@ -1279,7 +1298,7 @@ void cmd_context::reset(bool finalize) { } if (m_sexpr_manager) { dealloc(m_sexpr_manager); - m_sexpr_manager = 0; + m_sexpr_manager = nullptr; } SASSERT(!m_own_manager || !has_manager()); } @@ -1288,7 +1307,7 @@ void cmd_context::assert_expr(expr * t) { m_processing_pareto = false; if (!m_check_logic(t)) throw cmd_exception(m_check_logic.get_last_error()); - m_check_sat_result = 0; + m_check_sat_result = nullptr; m().inc_ref(t); m_assertions.push_back(t); if (produce_unsat_cores()) @@ -1305,7 +1324,7 @@ void cmd_context::assert_expr(symbol const & name, expr * t) { assert_expr(t); return; } - m_check_sat_result = 0; + m_check_sat_result = nullptr; m().inc_ref(t); m_assertions.push_back(t); expr * ans = m().mk_const(name, m().mk_bool_sort()); @@ -1316,7 +1335,7 @@ void cmd_context::assert_expr(symbol const & name, expr * t) { } void cmd_context::push() { - m_check_sat_result = 0; + m_check_sat_result = nullptr; init_manager(); m_scopes.push_back(scope()); scope & s = m_scopes.back(); @@ -1326,7 +1345,8 @@ void cmd_context::push() { s.m_macros_stack_lim = m_macros_stack.size(); s.m_aux_pdecls_lim = m_aux_pdecls.size(); s.m_assertions_lim = m_assertions.size(); - if (m_solver) + m().limit().push(m_params.rlimit()); + if (m_solver) m_solver->push(); if (m_opt) m_opt->push(); @@ -1349,9 +1369,10 @@ void cmd_context::restore_func_decls(unsigned old_sz) { } void cmd_context::restore_psort_inst(unsigned old_sz) { - for (unsigned i = old_sz; i < m_psort_inst_stack.size(); ++i) { + for (unsigned i = m_psort_inst_stack.size(); i-- > old_sz; ) { pdecl * s = m_psort_inst_stack[i]; - s->reset_cache(*m_pmanager); + s->reset_cache(pm()); + pm().dec_ref(s); } m_psort_inst_stack.resize(old_sz); } @@ -1362,7 +1383,7 @@ void cmd_context::restore_psort_decls(unsigned old_sz) { svector::iterator end = m_psort_decls_stack.end(); for (; it != end; ++it) { symbol const & s = *it; - psort_decl * d = 0; + psort_decl * d = nullptr; VERIFY(m_psort_decls.find(s, d)); pm().dec_ref(d); m_psort_decls.erase(s); @@ -1408,7 +1429,9 @@ void cmd_context::restore_assertions(unsigned old_sz) { SASSERT(m_assertions.empty()); return; } - SASSERT(old_sz <= m_assertions.size()); + if (old_sz == m_assertions.size()) + return; + SASSERT(old_sz < m_assertions.size()); SASSERT(!m_interactive_mode || m_assertions.size() == m_assertion_strings.size()); restore(m(), m_assertions, old_sz); if (produce_unsat_cores()) @@ -1418,7 +1441,7 @@ void cmd_context::restore_assertions(unsigned old_sz) { } void cmd_context::pop(unsigned n) { - m_check_sat_result = 0; + m_check_sat_result = nullptr; m_processing_pareto = false; if (n == 0) return; @@ -1439,6 +1462,9 @@ void cmd_context::pop(unsigned n) { restore_assertions(s.m_assertions_lim); restore_psort_inst(s.m_psort_inst_stack_lim); m_scopes.shrink(new_lvl); + while (n--) { + m().limit().pop(); + } } @@ -1449,7 +1475,7 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions TRACE("before_check_sat", dump_assertions(tout);); init_manager(); unsigned timeout = m_params.m_timeout; - unsigned rlimit = m_params.m_rlimit; + unsigned rlimit = m_params.rlimit(); scoped_watch sw(*this); lbool r; bool was_opt = false; @@ -1492,12 +1518,20 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions scoped_rlimit _rlimit(m().limit(), rlimit); try { r = m_solver->check_sat(num_assumptions, assumptions); + if (r == l_undef && m().canceled()) { + m_solver->set_reason_unknown(eh); + } } catch (z3_error & ex) { throw ex; } catch (z3_exception & ex) { - m_solver->set_reason_unknown(ex.msg()); + if (m().canceled()) { + m_solver->set_reason_unknown(eh); + } + else { + m_solver->set_reason_unknown(ex.msg()); + } r = l_undef; } m_solver->set_status(r); @@ -1509,7 +1543,6 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions } display_sat_result(r); if (r == l_true) { - complete_model(); validate_model(); } validate_check_sat_result(r); @@ -1517,16 +1550,15 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions // get_opt()->display_assignment(regular_stream()); } - if (r == l_true && m_params.m_dump_models) { - model_ref md; - get_check_sat_result()->get_model(md); + model_ref md; + if (r == l_true && m_params.m_dump_models && is_model_available(md)) { display_model(md); } } void cmd_context::get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector & conseq) { unsigned timeout = m_params.m_timeout; - unsigned rlimit = m_params.m_rlimit; + unsigned rlimit = m_params.rlimit(); lbool r; m_check_sat_result = m_solver.get(); // solver itself stores the result. m_solver->set_progress_callback(this); @@ -1556,10 +1588,10 @@ void cmd_context::reset_assertions() { } if (m_opt) { - m_opt = 0; + m_opt = nullptr; } if (m_solver) { - m_solver = 0; + m_solver = nullptr; mk_solver(); } restore_assertions(0); @@ -1592,6 +1624,7 @@ void cmd_context::display_dimacs() { void cmd_context::display_model(model_ref& mdl) { if (mdl) { + if (m_mc0) (*m_mc0)(mdl); model_params p; if (p.v1() || p.v2()) { std::ostringstream buffer; @@ -1683,14 +1716,10 @@ struct contains_underspecified_op_proc { /** \brief Complete the model if necessary. */ -void cmd_context::complete_model() { - if (!is_model_available() || - gparams::get_value("model.completion") != "true") +void cmd_context::complete_model(model_ref& md) const { + if (gparams::get_value("model.completion") != "true" || !md.get()) return; - model_ref md; - get_check_sat_result()->get_model(md); - SASSERT(md.get() != 0); params_ref p; p.set_uint("max_degree", UINT_MAX); // evaluate algebraic numbers of any degree. p.set_uint("sort_store", true); @@ -1753,12 +1782,11 @@ void cmd_context::complete_model() { \brief Check if the current model satisfies the quantifier free formulas. */ void cmd_context::validate_model() { + model_ref md; if (!validate_model_enabled()) return; - if (!is_model_available()) + if (!is_model_available(md)) return; - model_ref md; - get_check_sat_result()->get_model(md); SASSERT(md.get() != 0); params_ref p; p.set_uint("max_degree", UINT_MAX); // evaluate algebraic numbers of any degree. @@ -1778,7 +1806,7 @@ void cmd_context::validate_model() { for (; it != end; ++it) { expr * a = *it; if (is_ground(a)) { - r = 0; + r = nullptr; evaluator(a, r); TRACE("model_validate", tout << "checking\n" << mk_ismt2_pp(a, m()) << "\nresult:\n" << mk_ismt2_pp(r, m()) << "\n";); if (m().is_true(r)) @@ -1798,7 +1826,9 @@ void cmd_context::validate_model() { catch (contains_underspecified_op_proc::found) { continue; } - TRACE("model_validate", model_smt2_pp(tout, *this, *(md.get()), 0);); + TRACE("model_validate", model_smt2_pp(tout, *this, *md, 0);); + IF_VERBOSE(10, verbose_stream() << "model check failed on: " << mk_pp(a, m()) << "\n";); + IF_VERBOSE(11, model_smt2_pp(verbose_stream(), *this, *md, 0);); invalid_model = true; } } @@ -1830,8 +1860,8 @@ void cmd_context::set_interpolating_solver_factory(solver_factory * f) { void cmd_context::set_solver_factory(solver_factory * f) { m_solver_factory = f; - m_check_sat_result = 0; - if (has_manager() && f != 0) { + m_check_sat_result = nullptr; + if (has_manager() && f != nullptr) { mk_solver(); // assert formulas and create scopes in the new solver. unsigned lim = 0; @@ -1884,13 +1914,13 @@ void cmd_context::display_assertions() { regular_stream() << ")" << std::endl; } -bool cmd_context::is_model_available() const { +bool cmd_context::is_model_available(model_ref& md) const { if (produce_models() && has_manager() && (cs_state() == css_sat || cs_state() == css_unknown)) { - model_ref md; get_check_sat_result()->get_model(md); - return md.get() != 0; + complete_model(md); + return md.get() != nullptr; } return false; } @@ -1901,7 +1931,7 @@ format_ns::format * cmd_context::pp(sort * s) const { } cmd_context::pp_env & cmd_context::get_pp_env() const { - if (m_pp_env.get() == 0) { + if (m_pp_env.get() == nullptr) { const_cast(this)->m_pp_env = alloc(pp_env, *const_cast(this)); } return *(m_pp_env.get()); @@ -1913,11 +1943,11 @@ void cmd_context::pp(expr * n, unsigned num_vars, char const * var_prefix, forma void cmd_context::pp(expr * n, format_ns::format_ref & r) const { sbuffer buf; - pp(n, 0, 0, r, buf); + pp(n, 0, nullptr, r, buf); } void cmd_context::pp(func_decl * f, format_ns::format_ref & r) const { - mk_smt2_format(f, get_pp_env(), params_ref(), r); + mk_smt2_format(f, get_pp_env(), params_ref(), r, "declare-fun"); } void cmd_context::display(std::ostream & out, sort * s, unsigned indent) const { @@ -1938,7 +1968,7 @@ void cmd_context::display(std::ostream & out, expr * n, unsigned indent, unsigne void cmd_context::display(std::ostream & out, expr * n, unsigned indent) const { sbuffer buf; - display(out, n, indent, 0, 0, buf); + display(out, n, indent, 0, nullptr, buf); } void cmd_context::display(std::ostream & out, func_decl * d, unsigned indent) const { @@ -2015,15 +2045,15 @@ void cmd_context::dt_eh::operator()(sort * dt, pdecl* pd) { m_owner.insert(c); func_decl * r = m_dt_util.get_constructor_recognizer(c); m_owner.insert(r); - TRACE("new_dt_eh", tout << "new recognizer: " << r->get_name() << "\n";); + // TRACE("new_dt_eh", tout << "new recognizer: " << r->get_name() << "\n";); for (func_decl * a : *m_dt_util.get_constructor_accessors(c)) { TRACE("new_dt_eh", tout << "new accessor: " << a->get_name() << "\n";); m_owner.insert(a); } } if (m_owner.m_scopes.size() > 0) { + m_owner.pm().inc_ref(pd); m_owner.m_psort_inst_stack.push_back(pd); - } } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 99d6c8284..a0093191d 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -23,20 +23,21 @@ Notes: #include #include -#include "ast/ast.h" -#include "ast/ast_printer.h" -#include "cmd_context/pdecl.h" -#include "util/dictionary.h" -#include "solver/solver.h" -#include "ast/datatype_decl_plugin.h" #include "util/stopwatch.h" #include "util/cmd_context_types.h" #include "util/event_handler.h" #include "util/sexpr.h" +#include "util/dictionary.h" +#include "util/scoped_ptr_vector.h" +#include "ast/ast.h" +#include "ast/ast_printer.h" +#include "ast/datatype_decl_plugin.h" +#include "tactic/generic_model_converter.h" +#include "solver/solver.h" +#include "solver/progress_callback.h" +#include "cmd_context/pdecl.h" #include "cmd_context/tactic_manager.h" #include "cmd_context/check_logic.h" -#include "solver/progress_callback.h" -#include "util/scoped_ptr_vector.h" #include "cmd_context/context_params.h" @@ -45,7 +46,7 @@ class func_decls { bool signatures_collide(func_decl* f, func_decl* g) const; bool signatures_collide(unsigned n, sort*const* domain, sort* range, func_decl* g) const; public: - func_decls():m_decls(0) {} + func_decls():m_decls(nullptr) {} func_decls(ast_manager & m, func_decl * f); void finalize(ast_manager & m); bool contains(func_decl * f) const; @@ -54,7 +55,7 @@ public: void erase(ast_manager & m, func_decl * f); bool more_than_one() const; bool clash(func_decl * f) const; - bool empty() const { return m_decls == 0; } + bool empty() const { return m_decls == nullptr; } func_decl * first() const; func_decl * find(unsigned arity, sort * const * domain, sort * range) const; func_decl * find(ast_manager & m, unsigned num_args, expr * const * args, sort * range) const; @@ -76,7 +77,7 @@ struct macro_decl { class macro_decls { vector* m_decls; public: - macro_decls() { m_decls = 0; } + macro_decls() { m_decls = nullptr; } void finalize(ast_manager& m); bool insert(ast_manager& m, unsigned arity, sort *const* domain, expr* body); expr* find(unsigned arity, sort *const* domain) const; @@ -112,10 +113,10 @@ class ast_object_ref : public object_ref { ast * m_ast; public: ast_object_ref(cmd_context & ctx, ast * a); - virtual void finalize(cmd_context & ctx); + void finalize(cmd_context & ctx) override; ast * get_ast() const { return m_ast; } static char const * cls_kind() { return "AST"; } - virtual char const * kind() const { return cls_kind(); } + char const * kind() const override { return cls_kind(); } }; class stream_ref { @@ -125,7 +126,7 @@ class stream_ref { std::ostream * m_stream; bool m_owner; public: - stream_ref(std::string n, std::ostream & d):m_default_name(n), m_default(d), m_name(n), m_stream(&d), m_owner(false) {} + stream_ref(const std::string& n, std::ostream & d):m_default_name(n), m_default(d), m_name(n), m_stream(&d), m_owner(false) {} ~stream_ref() { reset(); } void set(char const * name); void set(std::ostream& strm); @@ -138,8 +139,8 @@ struct builtin_decl { family_id m_fid; decl_kind m_decl; builtin_decl * m_next; - builtin_decl():m_fid(null_family_id), m_decl(0), m_next(0) {} - builtin_decl(family_id fid, decl_kind k, builtin_decl * n = 0):m_fid(fid), m_decl(k), m_next(n) {} + builtin_decl():m_fid(null_family_id), m_decl(0), m_next(nullptr) {} + builtin_decl(family_id fid, decl_kind k, builtin_decl * n = nullptr):m_fid(fid), m_decl(k), m_next(n) {} }; class opt_wrapper : public check_sat_result { @@ -194,6 +195,7 @@ protected: static std::ostringstream g_error_stream; + generic_model_converter_ref m_mc0; ast_manager * m_manager; bool m_own_manager; bool m_manager_initialized; @@ -250,8 +252,8 @@ protected: datatype_util m_dt_util; public: dt_eh(cmd_context & owner); - virtual ~dt_eh(); - virtual void operator()(sort * dt, pdecl* pd); + ~dt_eh() override; + void operator()(sort * dt, pdecl* pd) override; }; friend class dt_eh; @@ -306,8 +308,8 @@ protected: public: - cmd_context(bool main_ctx = true, ast_manager * m = 0, symbol const & l = symbol::null); - ~cmd_context(); + cmd_context(bool main_ctx = true, ast_manager * m = nullptr, symbol const & l = symbol::null); + ~cmd_context() override; void set_cancel(bool f); context_params & params() { return m_params; } solver_factory &get_solver_factory() { return *m_solver_factory; } @@ -323,6 +325,7 @@ public: void set_numeral_as_real(bool f) { m_numeral_as_real = f; } void set_interactive_mode(bool flag) { m_interactive_mode = flag; } void set_ignore_check(bool flag) { m_ignore_check = flag; } + bool ignore_check() const { return m_ignore_check; } void set_exit_on_error(bool flag) { m_exit_on_error = flag; } bool exit_on_error() const { return m_exit_on_error; } bool interactive_mode() const { return m_interactive_mode; } @@ -352,9 +355,9 @@ public: status get_status() const { return m_status; } std::string reason_unknown() const; - bool has_manager() const { return m_manager != 0; } + bool has_manager() const { return m_manager != nullptr; } ast_manager & m() const { const_cast(this)->init_manager(); return *m_manager; } - virtual ast_manager & get_ast_manager() { return m(); } + ast_manager & get_ast_manager() override { return m(); } pdecl_manager & pm() const { if (!m_pmanager) const_cast(this)->init_manager(); return *m_pmanager; } sexpr_manager & sm() const { if (!m_sexpr_manager) const_cast(this)->m_sexpr_manager = alloc(sexpr_manager); return *m_sexpr_manager; } @@ -363,7 +366,7 @@ public: void set_check_sat_result(check_sat_result * r) { m_check_sat_result = r; } check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); } check_sat_state cs_state() const; - void complete_model(); + void complete_model(model_ref& mdl) const; void validate_model(); void display_model(model_ref& mdl); @@ -382,6 +385,8 @@ public: void insert_user_tactic(symbol const & s, sexpr * d); void insert_aux_pdecl(pdecl * p); void insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e); + void model_add(symbol const & s, unsigned arity, sort *const* domain, expr * t); + void model_del(func_decl* f); func_decl * find_func_decl(symbol const & s) const; func_decl * find_func_decl(symbol const & s, unsigned num_indices, unsigned const * indices, unsigned arity, sort * const * domain, sort * range) const; @@ -406,9 +411,10 @@ public: void reset_user_tactics(); void set_regular_stream(char const * name) { m_regular.set(name); } void set_regular_stream(std::ostream& out) { m_regular.set(out); } + void set_diagnostic_stream(std::ostream& out) { m_diagnostic.set(out); } void set_diagnostic_stream(char const * name); - virtual std::ostream & regular_stream() { return *m_regular; } - virtual std::ostream & diagnostic_stream() { return *m_diagnostic; } + std::ostream & regular_stream() override { return *m_regular; } + std::ostream & diagnostic_stream() override { return *m_diagnostic; } char const * get_regular_stream_name() const { return m_regular.name(); } char const * get_diagnostic_stream_name() const { return m_diagnostic.name(); } typedef dictionary::iterator cmd_iterator; @@ -440,7 +446,9 @@ public: dictionary const & get_macros() const { return m_macros; } - bool is_model_available() const; + model_converter* get_model_converter() { return m_mc0.get(); } + + bool is_model_available(model_ref& md) const; double get_seconds() const { return m_watch.get_seconds(); } @@ -462,14 +470,14 @@ public: } format_ns::format * pp(sort * s) const; - virtual void pp(sort * s, format_ns::format_ref & r) const { r = pp(s); } - virtual void pp(func_decl * f, format_ns::format_ref & r) const; - virtual void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const; - virtual void pp(expr * n, format_ns::format_ref & r) const; - virtual void display(std::ostream & out, sort * s, unsigned indent = 0) const; - virtual void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const; - virtual void display(std::ostream & out, expr * n, unsigned indent = 0) const; - virtual void display(std::ostream & out, func_decl * f, unsigned indent = 0) const; + void pp(sort * s, format_ns::format_ref & r) const override { r = pp(s); } + void pp(func_decl * f, format_ns::format_ref & r) const override; + void pp(expr * n, unsigned num_vars, char const * var_prefix, format_ns::format_ref & r, sbuffer & var_names) const override; + void pp(expr * n, format_ns::format_ref & r) const override; + void display(std::ostream & out, sort * s, unsigned indent = 0) const override; + void display(std::ostream & out, expr * n, unsigned indent, unsigned num_vars, char const * var_prefix, sbuffer & var_names) const override; + void display(std::ostream & out, expr * n, unsigned indent = 0) const override; + void display(std::ostream & out, func_decl * f, unsigned indent = 0) const override; // dump assertions in out using the pretty printer. void dump_assertions(std::ostream & out) const; @@ -478,8 +486,8 @@ public: void display_smt2_benchmark(std::ostream & out, unsigned num, expr * const * assertions, symbol const & logic = symbol::null) const; - virtual void slow_progress_sample(); - virtual void fast_progress_sample(); + void slow_progress_sample() override; + void fast_progress_sample() override; }; std::ostream & operator<<(std::ostream & out, cmd_context::status st); diff --git a/src/cmd_context/cmd_context_to_goal.cpp b/src/cmd_context/cmd_context_to_goal.cpp index beff9a7bd..a66f9e5de 100644 --- a/src/cmd_context/cmd_context_to_goal.cpp +++ b/src/cmd_context/cmd_context_to_goal.cpp @@ -33,14 +33,14 @@ void assert_exprs_from(cmd_context const & ctx, goal & t) { ptr_vector::const_iterator it2 = ctx.begin_assertion_names(); SASSERT(end - it == ctx.end_assertion_names() - it2); for (; it != end; ++it, ++it2) { - t.assert_expr(*it, proofs_enabled ? m.mk_asserted(*it) : 0, m.mk_leaf(*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) : 0, 0); + t.assert_expr(*it, proofs_enabled ? m.mk_asserted(*it) : nullptr, nullptr); } SASSERT(ctx.begin_assertion_names() == ctx.end_assertion_names()); } diff --git a/src/cmd_context/context_params.cpp b/src/cmd_context/context_params.cpp index 78f4223fc..ff39907da 100644 --- a/src/cmd_context/context_params.cpp +++ b/src/cmd_context/context_params.cpp @@ -62,7 +62,7 @@ void context_params::set_uint(unsigned & opt, char const * param, char const * v } if (is_uint) { - long val = strtol(value, 0, 10); + long val = strtol(value, nullptr, 10); opt = static_cast(val); } else { @@ -135,7 +135,7 @@ void context_params::set(char const * param, char const * value) { } void context_params::updt_params() { - updt_params(gparams::get()); + updt_params(gparams::get_ref()); } void context_params::updt_params(params_ref const & p) { @@ -198,7 +198,7 @@ void context_params::get_solver_params(ast_manager const & m, params_ref & p, bo ast_manager * context_params::mk_ast_manager() { ast_manager * r = alloc(ast_manager, m_proof ? PGM_ENABLED : PGM_DISABLED, - m_trace ? m_trace_file_name.c_str() : 0); + m_trace ? m_trace_file_name.c_str() : nullptr); if (m_smtlib2_compliant) r->enable_int_real_coercions(false); if (m_debug_ref_count) diff --git a/src/cmd_context/context_params.h b/src/cmd_context/context_params.h index df62057fe..3d2947b7a 100644 --- a/src/cmd_context/context_params.h +++ b/src/cmd_context/context_params.h @@ -27,6 +27,8 @@ class context_params { void set_bool(bool & opt, char const * param, char const * value); void set_uint(unsigned & opt, char const * param, char const * value); + unsigned m_rlimit; + public: bool m_auto_config; bool m_proof; @@ -42,10 +44,11 @@ public: bool m_unsat_core; bool m_smtlib2_compliant; // it must be here because it enable/disable the use of coercions in the ast_manager. unsigned m_timeout; - unsigned m_rlimit; + unsigned rlimit() const { return m_rlimit; } context_params(); void set(char const * param, char const * value); + void set_rlimit(unsigned lim) { m_rlimit = lim; } void updt_params(); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); diff --git a/src/cmd_context/echo_tactic.cpp b/src/cmd_context/echo_tactic.cpp index 848ae5429..1ea556bef 100644 --- a/src/cmd_context/echo_tactic.cpp +++ b/src/cmd_context/echo_tactic.cpp @@ -27,18 +27,15 @@ class echo_tactic : public skip_tactic { public: echo_tactic(cmd_context & ctx, char const * msg, bool newline):m_ctx(ctx), m_msg(msg), m_newline(newline) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { #pragma omp critical (echo_tactic) { m_ctx.regular_stream() << m_msg; if (m_newline) m_ctx.regular_stream() << std::endl; } - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; @@ -57,15 +54,12 @@ public: m_p->inc_ref(); } - ~probe_value_tactic() { + ~probe_value_tactic() override { m_p->dec_ref(); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { double val = (*m_p)(*(in.get())).get_value(); #pragma omp critical (probe_value_tactic) { @@ -75,7 +69,7 @@ public: if (m_newline) m_ctx.diagnostic_stream() << std::endl; } - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; diff --git a/src/cmd_context/eval_cmd.cpp b/src/cmd_context/eval_cmd.cpp index 9d3c1ced3..3f564edff 100644 --- a/src/cmd_context/eval_cmd.cpp +++ b/src/cmd_context/eval_cmd.cpp @@ -29,43 +29,41 @@ class eval_cmd : public parametric_cmd { public: eval_cmd():parametric_cmd("eval") {} - virtual char const * get_usage() const { return " ( )*"; } + char const * get_usage() const override { return " ( )*"; } - virtual char const * get_main_descr() const { + char const * get_main_descr() const override { return "evaluate the given term in the current model."; } - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { model_evaluator::get_param_descrs(p); insert_timeout(p); p.insert("model_index", CPK_UINT, "(default: 0) index of model from box optimization objective"); } - virtual void prepare(cmd_context & ctx) { + void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); - m_target = 0; + m_target = nullptr; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { - if (m_target == 0) return CPK_EXPR; + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { + if (m_target == nullptr) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } - virtual void set_next_arg(cmd_context & ctx, expr * arg) { + void set_next_arg(cmd_context & ctx, expr * arg) override { m_target = arg; } - virtual void execute(cmd_context & ctx) { - if (!ctx.is_model_available()) + void execute(cmd_context & ctx) override { + model_ref md; + if (!ctx.is_model_available(md)) throw cmd_exception("model is not available"); if (!m_target) throw cmd_exception("no arguments passed to eval"); - model_ref md; unsigned index = m_params.get_uint("model_index", 0); - check_sat_result * last_result = ctx.get_check_sat_result(); - SASSERT(last_result); if (index == 0 || !ctx.get_opt()) { - last_result->get_model(md); + // already have model. } else { ctx.get_opt()->get_box_model(md, index); diff --git a/src/cmd_context/extra_cmds/dbg_cmds.cpp b/src/cmd_context/extra_cmds/dbg_cmds.cpp index dfdfa6175..64eeac42e 100644 --- a/src/cmd_context/extra_cmds/dbg_cmds.cpp +++ b/src/cmd_context/extra_cmds/dbg_cmds.cpp @@ -29,7 +29,11 @@ Notes: #include "tactic/arith/bound_manager.h" #include "ast/used_vars.h" #include "ast/rewriter/var_subst.h" +#include "ast/ast_util.h" #include "util/gparams.h" +#include "qe/qe_mbp.h" +#include "qe/qe_mbi.h" +#include "qe/qe_term_graph.h" BINARY_SYM_CMD(get_quantifier_body_cmd, @@ -104,15 +108,15 @@ class subst_cmd : public cmd { ptr_vector m_subst; public: subst_cmd():cmd("dbg-subst") {} - virtual char const * get_usage() const { return " (*) "; } - virtual char const * get_descr(cmd_context & ctx) const { return "substitute the free variables in the AST referenced by using the ASTs referenced by *"; } - virtual unsigned get_arity() const { return 3; } - virtual void prepare(cmd_context & ctx) { m_idx = 0; m_source = 0; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + char const * get_usage() const override { return " (*) "; } + char const * get_descr(cmd_context & ctx) const override { return "substitute the free variables in the AST referenced by using the ASTs referenced by *"; } + unsigned get_arity() const override { return 3; } + void prepare(cmd_context & ctx) override { m_idx = 0; m_source = nullptr; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_idx == 1) return CPK_SYMBOL_LIST; return CPK_SYMBOL; } - virtual void set_next_arg(cmd_context & ctx, symbol const & s) { + void set_next_arg(cmd_context & ctx, symbol const & s) override { if (m_idx == 0) { m_source = get_expr_ref(ctx, s); } @@ -121,7 +125,7 @@ public: } m_idx++; } - virtual void set_next_arg(cmd_context & ctx, unsigned num, symbol const * s) { + void set_next_arg(cmd_context & ctx, unsigned num, symbol const * s) override { m_subst.reset(); unsigned i = num; while (i > 0) { @@ -130,7 +134,7 @@ public: } m_idx++; } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { expr_ref r(ctx.m()); beta_reducer p(ctx.m()); p(m_source, m_subst.size(), m_subst.c_ptr(), r); @@ -179,18 +183,18 @@ class lt_cmd : public cmd { expr * m_t2; public: lt_cmd():cmd("dbg-lt") {} - virtual char const * get_usage() const { return " "; } - virtual char const * get_descr(cmd_context & ctx) const { return "return true if the first term is smaller than the second one in the internal Z3 total order on terms."; } - virtual unsigned get_arity() const { return 2; } - virtual void prepare(cmd_context & ctx) { m_t1 = 0; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_EXPR; } - virtual void set_next_arg(cmd_context & ctx, expr * arg) { - if (m_t1 == 0) + char const * get_usage() const override { return " "; } + char const * get_descr(cmd_context & ctx) const override { return "return true if the first term is smaller than the second one in the internal Z3 total order on terms."; } + unsigned get_arity() const override { return 2; } + void prepare(cmd_context & ctx) override { m_t1 = nullptr; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR; } + void set_next_arg(cmd_context & ctx, expr * arg) override { + if (m_t1 == nullptr) m_t1 = arg; else m_t2 = arg; } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { bool r = lt(m_t1, m_t2); ctx.regular_stream() << (r ? "true" : "false") << std::endl; } @@ -271,7 +275,7 @@ UNARY_CMD(elim_unused_vars_cmd, "dbg-elim-unused-vars", "", "eliminate unu return; } expr_ref r(ctx.m()); - elim_unused_vars(ctx.m(), to_quantifier(arg), gparams::get(), r); + elim_unused_vars(ctx.m(), to_quantifier(arg), gparams::get_ref(), r); SASSERT(!is_quantifier(r) || !to_quantifier(r)->may_have_unused_vars()); ctx.display(ctx.regular_stream(), r); ctx.regular_stream() << std::endl; @@ -283,23 +287,23 @@ protected: ptr_vector m_args; public: instantiate_cmd_core(char const * name):cmd(name) {} - virtual char const * get_usage() const { return " (*)"; } - virtual char const * get_descr(cmd_context & ctx) const { return "instantiate the quantifier using the given expressions."; } - virtual unsigned get_arity() const { return 2; } - virtual void prepare(cmd_context & ctx) { m_q = 0; m_args.reset(); } + char const * get_usage() const override { return " (*)"; } + char const * get_descr(cmd_context & ctx) const override { return "instantiate the quantifier using the given expressions."; } + unsigned get_arity() const override { return 2; } + void prepare(cmd_context & ctx) override { m_q = nullptr; m_args.reset(); } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { - if (m_q == 0) return CPK_EXPR; + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { + if (m_q == nullptr) return CPK_EXPR; else return CPK_EXPR_LIST; } - virtual void set_next_arg(cmd_context & ctx, expr * s) { + void set_next_arg(cmd_context & ctx, expr * s) override { if (!is_quantifier(s)) throw cmd_exception("invalid command, quantifier expected."); m_q = to_quantifier(s); } - virtual void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) { + void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override { if (num != m_q->get_num_decls()) throw cmd_exception("invalid command, mismatch between the number of quantified variables and the number of arguments."); unsigned i = num; @@ -315,7 +319,7 @@ public: m_args.append(num, ts); } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { expr_ref r(ctx.m()); instantiate(ctx.m(), m_q, m_args.c_ptr(), r); ctx.display(ctx.regular_stream(), r); @@ -332,9 +336,9 @@ class instantiate_nested_cmd : public instantiate_cmd_core { public: instantiate_nested_cmd():instantiate_cmd_core("dbg-instantiate-nested") {} - virtual char const * get_descr(cmd_context & ctx) const { return "instantiate the quantifier nested in the outermost quantifier, this command is used to test the instantiation procedure with quantifiers that contain free variables."; } + char const * get_descr(cmd_context & ctx) const override { return "instantiate the quantifier nested in the outermost quantifier, this command is used to test the instantiation procedure with quantifiers that contain free variables."; } - virtual void set_next_arg(cmd_context & ctx, expr * s) { + void set_next_arg(cmd_context & ctx, expr * s) override { instantiate_cmd_core::set_next_arg(ctx, s); if (!is_quantifier(m_q->get_expr())) throw cmd_exception("invalid command, nested quantifier expected"); @@ -345,11 +349,189 @@ public: class print_dimacs_cmd : public cmd { public: print_dimacs_cmd():cmd("display-dimacs") {} - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "print benchmark in DIMACS format"; } - virtual unsigned get_arity() const { return 0; } - virtual void prepare(cmd_context & ctx) {} - virtual void execute(cmd_context & ctx) { ctx.display_dimacs(); } + char const * get_usage() const override { return ""; } + char const * get_descr(cmd_context & ctx) const override { return "print benchmark in DIMACS format"; } + unsigned get_arity() const override { return 0; } + void prepare(cmd_context & ctx) override {} + void execute(cmd_context & ctx) override { ctx.display_dimacs(); } +}; + +class mbp_cmd : public cmd { + expr* m_fml; + ptr_vector m_vars; +public: + mbp_cmd():cmd("mbp") {} + char const * get_usage() const override { return " ()"; } + char const * get_descr(cmd_context & ctx) const override { return "perform model based projection"; } + unsigned get_arity() const override { return 2; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { + if (m_fml == nullptr) return CPK_EXPR; + return CPK_EXPR_LIST; + } + void set_next_arg(cmd_context& ctx, expr * arg) override { m_fml = arg; } + void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context & ctx) override { m_fml = nullptr; m_vars.reset(); } + void execute(cmd_context & ctx) override { + ast_manager& m = ctx.m(); + app_ref_vector vars(m); + model_ref mdl; + if (!ctx.is_model_available(mdl) || !ctx.get_check_sat_result()) { + throw cmd_exception("model is not available"); + } + for (expr* v : m_vars) { + if (!is_uninterp_const(v)) { + throw cmd_exception("invalid variable argument. Uninterpreted variable expected"); + } + vars.push_back(to_app(v)); + } + qe::mbp mbp(m); + expr_ref fml(m_fml, m); + mbp.spacer(vars, *mdl.get(), fml); + ctx.regular_stream() << fml << "\n"; + } +}; + +class mbi_cmd : public cmd { + expr* m_a; + expr* m_b; + ptr_vector m_vars; +public: + mbi_cmd():cmd("mbi") {} + char const * get_usage() const override { return " (vars)"; } + char const * get_descr(cmd_context & ctx) const override { return "perform model based interpolation"; } + unsigned get_arity() const override { return 3; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { + if (m_a == nullptr) return CPK_EXPR; + if (m_b == nullptr) return CPK_EXPR; + return CPK_FUNC_DECL_LIST; + } + void set_next_arg(cmd_context& ctx, expr * arg) override { + if (m_a == nullptr) + m_a = arg; + else + m_b = arg; + } + void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; m_vars.reset(); } + void execute(cmd_context & ctx) override { + ast_manager& m = ctx.m(); + func_decl_ref_vector vars(m); + for (func_decl* v : m_vars) { + vars.push_back(v); + } + qe::interpolator mbi(m); + expr_ref a(m_a, m); + expr_ref b(m_b, m); + expr_ref itp(m); + solver_factory& sf = ctx.get_solver_factory(); + params_ref p; + solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null); + solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null); + sA->assert_expr(a); + sB->assert_expr(b); + qe::prop_mbi_plugin pA(sA.get()); + qe::prop_mbi_plugin pB(sB.get()); + pA.set_shared(vars); + pB.set_shared(vars); + lbool res = mbi.pingpong(pA, pB, itp); + ctx.regular_stream() << res << " " << itp << "\n"; + } +}; + + +class eufi_cmd : public cmd { + expr* m_a; + expr* m_b; + ptr_vector m_vars; +public: + eufi_cmd():cmd("eufi") {} + char const * get_usage() const override { return " (vars)"; } + char const * get_descr(cmd_context & ctx) const override { return "perform model based interpolation"; } + unsigned get_arity() const override { return 3; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { + if (m_a == nullptr) return CPK_EXPR; + if (m_b == nullptr) return CPK_EXPR; + return CPK_FUNC_DECL_LIST; + } + void set_next_arg(cmd_context& ctx, expr * arg) override { + if (m_a == nullptr) + m_a = arg; + else + m_b = arg; + } + void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; m_vars.reset(); } + void execute(cmd_context & ctx) override { + ast_manager& m = ctx.m(); + func_decl_ref_vector vars(m); + for (func_decl* v : m_vars) { + vars.push_back(v); + } + qe::interpolator mbi(m); + expr_ref a(m_a, m); + expr_ref b(m_b, m); + expr_ref itp(m); + solver_factory& sf = ctx.get_solver_factory(); + params_ref p; + solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null); + solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null); + solver_ref sNotA = sf(m, p, false /* no proofs */, true, true, symbol::null); + solver_ref sNotB = sf(m, p, false /* no proofs */, true, true, symbol::null); + sA->assert_expr(a); + sNotA->assert_expr(m.mk_not(a)); + sB->assert_expr(b); + sNotB->assert_expr(m.mk_not(b)); + qe::euf_arith_mbi_plugin pA(sA.get(), sNotA.get()); + qe::euf_arith_mbi_plugin pB(sB.get(), sNotB.get()); + pA.set_shared(vars); + pB.set_shared(vars); + lbool res = mbi.pogo(pA, pB, itp); + ctx.regular_stream() << res << " " << itp << "\n"; + } +}; + + +class euf_project_cmd : public cmd { + unsigned m_arg_index; + ptr_vector m_lits; + ptr_vector m_vars; +public: + euf_project_cmd():cmd("euf-project") {} + char const * get_usage() const override { return "(exprs) (vars)"; } + char const * get_descr(cmd_context & ctx) const override { return "perform congruence projection"; } + unsigned get_arity() const override { return 2; } + cmd_arg_kind next_arg_kind(cmd_context& ctx) const override { + if (m_arg_index == 0) return CPK_EXPR_LIST; + return CPK_FUNC_DECL_LIST; + } + void set_next_arg(cmd_context& ctx, unsigned num, expr * const* args) override { + m_lits.append(num, args); + m_arg_index = 1; + } + void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override { + m_vars.append(num, ts); + } + void prepare(cmd_context & ctx) override { m_arg_index = 0; m_lits.reset(); m_vars.reset(); } + void execute(cmd_context & ctx) override { + ast_manager& m = ctx.m(); + func_decl_ref_vector vars(m); + expr_ref_vector lits(m); + for (func_decl* v : m_vars) vars.push_back(v); + for (expr* e : m_lits) lits.push_back(e); + flatten_and(lits); + qe::term_graph tg(m); + tg.set_vars(vars, false); + tg.add_lits(lits); + expr_ref_vector p = tg.project(); + ctx.regular_stream() << p << "\n"; + } + }; @@ -377,4 +559,8 @@ void install_dbg_cmds(cmd_context & ctx) { ctx.insert(alloc(instantiate_cmd)); ctx.insert(alloc(instantiate_nested_cmd)); ctx.insert(alloc(set_next_id)); + ctx.insert(alloc(mbp_cmd)); + ctx.insert(alloc(mbi_cmd)); + ctx.insert(alloc(euf_project_cmd)); + ctx.insert(alloc(eufi_cmd)); } diff --git a/src/cmd_context/extra_cmds/polynomial_cmds.cpp b/src/cmd_context/extra_cmds/polynomial_cmds.cpp index 7a748ac66..1f4915ca5 100644 --- a/src/cmd_context/extra_cmds/polynomial_cmds.cpp +++ b/src/cmd_context/extra_cmds/polynomial_cmds.cpp @@ -104,7 +104,7 @@ class poly_isolate_roots_cmd : public cmd { } void set_next_arg(cmd_context & ctx, expr * arg) { - if (m_p.get() == 0) { + if (m_p.get() == nullptr) { scoped_mpz d(m_qm); if (!m_expr2poly.to_polynomial(arg, m_p, d)) throw cmd_exception("expression is not a polynomial"); @@ -132,7 +132,7 @@ class poly_isolate_roots_cmd : public cmd { } void execute(cmd_context & ctx) { - if (m_p.get() == 0) + if (m_p.get() == nullptr) throw cmd_exception("polynomial expected"); polynomial::var_vector xs; m_pm.vars(m_p, xs); @@ -162,37 +162,37 @@ class poly_isolate_roots_cmd : public cmd { scoped_ptr m_ctx; public: - poly_isolate_roots_cmd(char const * name = "poly/isolate-roots"):cmd(name), m_ctx(0) {} + poly_isolate_roots_cmd(char const * name = "poly/isolate-roots"):cmd(name), m_ctx(nullptr) {} - virtual char const * get_usage() const { return " ( )*"; } + char const * get_usage() const override { return " ( )*"; } - virtual char const * get_descr(cmd_context & ctx) const { return "isolate the roots a multivariate polynomial modulo an assignment"; } + char const * get_descr(cmd_context & ctx) const override { return "isolate the roots a multivariate polynomial modulo an assignment"; } - virtual unsigned get_arity() const { return VAR_ARITY; } + unsigned get_arity() const override { return VAR_ARITY; } - virtual void prepare(cmd_context & ctx) { + void prepare(cmd_context & ctx) override { m_ctx = alloc(context, ctx.m()); } - virtual void finalize(cmd_context & ctx) { - m_ctx = 0; + void finalize(cmd_context & ctx) override { + m_ctx = nullptr; } - virtual void failure_cleanup(cmd_context & ctx) { - m_ctx = 0; + void failure_cleanup(cmd_context & ctx) override { + m_ctx = nullptr; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR; } - virtual void set_next_arg(cmd_context & ctx, expr * arg) { + void set_next_arg(cmd_context & ctx, expr * arg) override { m_ctx->set_next_arg(ctx, arg); } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { m_ctx->execute(ctx); - m_ctx = 0; + m_ctx = nullptr; } }; @@ -204,31 +204,31 @@ class poly_factor_cmd : public parametric_cmd { public: poly_factor_cmd(char const * name = "poly/factor"):parametric_cmd(name) {} - virtual char const * get_usage() const { return " ( )*"; } + char const * get_usage() const override { return " ( )*"; } - virtual char const * get_main_descr() const { + char const * get_main_descr() const override { return "factor a polynomial"; } - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { polynomial::factor_params::get_param_descrs(p); } - virtual void prepare(cmd_context & ctx) { + void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); - m_target = 0; + m_target = nullptr; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { - if (m_target == 0) return CPK_EXPR; + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { + if (m_target == nullptr) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } - virtual void set_next_arg(cmd_context & ctx, expr * arg) { + void set_next_arg(cmd_context & ctx, expr * arg) override { m_target = arg; } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { polynomial::factor_params ps; ps.updt_params(m_params); factor(ctx, m_target, ps); diff --git a/src/cmd_context/interpolant_cmds.cpp b/src/cmd_context/interpolant_cmds.cpp deleted file mode 100644 index 8b9f0ebd8..000000000 --- a/src/cmd_context/interpolant_cmds.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/*++ - Copyright (c) 2013 Microsoft Corporation - - Module Name: - - interpolant_cmds.cpp - - Abstract: - Commands for interpolation. - - Author: - - Leonardo (leonardo) 2011-12-23 - - Notes: - - --*/ -#include -#include "cmd_context/cmd_context.h" -#include "cmd_context/cmd_util.h" -#include "util/scoped_timer.h" -#include "util/scoped_ctrl_c.h" -#include "util/cancel_eh.h" -#include "ast/ast_pp.h" -#include "ast/ast_smt_pp.h" -#include "ast/ast_smt2_pp.h" -#include "cmd_context/parametric_cmd.h" -#include "util/mpq.h" -#include "ast/expr2var.h" -#include "ast/pp.h" -#include "interp/iz3interp.h" -#include "interp/iz3checker.h" -#include "interp/iz3profiling.h" -#include "interp/interp_params.hpp" -#include "ast/scoped_proof.h" - -static void show_interpolant_and_maybe_check(cmd_context & ctx, - ptr_vector &cnsts, - expr *t, - ptr_vector &interps, - params_ref &m_params, - bool check) -{ - - if (m_params.get_bool("som", false)) - m_params.set_bool("flat", true); - th_rewriter s(ctx.m(), m_params); - - ctx.regular_stream() << "(interpolants"; - for(unsigned i = 0; i < interps.size(); i++){ - expr_ref r(ctx.m()); - proof_ref pr(ctx.m()); - s(to_expr(interps[i]),r,pr); - ctx.regular_stream() << "\n " << r; - } - ctx.regular_stream() << ")\n"; - - s.cleanup(); - - // verify, for the paranoid... - if(check || interp_params(m_params).check()){ - std::ostringstream err; - ast_manager &_m = ctx.m(); - - // need a solver -- make one here FIXME is this right? - bool proofs_enabled, models_enabled, unsat_core_enabled; - params_ref p; - ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); - scoped_ptr sp = (ctx.get_solver_factory())(_m, p, false, true, false, ctx.get_logic()); - - if(iz3check(_m,sp.get(),err,cnsts,t,interps)) - ctx.regular_stream() << "correct\n"; - else - ctx.regular_stream() << "incorrect: " << err.str().c_str() << "\n"; - } - - for(unsigned i = 0; i < interps.size(); i++){ - ctx.m().dec_ref(interps[i]); - } - - interp_params itp_params(m_params); - if(itp_params.profile()) - profiling::print(ctx.regular_stream()); - -} - -static void check_can_interpolate(cmd_context & ctx){ - if (!ctx.produce_interpolants()) - throw cmd_exception("interpolation is not enabled, use command (set-option :produce-interpolants true)"); -} - - -static void get_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check) { - - check_can_interpolate(ctx); - - // get the proof, if there is one - - if (!ctx.has_manager() || - ctx.cs_state() != cmd_context::css_unsat) - throw cmd_exception("proof is not available"); - expr_ref pr(ctx.m()); - pr = ctx.get_check_sat_result()->get_proof(); - if (pr == 0) - throw cmd_exception("proof is not available"); - - // get the assertions from the context - - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); - ptr_vector cnsts((unsigned)(end - it)); - for (int i = 0; it != end; ++it, ++i) - cnsts[i] = *it; - - // compute an interpolant - - ptr_vector interps; - - try { - iz3interpolate(ctx.m(),pr.get(),cnsts,t,interps,0); - } - catch (iz3_bad_tree &) { - throw cmd_exception("interpolation pattern contains non-asserted formula"); - } - catch (iz3_incompleteness &) { - throw cmd_exception("incompleteness in interpolator"); - } - - show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check); -} - -static void get_interpolant(cmd_context & ctx, expr * t, params_ref &m_params) { - get_interpolant_and_maybe_check(ctx,t,m_params,false); -} - -#if 0 -static void get_and_check_interpolant(cmd_context & ctx, params_ref &m_params, expr * t) { - get_interpolant_and_maybe_check(ctx,t,m_params,true); -} -#endif - -static void compute_interpolant_and_maybe_check(cmd_context & ctx, expr * t, params_ref &m_params, bool check){ - - // create a fresh solver suitable for interpolation - bool proofs_enabled, models_enabled, unsat_core_enabled; - params_ref p; - ast_manager &_m = ctx.m(); - // TODO: the following is a HACK to enable proofs in the old smt solver - // When we stop using that solver, this hack can be removed - scoped_proof_mode spm(_m,PGM_ENABLED); - ctx.params().get_solver_params(_m, p, proofs_enabled, models_enabled, unsat_core_enabled); - p.set_bool("proof", true); - scoped_ptr sp = (ctx.get_interpolating_solver_factory())(_m, p, true, models_enabled, false, ctx.get_logic()); - - ptr_vector cnsts; - ptr_vector interps; - model_ref m; - - // compute an interpolant - - lbool res; - try { - res = iz3interpolate(_m, *sp.get(), t, cnsts, interps, m, 0); - } - catch (iz3_incompleteness &) { - throw cmd_exception("incompleteness in interpolator"); - } - - switch(res){ - case l_false: - ctx.regular_stream() << "unsat\n"; - show_interpolant_and_maybe_check(ctx, cnsts, t, interps, m_params, check); - break; - - case l_true: - ctx.regular_stream() << "sat\n"; - // TODO: how to return the model to the context, if it exists? - break; - - case l_undef: - ctx.regular_stream() << "unknown\n"; - // TODO: how to return the model to the context, if it exists? - break; - } - - for(unsigned i = 0; i < cnsts.size(); i++) - ctx.m().dec_ref(cnsts[i]); - -} - -static expr *make_tree(cmd_context & ctx, const ptr_vector &exprs){ - if(exprs.size() == 0) - throw cmd_exception("not enough arguments"); - expr *foo = exprs[0]; - for(unsigned i = 1; i < exprs.size(); i++){ - foo = ctx.m().mk_and(ctx.m().mk_interp(foo),exprs[i]); - } - return foo; -} - -static void get_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { - expr_ref foo(make_tree(ctx, exprs),ctx.m()); - get_interpolant(ctx,foo.get(),m_params); -} - -static void compute_interpolant(cmd_context & ctx, const ptr_vector &exprs, params_ref &m_params) { - expr_ref foo(make_tree(ctx, exprs),ctx.m()); - compute_interpolant_and_maybe_check(ctx,foo.get(),m_params,false); -} - - -// UNARY_CMD(get_interpolant_cmd, "get-interpolant", "", "get interpolant for marked positions in fmla", CPK_EXPR, expr *, get_interpolant(ctx, arg);); - -// UNARY_CMD(get_and_check_interpolant_cmd, "get-and-check-interpolant", "", "get and check interpolant for marked positions in fmla", CPK_EXPR, expr *, get_and_check_interpolant(ctx, arg);); - -class get_interpolant_cmd : public parametric_cmd { -protected: - ptr_vector m_targets; -public: - get_interpolant_cmd(char const * name = "get-interpolant"):parametric_cmd(name) {} - - virtual char const * get_usage() const { return "+"; } - - virtual char const * get_main_descr() const { - return "get interpolant for formulas"; - } - - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { - } - - virtual void prepare(cmd_context & ctx) { - parametric_cmd::prepare(ctx); - m_targets.resize(0); - } - - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { - return CPK_EXPR; - } - - virtual void set_next_arg(cmd_context & ctx, expr * arg) { - m_targets.push_back(arg); - } - - virtual void execute(cmd_context & ctx) { - get_interpolant(ctx,m_targets,m_params); - } -}; - -class compute_interpolant_cmd : public get_interpolant_cmd { -public: - compute_interpolant_cmd(char const * name = "compute-interpolant"):get_interpolant_cmd(name) {} - - virtual void execute(cmd_context & ctx) { - compute_interpolant(ctx,m_targets,m_params); - } - -}; - -void install_interpolant_cmds(cmd_context & ctx) { - ctx.insert(alloc(get_interpolant_cmd)); - ctx.insert(alloc(compute_interpolant_cmd)); - // ctx.insert(alloc(get_and_check_interpolant_cmd)); -} diff --git a/src/cmd_context/interpolant_cmds.h b/src/cmd_context/interpolant_cmds.h deleted file mode 100644 index daef70926..000000000 --- a/src/cmd_context/interpolant_cmds.h +++ /dev/null @@ -1,24 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - interpolant_cmds.h - - Abstract: - Commands for interpolation. - - Author: - - Leonardo (leonardo) 2011-12-23 - - Notes: - - --*/ -#ifndef INTERPOLANT_CMDS_H_ -#define INTERPOLANT_CMDS_H_ - -class cmd_context; -void install_interpolant_cmds(cmd_context & ctx); - -#endif diff --git a/src/cmd_context/parametric_cmd.cpp b/src/cmd_context/parametric_cmd.cpp index 4a85821b2..524662ed3 100644 --- a/src/cmd_context/parametric_cmd.cpp +++ b/src/cmd_context/parametric_cmd.cpp @@ -20,7 +20,7 @@ Notes: #include "cmd_context/parametric_cmd.h" char const * parametric_cmd::get_descr(cmd_context & ctx) const { - if (m_descr == 0) { + if (m_descr == nullptr) { const_cast(this)->m_descr = alloc(string_buffer<>); m_descr->append(get_main_descr()); m_descr->append("\nThe following options are available:\n"); diff --git a/src/cmd_context/parametric_cmd.h b/src/cmd_context/parametric_cmd.h index 6d676d6f9..79517219d 100644 --- a/src/cmd_context/parametric_cmd.h +++ b/src/cmd_context/parametric_cmd.h @@ -30,49 +30,49 @@ public: params_ref m_params; scoped_ptr m_pdescrs; public: - parametric_cmd(char const * name):cmd(name), m_descr(0) {} - virtual ~parametric_cmd() { if (m_descr) dealloc(m_descr); } + parametric_cmd(char const * name):cmd(name), m_descr(nullptr) {} + ~parametric_cmd() override { if (m_descr) dealloc(m_descr); } virtual void init_pdescrs(cmd_context & ctx, param_descrs & d) = 0; param_descrs const & pdescrs(cmd_context & ctx) const; params_ref const & ps() const { return m_params; } virtual char const * get_main_descr() const = 0; - virtual char const * get_descr(cmd_context & ctx) const; - virtual unsigned get_arity() const { return VAR_ARITY; } - virtual void prepare(cmd_context & ctx) { m_last = symbol::null; m_params.reset(); } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const; - virtual void set_next_arg(cmd_context & ctx, symbol const & s); - virtual void set_next_arg(cmd_context & ctx, unsigned val) { + char const * get_descr(cmd_context & ctx) const override; + unsigned get_arity() const override { return VAR_ARITY; } + void prepare(cmd_context & ctx) override { m_last = symbol::null; m_params.reset(); } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override; + void set_next_arg(cmd_context & ctx, symbol const & s) override; + void set_next_arg(cmd_context & ctx, unsigned val) override { m_params.set_uint(m_last, val); m_last = symbol::null; } - virtual void set_next_arg(cmd_context & ctx, bool val) { + void set_next_arg(cmd_context & ctx, bool val) override { m_params.set_bool(m_last, val); m_last = symbol::null; } - virtual void set_next_arg(cmd_context & ctx, rational const & val) { + void set_next_arg(cmd_context & ctx, rational const & val) override { m_params.set_rat(m_last, val); m_last = symbol::null; } - virtual void set_next_arg(cmd_context & ctx, char const * val) { + void set_next_arg(cmd_context & ctx, char const * val) override { m_params.set_str(m_last, val); m_last = symbol::null; } - virtual void set_next_arg(cmd_context & ctx, sort * s) { + void set_next_arg(cmd_context & ctx, sort * s) override { NOT_IMPLEMENTED_YET(); // m_params.set_sort(m_last, s); // m_last = symbol::null; } - virtual void set_next_arg(cmd_context & ctx, expr * t) { + void set_next_arg(cmd_context & ctx, expr * t) override { NOT_IMPLEMENTED_YET(); // m_params.set_expr(m_last, t); // m_last = symbol::null; } - virtual void set_next_arg(cmd_context & ctx, func_decl * f) { + void set_next_arg(cmd_context & ctx, func_decl * f) override { NOT_IMPLEMENTED_YET(); // m_params.set_func_decl(m_last, f); // m_last = symbol::null; } - virtual void set_next_arg(cmd_context & ctx, sexpr * n) { UNREACHABLE(); } + void set_next_arg(cmd_context & ctx, sexpr * n) override { UNREACHABLE(); } }; #endif diff --git a/src/cmd_context/pdecl.cpp b/src/cmd_context/pdecl.cpp index f75fbfa29..5a0f6468b 100644 --- a/src/cmd_context/pdecl.cpp +++ b/src/cmd_context/pdecl.cpp @@ -18,6 +18,7 @@ Revision History: --*/ #include "cmd_context/pdecl.h" #include "ast/datatype_decl_plugin.h" +#include using namespace format_ns; class psort_inst_cache { @@ -25,7 +26,7 @@ class psort_inst_cache { sort * m_const; obj_map m_map; // if m_num_params == 1 value is a sort, otherwise it is a reference to another inst_cache public: - psort_inst_cache(unsigned num_params):m_num_params(num_params), m_const(0) { + psort_inst_cache(unsigned num_params):m_num_params(num_params), m_const(nullptr) { } ~psort_inst_cache() { SASSERT(m_map.empty()); SASSERT(m_const == 0); } @@ -35,7 +36,7 @@ public: SASSERT(m_map.empty()); if (m_const) m.m().dec_ref(m_const); - m_const = 0; + m_const = nullptr; } else { SASSERT(m_const == 0); @@ -71,7 +72,7 @@ public: m.m().inc_ref(r); return; } - void * next = 0; + void * next = nullptr; if (!curr->m_map.find(*s, next)) { next = new (m.a().allocate(sizeof(psort_inst_cache))) psort_inst_cache(curr->m_num_params-1); curr->m_map.insert(*s, next); @@ -90,22 +91,22 @@ public: psort_inst_cache const * curr = this; while (true) { if (curr->m_num_params == 1) { - void * r = 0; + void * r = nullptr; curr->m_map.find(*s, r); return static_cast(r); } else { - void * next = 0; + void * next = nullptr; curr->m_map.find(*s, next); - if (next == 0) - return 0; + if (next == nullptr) + return nullptr; s++; curr = static_cast(next); } } } - bool empty() const { return m_num_params == 0 ? m_const == 0 : m_map.empty(); } + bool empty() const { return m_num_params == 0 ? m_const == nullptr : m_map.empty(); } }; void psort::cache(pdecl_manager & m, sort * const * s, sort * r) { @@ -116,7 +117,7 @@ void psort::cache(pdecl_manager & m, sort * const * s, sort * r) { sort * psort::find(sort * const * s) const { if (!m_inst_cache) - return 0; + return nullptr; return m_inst_cache->find(s); } @@ -126,7 +127,7 @@ void psort::finalize(pdecl_manager & m) { void psort::reset_cache(pdecl_manager& m) { m.del_inst_cache(m_inst_cache); - m_inst_cache = 0; + m_inst_cache = nullptr; } /** @@ -136,25 +137,25 @@ class psort_sort : public psort { friend class pdecl_manager; sort * m_sort; psort_sort(unsigned id, pdecl_manager & m, sort * s):psort(id, 0), m_sort(s) { m.m().inc_ref(m_sort); } - virtual void finalize(pdecl_manager & m) { + void finalize(pdecl_manager & m) override { m.m().dec_ref(m_sort); psort::finalize(m); } - virtual bool check_num_params(pdecl * other) const { return true; } - virtual size_t obj_size() const { return sizeof(psort_sort); } + bool check_num_params(pdecl * other) const override { return true; } + size_t obj_size() const override { return sizeof(psort_sort); } sort * get_sort() const { return m_sort; } - virtual sort * instantiate(pdecl_manager & m, sort * const * s) { return m_sort; } + sort * instantiate(pdecl_manager & m, sort * const * s) override { return m_sort; } public: - virtual ~psort_sort() {} - virtual bool is_sort_wrapper() const { return true; } - virtual char const * hcons_kind() const { return "psort_sort"; } - virtual unsigned hcons_hash() const { return m_sort->get_id(); } - virtual bool hcons_eq(psort const * other) const { + ~psort_sort() override {} + bool is_sort_wrapper() const override { return true; } + char const * hcons_kind() const override { return "psort_sort"; } + unsigned hcons_hash() const override { return m_sort->get_id(); } + bool hcons_eq(psort const * other) const override { if (other->hcons_kind() != hcons_kind()) return false; return m_sort == static_cast(other)->m_sort; } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { out << m_sort->get_name(); } }; @@ -163,19 +164,19 @@ class psort_var : public psort { friend class pdecl_manager; unsigned m_idx; psort_var(unsigned id, unsigned num_params, unsigned idx):psort(id, num_params), m_idx(idx) { SASSERT(idx < num_params); } - virtual sort * instantiate(pdecl_manager & m, sort * const * s) { return s[m_idx]; } - virtual size_t obj_size() const { return sizeof(psort_var); } + sort * instantiate(pdecl_manager & m, sort * const * s) override { return s[m_idx]; } + size_t obj_size() const override { return sizeof(psort_var); } public: - virtual ~psort_var() {} - virtual char const * hcons_kind() const { return "psort_var"; } - virtual unsigned hcons_hash() const { return hash_u_u(m_num_params, m_idx); } - virtual bool hcons_eq(psort const * other) const { + ~psort_var() override {} + char const * hcons_kind() const override { return "psort_var"; } + unsigned hcons_hash() const override { return hash_u_u(m_num_params, m_idx); } + bool hcons_eq(psort const * other) const override { return other->hcons_kind() == hcons_kind() && get_num_params() == other->get_num_params() && m_idx == static_cast(other)->m_idx; } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { out << "s_" << m_idx; } unsigned idx() const { return m_idx; } @@ -196,13 +197,13 @@ class psort_app : public psort { DEBUG_CODE(if (num_args == num_params) { for (unsigned i = 0; i < num_params; i++) args[i]->check_num_params(this); }); } - virtual void finalize(pdecl_manager & m) { + void finalize(pdecl_manager & m) override { m.lazy_dec_ref(m_decl); m.lazy_dec_ref(m_args.size(), m_args.c_ptr()); psort::finalize(m); } - virtual size_t obj_size() const { return sizeof(psort_app); } + size_t obj_size() const override { return sizeof(psort_app); } struct khasher { unsigned operator()(psort_app const * d) const { return d->m_decl->hash(); } @@ -212,7 +213,7 @@ class psort_app : public psort { unsigned operator()(psort_app const * d, unsigned idx) const { return d->m_args[idx]->hash(); } }; - virtual sort * instantiate(pdecl_manager & m, sort * const * s) { + sort * instantiate(pdecl_manager & m, sort * const * s) override { sort * r = find(s); if (r) return r; @@ -228,12 +229,12 @@ class psort_app : public psort { } public: - virtual ~psort_app() {} - virtual char const * hcons_kind() const { return "psort_app"; } - virtual unsigned hcons_hash() const { + ~psort_app() override {} + char const * hcons_kind() const override { return "psort_app"; } + unsigned hcons_hash() const override { return get_composite_hash(const_cast(this), m_args.size()); } - virtual bool hcons_eq(psort const * other) const { + bool hcons_eq(psort const * other) const override { if (other->hcons_kind() != hcons_kind()) return false; if (get_num_params() != other->get_num_params()) @@ -249,7 +250,7 @@ public: } return true; } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { if (m_args.empty()) { out << m_decl->get_name(); } @@ -269,7 +270,7 @@ psort_decl::psort_decl(unsigned id, unsigned num_params, pdecl_manager & m, symb pdecl(id, num_params), m_name(n), m_psort_kind(PSORT_BASE), - m_inst_cache(0) { + m_inst_cache(nullptr) { } void psort_decl::finalize(pdecl_manager & m) { @@ -278,7 +279,7 @@ void psort_decl::finalize(pdecl_manager & m) { void psort_decl::reset_cache(pdecl_manager& m) { m.del_inst_cache(m_inst_cache); - m_inst_cache = 0; + m_inst_cache = nullptr; } void psort_decl::cache(pdecl_manager & m, sort * const * s, sort * r) { @@ -289,7 +290,7 @@ void psort_decl::cache(pdecl_manager & m, sort * const * s, sort * r) { sort * psort_decl::find(sort * const * s) { if (!m_inst_cache) - return 0; + return nullptr; return m_inst_cache->find(s); } @@ -303,7 +304,7 @@ psort_user_decl::psort_user_decl(unsigned id, unsigned num_params, pdecl_manager void psort_user_decl::finalize(pdecl_manager & m) { m.dec_ref(m_def); - m_def = 0; + m_def = nullptr; psort_decl::finalize(m); } @@ -312,7 +313,7 @@ sort * psort_user_decl::instantiate(pdecl_manager & m, unsigned n, sort * const sort * r = find(s); if (r) return r; - if (m_def == 0) { + if (m_def == nullptr) { buffer ps; for (unsigned i = 0; i < n; i++) ps.push_back(parameter(s[i])); @@ -462,7 +463,7 @@ accessor_decl * paccessor_decl::instantiate_decl(pdecl_manager & m, sort * const default: // missing refs must have been eliminated. UNREACHABLE(); - return 0; + return nullptr; } } @@ -522,7 +523,7 @@ pdatatype_decl::pdatatype_decl(unsigned id, unsigned num_params, pdecl_manager & symbol const & n, unsigned num_constructors, pconstructor_decl * const * constructors): psort_decl(id, num_params, m, n), m_constructors(num_constructors, constructors), - m_parent(0) { + m_parent(nullptr) { m.inc_ref(num_constructors, constructors); } @@ -633,7 +634,7 @@ bool pdatatype_decl::commit(pdecl_manager& m) { TRACE("datatype", tout << m_name << "\n";); sort_ref_vector ps(m.m()); for (unsigned i = 0; i < m_num_params; ++i) { - ps.push_back(m.m().mk_uninterpreted_sort(symbol(i), 0, 0)); + ps.push_back(m.m().mk_uninterpreted_sort(symbol(i), 0, nullptr)); } datatype_decl_buffer dts; dts.m_buffer.push_back(instantiate_decl(m, ps.c_ptr())); @@ -712,7 +713,7 @@ bool pdatatypes_decl::commit(pdecl_manager& m) { for (pdatatype_decl* d : m_datatypes) { sort_ref_vector ps(m.m()); for (unsigned i = 0; i < d->get_num_params(); ++i) { - ps.push_back(m.m().mk_uninterpreted_sort(symbol(i), 0, 0)); + ps.push_back(m.m().mk_uninterpreted_sort(symbol(i), 0, nullptr)); } dts.m_buffer.push_back(d->instantiate_decl(m, ps.c_ptr())); } @@ -752,16 +753,16 @@ struct pdecl_manager::app_sort_info : public pdecl_manager::sort_info { m.m().inc_array_ref(n, s); } - virtual ~app_sort_info() {} + ~app_sort_info() override {} - virtual unsigned obj_size() const { return sizeof(app_sort_info); } + unsigned obj_size() const override { return sizeof(app_sort_info); } - virtual void finalize(pdecl_manager & m) { + void finalize(pdecl_manager & m) override { sort_info::finalize(m); m.m().dec_array_ref(m_args.size(), m_args.c_ptr()); } - virtual void display(std::ostream & out, pdecl_manager const & m) const { + void display(std::ostream & out, pdecl_manager const & m) const override { if (m_args.empty()) { out << m_decl->get_name(); } @@ -775,7 +776,7 @@ struct pdecl_manager::app_sort_info : public pdecl_manager::sort_info { } } - virtual format * pp(pdecl_manager const & m) const { + format * pp(pdecl_manager const & m) const override { if (m_args.empty()) { return mk_string(m.m(), m_decl->get_name().str().c_str()); } @@ -796,11 +797,11 @@ struct pdecl_manager::indexed_sort_info : public pdecl_manager::sort_info { m_indices(n, s) { } - virtual ~indexed_sort_info() {} + ~indexed_sort_info() override {} - virtual unsigned obj_size() const { return sizeof(indexed_sort_info); } + unsigned obj_size() const override { return sizeof(indexed_sort_info); } - virtual void display(std::ostream & out, pdecl_manager const & m) const { + void display(std::ostream & out, pdecl_manager const & m) const override { if (m_indices.empty()) { out << m_decl->get_name(); } @@ -813,7 +814,7 @@ struct pdecl_manager::indexed_sort_info : public pdecl_manager::sort_info { } } - virtual format * pp(pdecl_manager const & m) const { + format * pp(pdecl_manager const & m) const override { if (m_indices.empty()) { return mk_string(m.m(), m_decl->get_name().str().c_str()); } @@ -834,7 +835,7 @@ void pdecl_manager::init_list() { ptype ListT(0); paccessor_decl * as[2] = { mk_paccessor_decl(1, symbol("head"), T), mk_paccessor_decl(1, symbol("tail"), ListT) }; - pconstructor_decl * cs[2] = { mk_pconstructor_decl(1, symbol("nil"), symbol("is-nil"), 0, 0), + pconstructor_decl * cs[2] = { mk_pconstructor_decl(1, symbol("nil"), symbol("is-nil"), 0, nullptr), mk_pconstructor_decl(1, symbol("insert"), symbol("is-insert"), 2, as) }; m_list = mk_pdatatype_decl(1, symbol("List"), 2, cs); inc_ref(m_list); @@ -844,20 +845,20 @@ void pdecl_manager::init_list() { pdecl_manager::pdecl_manager(ast_manager & m): m_manager(m), m_allocator(m.get_allocator()), - m_new_dt_eh(0) { - m_list = 0; + m_new_dt_eh(nullptr) { + m_list = nullptr; m_datatype_fid = m.mk_family_id("datatype"); } pdecl_manager::~pdecl_manager() { dec_ref(m_list); reset_sort_info(); - SASSERT(m_sort2psort.empty()); + SASSERT(m_sort2psort.empty()); SASSERT(m_table.empty()); } psort * pdecl_manager::mk_psort_cnst(sort * s) { - psort * r = 0; + psort * r = nullptr; if (m_sort2psort.find(s, r)) return r; r = new (a().allocate(sizeof(psort_sort))) psort_sort(m_id_gen.mk(), *this, s); @@ -866,7 +867,6 @@ psort * pdecl_manager::mk_psort_cnst(sort * s) { } psort * pdecl_manager::register_psort(psort * n) { - TRACE("register_psort", tout << "registering psort...\n"; n->display(tout); tout << "\n";); psort * r = m_table.insert_if_not_there(n); if (r != n) { del_decl_core(n); @@ -907,9 +907,9 @@ psort * pdecl_manager::mk_psort_app(unsigned num_params, psort_decl * d, unsigne psort * pdecl_manager::mk_psort_app(psort_decl * d) { SASSERT(d->get_num_params() == 0 || d->get_num_params() == PSORT_DECL_VAR_PARAMS); - sort * s = d->instantiate(*this, 0, static_cast(0)); - if (s == 0) - return 0; + sort * s = d->instantiate(*this, 0, static_cast(nullptr)); + if (s == nullptr) + return nullptr; return mk_psort_cnst(s); } @@ -946,6 +946,7 @@ void pdecl_manager::del_decl_core(pdecl * p) { } void pdecl_manager::del_decl(pdecl * p) { + TRACE("register_psort", tout << "del psort "; p->display(tout); tout << "\n";); if (p->is_psort()) { psort * _p = static_cast(p); if (_p->is_sort_wrapper()) @@ -1010,7 +1011,7 @@ void pdecl_manager::reset_sort_info() { } void pdecl_manager::display(std::ostream & out, sort * s) const { - sort_info * info = 0; + sort_info * info = nullptr; if (m_sort2info.find(s, info)) { info->display(out, *this); return; @@ -1019,7 +1020,7 @@ void pdecl_manager::display(std::ostream & out, sort * s) const { } format * pdecl_manager::pp(sort * s) const { - sort_info * info = 0; + sort_info * info = nullptr; if (m_sort2info.find(s, info)) { return info->pp(*this); } diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h index c72020827..12e6399fe 100644 --- a/src/cmd_context/pdecl.h +++ b/src/cmd_context/pdecl.h @@ -64,20 +64,20 @@ class psort : public pdecl { protected: psort_inst_cache * m_inst_cache; friend class pdecl_manager; - psort(unsigned id, unsigned num_params):pdecl(id, num_params), m_inst_cache(0) {} - virtual bool is_psort() const { return true; } - virtual void finalize(pdecl_manager & m); - virtual ~psort() {} + psort(unsigned id, unsigned num_params):pdecl(id, num_params), m_inst_cache(nullptr) {} + bool is_psort() const override { return true; } + void finalize(pdecl_manager & m) override; + ~psort() override {} virtual void cache(pdecl_manager & m, sort * const * s, sort * r); virtual sort * find(sort * const * s) const; public: virtual bool is_sort_wrapper() const { return false; } - virtual sort * instantiate(pdecl_manager & m, sort * const * s) { return 0; } + virtual sort * instantiate(pdecl_manager & m, sort * const * s) { return nullptr; } // we use hash-consing for psorts. virtual char const * hcons_kind() const = 0; virtual unsigned hcons_hash() const = 0; virtual bool hcons_eq(psort const * other) const = 0; - virtual void reset_cache(pdecl_manager& m); + void reset_cache(pdecl_manager& m) override; }; // for hash consing @@ -98,17 +98,17 @@ protected: void cache(pdecl_manager & m, sort * const * s, sort * r); sort * find(sort * const * s); psort_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n); - virtual void finalize(pdecl_manager & m); - virtual ~psort_decl() {} + void finalize(pdecl_manager & m) override; + ~psort_decl() override {} public: virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) = 0; - virtual sort * instantiate(pdecl_manager & m, unsigned n, unsigned const * s) { return 0; } - virtual sort * instantiate(pdecl_manager & m) { return instantiate(m, 0, static_cast(0)); } + virtual sort * instantiate(pdecl_manager & m, unsigned n, unsigned const * s) { return nullptr; } + virtual sort * instantiate(pdecl_manager & m) { return instantiate(m, 0, static_cast(nullptr)); } // return true if the declaration accepts a variable number of parameters. // Only builtin declarations can have a variable number of parameters. bool has_var_params() const { return m_num_params == PSORT_DECL_VAR_PARAMS; } symbol const & get_name() const { return m_name; } - virtual void reset_cache(pdecl_manager& m); + void reset_cache(pdecl_manager& m) override; bool is_user_decl() const { return m_psort_kind == PSORT_USER; } bool is_builtin_decl() const { return m_psort_kind == PSORT_BUILTIN; } bool is_dt_decl() const { return m_psort_kind == PSORT_DT; } @@ -119,12 +119,12 @@ protected: friend class pdecl_manager; psort * m_def; psort_user_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, psort * p); - virtual size_t obj_size() const { return sizeof(psort_user_decl); } - virtual void finalize(pdecl_manager & m); - virtual ~psort_user_decl() {} + size_t obj_size() const override { return sizeof(psort_user_decl); } + void finalize(pdecl_manager & m) override; + ~psort_user_decl() override {} public: - virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s); - virtual void display(std::ostream & out) const; + sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; + void display(std::ostream & out) const override; }; class psort_builtin_decl : public psort_decl { @@ -133,23 +133,23 @@ protected: family_id m_fid; decl_kind m_kind; psort_builtin_decl(unsigned id, pdecl_manager & m, symbol const & n, family_id fid, decl_kind k); - virtual size_t obj_size() const { return sizeof(psort_builtin_decl); } - virtual ~psort_builtin_decl() {} + size_t obj_size() const override { return sizeof(psort_builtin_decl); } + ~psort_builtin_decl() override {} public: - virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s); - virtual sort * instantiate(pdecl_manager & m, unsigned n, unsigned const * s); - virtual void display(std::ostream & out) const; + sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; + sort * instantiate(pdecl_manager & m, unsigned n, unsigned const * s) override; + void display(std::ostream & out) const override; }; class psort_dt_decl : public psort_decl { protected: friend class pdecl_manager; psort_dt_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n); - virtual size_t obj_size() const { return sizeof(psort_dt_decl); } - virtual ~psort_dt_decl() {} + size_t obj_size() const override { return sizeof(psort_dt_decl); } + ~psort_dt_decl() override {} public: - virtual sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s); - virtual void display(std::ostream & out) const; + sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; + void display(std::ostream & out) const override; }; @@ -172,7 +172,7 @@ class ptype { }; symbol m_missing_ref; public: - ptype():m_kind(PTR_PSORT), m_sort(0) {} + ptype():m_kind(PTR_PSORT), m_sort(nullptr) {} ptype(int idx):m_kind(PTR_REC_REF), m_idx(idx) {} ptype(psort * s):m_kind(PTR_PSORT), m_sort(s) {} ptype(symbol const & s):m_kind(PTR_MISSING_REF), m_missing_ref(s) {} @@ -190,16 +190,16 @@ class paccessor_decl : public pdecl { symbol m_name; ptype m_type; paccessor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, ptype const & r); - virtual void finalize(pdecl_manager & m); - virtual size_t obj_size() const { return sizeof(paccessor_decl); } + void finalize(pdecl_manager & m) override; + size_t obj_size() const override { return sizeof(paccessor_decl); } bool has_missing_refs(symbol & missing) const; bool fix_missing_refs(dictionary const & symbol2idx, symbol & missing); accessor_decl * instantiate_decl(pdecl_manager & m, sort * const * s); symbol const & get_name() const { return m_name; } ptype const & get_type() const { return m_type; } - virtual ~paccessor_decl() {} + ~paccessor_decl() override {} public: - virtual void display(std::ostream & out) const { pdecl::display(out); } + void display(std::ostream & out) const override { pdecl::display(out); } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; @@ -211,16 +211,16 @@ class pconstructor_decl : public pdecl { ptr_vector m_accessors; pconstructor_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, symbol const & r, unsigned num_accessors, paccessor_decl * const * accessors); - virtual void finalize(pdecl_manager & m); - virtual size_t obj_size() const { return sizeof(pconstructor_decl); } + void finalize(pdecl_manager & m) override; + size_t obj_size() const override { return sizeof(pconstructor_decl); } bool has_missing_refs(symbol & missing) const; bool fix_missing_refs(dictionary const & symbol2idx, symbol & missing); symbol const & get_name() const { return m_name; } symbol const & get_recognizer_name() const { return m_recogniser_name; } constructor_decl * instantiate_decl(pdecl_manager & m, sort * const * s); - virtual ~pconstructor_decl() {} + ~pconstructor_decl() override {} public: - virtual void display(std::ostream & out) const { pdecl::display(out); } + void display(std::ostream & out) const override { pdecl::display(out); } void display(std::ostream & out, pdatatype_decl const * const * dts) const; }; @@ -231,14 +231,14 @@ class pdatatype_decl : public psort_decl { pdatatypes_decl * m_parent; pdatatype_decl(unsigned id, unsigned num_params, pdecl_manager & m, symbol const & n, unsigned num_constructors, pconstructor_decl * const * constructors); - virtual void finalize(pdecl_manager & m); - virtual size_t obj_size() const { return sizeof(pdatatype_decl); } + void finalize(pdecl_manager & m) override; + size_t obj_size() const override { return sizeof(pdatatype_decl); } bool fix_missing_refs(dictionary const & symbol2idx, symbol & missing); datatype_decl * instantiate_decl(pdecl_manager & m, sort * const * s); - virtual ~pdatatype_decl() {} + ~pdatatype_decl() override {} public: - sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s); - virtual void display(std::ostream & out) const; + sort * instantiate(pdecl_manager & m, unsigned n, sort * const * s) override; + void display(std::ostream & out) const override; bool has_missing_refs(symbol & missing) const; bool has_duplicate_accessors(symbol & repeated) const; bool commit(pdecl_manager& m); @@ -252,11 +252,11 @@ class pdatatypes_decl : public pdecl { friend class pdatatype_decl; ptr_vector m_datatypes; pdatatypes_decl(unsigned id, unsigned num_params, pdecl_manager & m, unsigned num_datatypes, pdatatype_decl * const * dts); - virtual void finalize(pdecl_manager & m); - virtual size_t obj_size() const { return sizeof(pdatatypes_decl); } + void finalize(pdecl_manager & m) override; + size_t obj_size() const override { return sizeof(pdatatypes_decl); } bool fix_missing_refs(symbol & missing); bool instantiate(pdecl_manager & m, sort * const * s); - virtual ~pdatatypes_decl() {} + ~pdatatypes_decl() override {} public: pdatatype_decl const * const * children() const { return m_datatypes.c_ptr(); } pdatatype_decl * const * begin() const { return m_datatypes.begin(); } diff --git a/src/cmd_context/simplify_cmd.cpp b/src/cmd_context/simplify_cmd.cpp index 5112a6ea2..de548562e 100644 --- a/src/cmd_context/simplify_cmd.cpp +++ b/src/cmd_context/simplify_cmd.cpp @@ -36,13 +36,13 @@ class simplify_cmd : public parametric_cmd { public: th_solver(cmd_context& ctx): m_ctx(ctx) {} - virtual lbool check_sat(expr* e) { + lbool check_sat(expr* e) override { if (!m_solver) { m_solver = m_ctx.get_solver_factory()(m_ctx.m(), m_params, false, true, false, symbol::null); } m_solver->push(); m_solver->assert_expr(e); - lbool r = m_solver->check_sat(0,0); + lbool r = m_solver->check_sat(0,nullptr); m_solver->pop(1); return r; } @@ -52,13 +52,13 @@ class simplify_cmd : public parametric_cmd { public: simplify_cmd(char const * name = "simplify"):parametric_cmd(name) {} - virtual char const * get_usage() const { return " ( )*"; } + char const * get_usage() const override { return " ( )*"; } - virtual char const * get_main_descr() const { + char const * get_main_descr() const override { return "simplify the given term using builtin theory simplification rules."; } - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { th_rewriter::get_param_descrs(p); insert_timeout(p); p.insert("print", CPK_BOOL, "(default: true) print the simplified term."); @@ -66,25 +66,25 @@ public: p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); } - virtual ~simplify_cmd() { + ~simplify_cmd() override { } - virtual void prepare(cmd_context & ctx) { + void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); - m_target = 0; + m_target = nullptr; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { - if (m_target == 0) return CPK_EXPR; + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { + if (m_target == nullptr) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } - virtual void set_next_arg(cmd_context & ctx, expr * arg) { + void set_next_arg(cmd_context & ctx, expr * arg) override { m_target = arg; } - virtual void execute(cmd_context & ctx) { - if (m_target == 0) + void execute(cmd_context & ctx) override { + if (m_target == nullptr) throw cmd_exception("invalid simplify command, argument expected"); expr_ref r(ctx.m()); proof_ref pr(ctx.m()); diff --git a/src/cmd_context/tactic_cmds.cpp b/src/cmd_context/tactic_cmds.cpp index cbc90c62c..cbe44da2d 100644 --- a/src/cmd_context/tactic_cmds.cpp +++ b/src/cmd_context/tactic_cmds.cpp @@ -55,20 +55,20 @@ class declare_tactic_cmd : public cmd { public: declare_tactic_cmd(): cmd("declare-tactic"), - m_decl(0) { + m_decl(nullptr) { } - virtual char const * get_usage() const { return " "; } - virtual char const * get_descr(cmd_context & ctx) const { return "declare a new tactic, use (help-tactic) for the tactic language syntax."; } - virtual unsigned get_arity() const { return 2; } - virtual void prepare(cmd_context & ctx) { m_name = symbol::null; m_decl = 0; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + char const * get_usage() const override { return " "; } + char const * get_descr(cmd_context & ctx) const override { return "declare a new tactic, use (help-tactic) for the tactic language syntax."; } + unsigned get_arity() const override { return 2; } + void prepare(cmd_context & ctx) override { m_name = symbol::null; m_decl = nullptr; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_name == symbol::null) return CPK_SYMBOL; return CPK_SEXPR; } - virtual void set_next_arg(cmd_context & ctx, symbol const & s) { m_name = s; } - virtual void set_next_arg(cmd_context & ctx, sexpr * n) { m_decl = n; } - virtual void execute(cmd_context & ctx) { + void set_next_arg(cmd_context & ctx, symbol const & s) override { m_name = s; } + void set_next_arg(cmd_context & ctx, sexpr * n) override { m_decl = n; } + void execute(cmd_context & ctx) override { tactic_ref t = sexpr2tactic(ctx, m_decl); // make sure the tactic is well formed. ctx.insert_user_tactic(m_name, m_decl); } @@ -133,23 +133,23 @@ public: parametric_cmd(name) { } - virtual char const * get_usage() const { return " ( )*"; } + char const * get_usage() const override { return " ( )*"; } - virtual void prepare(cmd_context & ctx) { + void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); - m_tactic = 0; + m_tactic = nullptr; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { - if (m_tactic == 0) return CPK_SEXPR; + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { + if (m_tactic == nullptr) return CPK_SEXPR; return parametric_cmd::next_arg_kind(ctx); } - virtual void set_next_arg(cmd_context & ctx, sexpr * arg) { + void set_next_arg(cmd_context & ctx, sexpr * arg) override { m_tactic = arg; } - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { insert_timeout(p); insert_max_memory(p); p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); @@ -172,7 +172,7 @@ public: check_sat_tactic_result(ast_manager & m) : simple_check_sat_result(m) { } - virtual void get_labels(svector & r) { + void get_labels(svector & r) override { r.append(labels); } @@ -187,25 +187,27 @@ public: exec_given_tactic_cmd("check-sat-using") { } - virtual char const * get_main_descr() const { return "check if the current context is satisfiable using the given tactic, use (help-tactic) for the tactic language syntax."; } + char const * get_main_descr() const override { return "check if the current context is satisfiable using the given tactic, use (help-tactic) for the tactic language syntax."; } - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { exec_given_tactic_cmd::init_pdescrs(ctx, p); p.insert("print_unsat_core", CPK_BOOL, "(default: false) print unsatisfiable core."); p.insert("print_proof", CPK_BOOL, "(default: false) print proof."); p.insert("print_model", CPK_BOOL, "(default: false) print model."); } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { if (!m_tactic) { throw cmd_exception("check-sat-using needs a tactic argument"); } + if (ctx.ignore_check()) + return; params_ref p = ctx.params().merge_default_params(ps()); tactic_ref tref = using_params(sexpr2tactic(ctx, m_tactic), p); tref->set_logic(ctx.get_logic()); ast_manager & m = ctx.m(); unsigned timeout = p.get_uint("timeout", ctx.params().m_timeout); - unsigned rlimit = p.get_uint("rlimit", ctx.params().m_rlimit); + unsigned rlimit = p.get_uint("rlimit", ctx.params().rlimit()); labels_vec labels; goal_ref g = alloc(goal, m, ctx.produce_proofs(), ctx.produce_models(), ctx.produce_unsat_cores()); assert_exprs_from(ctx, *g); @@ -295,9 +297,9 @@ public: exec_given_tactic_cmd("apply") { } - virtual char const * get_main_descr() const { return "apply the given tactic to the current context, and print the resultant set of goals."; } + char const * get_main_descr() const override { return "apply the given tactic to the current context, and print the resultant set of goals."; } - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { p.insert("print", CPK_BOOL, "(default: true) print resultant goals."); #ifndef _EXTERNAL_RELEASE p.insert("print_proof", CPK_BOOL, "(default: false) print proof associated with each assertion."); @@ -308,7 +310,7 @@ public: exec_given_tactic_cmd::init_pdescrs(ctx, p); } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { if (!m_tactic) { throw cmd_exception("apply needs a tactic argument"); } @@ -321,12 +323,9 @@ public: assert_exprs_from(ctx, *g); unsigned timeout = p.get_uint("timeout", ctx.params().m_timeout); - unsigned rlimit = p.get_uint("rlimit", ctx.params().m_rlimit); + unsigned rlimit = p.get_uint("rlimit", ctx.params().rlimit()); goal_ref_buffer result_goals; - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); std::string reason_unknown; bool failed = false; @@ -337,7 +336,7 @@ public: scoped_timer timer(timeout, &eh); cmd_context::scoped_watch sw(ctx); try { - exec(t, g, result_goals, mc, pc, core); + exec(t, g, result_goals); } catch (tactic_exception & ex) { ctx.regular_stream() << "(error \"tactic failed: " << ex.msg() << "\")" << std::endl; @@ -396,8 +395,8 @@ public: } } - if (!failed && mc && p.get_bool("print_model_converter", false)) - mc->display(ctx.regular_stream()); + if (!failed && g->mc() && p.get_bool("print_model_converter", false)) + g->mc()->display(ctx.regular_stream()); if (p.get_bool("print_statistics", false)) display_statistics(ctx, tref.get()); @@ -597,9 +596,9 @@ static tactic * mk_echo(cmd_context & ctx, sexpr * n) { if (curr->is_string()) t = mk_echo_tactic(ctx, curr->get_string().c_str(), last); else - t = mk_probe_value_tactic(ctx, 0, sexpr2probe(ctx, curr), last); + t = mk_probe_value_tactic(ctx, nullptr, sexpr2probe(ctx, curr), last); tactic * new_res; - if (res.get() == 0) + if (res.get() == nullptr) new_res = t; else new_res = and_then(res.get(), t); @@ -608,7 +607,7 @@ static tactic * mk_echo(cmd_context & ctx, sexpr * n) { res = new_res; } UNREACHABLE(); - return 0; + return nullptr; } static tactic * mk_fail_if_branching(cmd_context & ctx, sexpr * n) { @@ -665,10 +664,10 @@ static tactic * mk_skip_if_failed(cmd_context & ctx, sexpr * n) { tactic * sexpr2tactic(cmd_context & ctx, sexpr * n) { if (n->is_symbol()) { tactic_cmd * cmd = ctx.find_tactic_cmd(n->get_symbol()); - if (cmd != 0) + if (cmd != nullptr) return cmd->mk(ctx.m()); sexpr * decl = ctx.find_user_tactic(n->get_symbol()); - if (decl != 0) + if (decl != nullptr) return sexpr2tactic(ctx, decl); throw cmd_exception("invalid tactic, unknown tactic ", n->get_symbol(), n->get_line(), n->get_pos()); } @@ -778,7 +777,7 @@ MK_NARY_PROBE(mk_mul); probe * sexpr2probe(cmd_context & ctx, sexpr * n) { if (n->is_symbol()) { probe_info * pinfo = ctx.find_probe(n->get_symbol()); - if (pinfo != 0) + if (pinfo != nullptr) return pinfo->get(); throw cmd_exception("invalid probe, unknown builtin probe ", n->get_symbol(), n->get_line(), n->get_pos()); } diff --git a/src/cmd_context/tactic_manager.cpp b/src/cmd_context/tactic_manager.cpp index 94b5e35ab..d4b3374b4 100644 --- a/src/cmd_context/tactic_manager.cpp +++ b/src/cmd_context/tactic_manager.cpp @@ -38,13 +38,13 @@ void tactic_manager::insert(probe_info * p) { } tactic_cmd * tactic_manager::find_tactic_cmd(symbol const & s) const { - tactic_cmd * c = 0; + tactic_cmd * c = nullptr; m_name2tactic.find(s, c); return c; } probe_info * tactic_manager::find_probe(symbol const & s) const { - probe_info * p = 0; + probe_info * p = nullptr; m_name2probe.find(s, p); return p; } diff --git a/src/duality/CMakeLists.txt b/src/duality/CMakeLists.txt deleted file mode 100644 index eb2d5c4f2..000000000 --- a/src/duality/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -z3_add_component(duality - SOURCES - duality_profiling.cpp - duality_rpfp.cpp - duality_solver.cpp - duality_wrapper.cpp - COMPONENT_DEPENDENCIES - interp - qe - smt -) diff --git a/src/duality/duality.h b/src/duality/duality.h deleted file mode 100644 index 657fa18b4..000000000 --- a/src/duality/duality.h +++ /dev/null @@ -1,1364 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - duality.h - - Abstract: - - main header for duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - -#pragma once - -#include "duality/duality_wrapper.h" -#include -#include -#include - -// make hash_map and hash_set available -using namespace stl_ext; - -namespace Duality { - - struct implicant_solver; - - /* Generic operations on Z3 formulas */ - - struct Z3User { - - context &ctx; - - typedef func_decl FuncDecl; - typedef expr Term; - - Z3User(context &_ctx) : ctx(_ctx){} - - const char *string_of_int(int n); - - Term conjoin(const std::vector &args); - - Term sum(const std::vector &args); - - Term CloneQuantifier(const Term &t, const Term &new_body); - - Term SubstRec(hash_map &memo, const Term &t); - - Term SubstRec(hash_map &memo, hash_map &map, const Term &t); - - void Strengthen(Term &x, const Term &y); - - // return the func_del of an app if it is uninterpreted - - bool get_relation(const Term &t, func_decl &R); - - // return true if term is an individual variable - // TODO: have to check that it is not a background symbol - - bool is_variable(const Term &t); - - FuncDecl SuffixFuncDecl(Term t, int n); - - - Term SubstRecHide(hash_map &memo, const Term &t, int number); - - void CollectConjuncts(const Term &f, std::vector &lits, bool pos = true); - - void SortTerms(std::vector &terms); - - Term SortSum(const Term &t); - - void Summarize(const Term &t); - - int CountOperators(const Term &t); - - Term SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val); - - Term CloneQuantAndSimp(const expr &t, const expr &body); - - Term RemoveRedundancy(const Term &t); - - Term IneqToEq(const Term &t); - - bool IsLiteral(const expr &lit, expr &atom, expr &val); - - expr Negate(const expr &f); - - expr SimplifyAndOr(const std::vector &args, bool is_and); - - expr ReallySimplifyAndOr(const std::vector &args, bool is_and); - - int MaxIndex(hash_map &memo, const Term &t); - - bool IsClosedFormula(const Term &t); - - Term AdjustQuantifiers(const Term &t); - - FuncDecl RenumberPred(const FuncDecl &f, int n); - - FuncDecl NumberPred(const FuncDecl &f, int n); - - Term ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming); - - - protected: - - void SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t); - int CountOperatorsRec(hash_set &memo, const Term &t); - void RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo); - Term RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t); - Term SubstAtomTriv(const expr &foo, const expr &atom, const expr &val); - expr ReduceAndOr(const std::vector &args, bool is_and, std::vector &res); - expr FinishAndOr(const std::vector &args, bool is_and); - expr PullCommonFactors(std::vector &args, bool is_and); - Term IneqToEqRec(hash_map &memo, const Term &t); - Term CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall); - Term PushQuantifier(const expr &t, const expr &body, bool is_forall); - void CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate); - Term DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t); - Term DeleteBound(int level, int num, const Term &t); - - }; - - /** This class represents a relation post-fixed point (RPFP) problem as - * a "problem graph". The graph consists of Nodes and hyper-edges. - * - * A node consists of - * - Annotation, a symbolic relation - * - Bound, a symbolic relation giving an upper bound on Annotation - * - * - * A hyper-edge consists of: - * - Children, a sequence of children Nodes, - * - F, a symbolic relational transformer, - * - Parent, a single parent Node. - * - * The graph is "solved" when: - * - For every Node n, n.Annotation subseteq n.Bound - * - For every hyperedge e, e.F(e.Children.Annotation) subseteq e.Parent.Annotation - * - * where, if x is a sequence of Nodes, x.Annotation is the sequences - * of Annotations of the nodes in the sequence. - * - * A symbolic Transformer consists of - * - RelParams, a sequence of relational symbols - * - IndParams, a sequence of individual symbols - * - Formula, a formula over RelParams and IndParams - * - * A Transformer t represents a function that takes sequence R of relations - * and yields the relation lambda (t.Indparams). Formula(R/RelParams). - * - * As a special case, a nullary Transformer (where RelParams is the empty sequence) - * represents a fixed relation. - * - * An RPFP consists of - * - Nodes, a set of Nodes - * - Edges, a set of hyper-edges - * - Context, a prover context that contains formula AST's - * - * Multiple RPFP's can use the same Context, but you should be careful - * that only one RPFP asserts constraints in the context at any time. - * - * */ - class RPFP : public Z3User - { - public: - - class Edge; - class Node; - bool HornClauses; - - - /** Interface class for interpolating solver. */ - - class LogicSolver { - public: - - context *ctx; /** Z3 context for formulas */ - solver *slvr; /** Z3 solver */ - bool need_goals; /** Can the solver use the goal tree to optimize interpolants? */ - solver aux_solver; /** For temporary use -- don't leave assertions here. */ - - /** Tree interpolation. This method assumes the formulas in TermTree - "assumptions" are currently asserted in the solver. The return - value indicates whether the assertions are satisfiable. In the - UNSAT case, a tree interpolant is returned in "interpolants". - In the SAT case, a model is returned. - */ - - virtual - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false - ) = 0; - - /** Declare a constant in the background theory. */ - virtual void declare_constant(const func_decl &f) = 0; - - /** Is this a background constant? */ - virtual bool is_constant(const func_decl &f) = 0; - - /** Get the constants in the background vocabulary */ - virtual hash_set &get_constants() = 0; - - /** Assert a background axiom. */ - virtual void assert_axiom(const expr &axiom) = 0; - - /** Get the background axioms. */ - virtual const std::vector &get_axioms() = 0; - - /** Return a string describing performance. */ - virtual std::string profile() = 0; - - virtual void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ){} - - /** Cancel, throw Canceled object if possible. */ - virtual void cancel(){ } - - /* Note: aux solver uses extensional array theory, since it - needs to be able to produce counter-models for - interpolants the have array equalities in them. - */ - LogicSolver(context &c) : aux_solver(c,true){} - - virtual ~LogicSolver(){} - }; - - /** This solver uses iZ3. */ - class iZ3LogicSolver : public LogicSolver { - public: - interpolating_context *ictx; /** iZ3 context for formulas */ - interpolating_solver *islvr; /** iZ3 solver */ - - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false) - { - literals _labels; - islvr->SetWeakInterpolants(weak); - return islvr->interpolate_tree(assumptions,interpolants,_model,_labels,true); - } - - void assert_axiom(const expr &axiom){ -#if 1 - // HACK: large "distict" predicates can kill the legacy SMT solver. - // encode them with a UIF - if(axiom.is_app() && axiom.decl().get_decl_kind() == Distinct) - if(axiom.num_args() > 10){ - sort s = axiom.arg(0).get_sort(); - std::vector sv; - sv.push_back(s); - int nargs = axiom.num_args(); - std::vector args(nargs); - func_decl f = ctx->fresh_func_decl("@distinct",sv,ctx->int_sort()); - for(int i = 0; i < nargs; i++){ - expr a = axiom.arg(i); - expr new_cnstr = f(a) == ctx->int_val(i); - args[i] = new_cnstr; - } - expr cnstr = ctx->make(And,args); - islvr->AssertInterpolationAxiom(cnstr); - return; - } -#endif - islvr->AssertInterpolationAxiom(axiom); - } - - const std::vector &get_axioms() { - return islvr->GetInterpolationAxioms(); - } - - std::string profile(){ - return islvr->profile(); - } - -#if 0 - iZ3LogicSolver(config &_config){ - ctx = ictx = new interpolating_context(_config); - slvr = islvr = new interpolating_solver(*ictx); - need_goals = false; - islvr->SetWeakInterpolants(true); - } -#endif - - iZ3LogicSolver(context &c, bool models = true) : LogicSolver(c) { - ctx = ictx = &c; - slvr = islvr = new interpolating_solver(*ictx, models); - need_goals = false; - islvr->SetWeakInterpolants(true); - } - - void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ){ -#if 0 - islvr->write_interpolation_problem(file_name,assumptions,theory); -#endif - - } - - void cancel(){islvr->cancel();} - - /** Declare a constant in the background theory. */ - virtual void declare_constant(const func_decl &f){ - bckg.insert(f); - } - - /** Is this a background constant? */ - virtual bool is_constant(const func_decl &f){ - return bckg.find(f) != bckg.end(); - } - - /** Get the constants in the background vocabulary */ - virtual hash_set &get_constants(){ - return bckg; - } - - ~iZ3LogicSolver(){ - // delete ictx; - delete islvr; - } - private: - hash_set bckg; - - }; - -#if 0 - /** Create a logic solver from a Z3 configuration. */ - static iZ3LogicSolver *CreateLogicSolver(config &_config){ - return new iZ3LogicSolver(_config); - } -#endif - - /** Create a logic solver from a low-level Z3 context. - Only use this if you know what you're doing. */ - static iZ3LogicSolver *CreateLogicSolver(context c){ - return new iZ3LogicSolver(c); - } - - LogicSolver *ls; - - protected: - int nodeCount; - int edgeCount; - - class stack_entry - { - public: - std::list edges; - std::list nodes; - std::list > constraints; - }; - - - public: - model dualModel; - protected: - literals dualLabels; - std::list stack; - std::vector axioms; // only saved here for printing purposes - solver &aux_solver; - hash_set *proof_core; - - public: - - /** Construct an RPFP graph with a given interpolating prover context. It is allowed to - have multiple RPFP's use the same context, but you should never have teo RPFP's - with the same conext asserting nodes or edges at the same time. Note, if you create - axioms in one RPFP, them create a second RPFP with the same context, the second will - inherit the axioms. - */ - - RPFP(LogicSolver *_ls) : Z3User(*(_ls->ctx)), dualModel(*(_ls->ctx)), aux_solver(_ls->aux_solver) - { - ls = _ls; - nodeCount = 0; - edgeCount = 0; - stack.push_back(stack_entry()); - HornClauses = false; - proof_core = 0; - } - - virtual ~RPFP(); - - /** Symbolic representation of a relational transformer */ - class Transformer - { - public: - std::vector RelParams; - std::vector IndParams; - Term Formula; - RPFP *owner; - hash_map labels; - - Transformer *Clone() - { - return new Transformer(*this); - } - - void SetEmpty(){ - Formula = owner->ctx.bool_val(false); - } - - void SetFull(){ - Formula = owner->ctx.bool_val(true); - } - - bool IsEmpty(){ - return eq(Formula,owner->ctx.bool_val(false)); - } - - bool IsFull(){ - return eq(Formula,owner->ctx.bool_val(true)); - } - - void UnionWith(const Transformer &other){ - Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); - Formula = Formula || t; - } - - void IntersectWith(const Transformer &other){ - Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); - Formula = Formula && t; - } - - bool SubsetEq(const Transformer &other){ - Term t = owner->SubstParams(other.IndParams,IndParams,other.Formula); - expr test = Formula && !t; - owner->aux_solver.push(); - owner->aux_solver.add(test); - check_result res = owner->aux_solver.check(); - owner->aux_solver.pop(1); - return res == unsat; - } - - void Complement(){ - Formula = !Formula; - } - - void Simplify(){ - Formula = Formula.simplify(); - } - - Transformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula, RPFP *_owner) - : RelParams(_RelParams), IndParams(_IndParams), Formula(_Formula) {owner = _owner;} - }; - - /** Create a symbolic transformer. */ - Transformer CreateTransformer(const std::vector &_RelParams, const std::vector &_IndParams, const Term &_Formula) - { - // var ops = new Util.ContextOps(ctx); - // var foo = ops.simplify_lhs(_Formula); - // t.Formula = foo.Item1; - // t.labels = foo.Item2; - return Transformer(_RelParams,_IndParams,_Formula,this); - } - - /** Create a relation (nullary relational transformer) */ - Transformer CreateRelation(const std::vector &_IndParams, const Term &_Formula) - { - return CreateTransformer(std::vector(), _IndParams, _Formula); - } - - /** A node in the RPFP graph */ - class Node - { - public: - FuncDecl Name; - Transformer Annotation; - Transformer Bound; - Transformer Underapprox; - RPFP *owner; - int number; - Edge *Outgoing; - std::vector Incoming; - Term dual; - Node *map; - unsigned recursion_bound; - - Node(const FuncDecl &_Name, const Transformer &_Annotation, const Transformer &_Bound, const Transformer &_Underapprox, const Term &_dual, RPFP *_owner, int _number) - : Name(_Name), Annotation(_Annotation), Bound(_Bound), Underapprox(_Underapprox), dual(_dual) {owner = _owner; number = _number; Outgoing = 0; recursion_bound = UINT_MAX;} - }; - - /** Create a node in the graph. The input is a term R(v_1...v_n) - * where R is an arbitrary relational symbol and v_1...v_n are - * arbitary distinct variables. The names are only of mnemonic value, - * however, the number and type of arguments determine the type - * of the relation at this node. */ - - Node *CreateNode(const Term &t) - { - std::vector _IndParams; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - _IndParams.push_back(t.arg(i)); - Node *n = new Node(t.decl(), - CreateRelation(_IndParams,ctx.bool_val(true)), - CreateRelation(_IndParams,ctx.bool_val(true)), - CreateRelation(_IndParams,ctx.bool_val(false)), - expr(ctx), this, ++nodeCount - ); - nodes.push_back(n); - return n; - } - - /** Clone a node (can be from another graph). */ - - Node *CloneNode(Node *old) - { - Node *n = new Node(old->Name, - old->Annotation, - old->Bound, - old->Underapprox, - expr(ctx), - this, - ++nodeCount - ); - nodes.push_back(n); - n->map = old; - return n; - } - - /** Delete a node. You can only do this if not connected to any edges.*/ - void DeleteNode(Node *node){ - if(node->Outgoing || !node->Incoming.empty()) - throw "cannot delete RPFP node"; - for(std::vector::iterator it = nodes.end(), en = nodes.begin(); it != en;){ - if(*(--it) == node){ - nodes.erase(it); - break; - } - } - delete node; - } - - /** This class represents a hyper-edge in the RPFP graph */ - - class Edge - { - public: - Transformer F; - Node *Parent; - std::vector Children; - RPFP *owner; - int number; - // these should be internal... - Term dual; - hash_map relMap; - hash_map varMap; - Edge *map; - Term labeled; - std::vector constraints; - - Edge(Node *_Parent, const Transformer &_F, const std::vector &_Children, RPFP *_owner, int _number) - : F(_F), Parent(_Parent), Children(_Children), dual(expr(_owner->ctx)) { - owner = _owner; - number = _number; - } - }; - - - /** Create a hyper-edge. */ - Edge *CreateEdge(Node *_Parent, const Transformer &_F, const std::vector &_Children) - { - Edge *e = new Edge(_Parent,_F,_Children,this,++edgeCount); - _Parent->Outgoing = e; - for(unsigned i = 0; i < _Children.size(); i++) - _Children[i]->Incoming.push_back(e); - edges.push_back(e); - return e; - } - - - /** Delete a hyper-edge and unlink it from any nodes. */ - void DeleteEdge(Edge *edge){ - if(edge->Parent) - edge->Parent->Outgoing = 0; - for(unsigned int i = 0; i < edge->Children.size(); i++){ - std::vector &ic = edge->Children[i]->Incoming; - for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ - if(*it == edge){ - ic.erase(it); - break; - } - } - } - for(std::vector::iterator it = edges.end(), en = edges.begin(); it != en;){ - if(*(--it) == edge){ - edges.erase(it); - break; - } - } - delete edge; - } - - /** Create an edge that lower-bounds its parent. */ - Edge *CreateLowerBoundEdge(Node *_Parent) - { - return CreateEdge(_Parent, _Parent->Annotation, std::vector()); - } - - - /** For incremental solving, asserts the constraint associated - * with this edge in the SMT context. If this edge is removed, - * you must pop the context accordingly. The second argument is - * the number of pushes we are inside. */ - - virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); - - /* Constrain an edge by the annotation of one of its children. */ - - void ConstrainParent(Edge *parent, Node *child); - - /** For incremental solving, asserts the negation of the upper bound associated - * with a node. - * */ - - void AssertNode(Node *n); - - /** Assert a constraint on an edge in the SMT context. - */ - void ConstrainEdge(Edge *e, const Term &t); - - /** Fix the truth values of atomic propositions in the given - edge to their values in the current assignment. */ - void FixCurrentState(Edge *root); - - void FixCurrentStateFull(Edge *edge, const expr &extra); - - void FixCurrentStateFull(Edge *edge, const std::vector &assumps, const hash_map &renaming); - - /** Declare a constant in the background theory. */ - - void DeclareConstant(const FuncDecl &f); - - /** Assert a background axiom. Background axioms can be used to provide the - * theory of auxilliary functions or relations. All symbols appearing in - * background axioms are considered global, and may appear in both transformer - * and relational solutions. Semantically, a solution to the RPFP gives - * an interpretation of the unknown relations for each interpretation of the - * auxilliary symbols that is consistent with the axioms. Axioms should be - * asserted before any calls to Push. They cannot be de-asserted by Pop. */ - - void AssertAxiom(const Term &t); - -#if 0 - /** Do not call this. */ - - void RemoveAxiom(const Term &t); -#endif - - /** Solve an RPFP graph. This means either strengthen the annotation - * so that the bound at the given root node is satisfied, or - * show that this cannot be done by giving a dual solution - * (i.e., a counterexample). - * - * In the current implementation, this only works for graphs that - * are: - * - tree-like - * - * - closed. - * - * In a tree-like graph, every nod has out most one incoming and one out-going edge, - * and there are no cycles. In a closed graph, every node has exactly one out-going - * edge. This means that the leaves of the tree are all hyper-edges with no - * children. Such an edge represents a relation (nullary transformer) and thus - * a lower bound on its parent. The parameter root must be the root of this tree. - * - * If Solve returns LBool.False, this indicates success. The annotation of the tree - * has been updated to satisfy the upper bound at the root. - * - * If Solve returns LBool.True, this indicates a counterexample. For each edge, - * you can then call Eval to determine the values of symbols in the transformer formula. - * You can also call Empty on a node to determine if its value in the counterexample - * is the empty relation. - * - * \param root The root of the tree - * \param persist Number of context pops through which result should persist - * - * - */ - - lbool Solve(Node *root, int persist); - - /** Same as Solve, but annotates only a single node. */ - - lbool SolveSingleNode(Node *root, Node *node); - - /** Get the constraint tree (but don't solve it) */ - - TermTree *GetConstraintTree(Node *root, Node *skip_descendant = 0); - - /** Dispose of the dual model (counterexample) if there is one. */ - - void DisposeDualModel(); - - /** Check satisfiability of asserted edges and nodes. Same functionality as - * Solve, except no primal solution (interpolant) is generated in the unsat case. */ - - check_result Check(Node *root, std::vector underapproxes = std::vector(), - std::vector *underapprox_core = 0); - - /** Update the model, attempting to make the propositional literals in assumps true. If possible, - return sat, else return unsat and keep the old model. */ - - check_result CheckUpdateModel(Node *root, std::vector assumps); - - /** Determines the value in the counterexample of a symbol occuring in the transformer formula of - * a given edge. */ - - Term Eval(Edge *e, Term t); - - /** Return the fact derived at node p in a counterexample. */ - - Term EvalNode(Node *p); - - /** Returns true if the given node is empty in the primal solution. For proecudure summaries, - this means that the procedure is not called in the current counter-model. */ - - bool Empty(Node *p); - - /** Compute an underapproximation of every node in a tree rooted at "root", - based on a previously computed counterexample. */ - - Term ComputeUnderapprox(Node *root, int persist); - - /** Try to strengthen the annotation of a node by removing disjuncts. */ - void Generalize(Node *root, Node *node); - - - /** Compute disjunctive interpolant for node by case splitting */ - void InterpolateByCases(Node *root, Node *node); - - /** Push a scope. Assertions made after Push can be undone by Pop. */ - - void Push(); - - /** Exception thrown when bad clause is encountered */ - - struct bad_clause { - std::string msg; - int i; - bad_clause(const std::string &_msg, int _i){ - msg = _msg; - i = _i; - } - }; - - struct parse_error { - std::string msg; - parse_error(const std::string &_msg){ - msg = _msg; - } - }; - - struct file_not_found { - }; - - struct bad_format { - }; - - // thrown on internal error - struct Bad { - }; - - // thrown on more serious internal error - struct ReallyBad { - }; - - // throw when greedy reduction fails - struct greedy_reduce_failed {}; - - /** Pop a scope (see Push). Note, you cannot pop axioms. */ - - void Pop(int num_scopes); - - /** Erase the proof by performing a Pop, Push and re-assertion of - all the popped constraints */ - void PopPush(); - - /** Return true if the given edge is used in the proof of unsat. - Can be called only after Solve or Check returns an unsat result. */ - - bool EdgeUsedInProof(Edge *edge); - - - /** Convert a collection of clauses to Nodes and Edges in the RPFP. - - Predicate unknowns are uninterpreted predicates not - occurring in the background theory. - - Clauses are of the form - - B => P(t_1,...,t_k) - - where P is a predicate unknown and predicate unknowns - occur only positivey in H and only under existential - quantifiers in prenex form. - - Each predicate unknown maps to a node. Each clause maps to - an edge. Let C be a clause B => P(t_1,...,t_k) where the - sequence of predicate unknowns occurring in B (in order - of occurrence) is P_1..P_n. The clause maps to a transformer - T where: - - T.Relparams = P_1..P_n - T.Indparams = x_1...x+k - T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k - - Throws exception bad_clause(msg,i) if a clause i is - in the wrong form. - - */ - - struct label_struct { - symbol name; - expr value; - bool pos; - label_struct(const symbol &s, const expr &e, bool b) - : name(s), value(e), pos(b) {} - }; - - -#ifdef _WINDOWS - __declspec(dllexport) -#endif - void FromClauses(const std::vector &clauses, const std::vector *bounds = 0); - - void FromFixpointContext(fixedpoint fp, std::vector &queries); - - void WriteSolution(std::ostream &s); - - void WriteCounterexample(std::ostream &s, Node *node); - - enum FileFormat {DualityFormat, SMT2Format, HornFormat}; - - /** Write the RPFP to a file (currently in SMTLIB 1.2 format) */ - void WriteProblemToFile(std::string filename, FileFormat format = DualityFormat); - - /** Read the RPFP from a file (in specificed format) */ - void ReadProblemFromFile(std::string filename, FileFormat format = DualityFormat); - - /** Translate problem to Horn clause form */ - void ToClauses(std::vector &cnsts, FileFormat format = DualityFormat); - - /** Translate the RPFP to a fixed point context, with queries */ - fixedpoint ToFixedPointProblem(std::vector &queries); - - /** Nodes of the graph. */ - std::vector nodes; - - /** Edges of the graph. */ - std::vector edges; - - /** Fuse a vector of transformers. If the total number of inputs of the transformers - is N, then the result is an N-ary transfomer whose output is the union of - the outputs of the given transformers. The is, suppose we have a vetor of transfoermers - {T_i(r_i1,...,r_iN(i) : i=1..M}. The the result is a transformer - - F(r_11,...,r_iN(1),...,r_M1,...,r_MN(M)) = - T_1(r_11,...,r_iN(1)) U ... U T_M(r_M1,...,r_MN(M)) - */ - - Transformer Fuse(const std::vector &trs); - - /** Fuse edges so that each node is the output of at most one edge. This - transformation is solution-preserving, but changes the numbering of edges in - counterexamples. - */ - void FuseEdges(); - - void RemoveDeadNodes(); - - Term SubstParams(const std::vector &from, - const std::vector &to, const Term &t); - - Term SubstParamsNoCapture(const std::vector &from, - const std::vector &to, const Term &t); - - Term Localize(Edge *e, const Term &t); - - void EvalNodeAsConstraint(Node *p, Transformer &res); - - TermTree *GetGoalTree(Node *root); - - int EvalTruth(hash_map &memo, Edge *e, const Term &f); - - void GetLabels(Edge *e, std::vector &labels); - - // int GetLabelsRec(hash_map *memo, const Term &f, std::vector &labels, bool labpos); - - /** Compute and save the proof core for future calls to - EdgeUsedInProof. You only need to call this if you will pop - the solver before calling EdgeUsedInProof. - */ - void ComputeProofCore(); - - int CumulativeDecisions(); - - void GreedyReduceNodes(std::vector &nodes); - - check_result CheckWithConstrainedNodes(std::vector &posnodes,std::vector &negnodes); - - solver &slvr(){ - return *ls->slvr; - } - - protected: - - void ClearProofCore(){ - if(proof_core) - delete proof_core; - proof_core = 0; - } - - Term SuffixVariable(const Term &t, int n); - - Term HideVariable(const Term &t, int n); - - void RedVars(Node *node, Term &b, std::vector &v); - - Term RedDualRela(Edge *e, std::vector &args, int idx); - - Term LocalizeRec(Edge *e, hash_map &memo, const Term &t); - - void SetEdgeMaps(Edge *e); - - Term ReducedDualEdge(Edge *e); - - TermTree *ToTermTree(Node *root, Node *skip_descendant = 0); - - TermTree *ToGoalTree(Node *root); - - void CollapseTermTreeRec(TermTree *root, TermTree *node); - - TermTree *CollapseTermTree(TermTree *node); - - void DecodeTree(Node *root, TermTree *interp, int persist); - - Term GetUpperBound(Node *n); - - TermTree *AddUpperBound(Node *root, TermTree *t); - -#if 0 - void WriteInterps(System.IO.StreamWriter f, TermTree t); -#endif - - void WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s); - - void WriteEdgeAssignment(std::ostream &s, Edge *e); - - - // Scan the clause body for occurrences of the predicate unknowns - - Term ScanBody(hash_map &memo, - const Term &t, - hash_map &pmap, - std::vector &res, - std::vector &nodes); - - Term RemoveLabelsRec(hash_map &memo, const Term &t, std::vector &lbls); - - Term RemoveLabels(const Term &t, std::vector &lbls); - - Term GetAnnotation(Node *n); - - - Term GetUnderapprox(Node *n); - - Term UnderapproxFlag(Node *n); - - hash_map underapprox_flag_rev; - - Node *UnderapproxFlagRev(const Term &flag); - - Term ProjectFormula(std::vector &keep_vec, const Term &f); - - int SubtermTruth(hash_map &memo, const Term &); - - void ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set *done, bool truth, hash_set &dont_cares); - - void Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares); - - Term UnderapproxFormula(const Term &f, hash_set &dont_cares); - - void ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares, bool extensional = true); - - public: - Term UnderapproxFullFormula(const Term &f, bool extensional = true); - - protected: - Term ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants); - - hash_map resolve_ite_memo; - - Term ResolveIte(hash_map &memo, const Term &t, std::vector &lits, - hash_set *done, hash_set &dont_cares); - - struct ArrayValue { - bool defined; - std::map entries; - expr def_val; - }; - - void EvalArrayTerm(const Term &t, ArrayValue &res); - - Term EvalArrayEquality(const Term &f); - - Term ModelValueAsConstraint(const Term &t); - - void GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, - hash_set *done, bool truth); - - Term SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t); - - Term SubstBound(hash_map &subst, const Term &t); - - void ConstrainEdgeLocalized(Edge *e, const Term &t); - - void GreedyReduce(solver &s, std::vector &conjuncts); - - void NegateLits(std::vector &lits); - - expr SimplifyOr(std::vector &lits); - - expr SimplifyAnd(std::vector &lits); - - void SetAnnotation(Node *root, const expr &t); - - void AddEdgeToSolver(Edge *edge); - - void AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge); - - void AddToProofCore(hash_set &core); - - void GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under); - - Term StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits); - - expr NegateLit(const expr &f); - - expr GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox); - - bool IsVar(const expr &t); - - void GetVarsRec(hash_set &memo, const expr &cnst, std::vector &vars); - - expr UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params); - - void AddParamsToTransformer(Transformer &trans, const std::vector ¶ms); - - expr AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms); - - expr GetRelRec(hash_set &memo, const expr &t, const func_decl &rel); - - expr GetRel(Edge *edge, int child_idx); - - void GetDefs(const expr &cnst, hash_map &defs); - - void GetDefsRec(const expr &cnst, hash_map &defs); - - void AddParamsToNode(Node *node, const std::vector ¶ms); - - void UnhoistLoop(Edge *loop_edge, Edge *init_edge); - - void Unhoist(); - - Term ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts); - - Term ElimIte(const Term &t); - - void MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node); - - virtual void slvr_add(const expr &e); - - virtual void slvr_pop(int i); - - virtual void slvr_push(); - - virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); - - virtual lbool ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false); - - virtual bool proof_core_contains(const expr &e); - - }; - - - /** RPFP solver base class. */ - - class Solver { - - public: - - class Counterexample { - private: - RPFP *tree; - RPFP::Node *root; - public: - Counterexample(){ - tree = 0; - root = 0; - } - Counterexample(RPFP *_tree, RPFP::Node *_root){ - tree = _tree; - root = _root; - } - ~Counterexample(){ - if(tree) delete tree; - } - void swap(Counterexample &other){ - std::swap(tree,other.tree); - std::swap(root,other.root); - } - void set(RPFP *_tree, RPFP::Node *_root){ - if(tree) delete tree; - tree = _tree; - root = _root; - } - void clear(){ - if(tree) delete tree; - tree = 0; - } - RPFP *get_tree() const {return tree;} - RPFP::Node *get_root() const {return root;} - private: - Counterexample &operator=(const Counterexample &); - Counterexample(const Counterexample &); - }; - - /** Solve the problem. You can optionally give an old - counterexample to use as a guide. This is chiefly useful for - abstraction refinement metholdologies, and is only used as a - heuristic. */ - - virtual bool Solve() = 0; - - virtual Counterexample &GetCounterexample() = 0; - - virtual bool SetOption(const std::string &option, const std::string &value) = 0; - - /** Learn heuristic information from another solver. This - is chiefly useful for abstraction refinement, when we want to - solve a series of similar problems. */ - - virtual void LearnFrom(Solver *old_solver) = 0; - - /** Return true if the solution be incorrect due to recursion bounding. - That is, the returned "solution" might contain all derivable facts up to the - given recursion bound, but not be actually a fixed point. - */ - - virtual bool IsResultRecursionBounded() = 0; - - virtual ~Solver(){} - - static Solver *Create(const std::string &solver_class, RPFP *rpfp); - - /** This can be called asynchrnously to cause Solve to throw a - Canceled exception at some time in the future. - */ - virtual void Cancel() = 0; - - /** Object thrown on cancellation */ - struct Canceled {}; - - /** Object thrown on incompleteness */ - struct Incompleteness {}; - }; -} - - -// Allow to hash on nodes and edges in deterministic way - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::RPFP::Node *p) const { - return p->number; - } - }; -} - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::RPFP::Edge *p) const { - return p->number; - } - }; -} - -// allow to walk sets of nodes without address dependency - -namespace std { - template <> - class less { - public: - bool operator()(Duality::RPFP::Node * const &s, Duality::RPFP::Node * const &t) const { - return s->number < t->number; // s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// #define LIMIT_STACK_WEIGHT 5 - - -namespace Duality { - /** Caching version of RPFP. Instead of asserting constraints, returns assumption literals */ - - class RPFP_caching : public RPFP { - public: - - /** appends assumption literals for edge to lits. if with_children is true, - includes that annotation of the edge's children. - */ - void AssertEdgeCache(Edge *e, std::vector &lits, bool with_children = false); - - /** appends assumption literals for node to lits */ - void AssertNodeCache(Node *, std::vector lits); - - /** check assumption lits, and return core */ - check_result CheckCore(const std::vector &assumps, std::vector &core); - - /** Clone another RPFP into this one, keeping a map */ - void Clone(RPFP *other); - - /** Get the clone of a node */ - Node *GetNodeClone(Node *other_node); - - /** Get the clone of an edge */ - Edge *GetEdgeClone(Edge *other_edge); - - /** Try to strengthen the parent of an edge */ - void GeneralizeCache(Edge *edge); - - /** Try to propagate some facts from children to parents of edge. - Return true if success. */ - bool PropagateCache(Edge *edge); - - /** Construct a caching RPFP using a LogicSolver */ - RPFP_caching(LogicSolver *_ls) : RPFP(_ls) {} - - /** Constraint an edge by its child's annotation. Return - assumption lits. */ - void ConstrainParentCache(Edge *parent, Node *child, std::vector &lits); - -#ifdef LIMIT_STACK_WEIGHT - virtual void AssertEdge(Edge *e, int persist = 0, bool with_children = false, bool underapprox = false); -#endif - - virtual ~RPFP_caching(){} - - protected: - hash_map AssumptionLits; - hash_map NodeCloneMap; - hash_map EdgeCloneMap; - std::vector alit_stack; - std::vector alit_stack_sizes; - - // to let us use one solver per edge - struct edge_solver { - hash_map AssumptionLits; - uptr slvr; - }; - hash_map edge_solvers; - -#ifdef LIMIT_STACK_WEIGHT - struct weight_counter { - int val; - weight_counter(){val = 0;} - void swap(weight_counter &other){ - std::swap(val,other.val); - } - }; - - struct big_stack_entry { - weight_counter weight_added; - std::vector new_alits; - std::vector alit_stack; - std::vector alit_stack_sizes; - }; - - std::vector new_alits; - weight_counter weight_added; - std::vector big_stack; -#endif - - - - void GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map = 0); - - void GreedyReduceCache(std::vector &assumps, std::vector &core); - - void FilterCore(std::vector &core, std::vector &full_core); - void ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits); - - virtual void slvr_add(const expr &e); - - virtual void slvr_pop(int i); - - virtual void slvr_push(); - - virtual check_result slvr_check(unsigned n = 0, expr * const assumptions = 0, unsigned *core_size = 0, expr *core = 0); - - virtual lbool ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals = 0, - bool weak = false); - - virtual bool proof_core_contains(const expr &e); - - void GetTermTreeAssertionLiterals(TermTree *assumptions); - - void GetTermTreeAssertionLiteralsRec(TermTree *assumptions); - - edge_solver &SolverForEdge(Edge *edge, bool models, bool axioms); - - public: - struct scoped_solver_for_edge { - solver *orig_slvr; - RPFP_caching *rpfp; - edge_solver *es; - scoped_solver_for_edge(RPFP_caching *_rpfp, Edge *edge, bool models = false, bool axioms = false){ - rpfp = _rpfp; - orig_slvr = rpfp->ls->slvr; - es = &(rpfp->SolverForEdge(edge,models,axioms)); - rpfp->ls->slvr = es->slvr.get(); - rpfp->AssumptionLits.swap(es->AssumptionLits); - } - ~scoped_solver_for_edge(){ - rpfp->ls->slvr = orig_slvr; - rpfp->AssumptionLits.swap(es->AssumptionLits); - } - }; - - }; - -} diff --git a/src/duality/duality_profiling.cpp b/src/duality/duality_profiling.cpp deleted file mode 100755 index 2e659f0a1..000000000 --- a/src/duality/duality_profiling.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - duality_profiling.cpp - - Abstract: - - collection performance information for duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - - -#include -#include -#include -#include -#include - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include "duality/duality_wrapper.h" -#include "interp/iz3profiling.h" - -namespace Duality { - - void show_time(){ - output_time(std::cout,current_time()); - std::cout << "\n"; - } - - typedef std::map nmap; - - struct node { - std::string name; - clock_t time; - clock_t start_time; - nmap sub; - struct node *parent; - - node(); - } top; - - node::node(){ - time = 0; - parent = 0; - } - - struct node *current; - - struct init { - init(){ - top.name = "TOTAL"; - current = ⊤ - } - } initializer; - - struct time_entry { - clock_t t; - time_entry(){t = 0;}; - void add(clock_t incr){t += incr;} - }; - - struct ltstr - { - bool operator()(const char* s1, const char* s2) const - { - return strcmp(s1, s2) < 0; - } - }; - - typedef std::map tmap; - - static std::ostream *pfs; - - void print_node(node &top, int indent, tmap &totals){ - for(int i = 0; i < indent; i++) (*pfs) << " "; - (*pfs) << top.name; - int dots = 70 - 2 * indent - top.name.size(); - for(int i = 0; i second,indent+1,totals); - } - - void print_profile(std::ostream &os) { - pfs = &os; - top.time = 0; - for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) - top.time += it->second.time; - tmap totals; - print_node(top,0,totals); - (*pfs) << "TOTALS:" << std::endl; - for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ - (*pfs) << (it->first) << " "; - output_time(*pfs, it->second.t); - (*pfs) << std::endl; - } - profiling::print(os); // print the interpolation stats - } - - void timer_start(const char *name){ - node &child = current->sub[name]; - if(child.name.empty()){ // a new node - child.parent = current; - child.name = name; - } - child.start_time = current_time(); - current = &child; - } - - void timer_stop(const char *name){ - if (current->name != name || !current->parent) { -#if 0 - std::cerr << "imbalanced timer_start and timer_stop"; - exit(1); -#endif - // in case we lost a timer stop due to an exception - while (current->name != name && current->parent) - current = current->parent; - if (current->parent) { - current->time += (current_time() - current->start_time); - current = current->parent; - } - return; - } - current->time += (current_time() - current->start_time); - current = current->parent; - } -} diff --git a/src/duality/duality_profiling.h b/src/duality/duality_profiling.h deleted file mode 100755 index 5f0e5120c..000000000 --- a/src/duality/duality_profiling.h +++ /dev/null @@ -1,38 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - duality_profiling.h - - Abstract: - - collection performance information for duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - -#ifndef DUALITYPROFILING_H -#define DUALITYPROFILING_H - -#include - -namespace Duality { - /** Start a timer with given name */ - void timer_start(const char *); - /** Stop a timer with given name */ - void timer_stop(const char *); - /** Print out timings */ - void print_profile(std::ostream &s); - /** Show the current time. */ - void show_time(); -} - -#endif - diff --git a/src/duality/duality_rpfp.cpp b/src/duality/duality_rpfp.cpp deleted file mode 100755 index 5dc2283ae..000000000 --- a/src/duality/duality_rpfp.cpp +++ /dev/null @@ -1,4233 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - duality_rpfp.h - - Abstract: - - implements relational post-fixedpoint problem - (RPFP) data structure. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include -#include -#include -#include - - -#include "duality/duality.h" -#include "duality/duality_profiling.h" - -// TODO: do we need these? -#ifdef Z3OPS - -class Z3_subterm_truth { -public: - virtual bool eval(Z3_ast f) = 0; - ~Z3_subterm_truth(){} -}; - -Z3_subterm_truth *Z3_mk_subterm_truth(Z3_context ctx, Z3_model model); - -#endif - -#include - -// TODO: use something official for this -int debug_gauss = 0; - -namespace Duality { - - static char string_of_int_buffer[20]; - - const char *Z3User::string_of_int(int n){ - sprintf(string_of_int_buffer,"%d",n); - return string_of_int_buffer; - } - - RPFP::Term RPFP::SuffixVariable(const Term &t, int n) - { - std::string name = t.decl().name().str() + "_" + string_of_int(n); - return ctx.constant(name.c_str(), t.get_sort()); - } - - RPFP::Term RPFP::HideVariable(const Term &t, int n) - { - std::string name = std::string("@p_") + t.decl().name().str() + "_" + string_of_int(n); - return ctx.constant(name.c_str(), t.get_sort()); - } - - void RPFP::RedVars(Node *node, Term &b, std::vector &v) - { - int idx = node->number; - if(HornClauses) - b = ctx.bool_val(true); - else { - std::string name = std::string("@b_") + string_of_int(idx); - symbol sym = ctx.str_symbol(name.c_str()); - b = ctx.constant(sym,ctx.bool_sort()); - } - // ctx.constant(name.c_str(), ctx.bool_sort()); - v = node->Annotation.IndParams; - for(unsigned i = 0; i < v.size(); i++) - v[i] = SuffixVariable(v[i],idx); - } - - void Z3User::SummarizeRec(hash_set &memo, std::vector &lits, int &ops, const Term &t){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if (k == And || k == Or || k == Not || k == Implies || k == Iff) { - ops++; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - SummarizeRec(memo, lits, ops, t.arg(i)); - return; - } - } - lits.push_back(t); - } - - int RPFP::CumulativeDecisions(){ -#if 0 - std::string stats = Z3_statistics_to_string(ctx); - int pos = stats.find("decisions:"); - pos += 10; - int end = stats.find('\n',pos); - std::string val = stats.substr(pos,end-pos); - return atoi(val.c_str()); -#endif - return slvr().get_num_decisions(); - } - - - void Z3User::Summarize(const Term &t){ - hash_set memo; std::vector lits; int ops = 0; - SummarizeRec(memo, lits, ops, t); - std::cout << ops << ": "; - for(unsigned i = 0; i < lits.size(); i++){ - if(i > 0) std::cout << ", "; - std::cout << lits[i]; - } - } - - int Z3User::CountOperatorsRec(hash_set &memo, const Term &t){ - if(memo.find(t) != memo.end()) - return 0; - memo.insert(t); - if(t.is_app()){ - decl_kind k = t.decl().get_decl_kind(); - if (k == And || k == Or) { - int count = 1; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - count += CountOperatorsRec(memo, t.arg(i)); - return count; - } - return 0; - } - if(t.is_quantifier()){ - int nbv = t.get_quantifier_num_bound(); - return CountOperatorsRec(memo,t.body()) + 2 * nbv; // count 2 for each quantifier - } - return 0; - } - - int Z3User::CountOperators(const Term &t){ - hash_set memo; - return CountOperatorsRec(memo,t); - } - - - Z3User::Term Z3User::conjoin(const std::vector &args){ - return ctx.make(And,args); - } - - Z3User::Term Z3User::sum(const std::vector &args){ - return ctx.make(Plus,args); - } - - RPFP::Term RPFP::RedDualRela(Edge *e, std::vector &args, int idx){ - Node *child = e->Children[idx]; - Term b(ctx); - std::vector v; - RedVars(child, b, v); - for (unsigned i = 0; i < args.size(); i++) { - if (eq(args[i].get_sort(), ctx.bool_sort())) - args[i] = ctx.make(Iff, args[i], v[i]); - else - args[i] = args[i] == v[i]; - } - return args.size() > 0 ? (b && conjoin(args)) : b; - } - - Z3User::Term Z3User::CloneQuantifier(const Term &t, const Term &new_body){ -#if 0 - Z3_context c = ctx; - Z3_ast a = t; - std::vector pats; - int num_pats = Z3_get_quantifier_num_patterns(c,a); - for(int i = 0; i < num_pats; i++) - pats.push_back(Z3_get_quantifier_pattern_ast(c,a,i)); - std::vector no_pats; - int num_no_pats = Z3_get_quantifier_num_patterns(c,a); - for(int i = 0; i < num_no_pats; i++) - no_pats.push_back(Z3_get_quantifier_no_pattern_ast(c,a,i)); - int bound = Z3_get_quantifier_num_bound(c,a); - std::vector sorts; - std::vector names; - for(int i = 0; i < bound; i++){ - sorts.push_back(Z3_get_quantifier_bound_sort(c,a,i)); - names.push_back(Z3_get_quantifier_bound_name(c,a,i)); - } - Z3_ast foo = Z3_mk_quantifier_ex(c, - Z3_is_quantifier_forall(c,a), - Z3_get_quantifier_weight(c,a), - 0, - 0, - num_pats, - VEC2PTR(pats), - num_no_pats, - VEC2PTR(no_pats), - bound, - VEC2PTR(sorts), - VEC2PTR(names), - new_body); - return expr(ctx,foo); -#endif - return clone_quantifier(t,new_body); - } - - - RPFP::Term RPFP::LocalizeRec(Edge *e, hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - hash_map::iterator it = e->varMap.find(t); - if (it != e->varMap.end()){ - res = it->second; - return res; - } - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - args.push_back(LocalizeRec(e, memo, t.arg(i))); - hash_map::iterator rit = e->relMap.find(f); - if (rit != e->relMap.end()) - res = RedDualRela(e, args, (rit->second)); - else { - if (args.size() == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)) { - res = HideVariable(t, e->number); - } - else { - res = f(args.size(), VEC2PTR(args)); - } - } - } - else if (t.is_quantifier()) { - std::vector pats; - t.get_patterns(pats); - for (unsigned i = 0; i < pats.size(); i++) - pats[i] = LocalizeRec(e, memo, pats[i]); - Term body = LocalizeRec(e, memo, t.body()); - res = clone_quantifier(t, body, pats); - } - else res = t; - return res; - } - - void RPFP::SetEdgeMaps(Edge *e){ - timer_start("SetEdgeMaps"); - e->relMap.clear(); - e->varMap.clear(); - for(unsigned i = 0; i < e->F.RelParams.size(); i++){ - e->relMap[e->F.RelParams[i]] = i; - } - Term b(ctx); - std::vector v; - RedVars(e->Parent, b, v); - for(unsigned i = 0; i < e->F.IndParams.size(); i++){ - // func_decl parentParam = e->Parent.Annotation.IndParams[i]; - expr oldname = e->F.IndParams[i]; - expr newname = v[i]; - e->varMap[oldname] = newname; - } - timer_stop("SetEdgeMaps"); - - } - - - RPFP::Term RPFP::Localize(Edge *e, const Term &t){ - timer_start("Localize"); - hash_map memo; - if(e->F.IndParams.size() > 0 && e->varMap.empty()) - SetEdgeMaps(e); // TODO: why is this needed? - Term res = LocalizeRec(e,memo,t); - timer_stop("Localize"); - return res; - } - - - RPFP::Term RPFP::ReducedDualEdge(Edge *e) - { - SetEdgeMaps(e); - timer_start("RedVars"); - Term b(ctx); - std::vector v; - RedVars(e->Parent, b, v); - timer_stop("RedVars"); - // ast_to_track = (ast)b; - return implies(b, Localize(e, e->F.Formula)); - } - - TermTree *RPFP::ToTermTree(Node *root, Node *skip_descendant) - { - if(skip_descendant && root == skip_descendant) - return new TermTree(ctx.bool_val(true)); - Edge *e = root->Outgoing; - if(!e) return new TermTree(ctx.bool_val(true), std::vector()); - std::vector children(e->Children.size()); - for(unsigned i = 0; i < children.size(); i++) - children[i] = ToTermTree(e->Children[i],skip_descendant); - // Term top = ReducedDualEdge(e); - Term top = e->dual.null() ? ctx.bool_val(true) : e->dual; - TermTree *res = new TermTree(top, children); - for(unsigned i = 0; i < e->constraints.size(); i++) - res->addTerm(e->constraints[i]); - return res; - } - - TermTree *RPFP::GetGoalTree(Node *root){ - std::vector children(1); - children[0] = ToGoalTree(root); - return new TermTree(ctx.bool_val(false),children); - } - - TermTree *RPFP::ToGoalTree(Node *root) - { - Term b(ctx); - std::vector v; - RedVars(root, b, v); - Term goal = root->Name(v); - Edge *e = root->Outgoing; - if(!e) return new TermTree(goal, std::vector()); - std::vector children(e->Children.size()); - for(unsigned i = 0; i < children.size(); i++) - children[i] = ToGoalTree(e->Children[i]); - // Term top = ReducedDualEdge(e); - return new TermTree(goal, children); - } - - Z3User::Term Z3User::SubstRec(hash_map &memo, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, t.arg(i))); - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()) { - std::vector pats; - t.get_patterns(pats); - for (unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstRec(memo, pats[i]); - Term body = SubstRec(memo, t.body()); - res = clone_quantifier(t, body, pats); - } - // res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - Z3User::Term Z3User::SubstRec(hash_map &memo, hash_map &map, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, map, t.arg(i))); - hash_map::iterator it = map.find(f); - if (it != map.end()) - f = it->second; - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()) { - std::vector pats; - t.get_patterns(pats); - for (unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstRec(memo, map, pats[i]); - Term body = SubstRec(memo, map, t.body()); - res = clone_quantifier(t, body, pats); - } - // res = CloneQuantifier(t,SubstRec(memo, t.body())); - else res = t; - return res; - } - - Z3User::Term Z3User::ExtractStores(hash_map &memo, const Term &t, std::vector &cnstrs, hash_map &renaming) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - args.push_back(ExtractStores(memo, t.arg(i), cnstrs, renaming)); - res = f(args.size(), VEC2PTR(args)); - if (f.get_decl_kind() == Store) { - func_decl fresh = ctx.fresh_func_decl("@arr", res.get_sort()); - expr y = fresh(); - expr equ = ctx.make(Equal, y, res); - cnstrs.push_back(equ); - renaming[y] = res; - res = y; - } - } - else res = t; - return res; - } - - - bool Z3User::IsLiteral(const expr &lit, expr &atom, expr &val){ - if (!(lit.is_quantifier() && IsClosedFormula(lit))) { - if (!lit.is_app()) - return false; - decl_kind k = lit.decl().get_decl_kind(); - if (k == Not) { - if (IsLiteral(lit.arg(0), atom, val)) { - val = eq(val, ctx.bool_val(true)) ? ctx.bool_val(false) : ctx.bool_val(true); - return true; - } - return false; - } - if (k == And || k == Or || k == Iff || k == Implies) - return false; - } - atom = lit; - val = ctx.bool_val(true); - return true; - } - - expr Z3User::Negate(const expr &f){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - return f.arg(0); - else if(eq(f,ctx.bool_val(true))) - return ctx.bool_val(false); - else if(eq(f,ctx.bool_val(false))) - return ctx.bool_val(true); - return !f; - } - - expr Z3User::ReduceAndOr(const std::vector &args, bool is_and, std::vector &res){ - for(unsigned i = 0; i < args.size(); i++) - if (!eq(args[i], ctx.bool_val(is_and))) { - if (eq(args[i], ctx.bool_val(!is_and))) - return ctx.bool_val(!is_and); - res.push_back(args[i]); - } - return expr(); - } - - expr Z3User::FinishAndOr(const std::vector &args, bool is_and){ - if(args.size() == 0) - return ctx.bool_val(is_and); - if(args.size() == 1) - return args[0]; - return ctx.make(is_and ? And : Or,args); - } - - expr Z3User::SimplifyAndOr(const std::vector &args, bool is_and){ - std::vector sargs; - expr res = ReduceAndOr(args,is_and,sargs); - if(!res.null()) return res; - return FinishAndOr(sargs,is_and); - } - - expr Z3User::PullCommonFactors(std::vector &args, bool is_and){ - - // first check if there's anything to do... - if(args.size() < 2) - return FinishAndOr(args,is_and); - for (unsigned i = 0; i < args.size(); i++) { - const expr &a = args[i]; - if (!(a.is_app() && a.decl().get_decl_kind() == (is_and ? Or : And))) - return FinishAndOr(args, is_and); - } - std::vector common; - for (unsigned i = 0; i < args.size(); i++) { - unsigned n = args[i].num_args(); - std::vector v(n), w; - for (unsigned j = 0; j < n; j++) - v[j] = args[i].arg(j); - std::less comp; - std::sort(v.begin(), v.end(), comp); - if (i == 0) - common.swap(v); - else { - std::set_intersection(common.begin(), common.end(), v.begin(), v.end(), std::inserter(w, w.begin()), comp); - common.swap(w); - } - } - if(common.empty()) - return FinishAndOr(args,is_and); - std::set common_set(common.begin(),common.end()); - for(unsigned i = 0; i < args.size(); i++){ - unsigned n = args[i].num_args(); - std::vector lits; - for (unsigned j = 0; j < n; j++) { - const expr b = args[i].arg(j); - if (common_set.find(b) == common_set.end()) - lits.push_back(b); - } - args[i] = SimplifyAndOr(lits,!is_and); - } - common.push_back(SimplifyAndOr(args,is_and)); - return SimplifyAndOr(common,!is_and); - } - - expr Z3User::ReallySimplifyAndOr(const std::vector &args, bool is_and){ - std::vector sargs; - expr res = ReduceAndOr(args,is_and,sargs); - if(!res.null()) return res; - return PullCommonFactors(sargs,is_and); - } - - Z3User::Term Z3User::SubstAtomTriv(const expr &foo, const expr &atom, const expr &val){ - if(eq(foo,atom)) - return val; - else if(foo.is_app() && foo.decl().get_decl_kind() == Not && eq(foo.arg(0),atom)) - return Negate(val); - else - return foo; - } - - Z3User::Term Z3User::PushQuantifier(const expr &t, const expr &body, bool is_forall){ - if (t.get_quantifier_num_bound() == 1) { - std::vector fmlas, free, not_free; - CollectJuncts(body, fmlas, is_forall ? Or : And, false); - for (unsigned i = 0; i < fmlas.size(); i++) { - const expr &fmla = fmlas[i]; - if (fmla.has_free(0)) - free.push_back(fmla); - else - not_free.push_back(fmla); - } - decl_kind op = is_forall ? Or : And; - if (free.empty()) - return DeleteBound(0, 1, SimplifyAndOr(not_free, op == And)); - expr q = clone_quantifier(is_forall ? Forall : Exists, t, SimplifyAndOr(free, op == And)); - if (!not_free.empty()) - q = ctx.make(op, q, DeleteBound(0, 1, SimplifyAndOr(not_free, op == And))); - return q; - } - return clone_quantifier(is_forall ? Forall : Exists,t,body); - } - - Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body, bool is_forall) { - if (body.is_app()) { - if (body.decl().get_decl_kind() == (is_forall ? And : Or)) { // quantifier distributes - int nargs = body.num_args(); - std::vector args(nargs); - for (int i = 0; i < nargs; i++) - args[i] = CloneQuantAndSimp(t, body.arg(i), is_forall); - return SimplifyAndOr(args, body.decl().get_decl_kind() == And); - } - else if (body.decl().get_decl_kind() == (is_forall ? Or : And)) { // quantifier distributes - return PushQuantifier(t, body, is_forall); // may distribute partially - } - else if (body.decl().get_decl_kind() == Not) { - return ctx.make(Not, CloneQuantAndSimp(t, body.arg(0), !is_forall)); - } - } - if (t.get_quantifier_num_bound() == 1 && !body.has_free(0)) - return DeleteBound(0, 1, body); // drop the quantifier - return clone_quantifier(is_forall ? Forall : Exists, t, body); - } - - Z3User::Term Z3User::CloneQuantAndSimp(const expr &t, const expr &body){ - return CloneQuantAndSimp(t,body,t.is_quantifier_forall()); - } - - Z3User::Term Z3User::SubstAtom(hash_map &memo, const expr &t, const expr &atom, const expr &val) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - decl_kind k = f.get_decl_kind(); - - // TODO: recur here, but how much? We don't want to be quadractic in formula size - - if (k == And || k == Or) { - int nargs = t.num_args(); - std::vector args(nargs); - for (int i = 0; i < nargs; i++) - args[i] = SubstAtom(memo, t.arg(i), atom, val); - res = ReallySimplifyAndOr(args, k == And); - return res; - } - } - else if (t.is_quantifier() && atom.is_quantifier()) { - if (eq(t, atom)) - res = val; - else - res = clone_quantifier(t, SubstAtom(memo, t.body(), atom, val)); - return res; - } - res = SubstAtomTriv(t, atom, val); - return res; - } - - void Z3User::RemoveRedundancyOp(bool pol, std::vector &args, hash_map &smemo) { - for (unsigned i = 0; i < args.size(); i++) { - const expr &lit = args[i]; - expr atom, val; - if (IsLiteral(lit, atom, val)) { - if (atom.is_app() && atom.decl().get_decl_kind() == Equal) - if (pol ? eq(val, ctx.bool_val(true)) : eq(val, ctx.bool_val(false))) { - expr lhs = atom.arg(0), rhs = atom.arg(1); - if (lhs.is_numeral()) - std::swap(lhs, rhs); - if (rhs.is_numeral() && lhs.is_app()) { - for (unsigned j = 0; j < args.size(); j++) - if (j != i) { - smemo.clear(); - smemo[lhs] = rhs; - args[j] = SubstRec(smemo, args[j]); - } - } - } - for (unsigned j = 0; j < args.size(); j++) - if (j != i) { - smemo.clear(); - args[j] = SubstAtom(smemo, args[j], atom, pol ? val : !val); - } - } - } - } - - - Z3User::Term Z3User::RemoveRedundancyRec(hash_map &memo, hash_map &smemo, const Term &t) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - args.push_back(RemoveRedundancyRec(memo, smemo, t.arg(i))); - - decl_kind k = f.get_decl_kind(); - if (k == And) { - RemoveRedundancyOp(true, args, smemo); - res = ReallySimplifyAndOr(args, true); - } - else if (k == Or) { - RemoveRedundancyOp(false, args, smemo); - res = ReallySimplifyAndOr(args, false); - } - else { - if (k == Equal && args[0].get_id() > args[1].get_id()) - std::swap(args[0], args[1]); - res = f(args.size(), VEC2PTR(args)); - } - } - else if (t.is_quantifier()) { - Term body = RemoveRedundancyRec(memo, smemo, t.body()); - res = CloneQuantAndSimp(t, body); - } - else res = t; - return res; - } - - Z3User::Term Z3User::RemoveRedundancy(const Term &t){ - hash_map memo; - hash_map smemo; - return RemoveRedundancyRec(memo,smemo,t); - } - - Z3User::Term Z3User::AdjustQuantifiers(const Term &t) - { - if(t.is_quantifier() || (t.is_app() && t.has_quantifiers())) - return t.qe_lite(); - return t; - } - - Z3User::Term Z3User::IneqToEqRec(hash_map &memo, const Term &t) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - args.push_back(IneqToEqRec(memo, t.arg(i))); - - decl_kind k = f.get_decl_kind(); - if (k == And) { - for (int i = 0; i < nargs - 1; i++) { - if ((args[i].is_app() && args[i].decl().get_decl_kind() == Geq && - args[i + 1].is_app() && args[i + 1].decl().get_decl_kind() == Leq) - || - (args[i].is_app() && args[i].decl().get_decl_kind() == Leq && - args[i + 1].is_app() && args[i + 1].decl().get_decl_kind() == Geq)) - if (eq(args[i].arg(0), args[i + 1].arg(0)) && eq(args[i].arg(1), args[i + 1].arg(1))) { - args[i] = ctx.make(Equal, args[i].arg(0), args[i].arg(1)); - args[i + 1] = ctx.bool_val(true); - } - } - } - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()) { - Term body = IneqToEqRec(memo, t.body()); - res = clone_quantifier(t, body); - } - else res = t; - return res; - } - - Z3User::Term Z3User::IneqToEq(const Term &t){ - hash_map memo; - return IneqToEqRec(memo,t); - } - - Z3User::Term Z3User::SubstRecHide(hash_map &memo, const Term &t, int number) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (!bar.second) return res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted) { - std::string name = std::string("@q_") + t.decl().name().str() + "_" + string_of_int(number); - res = ctx.constant(name.c_str(), t.get_sort()); - return res; - } - for (int i = 0; i < nargs; i++) - args.push_back(SubstRec(memo, t.arg(i))); - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()) - res = CloneQuantifier(t, SubstRec(memo, t.body())); - else res = t; - return res; - } - - RPFP::Term RPFP::SubstParams(const std::vector &from, - const std::vector &to, const Term &t) { - hash_map memo; - bool some_diff = false; - for (unsigned i = 0; i < from.size(); i++) - if (i < to.size() && !eq(from[i], to[i])) { - memo[from[i]] = to[i]; - some_diff = true; - } - return some_diff ? SubstRec(memo, t) : t; - } - - RPFP::Term RPFP::SubstParamsNoCapture(const std::vector &from, - const std::vector &to, const Term &t) { - hash_map memo; - bool some_diff = false; - for (unsigned i = 0; i < from.size(); i++) - if (i < to.size() && !eq(from[i], to[i])) { - memo[from[i]] = to[i]; - // if the new param is not being mapped to anything else, we need to rename it to prevent capture - // note, if the new param *is* mapped later in the list, it will override this substitution - const expr &w = to[i]; - if (memo.find(w) == memo.end()) { - std::string old_name = w.decl().name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); - expr y = fresh(); - memo[w] = y; - } - some_diff = true; - } - return some_diff ? SubstRec(memo, t) : t; - } - - - - RPFP::Transformer RPFP::Fuse(const std::vector &trs) { - assert(!trs.empty()); - const std::vector ¶ms = trs[0]->IndParams; - std::vector fmlas(trs.size()); - fmlas[0] = trs[0]->Formula; - for (unsigned i = 1; i < trs.size(); i++) - fmlas[i] = SubstParamsNoCapture(trs[i]->IndParams, params, trs[i]->Formula); - std::vector rel_params = trs[0]->RelParams; - for (unsigned i = 1; i < trs.size(); i++) { - const std::vector ¶ms2 = trs[i]->RelParams; - hash_map map; - for (unsigned j = 0; j < params2.size(); j++) { - func_decl rel = RenumberPred(params2[j], rel_params.size()); - rel_params.push_back(rel); - map[params2[j]] = rel; - } - hash_map memo; - fmlas[i] = SubstRec(memo, map, fmlas[i]); - } - return Transformer(rel_params, params, ctx.make(Or, fmlas), trs[0]->owner); - } - - - void Z3User::Strengthen(Term &x, const Term &y) - { - if (eq(x,ctx.bool_val(true))) - x = y; - else - x = x && y; - } - - void RPFP::SetAnnotation(Node *root, const expr &t){ - hash_map memo; - Term b; - std::vector v; - RedVars(root, b, v); - memo[b] = ctx.bool_val(true); - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = root->Annotation.IndParams[i]; - Term annot = SubstRec(memo, t); - // Strengthen(ref root.Annotation.Formula, annot); - root->Annotation.Formula = annot; - } - - void RPFP::DecodeTree(Node *root, TermTree *interp, int persist) { - std::vector &ic = interp->getChildren(); - if (ic.size() > 0) { - std::vector &nc = root->Outgoing->Children; - for (unsigned i = 0; i < nc.size(); i++) - DecodeTree(nc[i], ic[i], persist); - } - SetAnnotation(root, interp->getTerm()); -#if 0 - if(persist != 0) - Z3_persist_ast(ctx,root->Annotation.Formula,persist); -#endif - } - - RPFP::Term RPFP::GetUpperBound(Node *n) - { - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned int i = 0; i < v.size(); i++) - memo[n->Bound.IndParams[i]] = v[i]; - Term cnst = SubstRec(memo, n->Bound.Formula); - return b && !cnst; - } - - RPFP::Term RPFP::GetAnnotation(Node *n) - { - if(eq(n->Annotation.Formula,ctx.bool_val(true))) - return n->Annotation.Formula; - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[n->Annotation.IndParams[i]] = v[i]; - Term cnst = SubstRec(memo, n->Annotation.Formula); - return !b || cnst; - } - - RPFP::Term RPFP::GetUnderapprox(Node *n) - { - // TODO: cache this - Term b(ctx); std::vector v; - RedVars(n, b, v); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[n->Underapprox.IndParams[i]] = v[i]; - Term cnst = SubstRecHide(memo, n->Underapprox.Formula, n->number); - return !b || cnst; - } - - TermTree *RPFP::AddUpperBound(Node *root, TermTree *t) - { - Term f = !((ast)(root->dual)) ? ctx.bool_val(true) : root->dual; - std::vector c(1); c[0] = t; - return new TermTree(f, c); - } - -#if 0 - void RPFP::WriteInterps(System.IO.StreamWriter f, TermTree t) - { - foreach (var c in t.getChildren()) - WriteInterps(f, c); - f.WriteLine(t.getTerm()); - } -#endif - - - expr RPFP::GetEdgeFormula(Edge *e, int persist, bool with_children, bool underapprox) { - if (e->dual.null()) { - timer_start("ReducedDualEdge"); - e->dual = ReducedDualEdge(e); - timer_stop("ReducedDualEdge"); - timer_start("getting children"); - if (underapprox) { - std::vector cus(e->Children.size()); - for (unsigned i = 0; i < e->Children.size(); i++) - cus[i] = !UnderapproxFlag(e->Children[i]) || GetUnderapprox(e->Children[i]); - expr cnst = conjoin(cus); - e->dual = e->dual && cnst; - } - timer_stop("getting children"); - timer_start("Persisting"); - std::list::reverse_iterator it = stack.rbegin(); - for (int i = 0; i < persist && it != stack.rend(); i++) - it++; - if (it != stack.rend()) - it->edges.push_back(e); - timer_stop("Persisting"); - //Console.WriteLine("{0}", cnst); - } - return e->dual; - } - - /** For incremental solving, asserts the constraint associated - * with this edge in the SMT context. If this edge is removed, - * you must pop the context accordingly. The second argument is - * the number of pushes we are inside. The constraint formula - * will survive "persist" pops of the context. If you plan - * to reassert the edge after popping the context once, - * you can save time re-constructing the formula by setting - * "persist" to one. If you set "persist" too high, however, - * you could have a memory leak. - * - * The flag "with children" causes the annotations of the children - * to be asserted. The flag underapprox causes the underapproximations - * of the children to be asserted *conditionally*. See Check() on - * how to actually enforce these constraints. - * - */ - - void RPFP::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) { - if (eq(e->F.Formula, ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); - timer_start("solver add"); - slvr_add(e->dual); - timer_stop("solver add"); - if (with_children) - for (unsigned i = 0; i < e->Children.size(); i++) - ConstrainParent(e, e->Children[i]); - } - - -#ifdef LIMIT_STACK_WEIGHT - void RPFP_caching::AssertEdge(Edge *e, int persist, bool with_children, bool underapprox) - { - unsigned old_new_alits = new_alits.size(); - if(eq(e->F.Formula,ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, persist, with_children, underapprox); - timer_start("solver add"); - slvr_add(e->dual); - timer_stop("solver add"); - if(old_new_alits < new_alits.size()) - weight_added.val++; - if(with_children) - for(unsigned i = 0; i < e->Children.size(); i++) - ConstrainParent(e,e->Children[i]); - } -#endif - - // caching verion of above - void RPFP_caching::AssertEdgeCache(Edge *e, std::vector &lits, bool with_children) { - if (eq(e->F.Formula, ctx.bool_val(true)) && (!with_children || e->Children.empty())) - return; - expr fmla = GetEdgeFormula(e, 0, with_children, false); - GetAssumptionLits(fmla, lits); - if (with_children) - for (unsigned i = 0; i < e->Children.size(); i++) - ConstrainParentCache(e, e->Children[i], lits); - } - - void RPFP::slvr_add(const expr &e){ - slvr().add(e); - } - - void RPFP_caching::slvr_add(const expr &e){ - GetAssumptionLits(e,alit_stack); - } - - void RPFP::slvr_pop(int i){ - slvr().pop(i); - } - - void RPFP::slvr_push(){ - slvr().push(); - } - - void RPFP_caching::slvr_pop(int i){ - for(int j = 0; j < i; j++){ -#ifdef LIMIT_STACK_WEIGHT - if(alit_stack_sizes.empty()){ - if(big_stack.empty()) - throw "stack underflow"; - for(unsigned k = 0; k < new_alits.size(); k++){ - if(AssumptionLits.find(new_alits[k]) == AssumptionLits.end()) - throw "foo!"; - AssumptionLits.erase(new_alits[k]); - } - big_stack_entry &bsb = big_stack.back(); - bsb.alit_stack_sizes.swap(alit_stack_sizes); - bsb.alit_stack.swap(alit_stack); - bsb.new_alits.swap(new_alits); - bsb.weight_added.swap(weight_added); - big_stack.pop_back(); - slvr().pop(1); - continue; - } -#endif - alit_stack.resize(alit_stack_sizes.back()); - alit_stack_sizes.pop_back(); - } - } - - void RPFP_caching::slvr_push(){ -#ifdef LIMIT_STACK_WEIGHT - if(weight_added.val > LIMIT_STACK_WEIGHT){ - big_stack.resize(big_stack.size()+1); - big_stack_entry &bsb = big_stack.back(); - bsb.alit_stack_sizes.swap(alit_stack_sizes); - bsb.alit_stack.swap(alit_stack); - bsb.new_alits.swap(new_alits); - bsb.weight_added.swap(weight_added); - slvr().push(); - for(unsigned i = 0; i < bsb.alit_stack.size(); i++) - slvr().add(bsb.alit_stack[i]); - return; - } -#endif - alit_stack_sizes.push_back(alit_stack.size()); - } - - check_result RPFP::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - return slvr().check(n, assumptions, core_size, core); - } - - check_result RPFP_caching::slvr_check(unsigned n, expr * const assumptions, unsigned *core_size, expr *core){ - unsigned oldsiz = alit_stack.size(); - if(n && assumptions) - std::copy(assumptions,assumptions+n,std::inserter(alit_stack,alit_stack.end())); - check_result res; - if (core_size && core) { - std::vector full_core(alit_stack.size()), core1(n); - std::copy(assumptions, assumptions + n, core1.begin()); - res = slvr().check(alit_stack.size(), VEC2PTR(alit_stack), core_size, VEC2PTR(full_core)); - full_core.resize(*core_size); - if (res == unsat) { - FilterCore(core1, full_core); - *core_size = core1.size(); - std::copy(core1.begin(), core1.end(), core); - } - } - else - res = slvr().check(alit_stack.size(), VEC2PTR(alit_stack)); - alit_stack.resize(oldsiz); - return res; - } - - lbool RPFP::ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals, - bool weak) { - return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); - } - - lbool RPFP_caching::ls_interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - TermTree *goals, - bool weak) { - GetTermTreeAssertionLiterals(assumptions); - return ls->interpolate_tree(assumptions, interpolants, _model, goals, weak); - } - - void RPFP_caching::GetTermTreeAssertionLiteralsRec(TermTree *assumptions){ - std::vector alits; - hash_map map; - GetAssumptionLits(assumptions->getTerm(),alits,&map); - std::vector &ts = assumptions->getTerms(); - for(unsigned i = 0; i < ts.size(); i++) - GetAssumptionLits(ts[i],alits,&map); - assumptions->setTerm(ctx.bool_val(true)); - ts = alits; - for(unsigned i = 0; i < alits.size(); i++) - ts.push_back(ctx.make(Implies,alits[i],map[alits[i]])); - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - GetTermTreeAssertionLiterals(assumptions->getChildren()[i]); - return; - } - - void RPFP_caching::GetTermTreeAssertionLiterals(TermTree *assumptions) { - // optimize binary case - if (assumptions->getChildren().size() == 1 - && assumptions->getChildren()[0]->getChildren().size() == 0) { - hash_map map; - TermTree *child = assumptions->getChildren()[0]; - std::vector dummy; - GetAssumptionLits(child->getTerm(), dummy, &map); - std::vector &ts = child->getTerms(); - for (unsigned i = 0; i < ts.size(); i++) - GetAssumptionLits(ts[i], dummy, &map); - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - if (!proof_core) { // save the proof core for later use - proof_core = new hash_set < ast > ; - for (unsigned i = 0; i < assumps.size(); i++) - proof_core->insert(assumps[i]); - } - std::vector *cnsts[2] = { &child->getTerms(), &assumptions->getTerms() }; - for (unsigned i = 0; i < assumps.size(); i++) { - expr &as = assumps[i]; - expr alit = (as.is_app() && as.decl().get_decl_kind() == Implies) ? as.arg(0) : as; - bool isA = map.find(alit) != map.end(); - cnsts[isA ? 0 : 1]->push_back(as); - } - } - else - GetTermTreeAssertionLiteralsRec(assumptions); - } - - void RPFP::AddToProofCore(hash_set &core){ - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - core.insert(assumps[i]); - } - - void RPFP::ComputeProofCore(){ - if(!proof_core){ - proof_core = new hash_set; - AddToProofCore(*proof_core); - } - } - - - void RPFP_caching::GetAssumptionLits(const expr &fmla, std::vector &lits, hash_map *opt_map) { - std::vector conjs; - CollectConjuncts(fmla, conjs); - for (unsigned i = 0; i < conjs.size(); i++) { - const expr &conj = conjs[i]; - std::pair foo(conj, expr(ctx)); - std::pair::iterator, bool> bar = AssumptionLits.insert(foo); - Term &res = bar.first->second; - if (bar.second) { - func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); - res = pred(); -#ifdef LIMIT_STACK_WEIGHT - new_alits.push_back(conj); -#endif - slvr().add(ctx.make(Implies, res, conj)); - // std::cout << res << ": " << conj << "\n"; - } - if (opt_map) - (*opt_map)[res] = conj; - lits.push_back(res); - } - } - - void RPFP::ConstrainParent(Edge *parent, Node *child){ - ConstrainEdgeLocalized(parent,GetAnnotation(child)); - } - - void RPFP_caching::ConstrainParentCache(Edge *parent, Node *child, std::vector &lits){ - ConstrainEdgeLocalizedCache(parent,GetAnnotation(child),lits); - } - - - /** For incremental solving, asserts the negation of the upper bound associated - * with a node. - * */ - - void RPFP::AssertNode(Node *n) - { - if (n->dual.null()) { - n->dual = GetUpperBound(n); - stack.back().nodes.push_back(n); - slvr_add(n->dual); - } - } - - // caching version of above - void RPFP_caching::AssertNodeCache(Node *n, std::vector lits){ - if (n->dual.null()) { - n->dual = GetUpperBound(n); - stack.back().nodes.push_back(n); - GetAssumptionLits(n->dual, lits); - } - } - - /** Clone another RPFP into this one, keeping a map */ - void RPFP_caching::Clone(RPFP *other) { -#if 0 - for(unsigned i = 0; i < other->nodes.size(); i++) - NodeCloneMap[other->nodes[i]] = CloneNode(other->nodes[i]); -#endif - for (unsigned i = 0; i < other->edges.size(); i++) { - Edge *edge = other->edges[i]; - Node *parent = CloneNode(edge->Parent); - std::vector cs; - for (unsigned j = 0; j < edge->Children.size(); j++) - // cs.push_back(NodeCloneMap[edge->Children[j]]); - cs.push_back(CloneNode(edge->Children[j])); - EdgeCloneMap[edge] = CreateEdge(parent, edge->F, cs); - } - } - - /** Get the clone of a node */ - RPFP::Node *RPFP_caching::GetNodeClone(Node *other_node){ - return NodeCloneMap[other_node]; - } - - /** Get the clone of an edge */ - RPFP::Edge *RPFP_caching::GetEdgeClone(Edge *other_edge){ - return EdgeCloneMap[other_edge]; - } - - /** check assumption lits, and return core */ - check_result RPFP_caching::CheckCore(const std::vector &assumps, std::vector &core){ - core.resize(assumps.size()); - unsigned core_size; - check_result res = slvr().check(assumps.size(),(expr *)VEC2PTR(assumps),&core_size, VEC2PTR(core)); - if(res == unsat) - core.resize(core_size); - else - core.clear(); - return res; - } - - - /** Assert a constraint on an edge in the SMT context. - */ - - void RPFP::ConstrainEdge(Edge *e, const Term &t) - { - Term tl = Localize(e, t); - ConstrainEdgeLocalized(e,tl); - } - - void RPFP::ConstrainEdgeLocalized(Edge *e, const Term &tl) - { - e->constraints.push_back(tl); - stack.back().constraints.push_back(std::pair(e,tl)); - slvr_add(tl); - } - - void RPFP_caching::ConstrainEdgeLocalizedCache(Edge *e, const Term &tl, std::vector &lits) - { - e->constraints.push_back(tl); - stack.back().constraints.push_back(std::pair(e,tl)); - GetAssumptionLits(tl,lits); - } - - - /** Declare a constant in the background theory. */ - - void RPFP::DeclareConstant(const FuncDecl &f){ - ls->declare_constant(f); - } - - /** Assert a background axiom. Background axioms can be used to provide the - * theory of auxilliary functions or relations. All symbols appearing in - * background axioms are considered global, and may appear in both transformer - * and relational solutions. Semantically, a solution to the RPFP gives - * an interpretation of the unknown relations for each interpretation of the - * auxilliary symbols that is consistent with the axioms. Axioms should be - * asserted before any calls to Push. They cannot be de-asserted by Pop. */ - - void RPFP::AssertAxiom(const Term &t) - { - ls->assert_axiom(t); - axioms.push_back(t); // for printing only - } - -#if 0 - /** Do not call this. */ - - void RPFP::RemoveAxiom(const Term &t) - { - slvr().RemoveInterpolationAxiom(t); - } -#endif - - /** Solve an RPFP graph. This means either strengthen the annotation - * so that the bound at the given root node is satisfied, or - * show that this cannot be done by giving a dual solution - * (i.e., a counterexample). - * - * In the current implementation, this only works for graphs that - * are: - * - tree-like - * - * - closed. - * - * In a tree-like graph, every nod has out most one incoming and one out-going edge, - * and there are no cycles. In a closed graph, every node has exactly one out-going - * edge. This means that the leaves of the tree are all hyper-edges with no - * children. Such an edge represents a relation (nullary transformer) and thus - * a lower bound on its parent. The parameter root must be the root of this tree. - * - * If Solve returns LBool.False, this indicates success. The annotation of the tree - * has been updated to satisfy the upper bound at the root. - * - * If Solve returns LBool.True, this indicates a counterexample. For each edge, - * you can then call Eval to determine the values of symbols in the transformer formula. - * You can also call Empty on a node to determine if its value in the counterexample - * is the empty relation. - * - * \param root The root of the tree - * \param persist Number of context pops through which result should persist - * - * - */ - - lbool RPFP::Solve(Node *root, int persist) - { - timer_start("Solve"); - TermTree *tree = GetConstraintTree(root); - TermTree *interpolant = NULL; - TermTree *goals = NULL; - if(ls->need_goals) - goals = GetGoalTree(root); - ClearProofCore(); - - // if (dualModel != null) dualModel.Dispose(); - // if (dualLabels != null) dualLabels.Dispose(); - - timer_start("interpolate_tree"); - lbool res = ls_interpolate_tree(tree, interpolant, dualModel,goals,true); - timer_stop("interpolate_tree"); - if (res == l_false) { - DecodeTree(root, interpolant->getChildren()[0], persist); - delete interpolant; - } - - delete tree; - if(goals) - delete goals; - - timer_stop("Solve"); - return res; - } - - void RPFP::CollapseTermTreeRec(TermTree *root, TermTree *node){ - root->addTerm(node->getTerm()); - std::vector &cnsts = node->getTerms(); - for(unsigned i = 0; i < cnsts.size(); i++) - root->addTerm(cnsts[i]); - std::vector &chs = node->getChildren(); - for(unsigned i = 0; i < chs.size(); i++){ - CollapseTermTreeRec(root,chs[i]); - } - } - - TermTree *RPFP::CollapseTermTree(TermTree *node){ - std::vector &chs = node->getChildren(); - for(unsigned i = 0; i < chs.size(); i++) - CollapseTermTreeRec(node,chs[i]); - for(unsigned i = 0; i < chs.size(); i++) - delete chs[i]; - chs.clear(); - return node; - } - - lbool RPFP::SolveSingleNode(Node *root, Node *node) - { - timer_start("Solve"); - TermTree *tree = CollapseTermTree(GetConstraintTree(root,node)); - tree->getChildren().push_back(CollapseTermTree(ToTermTree(node))); - TermTree *interpolant = NULL; - ClearProofCore(); - - timer_start("interpolate_tree"); - lbool res = ls_interpolate_tree(tree, interpolant, dualModel,0,true); - timer_stop("interpolate_tree"); - if (res == l_false) { - DecodeTree(node, interpolant->getChildren()[0], 0); - delete interpolant; - } - - delete tree; - timer_stop("Solve"); - return res; - } - - /** Get the constraint tree (but don't solve it) */ - - TermTree *RPFP::GetConstraintTree(Node *root, Node *skip_descendant) - { - return AddUpperBound(root, ToTermTree(root,skip_descendant)); - } - - /** Dispose of the dual model (counterexample) if there is one. */ - - void RPFP::DisposeDualModel() - { - dualModel = model(ctx,NULL); - } - - RPFP::Term RPFP::UnderapproxFlag(Node *n){ - expr flag = ctx.constant((std::string("@under") + string_of_int(n->number)).c_str(), ctx.bool_sort()); - underapprox_flag_rev[flag] = n; - return flag; - } - - RPFP::Node *RPFP::UnderapproxFlagRev(const Term &flag){ - return underapprox_flag_rev[flag]; - } - - /** Check satisfiability of asserted edges and nodes. Same functionality as - * Solve, except no primal solution (interpolant) is generated in the unsat case. - * The vector underapproxes gives the set of node underapproximations to be enforced - * (assuming these were conditionally asserted by AssertEdge). - * - */ - - check_result RPFP::Check(Node *root, std::vector underapproxes, std::vector *underapprox_core) { - timer_start("Check"); - ClearProofCore(); - // if (dualModel != null) dualModel.Dispose(); - check_result res; - if (!underapproxes.size()) - res = slvr_check(); - else { - std::vector us(underapproxes.size()); - for (unsigned i = 0; i < underapproxes.size(); i++) - us[i] = UnderapproxFlag(underapproxes[i]); - slvr_check(); // TODO: no idea why I need to do this - if (underapprox_core) { - std::vector unsat_core(us.size()); - unsigned core_size = 0; - res = slvr_check(us.size(), VEC2PTR(us), &core_size, VEC2PTR(unsat_core)); - underapprox_core->resize(core_size); - for (unsigned i = 0; i < core_size; i++) - (*underapprox_core)[i] = UnderapproxFlagRev(unsat_core[i]); - } - else { - res = slvr_check(us.size(), VEC2PTR(us)); - bool dump = false; - if (dump) { - std::vector cnsts; - // cnsts.push_back(axioms[0]); - cnsts.push_back(root->dual); - cnsts.push_back(root->Outgoing->dual); - ls->write_interpolation_problem("temp.smt", cnsts, std::vector()); - } - } - // check_result temp = slvr_check(); - } - dualModel = slvr().get_model(); - timer_stop("Check"); - return res; - } - - check_result RPFP::CheckUpdateModel(Node *root, std::vector assumps){ - // check_result temp1 = slvr_check(); // no idea why I need to do this - ClearProofCore(); - check_result res = slvr_check(assumps.size(), VEC2PTR(assumps)); - model mod = slvr().get_model(); - if(!mod.null()) - dualModel = mod;; - return res; - } - - /** Determines the value in the counterexample of a symbol occuring in the transformer formula of - * a given edge. */ - - RPFP::Term RPFP::Eval(Edge *e, Term t) - { - Term tl = Localize(e, t); - return dualModel.eval(tl); - } - - /** Returns true if the given node is empty in the primal solution. For proecudure summaries, - this means that the procedure is not called in the current counter-model. */ - - bool RPFP::Empty(Node *p) - { - Term b; std::vector v; - RedVars(p, b, v); - // dualModel.show_internals(); - // std::cout << "b: " << b << std::endl; - expr bv = dualModel.eval(b); - // std::cout << "bv: " << bv << std::endl; - bool res = !eq(bv,ctx.bool_val(true)); - return res; - } - - RPFP::Term RPFP::EvalNode(Node *p) - { - Term b; std::vector v; - RedVars(p, b, v); - std::vector args; - for(unsigned i = 0; i < v.size(); i++) - args.push_back(dualModel.eval(v[i])); - return (p->Name)(args); - } - - void RPFP::EvalArrayTerm(const RPFP::Term &t, ArrayValue &res){ - if (t.is_app()) { - decl_kind k = t.decl().get_decl_kind(); - if (k == AsArray) { - func_decl fd = t.decl().get_func_decl_parameter(0); - func_interp r = dualModel.get_func_interp(fd); - int num = r.num_entries(); - res.defined = true; - for (int i = 0; i < num; i++) { - expr arg = r.get_arg(i, 0); - expr value = r.get_value(i); - res.entries[arg] = value; - } - res.def_val = r.else_value(); - return; - } - else if (k == Store) { - EvalArrayTerm(t.arg(0), res); - if (!res.defined)return; - expr addr = t.arg(1); - expr val = t.arg(2); - if (addr.is_numeral() && val.is_numeral()) { - if (eq(val, res.def_val)) - res.entries.erase(addr); - else - res.entries[addr] = val; - } - else - res.defined = false; - return; - } - } - res.defined = false; - } - - int eae_count = 0; - - RPFP::Term RPFP::EvalArrayEquality(const RPFP::Term &f) { - ArrayValue lhs, rhs; - eae_count++; - EvalArrayTerm(f.arg(0), lhs); - EvalArrayTerm(f.arg(1), rhs); - if (lhs.defined && rhs.defined) { - if (eq(lhs.def_val, rhs.def_val)) - if (lhs.entries == rhs.entries) - return ctx.bool_val(true); - return ctx.bool_val(false); - } - return f; - } - - /** Compute truth values of all boolean subterms in current model. - Assumes formulas has been simplified by Z3, so only boolean ops - ands and, or, not. Returns result in memo. - */ - - int RPFP::SubtermTruth(hash_map &memo, const Term &f) { - TRACE("duality", tout << f << "\n";); - if (memo.find(f) != memo.end()) { - return memo[f]; - } - int res; - if (f.is_app()) { - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if (k == Implies) { - res = SubtermTruth(memo, !f.arg(0) || f.arg(1)); - goto done; - } - if (k == And) { - res = 1; - for (int i = 0; i < nargs; i++) { - int ar = SubtermTruth(memo, f.arg(i)); - if (ar == 0) { - res = 0; - goto done; - } - if (ar == 2)res = 2; - } - goto done; - } - else if (k == Or) { - res = 0; - for (int i = 0; i < nargs; i++) { - int ar = SubtermTruth(memo, f.arg(i)); - if (ar == 1) { - res = 1; - goto done; - } - if (ar == 2)res = 2; - } - goto done; - } - else if (k == Not) { - int ar = SubtermTruth(memo, f.arg(0)); - res = (ar == 0) ? 1 : ((ar == 1) ? 0 : 2); - goto done; - } - } - { - bool pos; std::vector names; - if (f.is_label(pos, names)) { - res = SubtermTruth(memo, f.arg(0)); - goto done; - } - } - { - expr bv = dualModel.eval(f); - if (bv.is_app() && bv.decl().get_decl_kind() == Equal && - bv.arg(0).is_array()) { - bv = EvalArrayEquality(bv); - } - // Hack!!!! array equalities can occur negatively! - if (bv.is_app() && bv.decl().get_decl_kind() == Not && - bv.arg(0).decl().get_decl_kind() == Equal && - bv.arg(0).arg(0).is_array()) { - bv = dualModel.eval(!EvalArrayEquality(bv.arg(0))); - } - if (eq(bv, ctx.bool_val(true))) - res = 1; - else if (eq(bv, ctx.bool_val(false))) - res = 0; - else - res = 2; - } - done: - memo[f] = res; - return res; - } - - int RPFP::EvalTruth(hash_map &memo, Edge *e, const Term &f){ - Term tl = Localize(e, f); - return SubtermTruth(memo,tl); - } - - /** Compute truth values of all boolean subterms in current model. - Assumes formulas has been simplified by Z3, so only boolean ops - ands and, or, not. Returns result in memo. - */ - - - void RPFP::GetLabelsRec(hash_map &memo, const Term &f, std::vector &labels, - hash_set *done, bool truth) { - if (done[truth].find(f) != done[truth].end()) - return; /* already processed */ - if (f.is_app()) { - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if (k == Implies) { - GetLabelsRec(memo, f.arg(1) || !f.arg(0), labels, done, truth); - goto done; - } - if (k == Iff) { - int b = SubtermTruth(memo, f.arg(0)); - if (b == 2) { - goto done; - // bypass error - std::ostringstream os; - os << "disaster in SubtermTruth processing " << f; - throw default_exception(os.str()); - } - GetLabelsRec(memo, f.arg(1), labels, done, truth ? b : !b); - goto done; - } - if (truth ? k == And : k == Or) { - for (int i = 0; i < nargs; i++) - GetLabelsRec(memo, f.arg(i), labels, done, truth); - goto done; - } - if (truth ? k == Or : k == And) { - for (int i = 0; i < nargs; i++) { - Term a = f.arg(i); - timer_start("SubtermTruth"); - int b = SubtermTruth(memo, a); - timer_stop("SubtermTruth"); - if (truth ? (b == 1) : (b == 0)) { - GetLabelsRec(memo, a, labels, done, truth); - goto done; - } - } - /* Unreachable! */ - // throw "error in RPFP::GetLabelsRec"; - goto done; - } - else if (k == Not) { - GetLabelsRec(memo, f.arg(0), labels, done, !truth); - goto done; - } - else { - bool pos; std::vector names; - if (f.is_label(pos, names)) { - GetLabelsRec(memo, f.arg(0), labels, done, truth); - if (pos == truth) - for (unsigned i = 0; i < names.size(); i++) - labels.push_back(names[i]); - goto done; - } - } - } - done: - done[truth].insert(f); - } - - void RPFP::GetLabels(Edge *e, std::vector &labels){ - if(!e->map || e->map->labeled.null()) - return; - Term tl = Localize(e, e->map->labeled); - hash_map memo; - hash_set done[2]; - GetLabelsRec(memo,tl,labels,done,true); - } - -#ifdef Z3OPS - static Z3_subterm_truth *stt; -#endif - - int ir_count = 0; - - void RPFP::ImplicantRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set *done, bool truth, hash_set &dont_cares) { - if (done[truth].find(f) != done[truth].end()) - return; /* already processed */ -#if 0 - int this_count = ir_count++; - if(this_count == 50092) - std::cout << "foo!\n"; -#endif - if (f.is_app()) { - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if (k == Implies) { - ImplicantRed(memo, f.arg(1) || !f.arg(0), lits, done, truth, dont_cares); - goto done; - } - if (k == Iff) { - int b = SubtermTruth(memo, f.arg(0)); - if (b == 2) { - std::ostringstream os; - os << "disaster in SubtermTruth processing " << f; - throw default_exception(os.str()); - } - ImplicantRed(memo, f.arg(1), lits, done, truth ? b : !b, dont_cares); - goto done; - } - if (truth ? k == And : k == Or) { - for (int i = 0; i < nargs; i++) - ImplicantRed(memo, f.arg(i), lits, done, truth, dont_cares); - goto done; - } - if (truth ? k == Or : k == And) { - for (int i = 0; i < nargs; i++) { - Term a = f.arg(i); -#if 0 - if(i == nargs - 1){ // last chance! - ImplicantRed(memo,a,lits,done,truth,dont_cares); - goto done; - } -#endif - timer_start("SubtermTruth"); -#ifdef Z3OPS - bool b = stt->eval(a); -#else - int b = SubtermTruth(memo, a); -#endif - timer_stop("SubtermTruth"); - if (truth ? (b == 1) : (b == 0)) { - ImplicantRed(memo, a, lits, done, truth, dont_cares); - goto done; - } - } - /* Unreachable! */ - // TODO: need to indicate this failure to caller - // std::cerr << "error in RPFP::ImplicantRed"; - goto done; - } - else if (k == Not) { - ImplicantRed(memo, f.arg(0), lits, done, !truth, dont_cares); - goto done; - } - } - { - if (dont_cares.find(f) == dont_cares.end()) { - expr rf = ResolveIte(memo, f, lits, done, dont_cares); - expr bv = truth ? rf : !rf; - lits.push_back(bv); - } - } - done: - done[truth].insert(f); - } - - void RPFP::ImplicantFullRed(hash_map &memo, const Term &f, std::vector &lits, - hash_set &done, hash_set &dont_cares, bool extensional) { - if (done.find(f) != done.end()) - return; /* already processed */ - if (f.is_app()) { - int nargs = f.num_args(); - decl_kind k = f.decl().get_decl_kind(); - if (k == Implies || k == Iff || k == And || k == Or || k == Not) { - for (int i = 0; i < nargs; i++) - ImplicantFullRed(memo, f.arg(i), lits, done, dont_cares, extensional); - goto done; - } - } - { - if (dont_cares.find(f) == dont_cares.end()) { - int b = SubtermTruth(memo, f); - if (b != 0 && b != 1) goto done; - if (f.is_app() && f.decl().get_decl_kind() == Equal && f.arg(0).is_array()) { - if (b == 1 && !extensional) { - expr x = dualModel.eval(f.arg(0)); expr y = dualModel.eval(f.arg(1)); - if (!eq(x, y)) - b = 0; - } - if (b == 0) - goto done; - } - expr bv = (b == 1) ? f : !f; - lits.push_back(bv); - } - } - done: - done.insert(f); - } - - RPFP::Term RPFP::ResolveIte(hash_map &memo, const Term &t, std::vector &lits, - hash_set *done, hash_set &dont_cares) { - if (resolve_ite_memo.find(t) != resolve_ite_memo.end()) - return resolve_ite_memo[t]; - Term res; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (f.get_decl_kind() == Ite) { - timer_start("SubtermTruth"); -#ifdef Z3OPS - bool sel = stt->eval(t.arg(0)); -#else - int xval = SubtermTruth(memo, t.arg(0)); - bool sel; - if (xval == 0)sel = false; - else if (xval == 1)sel = true; - else - throw "unresolved ite in model"; -#endif - timer_stop("SubtermTruth"); - ImplicantRed(memo, t.arg(0), lits, done, sel, dont_cares); - res = ResolveIte(memo, t.arg(sel ? 1 : 2), lits, done, dont_cares); - } - else { - for (int i = 0; i < nargs; i++) - args.push_back(ResolveIte(memo, t.arg(i), lits, done, dont_cares)); - res = f(args.size(), VEC2PTR(args)); - } - } - else res = t; - resolve_ite_memo[t] = res; - return res; - } - - RPFP::Term RPFP::ElimIteRec(hash_map &memo, const Term &t, std::vector &cnsts) { - std::pair foo(t, expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if (bar.second) { - if (t.is_app()) { - int nargs = t.num_args(); - std::vector args; - if (t.decl().get_decl_kind() == Equal) { - expr lhs = t.arg(0); - expr rhs = t.arg(1); - if (rhs.decl().get_decl_kind() == Ite) { - expr rhs_args[3]; - lhs = ElimIteRec(memo, lhs, cnsts); - for (int i = 0; i < 3; i++) - rhs_args[i] = ElimIteRec(memo, rhs.arg(i), cnsts); - res = (rhs_args[0] && (lhs == rhs_args[1])) || ((!rhs_args[0]) && (lhs == rhs_args[2])); - goto done; - } - } - if (t.decl().get_decl_kind() == Ite) { - func_decl sym = ctx.fresh_func_decl("@ite", t.get_sort()); - res = sym(); - cnsts.push_back(ElimIteRec(memo, ctx.make(Equal, res, t), cnsts)); - } - else { - for (int i = 0; i < nargs; i++) - args.push_back(ElimIteRec(memo, t.arg(i), cnsts)); - res = t.decl()(args.size(), VEC2PTR(args)); - } - } - else if (t.is_quantifier()) - res = clone_quantifier(t, ElimIteRec(memo, t.body(), cnsts)); - else - res = t; - } - done: - return res; - } - - RPFP::Term RPFP::ElimIte(const Term &t){ - hash_map memo; - std::vector cnsts; - expr res = ElimIteRec(memo,t,cnsts); - if(!cnsts.empty()){ - cnsts.push_back(res); - res = ctx.make(And,cnsts); - } - return res; - } - - void RPFP::Implicant(hash_map &memo, const Term &f, std::vector &lits, hash_set &dont_cares){ - hash_set done[2]; - ImplicantRed(memo,f,lits,done,true, dont_cares); - } - - - /** Underapproximate a formula using current counterexample. */ - - RPFP::Term RPFP::UnderapproxFormula(const Term &f, hash_set &dont_cares){ - /* first compute truth values of subterms */ - hash_map memo; -#ifdef Z3OPS - stt = Z3_mk_subterm_truth(ctx,dualModel); -#endif - // SubtermTruth(memo,f); - /* now compute an implicant */ - std::vector lits; - Implicant(memo,f,lits, dont_cares); -#ifdef Z3OPS - delete stt; stt = 0; -#endif - /* return conjunction of literals */ - return conjoin(lits); - } - - RPFP::Term RPFP::UnderapproxFullFormula(const Term &f, bool extensional){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - /* first compute truth values of subterms */ - hash_map memo; - hash_set done; - std::vector lits; - ImplicantFullRed(memo,f,lits,done,dont_cares, extensional); - timer_stop("UnderapproxFormula"); - /* return conjunction of literals */ - return conjoin(lits); - } - - struct VariableProjector : Z3User { - - struct elim_cand { - Term var; - int sup; - Term val; - }; - - typedef expr Term; - - hash_set keep; - hash_map var_ord; - int num_vars; - std::vector elim_cands; - hash_map > sup_map; - hash_map elim_map; - std::vector ready_cands; - hash_map cand_map; - params simp_params; - - VariableProjector(Z3User &_user, std::vector &keep_vec) : - Z3User(_user), simp_params() { - num_vars = 0; - for (unsigned i = 0; i < keep_vec.size(); i++) { - keep.insert(keep_vec[i]); - var_ord[keep_vec[i]] = num_vars++; - } - } - int VarNum(const Term &v) { - if (var_ord.find(v) == var_ord.end()) - var_ord[v] = num_vars++; - return var_ord[v]; - } - - bool IsVar(const Term &t){ - return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; - } - - bool IsPropLit(const Term &t, Term &a) { - if (IsVar(t)) { - a = t; - return true; - } - else if (t.is_app() && t.decl().get_decl_kind() == Not) - return IsPropLit(t.arg(0), a); - return false; - } - - void CountOtherVarsRec(hash_map &memo, - const Term &t, - int id, - int &count) { - std::pair foo(t, 0); - std::pair::iterator, bool> bar = memo.insert(foo); - // int &res = bar.first->second; - if (!bar.second) return; - if (t.is_app()) { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted) { - if (cand_map.find(t) != cand_map.end()) { - count++; - sup_map[t].push_back(id); - } - } - for (int i = 0; i < nargs; i++) - CountOtherVarsRec(memo, t.arg(i), id, count); - } - else if (t.is_quantifier()) - CountOtherVarsRec(memo, t.body(), id, count); - } - - void NewElimCand(const Term &lhs, const Term &rhs) { - if (debug_gauss) { - std::cout << "mapping " << lhs << " to " << rhs << std::endl; - } - elim_cand cand; - cand.var = lhs; - cand.sup = 0; - cand.val = rhs; - elim_cands.push_back(cand); - cand_map[lhs] = elim_cands.size() - 1; - } - - void MakeElimCand(const Term &lhs, const Term &rhs) { - if (eq(lhs, rhs)) - return; - if (!IsVar(lhs)) { - if (IsVar(rhs)) { - MakeElimCand(rhs, lhs); - return; - } - else { - std::cout << "would have mapped a non-var\n"; - return; - } - } - if (IsVar(rhs) && VarNum(rhs) > VarNum(lhs)) { - MakeElimCand(rhs, lhs); - return; - } - if (keep.find(lhs) != keep.end()) - return; - if (cand_map.find(lhs) == cand_map.end()) - NewElimCand(lhs, rhs); - else { - int cand_idx = cand_map[lhs]; - if (IsVar(rhs) && cand_map.find(rhs) == cand_map.end() - && keep.find(rhs) == keep.end()) - NewElimCand(rhs, elim_cands[cand_idx].val); - elim_cands[cand_idx].val = rhs; - } - } - - Term FindRep(const Term &t) { - if (cand_map.find(t) == cand_map.end()) - return t; - Term &res = elim_cands[cand_map[t]].val; - if (IsVar(res)) { - assert(VarNum(res) < VarNum(t)); - res = FindRep(res); - return res; - } - return t; - } - - void GaussElimCheap(const std::vector &lits_in, - std::vector &lits_out) { - for (unsigned i = 0; i < lits_in.size(); i++) { - Term lit = lits_in[i]; - if (lit.is_app()) { - decl_kind k = lit.decl().get_decl_kind(); - if (k == Equal || k == Iff) - MakeElimCand(FindRep(lit.arg(0)), FindRep(lit.arg(1))); - } - } - - for (unsigned i = 0; i < elim_cands.size(); i++) { - elim_cand &cand = elim_cands[i]; - hash_map memo; - CountOtherVarsRec(memo, cand.val, i, cand.sup); - if (cand.sup == 0) - ready_cands.push_back(i); - } - - while (!ready_cands.empty()) { - elim_cand &cand = elim_cands[ready_cands.back()]; - ready_cands.pop_back(); - Term rep = FindRep(cand.var); - if (!eq(rep, cand.var)) - if (cand_map.find(rep) != cand_map.end()) { - int rep_pos = cand_map[rep]; - cand.val = elim_cands[rep_pos].val; - } - Term val = SubstRec(elim_map, cand.val); - if (debug_gauss) { - std::cout << "subbing " << cand.var << " --> " << val << std::endl; - } - elim_map[cand.var] = val; - std::vector &sup = sup_map[cand.var]; - for (unsigned i = 0; i < sup.size(); i++) { - int c = sup[i]; - if ((--elim_cands[c].sup) == 0) - ready_cands.push_back(c); - } - } - - for (unsigned i = 0; i < lits_in.size(); i++) { - Term lit = lits_in[i]; - lit = SubstRec(elim_map, lit); - lit = lit.simplify(); - if (eq(lit, ctx.bool_val(true))) - continue; - Term a; - if (IsPropLit(lit, a)) - if (keep.find(lit) == keep.end()) - continue; - lits_out.push_back(lit); - } - } - - // maps variables to constrains in which the occur pos, neg - hash_map la_index[2]; - hash_map la_coeffs[2]; - std::vector la_pos_vars; - bool fixing; - - void IndexLAcoeff(const Term &coeff1, const Term &coeff2, Term t, int id) { - Term coeff = coeff1 * coeff2; - coeff = coeff.simplify(); - Term is_pos = (coeff >= ctx.int_val(0)); - is_pos = is_pos.simplify(); - if (eq(is_pos, ctx.bool_val(true))) - IndexLA(true, coeff, t, id); - else - IndexLA(false, coeff, t, id); - } - - void IndexLAremove(const Term &t) { - if (IsVar(t)) { - la_index[0][t] = -1; // means ineligible to be eliminated - la_index[1][t] = -1; // (more that one occurrence, or occurs not in linear comb) - } - else if (t.is_app()) { - int nargs = t.num_args(); - for (int i = 0; i < nargs; i++) - IndexLAremove(t.arg(i)); - } - // TODO: quantifiers? - } - - - void IndexLA(bool pos, const Term &coeff, const Term &t, int id) { - if (t.is_numeral()) - return; - if (t.is_app()) { - int nargs = t.num_args(); - switch (t.decl().get_decl_kind()) { - case Plus: - for (int i = 0; i < nargs; i++) - IndexLA(pos, coeff, t.arg(i), id); - break; - case Sub: - IndexLA(pos, coeff, t.arg(0), id); - IndexLA(!pos, coeff, t.arg(1), id); - break; - case Times: - if (t.arg(0).is_numeral()) - IndexLAcoeff(coeff, t.arg(0), t.arg(1), id); - else if (t.arg(1).is_numeral()) - IndexLAcoeff(coeff, t.arg(1), t.arg(0), id); - break; - default: - if (IsVar(t) && (fixing || la_index[pos].find(t) == la_index[pos].end())) { - la_index[pos][t] = id; - la_coeffs[pos][t] = coeff; - if (pos && !fixing) - la_pos_vars.push_back(t); // this means we only add a var once - } - else - IndexLAremove(t); - } - } - } - - void IndexLAstart(bool pos, const Term &t, int id){ - IndexLA(pos,(pos ? ctx.int_val(1) : ctx.int_val(-1)), t, id); - } - - void IndexLApred(bool pos, const Term &p, int id) { - if (p.is_app()) { - switch (p.decl().get_decl_kind()) { - case Not: - IndexLApred(!pos, p.arg(0), id); - break; - case Leq: - case Lt: - IndexLAstart(!pos, p.arg(0), id); - IndexLAstart(pos, p.arg(1), id); - break; - case Geq: - case Gt: - IndexLAstart(pos, p.arg(0), id); - IndexLAstart(!pos, p.arg(1), id); - break; - default: - IndexLAremove(p); - } - } - } - - void IndexLAfix(const Term &p, int id){ - fixing = true; - IndexLApred(true,p,id); - fixing = false; - } - - bool IsCanonIneq(const Term &lit, Term &term, Term &bound) { - // std::cout << Z3_simplify_get_help(ctx) << std::endl; - bool pos = lit.decl().get_decl_kind() != Not; - Term atom = pos ? lit : lit.arg(0); - if (atom.decl().get_decl_kind() == Leq) { - if (pos) { - bound = atom.arg(0); - term = atom.arg(1).simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - else { - bound = (atom.arg(1) + ctx.int_val(1)); - term = atom.arg(0); - // std::cout << "simplifying bound: " << bound << std::endl; - bound = bound.simplify(); - term = term.simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - return true; - } - else if (atom.decl().get_decl_kind() == Geq) { - if (pos) { - bound = atom.arg(1); // integer axiom - term = atom.arg(0).simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - return true; - } - else { - bound = -(atom.arg(1) - ctx.int_val(1)); // integer axiom - term = -atom.arg(0); - bound = bound.simplify(); - term = term.simplify(simp_params); -#if Z3_MAJOR_VERSION < 4 - term = SortSum(term); -#endif - } - return true; - } - return false; - } - - Term CanonIneqTerm(const Term &p){ - Term term,bound; - Term ps = p.simplify(); - VERIFY(IsCanonIneq(ps,term,bound)); - return term - bound; - } - - void ElimRedundantBounds(std::vector &lits) { - hash_map best_bound; - for (unsigned i = 0; i < lits.size(); i++) { - lits[i] = lits[i].simplify(simp_params); - Term term, bound; - if (IsCanonIneq(lits[i], term, bound)) { - if (best_bound.find(term) == best_bound.end()) - best_bound[term] = i; - else { - int best = best_bound[term]; - Term bterm, bbound; - IsCanonIneq(lits[best], bterm, bbound); - Term comp = bound > bbound; - comp = comp.simplify(); - if (eq(comp, ctx.bool_val(true))) { - lits[best] = ctx.bool_val(true); - best_bound[term] = i; - } - else { - lits[i] = ctx.bool_val(true); - } - } - } - } - } - - void FourierMotzkinCheap(const std::vector &lits_in, - std::vector &lits_out) { - simp_params.set(":som", true); - simp_params.set(":sort-sums", true); - fixing = false; lits_out = lits_in; - ElimRedundantBounds(lits_out); - for (unsigned i = 0; i < lits_out.size(); i++) - IndexLApred(true, lits_out[i], i); - - for (unsigned i = 0; i < la_pos_vars.size(); i++) { - Term var = la_pos_vars[i]; - if (la_index[false].find(var) != la_index[false].end()) { - int pos_idx = la_index[true][var]; - int neg_idx = la_index[false][var]; - if (pos_idx >= 0 && neg_idx >= 0) { - if (keep.find(var) != keep.end()) { - std::cout << "would have eliminated keep var\n"; - continue; - } - Term tpos = CanonIneqTerm(lits_out[pos_idx]); - Term tneg = CanonIneqTerm(lits_out[neg_idx]); - Term pos_coeff = la_coeffs[true][var]; - Term neg_coeff = -la_coeffs[false][var]; - Term comb = neg_coeff * tpos + pos_coeff * tneg; - Term ineq = ctx.int_val(0) <= comb; - ineq = ineq.simplify(); - lits_out[pos_idx] = ineq; - lits_out[neg_idx] = ctx.bool_val(true); - IndexLAfix(ineq, pos_idx); - } - } - } - } - - Term ProjectFormula(const Term &f){ - std::vector lits, new_lits1, new_lits2; - CollectConjuncts(f,lits); - timer_start("GaussElimCheap"); - GaussElimCheap(lits,new_lits1); - timer_stop("GaussElimCheap"); - timer_start("FourierMotzkinCheap"); - FourierMotzkinCheap(new_lits1,new_lits2); - timer_stop("FourierMotzkinCheap"); - return conjoin(new_lits2); - } - }; - - void Z3User::CollectConjuncts(const Term &f, std::vector &lits, bool pos) { - if (f.is_app() && f.decl().get_decl_kind() == Not) - CollectConjuncts(f.arg(0), lits, !pos); - else if (pos && f.is_app() && f.decl().get_decl_kind() == And) { - int num_args = f.num_args(); - for (int i = 0; i < num_args; i++) - CollectConjuncts(f.arg(i), lits, true); - } - else if (!pos && f.is_app() && f.decl().get_decl_kind() == Or) { - int num_args = f.num_args(); - for (int i = 0; i < num_args; i++) - CollectConjuncts(f.arg(i), lits, false); - } - else if (pos) { - if (!eq(f, ctx.bool_val(true))) - lits.push_back(f); - } - else { - if (!eq(f, ctx.bool_val(false))) - lits.push_back(!f); - } - } - - void Z3User::CollectJuncts(const Term &f, std::vector &lits, decl_kind op, bool negate) { - if (f.is_app() && f.decl().get_decl_kind() == Not) - CollectJuncts(f.arg(0), lits, (op == And) ? Or : And, !negate); - else if (f.is_app() && f.decl().get_decl_kind() == op) { - int num_args = f.num_args(); - for (int i = 0; i < num_args; i++) - CollectJuncts(f.arg(i), lits, op, negate); - } - else { - expr junct = negate ? Negate(f) : f; - lits.push_back(junct); - } - } - - struct TermLt { - bool operator()(const expr &x, const expr &y){ - unsigned xid = x.get_id(); - unsigned yid = y.get_id(); - return xid < yid; - } - }; - - void Z3User::SortTerms(std::vector &terms){ - TermLt foo; - std::sort(terms.begin(),terms.end(),foo); - } - - Z3User::Term Z3User::SortSum(const Term &t){ - if(!(t.is_app() && t.decl().get_decl_kind() == Plus)) - return t; - int nargs = t.num_args(); - if(nargs < 2) return t; - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = t.arg(i); - SortTerms(args); - if(nargs == 2) - return args[0] + args[1]; - return sum(args); - } - - - RPFP::Term RPFP::ProjectFormula(std::vector &keep_vec, const Term &f){ - VariableProjector vp(*this,keep_vec); - return vp.ProjectFormula(f); - } - - /** Compute an underapproximation of every node in a tree rooted at "root", - based on a previously computed counterexample. The underapproximation - may contain free variables that are implicitly existentially quantified. - */ - - RPFP::Term RPFP::ComputeUnderapprox(Node *root, int persist){ - /* if terminated underapprox is empty set (false) */ - bool show_model = false; - if(show_model) - std::cout << dualModel << std::endl; - if(!root->Outgoing){ - root->Underapprox.SetEmpty(); - return ctx.bool_val(true); - } - /* if not used in cex, underapprox is empty set (false) */ - if(Empty(root)){ - root->Underapprox.SetEmpty(); - return ctx.bool_val(true); - } - /* compute underapprox of children first */ - std::vector &chs = root->Outgoing->Children; - std::vector chu(chs.size()); - for(unsigned i = 0; i < chs.size(); i++) - chu[i] = ComputeUnderapprox(chs[i],persist); - - Term b; std::vector v; - RedVars(root, b, v); - /* underapproximate the edge formula */ - hash_set dont_cares; - dont_cares.insert(b); - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = root->Outgoing->dual.null() ? ctx.bool_val(true) : root->Outgoing->dual; - Term eu = UnderapproxFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - /* combine with children */ - chu.push_back(eu); - eu = conjoin(chu); - /* project onto appropriate variables */ - eu = ProjectFormula(v,eu); - eu = eu.simplify(); - -#if 0 - /* check the result is consistent */ - { - hash_map memo; - int res = SubtermTruth(memo, eu); - if(res != 1) - throw "inconsistent projection"; - } -#endif - - /* rename to appropriate variable names */ - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = root->Annotation.IndParams[i]; /* copy names from annotation */ - Term funder = SubstRec(memo, eu); - root->Underapprox = CreateRelation(root->Annotation.IndParams,funder); -#if 0 - if(persist) - Z3_persist_ast(ctx,root->Underapprox.Formula,persist); -#endif - return eu; - } - - void RPFP::FixCurrentState(Edge *edge){ - hash_set dont_cares; - resolve_ite_memo.clear(); - timer_start("UnderapproxFormula"); - Term dual = edge->dual.null() ? ctx.bool_val(true) : edge->dual; - Term eu = UnderapproxFormula(dual,dont_cares); - timer_stop("UnderapproxFormula"); - ConstrainEdgeLocalized(edge,eu); - } - - void RPFP::GetGroundLitsUnderQuants(hash_set *memo, const Term &f, std::vector &res, int under){ - if(memo[under].find(f) != memo[under].end()) - return; - memo[under].insert(f); - if (f.is_app()) { - if (!under && !f.has_quantifiers()) - return; - decl_kind k = f.decl().get_decl_kind(); - if (k == And || k == Or || k == Implies || k == Iff) { - int num_args = f.num_args(); - for (int i = 0; i < num_args; i++) - GetGroundLitsUnderQuants(memo, f.arg(i), res, under); - return; - } - } - else if (f.is_quantifier()){ -#if 0 - // treat closed quantified formula as a literal 'cause we hate nested quantifiers - if(under && IsClosedFormula(f)) - res.push_back(f); - else -#endif - GetGroundLitsUnderQuants(memo,f.body(),res,1); - return; - } - if(f.is_var()){ - // std::cout << "foo!\n"; - return; - } - if(under && f.is_ground()) - res.push_back(f); - } - - RPFP::Term RPFP::StrengthenFormulaByCaseSplitting(const Term &f, std::vector &case_lits){ - hash_set memo[2]; - std::vector lits; - GetGroundLitsUnderQuants(memo, f, lits, 0); - hash_set lits_hash; - for(unsigned i = 0; i < lits.size(); i++) - lits_hash.insert(lits[i]); - hash_map subst; - hash_map stt_memo; - std::vector conjuncts; - for(unsigned i = 0; i < lits.size(); i++){ - const expr &lit = lits[i]; - if(lits_hash.find(NegateLit(lit)) == lits_hash.end()){ - case_lits.push_back(lit); - bool tval = false; - expr atom = lit; - if(lit.is_app() && lit.decl().get_decl_kind() == Not){ - tval = true; - atom = lit.arg(0); - } - expr etval = ctx.bool_val(tval); - if(atom.is_quantifier()) - subst[atom] = etval; // this is a bit desperate, since we can't eval quants - else { - int b = SubtermTruth(stt_memo,atom); - if(b == (tval ? 1 : 0)) - subst[atom] = etval; - else { - if(b == 0 || b == 1){ - etval = ctx.bool_val(b ? true : false); - subst[atom] = etval; - conjuncts.push_back(b ? atom : !atom); - } - } - } - } - } - expr g = f; - if(!subst.empty()){ - g = SubstRec(subst,f); - if(conjuncts.size()) - g = g && ctx.make(And,conjuncts); - g = g.simplify(); - } -#if 1 - expr g_old = g; - g = RemoveRedundancy(g); - bool changed = !eq(g,g_old); - g = g.simplify(); - if(changed) { // a second pass can get some more simplification - g = RemoveRedundancy(g); - g = g.simplify(); - } -#else - g = RemoveRedundancy(g); - g = g.simplify(); -#endif - g = AdjustQuantifiers(g); - return g; - } - - RPFP::Term RPFP::ModelValueAsConstraint(const Term &t) { - if (t.is_array()) { - ArrayValue arr; - Term e = dualModel.eval(t); - EvalArrayTerm(e, arr); - if (arr.defined) { - std::vector cs; - for (std::map::iterator it = arr.entries.begin(), en = arr.entries.end(); it != en; ++it) { - expr foo = select(t, expr(ctx, it->first)) == expr(ctx, it->second); - cs.push_back(foo); - } - return conjoin(cs); - } - } - else { - expr r = dualModel.get_const_interp(t.decl()); - if (!r.null()) { - expr res = t == expr(ctx, r); - return res; - } - } - return ctx.bool_val(true); - } - - void RPFP::EvalNodeAsConstraint(Node *p, Transformer &res) - { - Term b; std::vector v; - RedVars(p, b, v); - std::vector args; - for(unsigned i = 0; i < v.size(); i++){ - expr val = ModelValueAsConstraint(v[i]); - if(!eq(val,ctx.bool_val(true))) - args.push_back(val); - } - expr cnst = conjoin(args); - hash_map memo; - for (unsigned i = 0; i < v.size(); i++) - memo[v[i]] = p->Annotation.IndParams[i]; /* copy names from annotation */ - Term funder = SubstRec(memo, cnst); - res = CreateRelation(p->Annotation.IndParams,funder); - } - -#if 0 - void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ - // verify - s.push(); - expr conj = ctx.make(And,conjuncts); - s.add(conj); - check_result res = s.check(); - if(res != unsat) - throw "should be unsat"; - s.pop(1); - - for(unsigned i = 0; i < conjuncts.size(); ){ - std::swap(conjuncts[i],conjuncts.back()); - expr save = conjuncts.back(); - conjuncts.pop_back(); - s.push(); - expr conj = ctx.make(And,conjuncts); - s.add(conj); - check_result res = s.check(); - s.pop(1); - if(res != unsat){ - conjuncts.push_back(save); - std::swap(conjuncts[i],conjuncts.back()); - i++; - } - } - } -#endif - - void RPFP::GreedyReduce(solver &s, std::vector &conjuncts){ - std::vector lits(conjuncts.size()); - for(unsigned i = 0; i < lits.size(); i++){ - func_decl pred = ctx.fresh_func_decl("@alit", ctx.bool_sort()); - lits[i] = pred(); - s.add(ctx.make(Implies,lits[i],conjuncts[i])); - } - - // verify - check_result res = s.check(lits.size(), VEC2PTR(lits)); - if(res != unsat){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - s.add(theory[i]); - for(int k = 0; k < 100; k++) // keep trying, maybe MBQI will do something! - if(s.check(lits.size(), VEC2PTR(lits)) == unsat) - goto is_unsat; - throw "should be unsat"; - } - is_unsat: - for(unsigned i = 0; i < conjuncts.size(); ){ - std::swap(conjuncts[i],conjuncts.back()); - std::swap(lits[i],lits.back()); - check_result res = s.check(lits.size()-1, VEC2PTR(lits)); - if(res != unsat){ - std::swap(conjuncts[i],conjuncts.back()); - std::swap(lits[i],lits.back()); - i++; - } - else { - conjuncts.pop_back(); - lits.pop_back(); - } - } - } - - void foobar(){ - } - - void RPFP::GreedyReduceNodes(std::vector &nodes){ - std::vector lits; - for(unsigned i = 0; i < nodes.size(); i++){ - Term b; std::vector v; - RedVars(nodes[i], b, v); - lits.push_back(!b); - expr bv = dualModel.eval(b); - if(eq(bv,ctx.bool_val(true))){ - check_result res = slvr_check(lits.size(), VEC2PTR(lits)); - if(res == unsat) - lits.pop_back(); - else - foobar(); - } - } - } - - check_result RPFP::CheckWithConstrainedNodes(std::vector &posnodes,std::vector &negnodes){ - timer_start("Check"); - std::vector lits; - for(unsigned i = 0; i < posnodes.size(); i++){ - Term b; std::vector v; - RedVars(posnodes[i], b, v); - lits.push_back(b); - } - for(unsigned i = 0; i < negnodes.size(); i++){ - Term b; std::vector v; - RedVars(negnodes[i], b, v); - lits.push_back(!b); - } - check_result res = slvr_check(lits.size(), VEC2PTR(lits)); - if(res == unsat && posnodes.size()){ - lits.resize(posnodes.size()); - res = slvr_check(lits.size(), VEC2PTR(lits)); - } - dualModel = slvr().get_model(); -#if 0 - if(!dualModel.null()){ - std::cout << "posnodes called:\n"; - for(unsigned i = 0; i < posnodes.size(); i++) - if(!Empty(posnodes[i])) - std::cout << posnodes[i]->Name.name() << "\n"; - std::cout << "negnodes called:\n"; - for(unsigned i = 0; i < negnodes.size(); i++) - if(!Empty(negnodes[i])) - std::cout << negnodes[i]->Name.name() << "\n"; - } -#endif - timer_stop("Check"); - return res; - } - - - void RPFP_caching::FilterCore(std::vector &core, std::vector &full_core){ - hash_set core_set; - std::copy(full_core.begin(),full_core.end(),std::inserter(core_set,core_set.begin())); - std::vector new_core; - for(unsigned i = 0; i < core.size(); i++) - if(core_set.find(core[i]) != core_set.end()) - new_core.push_back(core[i]); - core.swap(new_core); - } - - void RPFP_caching::GreedyReduceCache(std::vector &assumps, std::vector &core){ - std::vector lits = assumps, full_core; - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - - // verify - check_result res = CheckCore(lits,full_core); - if(res != unsat){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - GetAssumptionLits(theory[i],assumps); - lits = assumps; - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - - for(int k = 0; k < 4; k++) // keep trying, maybe MBQI will do something! - if((res = CheckCore(lits,full_core)) == unsat) - goto is_unsat; - throw greedy_reduce_failed(); - } - is_unsat: - FilterCore(core,full_core); - - std::vector dummy; - if(CheckCore(full_core,dummy) != unsat) - throw "should be unsat"; - - for(unsigned i = 0; i < core.size(); ){ - expr temp = core[i]; - std::swap(core[i],core.back()); - core.pop_back(); - lits.resize(assumps.size()); - std::copy(core.begin(),core.end(),std::inserter(lits,lits.end())); - res = CheckCore(lits,full_core); - if(res != unsat){ - core.push_back(temp); - std::swap(core[i],core.back()); - i++; - } - } - } - - expr RPFP::NegateLit(const expr &f){ - if(f.is_app() && f.decl().get_decl_kind() == Not) - return f.arg(0); - else - return !f; - } - - void RPFP::NegateLits(std::vector &lits){ - for(unsigned i = 0; i < lits.size(); i++){ - expr &f = lits[i]; - if(f.is_app() && f.decl().get_decl_kind() == Not) - f = f.arg(0); - else - f = !f; - } - } - - expr RPFP::SimplifyOr(std::vector &lits){ - if(lits.size() == 0) - return ctx.bool_val(false); - if(lits.size() == 1) - return lits[0]; - return ctx.make(Or,lits); - } - - expr RPFP::SimplifyAnd(std::vector &lits){ - if(lits.size() == 0) - return ctx.bool_val(true); - if(lits.size() == 1) - return lits[0]; - return ctx.make(And,lits); - } - - - /* This is a wrapper for a solver that is intended to compute - implicants from models. It works around a problem in Z3 with - models in the non-extensional array theory. It does this by - naming all of the store terms in a formula. That is, (store ...) - is replaced by "name" with an added constraint name = (store - ...). This allows us to determine from the model whether an array - equality is true or false (it is false if the two sides are - mapped to different function symbols, even if they have the same - contents). - */ - - struct implicant_solver { - RPFP *owner; - solver &aux_solver; - std::vector assumps, namings; - std::vector assump_stack, naming_stack; - hash_map renaming, renaming_memo; - - void add(const expr &e){ - expr t = e; - if(!aux_solver.extensional_array_theory()){ - unsigned i = namings.size(); - t = owner->ExtractStores(renaming_memo,t,namings,renaming); - for(; i < namings.size(); i++) - aux_solver.add(namings[i]); - } - assumps.push_back(t); - aux_solver.add(t); - } - - void push() { - assump_stack.push_back(assumps.size()); - naming_stack.push_back(namings.size()); - aux_solver.push(); - } - - // When we pop the solver, we have to re-add any namings that were lost - - void pop(int n) { - aux_solver.pop(n); - int new_assumps = assump_stack[assump_stack.size()-n]; - int new_namings = naming_stack[naming_stack.size()-n]; - for(unsigned i = new_namings; i < namings.size(); i++) - aux_solver.add(namings[i]); - assumps.resize(new_assumps); - namings.resize(new_namings); - assump_stack.resize(assump_stack.size()-1); - naming_stack.resize(naming_stack.size()-1); - } - - check_result check() { - return aux_solver.check(); - } - - model get_model() { - return aux_solver.get_model(); - } - - expr get_implicant() { - owner->dualModel = aux_solver.get_model(); - expr dual = owner->ctx.make(And,assumps); - bool ext = aux_solver.extensional_array_theory(); - expr eu = owner->UnderapproxFullFormula(dual,ext); - // if we renamed store terms, we have to undo - if(!ext) - eu = owner->SubstRec(renaming,eu); - return eu; - } - - implicant_solver(RPFP *_owner, solver &_aux_solver) - : owner(_owner), aux_solver(_aux_solver) - {} - }; - - // set up edge constraint in aux solver - void RPFP::AddEdgeToSolver(implicant_solver &aux_solver, Edge *edge){ - if(!edge->dual.null()) - aux_solver.add(edge->dual); - for(unsigned i = 0; i < edge->constraints.size(); i++){ - expr tl = edge->constraints[i]; - aux_solver.add(tl); - } - } - - void RPFP::AddEdgeToSolver(Edge *edge){ - if(!edge->dual.null()) - aux_solver.add(edge->dual); - for(unsigned i = 0; i < edge->constraints.size(); i++){ - expr tl = edge->constraints[i]; - aux_solver.add(tl); - } - } - - static int by_case_counter = 0; - - void RPFP::InterpolateByCases(Node *root, Node *node){ - timer_start("InterpolateByCases"); - bool axioms_added = false; - hash_set axioms_needed; - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - axioms_needed.insert(theory[i]); - implicant_solver is(this,aux_solver); - is.push(); - AddEdgeToSolver(is,node->Outgoing); - node->Annotation.SetEmpty(); - hash_set *core = new hash_set; - core->insert(node->Outgoing->dual); - expr prev_annot = ctx.bool_val(false); - expr prev_impl = ctx.bool_val(false); - int repeated_case_count = 0; - while(1){ - by_case_counter++; - is.push(); - expr annot = !GetAnnotation(node); - Transformer old_annot = node->Annotation; - is.add(annot); - if(is.check() == unsat){ - is.pop(1); - break; - } - is.pop(1); - Push(); - expr the_impl = is.get_implicant(); - if(eq(the_impl,prev_impl)){ - // std::cout << "got old implicant\n"; - repeated_case_count++; - } - prev_impl = the_impl; - ConstrainEdgeLocalized(node->Outgoing,the_impl); - ConstrainEdgeLocalized(node->Outgoing,!GetAnnotation(node)); //TODO: need this? - - { - check_result foo = Check(root); - if(foo != unsat){ - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw ReallyBad(); - // slvr().print("should_be_unsat.smt2"); - // throw "should be unsat"; - } - std::vector assumps, axioms_to_add; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++){ - (*core).insert(assumps[i]); - if(axioms_needed.find(assumps[i]) != axioms_needed.end()){ - axioms_to_add.push_back(assumps[i]); - axioms_needed.erase(assumps[i]); - } - } - // AddToProofCore(*core); - - try { - SolveSingleNode(root,node); - } - catch (char const *msg){ - // This happens if interpolation fails - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw msg; - } - { - expr itp = GetAnnotation(node); - dualModel = is.get_model(); // TODO: what does this mean? - std::vector case_lits; - itp = StrengthenFormulaByCaseSplitting(itp, case_lits); - SetAnnotation(node,itp); - node->Annotation.Formula = node->Annotation.Formula.simplify(); - } - - for(unsigned i = 0; i < axioms_to_add.size(); i++) - is.add(axioms_to_add[i]); - -#define TEST_BAD -#ifdef TEST_BAD - { - static int bad_count = 0, num_bads = 1; - if(bad_count >= num_bads){ - bad_count = 0; - num_bads = num_bads * 2; - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw Bad(); - } - bad_count++; - } -#endif - } - - if(node->Annotation.IsEmpty() || eq(node->Annotation.Formula,prev_annot) || (repeated_case_count > 0 && !axioms_added) || (repeated_case_count >= 10)){ - //looks_bad: - if(!axioms_added){ - // add the axioms in the off chance they are useful - const std::vector &theory = ls->get_axioms(); - for(unsigned i = 0; i < theory.size(); i++) - is.add(theory[i]); - axioms_added = true; - } - else { - //#define KILL_ON_BAD_INTERPOLANT -#ifdef KILL_ON_BAD_INTERPOLANT - std::cout << "bad in InterpolateByCase -- core:\n"; -#if 0 - std::vector assumps; - slvr().get_proof().get_assumptions(assumps); - for(unsigned i = 0; i < assumps.size(); i++) - assumps[i].show(); -#endif - std::cout << "checking for inconsistency\n"; - std::cout << "model:\n"; - is.get_model().show(); - expr impl = is.get_implicant(); - std::vector conjuncts; - CollectConjuncts(impl,conjuncts,true); - std::cout << "impl:\n"; - for(unsigned i = 0; i < conjuncts.size(); i++) - conjuncts[i].show(); - std::cout << "annot:\n"; - annot.show(); - is.add(annot); - for(unsigned i = 0; i < conjuncts.size(); i++) - is.add(conjuncts[i]); - if(is.check() == unsat){ - std::cout << "inconsistent!\n"; - std::vector is_assumps; - is.aux_solver.get_proof().get_assumptions(is_assumps); - std::cout << "core:\n"; - for(unsigned i = 0; i < is_assumps.size(); i++) - is_assumps[i].show(); - } - else { - std::cout << "consistent!\n"; - is.aux_solver.print("should_be_inconsistent.smt2"); - } - std::cout << "by_case_counter = " << by_case_counter << "\n"; - throw "ack!"; -#endif - Pop(1); - is.pop(1); - delete core; - timer_stop("InterpolateByCases"); - throw ReallyBad(); - } - } - Pop(1); - prev_annot = node->Annotation.Formula; - node->Annotation.UnionWith(old_annot); - } - if(proof_core) - delete proof_core; // shouldn't happen - proof_core = core; - is.pop(1); - timer_stop("InterpolateByCases"); - } - - void RPFP::Generalize(Node *root, Node *node){ - timer_start("Generalize"); - aux_solver.push(); - AddEdgeToSolver(node->Outgoing); - expr fmla = GetAnnotation(node); - std::vector conjuncts; - CollectConjuncts(fmla,conjuncts,false); - GreedyReduce(aux_solver,conjuncts); // try to remove conjuncts one at a tme - aux_solver.pop(1); - NegateLits(conjuncts); - SetAnnotation(node,SimplifyOr(conjuncts)); - timer_stop("Generalize"); - } - - RPFP_caching::edge_solver &RPFP_caching::SolverForEdge(Edge *edge, bool models, bool axioms){ - edge_solver &es = edge_solvers[edge]; - uptr &p = es.slvr; - if(!p.get()){ - scoped_no_proof no_proofs_please(ctx.m()); // no proofs - p.set(new solver(ctx,true,models)); // no models - if(axioms){ - RPFP::LogicSolver *ls = edge->owner->ls; - const std::vector &axs = ls->get_axioms(); - for(unsigned i = 0; i < axs.size(); i++) - p.get()->add(axs[i]); - } - } - return es; - } - - - // caching version of above - void RPFP_caching::GeneralizeCache(Edge *edge){ - timer_start("Generalize"); - scoped_solver_for_edge ssfe(this,edge); - Node *node = edge->Parent; - std::vector assumps, core, conjuncts; - AssertEdgeCache(edge,assumps); - for(unsigned i = 0; i < edge->Children.size(); i++){ - expr as = GetAnnotation(edge->Children[i]); - std::vector clauses; - if(!as.is_true()){ - CollectConjuncts(as.arg(1),clauses); - for(unsigned j = 0; j < clauses.size(); j++) - GetAssumptionLits(as.arg(0) || clauses[j],assumps); - } - } - expr fmla = GetAnnotation(node); - std::vector lits; - if(fmla.is_true()){ - timer_stop("Generalize"); - return; - } - assumps.push_back(fmla.arg(0).arg(0)); - CollectConjuncts(!fmla.arg(1),lits); -#if 0 - for(unsigned i = 0; i < lits.size(); i++){ - const expr &lit = lits[i]; - if(lit.is_app() && lit.decl().get_decl_kind() == Equal){ - lits[i] = ctx.make(Leq,lit.arg(0),lit.arg(1)); - lits.push_back(ctx.make(Leq,lit.arg(1),lit.arg(0))); - } - } -#endif - hash_map lit_map; - for(unsigned i = 0; i < lits.size(); i++) - GetAssumptionLits(lits[i],core,&lit_map); - GreedyReduceCache(assumps,core); - for(unsigned i = 0; i < core.size(); i++) - conjuncts.push_back(lit_map[core[i]]); - NegateLits(conjuncts); - SetAnnotation(node,SimplifyOr(conjuncts)); - timer_stop("Generalize"); - } - - // caching version of above - bool RPFP_caching::PropagateCache(Edge *edge){ - timer_start("PropagateCache"); - scoped_solver_for_edge ssfe(this,edge); - bool some = false; - { - std::vector candidates, skip; - Node *node = edge->Parent; - CollectConjuncts(node->Annotation.Formula,skip); - for(unsigned i = 0; i < edge->Children.size(); i++){ - Node *child = edge->Children[i]; - if(child->map == node->map){ - CollectConjuncts(child->Annotation.Formula,candidates); - break; - } - } - if(candidates.empty()) goto done; - hash_set skip_set; - std::copy(skip.begin(),skip.end(),std::inserter(skip_set,skip_set.begin())); - std::vector new_candidates; - for(unsigned i = 0; i < candidates.size(); i++) - if(skip_set.find(candidates[i]) == skip_set.end()) - new_candidates.push_back(candidates[i]); - candidates.swap(new_candidates); - if(candidates.empty()) goto done; - std::vector assumps, core, conjuncts; - AssertEdgeCache(edge,assumps); - for(unsigned i = 0; i < edge->Children.size(); i++){ - expr ass = GetAnnotation(edge->Children[i]); - if(eq(ass,ctx.bool_val(true))) - continue; - std::vector clauses; - CollectConjuncts(ass.arg(1),clauses); - for(unsigned j = 0; j < clauses.size(); j++) - GetAssumptionLits(ass.arg(0) || clauses[j],assumps); - } - for(unsigned i = 0; i < candidates.size(); i++){ - unsigned old_size = assumps.size(); - node->Annotation.Formula = candidates[i]; - expr fmla = GetAnnotation(node); - assumps.push_back(fmla.arg(0).arg(0)); - GetAssumptionLits(!fmla.arg(1),assumps); - std::vector full_core; - check_result res = CheckCore(assumps,full_core); - if(res == unsat) - conjuncts.push_back(candidates[i]); - assumps.resize(old_size); - } - if(conjuncts.empty()) - goto done; - SetAnnotation(node,SimplifyAnd(conjuncts)); - some = true; - } - done: - timer_stop("PropagateCache"); - return some; - } - - - /** Push a scope. Assertions made after Push can be undone by Pop. */ - - void RPFP::Push() - { - stack.push_back(stack_entry()); - slvr_push(); - } - - /** Pop a scope (see Push). Note, you cannot pop axioms. */ - - void RPFP::Pop(int num_scopes) - { - slvr_pop(num_scopes); - for (int i = 0; i < num_scopes; i++) - { - stack_entry &back = stack.back(); - for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) - (*it)->dual = expr(ctx,NULL); - for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) - (*it)->dual = expr(ctx,NULL); - for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - (*it).first->constraints.pop_back(); - stack.pop_back(); - } - } - - /** Erase the proof by performing a Pop, Push and re-assertion of - all the popped constraints */ - - void RPFP::PopPush(){ - slvr_pop(1); - slvr_push(); - stack_entry &back = stack.back(); - for(std::list::iterator it = back.edges.begin(), en = back.edges.end(); it != en; ++it) - slvr_add((*it)->dual); - for(std::list::iterator it = back.nodes.begin(), en = back.nodes.end(); it != en; ++it) - slvr_add((*it)->dual); - for(std::list >::iterator it = back.constraints.begin(), en = back.constraints.end(); it != en; ++it) - slvr_add((*it).second); - } - - - - // This returns a new FuncDel with same sort as top-level function - // of term t, but with numeric suffix appended to name. - - Z3User::FuncDecl Z3User::SuffixFuncDecl(Term t, int n) - { - std::string name = t.decl().name().str() + "_" + string_of_int(n); - std::vector sorts; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - sorts.push_back(t.arg(i).get_sort()); - return ctx.function(name.c_str(), nargs, VEC2PTR(sorts), t.get_sort()); - } - - Z3User::FuncDecl Z3User::RenumberPred(const FuncDecl &f, int n) - { - std::string name = f.name().str(); - name = name.substr(0,name.rfind('_')) + "_" + string_of_int(n); - int arity = f.arity(); - std::vector domain; - for(int i = 0; i < arity; i++) - domain.push_back(f.domain(i)); - return ctx.function(name.c_str(), arity, VEC2PTR(domain), f.range()); - } - - Z3User::FuncDecl Z3User::NumberPred(const FuncDecl &f, int n) - { - std::string name = f.name().str(); - name = name + "_" + string_of_int(n); - int arity = f.arity(); - std::vector domain; - for(int i = 0; i < arity; i++) - domain.push_back(f.domain(i)); - return ctx.function(name.c_str(), arity, VEC2PTR(domain), f.range()); - } - - // Scan the clause body for occurrences of the predicate unknowns - - RPFP::Term RPFP::ScanBody(hash_map &memo, - const Term &t, - hash_map &pmap, - std::vector &parms, - std::vector &nodes) - { - if(memo.find(t) != memo.end()) - return memo[t]; - Term res(ctx); - if (t.is_app()) { - func_decl f = t.decl(); - if(pmap.find(f) != pmap.end()){ - nodes.push_back(pmap[f]); - f = SuffixFuncDecl(t,parms.size()); - parms.push_back(f); - } - int nargs = t.num_args(); - std::vector args; - for(int i = 0; i < nargs; i++) - args.push_back(ScanBody(memo,t.arg(i),pmap,parms,nodes)); - res = f(nargs, VEC2PTR(args)); - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,ScanBody(memo,t.body(),pmap,parms,nodes)); - else - res = t; - memo[t] = res; - return res; - } - - // return the func_del of an app if it is uninterpreted - - bool Z3User::get_relation(const Term &t, func_decl &R){ - if(!t.is_app()) - return false; - R = t.decl(); - return R.get_decl_kind() == Uninterpreted; - } - - // return true if term is an individual variable - // TODO: have to check that it is not a background symbol - - bool Z3User::is_variable(const Term &t){ - if(!t.is_app()) - return t.is_var(); - return t.decl().get_decl_kind() == Uninterpreted - && t.num_args() == 0; - } - - RPFP::Term RPFP::RemoveLabelsRec(hash_map &memo, const Term &t, - std::vector &lbls){ - if(memo.find(t) != memo.end()) - return memo[t]; - Term res(ctx); - if (t.is_app()){ - func_decl f = t.decl(); - std::vector names; - bool pos; - if(t.is_label(pos,names)){ - res = RemoveLabelsRec(memo,t.arg(0),lbls); - for(unsigned i = 0; i < names.size(); i++) - lbls.push_back(label_struct(names[i],res,pos)); - } - else { - int nargs = t.num_args(); - std::vector args; - for(int i = 0; i < nargs; i++) - args.push_back(RemoveLabelsRec(memo,t.arg(i),lbls)); - res = f(nargs, VEC2PTR(args)); - } - } - else if (t.is_quantifier()) - res = CloneQuantifier(t,RemoveLabelsRec(memo,t.body(),lbls)); - else - res = t; - memo[t] = res; - return res; - } - - RPFP::Term RPFP::RemoveLabels(const Term &t, std::vector &lbls){ - hash_map memo ; - return RemoveLabelsRec(memo,t,lbls); - } - - RPFP::Term RPFP::SubstBoundRec(hash_map > &memo, hash_map &subst, int level, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo[level].insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - if(nargs == 0 && f.get_decl_kind() == Uninterpreted) - ls->declare_constant(f); // keep track of background constants - for(int i = 0; i < nargs; i++) - args.push_back(SubstBoundRec(memo, subst, level, t.arg(i))); - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = SubstBoundRec(memo, subst, level + bound, pats[i]); - res = clone_quantifier(t, SubstBoundRec(memo, subst, level + bound, t.body()), pats); - } - else if (t.is_var()) { - int idx = t.get_index_value(); - if(idx >= level && subst.find(idx-level) != subst.end()){ - res = subst[idx-level]; - } - else res = t; - } - else res = t; - return res; - } - - RPFP::Term RPFP::SubstBound(hash_map &subst, const Term &t){ - hash_map > memo; - return SubstBoundRec(memo, subst, 0, t); - } - - // Eliminate the deBruijn indices from level to level+num-1 - Z3User::Term Z3User::DeleteBoundRec(hash_map > &memo, int level, int num, const Term &t) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo[level].insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(DeleteBoundRec(memo, level, num, t.arg(i))); - res = f(args.size(), VEC2PTR(args)); - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - std::vector pats; - t.get_patterns(pats); - for(unsigned i = 0; i < pats.size(); i++) - pats[i] = DeleteBoundRec(memo, level + bound, num, pats[i]); - res = clone_quantifier(t, DeleteBoundRec(memo, level + bound, num, t.body()), pats); - } - else if (t.is_var()) { - int idx = t.get_index_value(); - if(idx >= level){ - res = ctx.make_var(idx-num,t.get_sort()); - } - else res = t; - } - else res = t; - return res; - } - - Z3User::Term Z3User::DeleteBound(int level, int num, const Term &t){ - hash_map > memo; - return DeleteBoundRec(memo, level, num, t); - } - - int Z3User::MaxIndex(hash_map &memo, const Term &t) - { - std::pair foo(t,-1); - std::pair::iterator, bool> bar = memo.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()){ - func_decl f = t.decl(); - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - int m = MaxIndex(memo, t.arg(i)); - if(m > res) - res = m; - } - } - else if (t.is_quantifier()){ - int bound = t.get_quantifier_num_bound(); - res = MaxIndex(memo,t.body()) - bound; - } - else if (t.is_var()) { - res = t.get_index_value(); - } - return res; - } - - bool Z3User::IsClosedFormula(const Term &t){ - hash_map memo; - return MaxIndex(memo,t) < 0; - } - - - /** Convert a collection of clauses to Nodes and Edges in the RPFP. - - Predicate unknowns are uninterpreted predicates not - occurring in the background theory. - - Clauses are of the form - - B => P(t_1,...,t_k) - - where P is a predicate unknown and predicate unknowns - occur only positivey in H and only under existential - quantifiers in prenex form. - - Each predicate unknown maps to a node. Each clause maps to - an edge. Let C be a clause B => P(t_1,...,t_k) where the - sequence of predicate unknowns occurring in B (in order - of occurrence) is P_1..P_n. The clause maps to a transformer - T where: - - T.Relparams = P_1..P_n - T.Indparams = x_1...x+k - T.Formula = B /\ t_1 = x_1 /\ ... /\ t_k = x_k - - Throws exception bad_clause(msg,i) if a clause i is - in the wrong form. - - */ - - - static bool canonical_clause(const expr &clause){ - if(clause.decl().get_decl_kind() != Implies) - return false; - expr arg = clause.arg(1); - return arg.is_app() && (arg.decl().get_decl_kind() == False || - arg.decl().get_decl_kind() == Uninterpreted); - } - -#define USE_QE_LITE - - void RPFP::FromClauses(const std::vector &unskolemized_clauses, const std::vector *bounds){ - hash_map pmap; - func_decl fail_pred = ctx.fresh_func_decl("@Fail", ctx.bool_sort()); - - std::vector clauses(unskolemized_clauses); - // first, skolemize the clauses - -#ifndef USE_QE_LITE - for(unsigned i = 0; i < clauses.size(); i++){ - expr &t = clauses[i]; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - hash_map subst; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - subst[bound-1-j] = skolem; - } - t = SubstBound(subst,t.body()); - } - } -#else - std::vector > substs(clauses.size()); -#endif - - // create the nodes from the heads of the clauses - - for(unsigned i = 0; i < clauses.size(); i++){ - Term &clause = clauses[i]; - -#ifdef USE_QE_LITE - Term &t = clause; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - substs[i][bound-1-j] = skolem; - } - clause = t.body(); - } - -#endif - - if(clause.is_app() && clause.decl().get_decl_kind() == Uninterpreted) - clause = implies(ctx.bool_val(true),clause); - if(!canonical_clause(clause)) - clause = implies((!clause).simplify(),ctx.bool_val(false)); - Term head = clause.arg(1); - func_decl R(ctx); - bool is_query = false; - if (eq(head,ctx.bool_val(false))){ - R = fail_pred; - // R = ctx.constant("@Fail", ctx.bool_sort()).decl(); - is_query = true; - } - else if(!get_relation(head,R)) - throw bad_clause("rhs must be a predicate application",i); - if(pmap.find(R) == pmap.end()){ - - // If the node doesn't exitst, create it. The Indparams - // are arbitrary, but we use the rhs arguments if they - // are variables for mnomonic value. - - hash_set seen; - std::vector Indparams; - for(unsigned j = 0; j < head.num_args(); j++){ - Term arg = head.arg(j); - if(!is_variable(arg) || seen.find(arg) != seen.end()){ - std::string name = std::string("@a_") + string_of_int(j); - arg = ctx.constant(name.c_str(),arg.get_sort()); - } - seen.insert(arg); - Indparams.push_back(arg); - } -#ifdef USE_QE_LITE - { - hash_map > sb_memo; - for(unsigned j = 0; j < Indparams.size(); j++) - Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); - } -#endif - Node *node = CreateNode(R(Indparams.size(),VEC2PTR(Indparams))); - //nodes.push_back(node); - pmap[R] = node; - if (is_query) - node->Bound = CreateRelation(std::vector(), ctx.bool_val(false)); - node->recursion_bound = bounds ? 0 : UINT_MAX; - } - } - - bool some_labels = false; - - // create the edges - - for(unsigned i = 0; i < clauses.size(); i++){ - Term clause = clauses[i]; - Term body = clause.arg(0); - Term head = clause.arg(1); - func_decl R(ctx); - if (eq(head,ctx.bool_val(false))) - R = fail_pred; - //R = ctx.constant("@Fail", ctx.bool_sort()).decl(); - else get_relation(head,R); - Node *Parent = pmap[R]; - std::vector Indparams; - hash_set seen; - for(unsigned j = 0; j < head.num_args(); j++){ - Term arg = head.arg(j); - if(!is_variable(arg) || seen.find(arg) != seen.end()){ - std::string name = std::string("@a_") + string_of_int(j); - Term var = ctx.constant(name.c_str(),arg.get_sort()); - body = body && (arg == var); - arg = var; - } - seen.insert(arg); - Indparams.push_back(arg); - } - - // We extract the relparams positionally - - std::vector Relparams; - hash_map scan_memo; - std::vector Children; - body = ScanBody(scan_memo,body,pmap,Relparams,Children); - Term labeled = body; - std::vector lbls; // TODO: throw this away for now - body = RemoveLabels(body,lbls); - if(!eq(labeled,body)) - some_labels = true; // remember if there are labels, as we then can't do qe_lite - // body = IneqToEq(body); // UFO converts x=y to (x<=y & x >= y). Undo this. - body = body.simplify(); - -#ifdef USE_QE_LITE - std::set idxs; - if(!some_labels){ // can't do qe_lite if we have to reconstruct labels - for(unsigned j = 0; j < Indparams.size(); j++) - if(Indparams[j].is_var()) - idxs.insert(Indparams[j].get_index_value()); - body = body.qe_lite(idxs,false); - } - hash_map > sb_memo; - body = SubstBoundRec(sb_memo,substs[i],0,body); - if(some_labels) - labeled = SubstBoundRec(sb_memo,substs[i],0,labeled); - for(unsigned j = 0; j < Indparams.size(); j++) - Indparams[j] = SubstBoundRec(sb_memo, substs[i], 0, Indparams[j]); -#endif - - // Create the edge - Transformer T = CreateTransformer(Relparams,Indparams,body); - Edge *edge = CreateEdge(Parent,T,Children); - edge->labeled = labeled;; // remember for label extraction - if(bounds) - Parent->recursion_bound = std::max(Parent->recursion_bound,(*bounds)[i]); - // edges.push_back(edge); - } - - // undo hoisting of expressions out of loops - RemoveDeadNodes(); - Unhoist(); - // FuseEdges(); - } - - - // The following mess is used to undo hoisting of expressions outside loops by compilers - - expr RPFP::UnhoistPullRec(hash_map & memo, const expr &w, hash_map & init_defs, hash_map & const_params, hash_map &const_params_inv, std::vector &new_params){ - if(memo.find(w) != memo.end()) - return memo[w]; - expr res; - if(init_defs.find(w) != init_defs.end()){ - expr d = init_defs[w]; - std::vector vars; - hash_set get_vars_memo; - GetVarsRec(get_vars_memo,d,vars); - hash_map map; - for(unsigned j = 0; j < vars.size(); j++){ - expr x = vars[j]; - map[x] = UnhoistPullRec(memo,x,init_defs,const_params,const_params_inv,new_params); - } - expr defn = SubstRec(map,d); - res = defn; - } - else if(const_params_inv.find(w) == const_params_inv.end()){ - std::string old_name = w.decl().name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), w.get_sort()); - expr y = fresh(); - const_params[y] = w; - const_params_inv[w] = y; - new_params.push_back(y); - res = y; - } - else - res = const_params_inv[w]; - memo[w] = res; - return res; - } - - void RPFP::AddParamsToTransformer(Transformer &trans, const std::vector ¶ms){ - std::copy(params.begin(),params.end(),std::inserter(trans.IndParams,trans.IndParams.end())); - } - - expr RPFP::AddParamsToApp(const expr &app, const func_decl &new_decl, const std::vector ¶ms){ - int n = app.num_args(); - std::vector args(n); - for (int i = 0; i < n; i++) - args[i] = app.arg(i); - std::copy(params.begin(),params.end(),std::inserter(args,args.end())); - return new_decl(args); - } - - expr RPFP::GetRelRec(hash_set &memo, const expr &t, const func_decl &rel){ - if(memo.find(t) != memo.end()) - return expr(); - memo.insert(t); - if (t.is_app()) - { - func_decl f = t.decl(); - if(f == rel) - return t; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - expr res = GetRelRec(memo,t.arg(i),rel); - if(!res.null()) - return res; - } - } - else if (t.is_quantifier()) - return GetRelRec(memo,t.body(),rel); - return expr(); - } - - expr RPFP::GetRel(Edge *edge, int child_idx){ - func_decl &rel = edge->F.RelParams[child_idx]; - hash_set memo; - return GetRelRec(memo,edge->F.Formula,rel); - } - - void RPFP::GetDefsRec(const expr &cnst, hash_map &defs){ - if(cnst.is_app()){ - switch(cnst.decl().get_decl_kind()){ - case And: { - int n = cnst.num_args(); - for(int i = 0; i < n; i++) - GetDefsRec(cnst.arg(i),defs); - break; - } - case Equal: { - expr lhs = cnst.arg(0); - expr rhs = cnst.arg(1); - if(IsVar(lhs)) - defs[lhs] = rhs; - break; - } - default: - break; - } - } - } - - void RPFP::GetDefs(const expr &cnst, hash_map &defs){ - // GetDefsRec(IneqToEq(cnst),defs); - GetDefsRec(cnst,defs); - } - - bool RPFP::IsVar(const expr &t){ - return t.is_app() && t.num_args() == 0 && t.decl().get_decl_kind() == Uninterpreted; - } - - void RPFP::GetVarsRec(hash_set &memo, const expr &t, std::vector &vars){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if (t.is_app()) - { - if(IsVar(t)){ - vars.push_back(t); - return; - } - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++){ - GetVarsRec(memo,t.arg(i),vars); - } - } - else if (t.is_quantifier()) - GetVarsRec(memo,t.body(),vars); - } - - void RPFP::AddParamsToNode(Node *node, const std::vector ¶ms){ - int arity = node->Annotation.IndParams.size(); - std::vector domain; - for(int i = 0; i < arity; i++) - domain.push_back(node->Annotation.IndParams[i].get_sort()); - for(unsigned i = 0; i < params.size(); i++) - domain.push_back(params[i].get_sort()); - std::string old_name = node->Name.name().str(); - func_decl fresh = ctx.fresh_func_decl(old_name.c_str(), domain, ctx.bool_sort()); - node->Name = fresh; - AddParamsToTransformer(node->Annotation,params); - AddParamsToTransformer(node->Bound,params); - AddParamsToTransformer(node->Underapprox,params); - } - - void RPFP::UnhoistLoop(Edge *loop_edge, Edge *init_edge){ - loop_edge->F.Formula = IneqToEq(loop_edge->F.Formula); - init_edge->F.Formula = IneqToEq(init_edge->F.Formula); - expr pre = GetRel(loop_edge,0); - if(pre.null()) - return; // this means the loop got simplified away - int nparams = loop_edge->F.IndParams.size(); - hash_map const_params, const_params_inv; - std::vector work_list; - // find the parameters that are constant in the loop - for(int i = 0; i < nparams; i++){ - if(eq(pre.arg(i),loop_edge->F.IndParams[i])){ - const_params[pre.arg(i)] = init_edge->F.IndParams[i]; - const_params_inv[init_edge->F.IndParams[i]] = pre.arg(i); - work_list.push_back(pre.arg(i)); - } - } - // get the definitions in the initialization - hash_map defs,memo,subst; - GetDefs(init_edge->F.Formula,defs); - // try to pull them inside the loop - std::vector new_params; - for(unsigned i = 0; i < work_list.size(); i++){ - expr v = work_list[i]; - expr w = const_params[v]; - expr def = UnhoistPullRec(memo,w,defs,const_params,const_params_inv,new_params); - if(!eq(def,v)) - subst[v] = def; - } - // do the substitutions - if(subst.empty()) - return; - subst[pre] = pre; // don't substitute inside the precondition itself - loop_edge->F.Formula = SubstRec(subst,loop_edge->F.Formula); - loop_edge->F.Formula = ElimIte(loop_edge->F.Formula); - init_edge->F.Formula = ElimIte(init_edge->F.Formula); - // add the new parameters - if(new_params.empty()) - return; - Node *parent = loop_edge->Parent; - AddParamsToNode(parent,new_params); - AddParamsToTransformer(loop_edge->F,new_params); - AddParamsToTransformer(init_edge->F,new_params); - std::vector &incoming = parent->Incoming; - for(unsigned i = 0; i < incoming.size(); i++){ - Edge *in_edge = incoming[i]; - std::vector &chs = in_edge->Children; - for(unsigned j = 0; j < chs.size(); j++) - if(chs[j] == parent){ - expr lit = GetRel(in_edge,j); - expr new_lit = AddParamsToApp(lit,parent->Name,new_params); - func_decl fd = SuffixFuncDecl(new_lit,j); - int nargs = new_lit.num_args(); - std::vector args; - for(int k = 0; k < nargs; k++) - args.push_back(new_lit.arg(k)); - new_lit = fd(nargs, VEC2PTR(args)); - in_edge->F.RelParams[j] = fd; - hash_map map; - map[lit] = new_lit; - in_edge->F.Formula = SubstRec(map,in_edge->F.Formula); - } - } - } - - void RPFP::Unhoist(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++) - outgoing[edges[i]->Parent].push_back(edges[i]); - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &outs = outgoing[node]; - // if we're not a simple loop with one entry, give up - if(outs.size() == 2){ - for(int j = 0; j < 2; j++){ - Edge *loop_edge = outs[j]; - Edge *init_edge = outs[1-j]; - if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent){ - UnhoistLoop(loop_edge,init_edge); - break; - } - } - } - } - } - - void RPFP::FuseEdges(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++){ - outgoing[edges[i]->Parent].push_back(edges[i]); - } - hash_set edges_to_delete; - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &outs = outgoing[node]; - if(outs.size() > 1 && outs.size() <= 16){ - std::vector trs(outs.size()); - for(unsigned j = 0; j < outs.size(); j++) - trs[j] = &outs[j]->F; - Transformer tr = Fuse(trs); - std::vector chs; - for(unsigned j = 0; j < outs.size(); j++) - for(unsigned k = 0; k < outs[j]->Children.size(); k++) - chs.push_back(outs[j]->Children[k]); - CreateEdge(node,tr,chs); - for(unsigned j = 0; j < outs.size(); j++) - edges_to_delete.insert(outs[j]); - } - } - std::vector new_edges; - hash_set all_nodes; - for(unsigned j = 0; j < edges.size(); j++){ - if(edges_to_delete.find(edges[j]) == edges_to_delete.end()){ -#if 0 - if(all_nodes.find(edges[j]->Parent) != all_nodes.end()) - throw "help!"; - all_nodes.insert(edges[j]->Parent); -#endif - new_edges.push_back(edges[j]); - } - else - delete edges[j]; - } - edges.swap(new_edges); - } - - void RPFP::MarkLiveNodes(hash_map > &outgoing, hash_set &live_nodes, Node *node){ - if(live_nodes.find(node) != live_nodes.end()) - return; - live_nodes.insert(node); - std::vector &outs = outgoing[node]; - for(unsigned i = 0; i < outs.size(); i++) - for(unsigned j = 0; j < outs[i]->Children.size(); j++) - MarkLiveNodes(outgoing, live_nodes,outs[i]->Children[j]); - } - - void RPFP::RemoveDeadNodes(){ - hash_map > outgoing; - for(unsigned i = 0; i < edges.size(); i++) - outgoing[edges[i]->Parent].push_back(edges[i]); - hash_set live_nodes; - for(unsigned i = 0; i < nodes.size(); i++) - if(!nodes[i]->Bound.IsFull()) - MarkLiveNodes(outgoing,live_nodes,nodes[i]); - std::vector new_edges; - for(unsigned j = 0; j < edges.size(); j++){ - if(live_nodes.find(edges[j]->Parent) != live_nodes.end()) - new_edges.push_back(edges[j]); - else { - Edge *edge = edges[j]; - for(unsigned int i = 0; i < edge->Children.size(); i++){ - std::vector &ic = edge->Children[i]->Incoming; - for(std::vector::iterator it = ic.begin(), en = ic.end(); it != en; ++it){ - if(*it == edge){ - ic.erase(it); - break; - } - } - } - delete edge; - } - } - edges.swap(new_edges); - std::vector new_nodes; - for(unsigned j = 0; j < nodes.size(); j++){ - if(live_nodes.find(nodes[j]) != live_nodes.end()) - new_nodes.push_back(nodes[j]); - else - delete nodes[j]; - } - nodes.swap(new_nodes); - } - - void RPFP::WriteSolution(std::ostream &s){ - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - Term asgn = (node->Name)(node->Annotation.IndParams) == node->Annotation.Formula; - s << asgn << std::endl; - } - } - - void RPFP::WriteEdgeVars(Edge *e, hash_map &memo, const Term &t, std::ostream &s) - { - std::pair foo(t,0); - std::pair::iterator, bool> bar = memo.insert(foo); - // int &res = bar.first->second; - if(!bar.second) return; - hash_map::iterator it = e->varMap.find(t); - if (it != e->varMap.end()){ - return; - } - if (t.is_app()) - { - func_decl f = t.decl(); - // int idx; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - WriteEdgeVars(e, memo, t.arg(i),s); - if (nargs == 0 && f.get_decl_kind() == Uninterpreted && !ls->is_constant(f)){ - Term rename = HideVariable(t,e->number); - Term value = dualModel.eval(rename); - s << " (= " << t << " " << value << ")\n"; - } - } - else if (t.is_quantifier()) - WriteEdgeVars(e,memo,t.body(),s); - return; - } - - void RPFP::WriteEdgeAssignment(std::ostream &s, Edge *e){ - s << "(\n"; - hash_map memo; - WriteEdgeVars(e, memo, e->F.Formula ,s); - s << ")\n"; - } - - void RPFP::WriteCounterexample(std::ostream &s, Node *node){ - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *child = node->Outgoing->Children[i]; - if(!Empty(child)) - WriteCounterexample(s,child); - } - s << "(" << node->number << " : " << EvalNode(node) << " <- "; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *child = node->Outgoing->Children[i]; - if(!Empty(child)) - s << " " << node->Outgoing->Children[i]->number; - } - s << ")" << std::endl; - WriteEdgeAssignment(s,node->Outgoing); - } - - RPFP::Term RPFP::ToRuleRec(Edge *e, hash_map &memo, const Term &t, std::vector &quants) - { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - Term &res = bar.first->second; - if(!bar.second) return res; - if (t.is_app()) - { - func_decl f = t.decl(); - // int idx; - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(ToRuleRec(e, memo, t.arg(i),quants)); - hash_map::iterator rit = e->relMap.find(f); - if(rit != e->relMap.end()){ - Node* child = e->Children[rit->second]; - FuncDecl op = child->Name; - res = op(args.size(), VEC2PTR(args)); - } - else { - res = f(args.size(), VEC2PTR(args)); - if(nargs == 0 && t.decl().get_decl_kind() == Uninterpreted) - quants.push_back(t); - } - } - else if (t.is_quantifier()) - { - Term body = ToRuleRec(e,memo,t.body(),quants); - res = CloneQuantifier(t,body); - } - else res = t; - return res; - } - - - void RPFP::ToClauses(std::vector &cnsts, FileFormat format){ - cnsts.resize(edges.size()); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - SetEdgeMaps(edge); - std::vector quants; - hash_map memo; - Term lhs = ToRuleRec(edge, memo, edge->F.Formula,quants); - Term rhs = (edge->Parent->Name)(edge->F.IndParams.size(),&edge->F.IndParams[0]); - for(unsigned j = 0; j < edge->F.IndParams.size(); j++) - ToRuleRec(edge,memo,edge->F.IndParams[j],quants); // just to get quants - Term cnst = implies(lhs,rhs); -#if 0 - for(unsigned i = 0; i < quants.size(); i++){ - std::cout << expr(ctx,(Z3_ast)quants[i]) << " : " << sort(ctx,Z3_get_sort(ctx,(Z3_ast)quants[i])) << std::endl; - } -#endif - if(format != DualityFormat) - cnst= forall(quants,cnst); - cnsts[i] = cnst; - } - // int num_rules = cnsts.size(); - - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - if(!node->Bound.IsFull()){ - Term lhs = (node->Name)(node->Bound.IndParams) && !node->Bound.Formula; - Term cnst = implies(lhs,ctx.bool_val(false)); - if(format != DualityFormat){ - std::vector quants; - for(unsigned j = 0; j < node->Bound.IndParams.size(); j++) - quants.push_back(node->Bound.IndParams[j]); - if(format == HornFormat) - cnst= exists(quants,!cnst); - else - cnst= forall(quants, cnst); - } - cnsts.push_back(cnst); - } - } - - } - - - bool RPFP::proof_core_contains(const expr &e){ - return proof_core->find(e) != proof_core->end(); - } - - bool RPFP_caching::proof_core_contains(const expr &e){ - std::vector foo; - GetAssumptionLits(e,foo); - for(unsigned i = 0; i < foo.size(); i++) - if(proof_core->find(foo[i]) != proof_core->end()) - return true; - return false; - } - - bool RPFP::EdgeUsedInProof(Edge *edge){ - ComputeProofCore(); - if(!edge->dual.null() && proof_core_contains(edge->dual)) - return true; - for(unsigned i = 0; i < edge->constraints.size(); i++) - if(proof_core_contains(edge->constraints[i])) - return true; - return false; - } - - RPFP::~RPFP(){ - ClearProofCore(); - for(unsigned i = 0; i < nodes.size(); i++) - delete nodes[i]; - for(unsigned i = 0; i < edges.size(); i++) - delete edges[i]; - } -} - -#if 0 -void show_ast(expr *a){ - std::cout << *a; -} -#endif diff --git a/src/duality/duality_solver.cpp b/src/duality/duality_solver.cpp deleted file mode 100644 index 1711b65ad..000000000 --- a/src/duality/duality_solver.cpp +++ /dev/null @@ -1,3600 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - duality_solver.h - - Abstract: - - implements relational post-fixedpoint problem - (RPFP) solver - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#endif - -#include "duality/duality.h" -#include "duality/duality_profiling.h" - -#include -#include -#include -#include -#include -#include - -// TODO: make these official options or get rid of them - -#define NEW_CAND_SEL -// #define LOCALIZE_CONJECTURES -// #define CANDS_FROM_UPDATES -#define CANDS_FROM_COVER_FAIL -#define DEPTH_FIRST_EXPAND -#define MINIMIZE_CANDIDATES -// #define MINIMIZE_CANDIDATES_HARDER -#define BOUNDED -// #define CHECK_CANDS_FROM_IND_SET -#define UNDERAPPROX_NODES -#define NEW_EXPAND -#define EARLY_EXPAND -// #define TOP_DOWN -// #define EFFORT_BOUNDED_STRAT -#define SKIP_UNDERAPPROX_NODES -// #define KEEP_EXPANSIONS -// #define USE_CACHING_RPFP -// #define PROPAGATE_BEFORE_CHECK -#define NEW_STRATIFIED_INLINING - -#define USE_RPFP_CLONE -#define USE_NEW_GEN_CANDS - -//#define NO_PROPAGATE -//#define NO_GENERALIZE -//#define NO_DECISIONS - -namespace Duality { - - // TODO: must be a better place for this... - static char string_of_int_buffer[20]; - - static const char *string_of_int(int n){ - sprintf(string_of_int_buffer,"%d",n); - return string_of_int_buffer; - } - - /** Generic object for producing diagnostic output. */ - - class Reporter { - protected: - RPFP *rpfp; - public: - Reporter(RPFP *_rpfp){ - rpfp = _rpfp; - } - virtual void Extend(RPFP::Node *node){} - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){} - virtual void Bound(RPFP::Node *node){} - virtual void Expand(RPFP::Edge *edge){} - virtual void AddCover(RPFP::Node *covered, std::vector &covering){} - virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){} - virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){} - virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){} - virtual void Dominates(RPFP::Node *node, RPFP::Node *other){} - virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){} - virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){} - virtual void Reject(RPFP::Edge *edge, const std::vector &Children){} - virtual void Message(const std::string &msg){} - virtual void Depth(int){} - virtual ~Reporter(){} - }; - - Reporter *CreateStdoutReporter(RPFP *rpfp); - Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname); - - /** Object we throw in case of catastrophe. */ - - struct InternalError { - std::string msg; - InternalError(const std::string _msg) - : msg(_msg) {} - }; - - - /** This is the main solver. It takes anarbitrary (possibly cyclic) - RPFP and either annotates it with a solution, or returns a - counterexample derivation in the form of an embedd RPFP tree. */ - - class Duality : public Solver { - - public: - Duality(RPFP *_rpfp) - : ctx(_rpfp->ctx), - slvr(_rpfp->slvr()), - nodes(_rpfp->nodes), - edges(_rpfp->edges) - { - rpfp = _rpfp; - reporter = 0; - conj_reporter = 0; - heuristic = 0; - unwinding = 0; - FullExpand = false; - NoConj = false; - FeasibleEdges = true; - UseUnderapprox = true; - Report = false; - StratifiedInlining = false; - RecursionBound = -1; - BatchExpand = false; - { - scoped_no_proof no_proofs_please(ctx.m()); -#ifdef USE_RPFP_CLONE - clone_rpfp = new RPFP_caching(rpfp->ls); - clone_rpfp->Clone(rpfp); -#endif -#ifdef USE_NEW_GEN_CANDS - gen_cands_rpfp = new RPFP_caching(rpfp->ls); - gen_cands_rpfp->Clone(rpfp); -#endif - } - } - - ~Duality(){ -#ifdef USE_RPFP_CLONE - delete clone_rpfp; -#endif -#ifdef USE_NEW_GEN_CANDS - delete gen_cands_rpfp; -#endif - if(unwinding) delete unwinding; - } - -#ifdef USE_RPFP_CLONE - RPFP_caching *clone_rpfp; -#endif -#ifdef USE_NEW_GEN_CANDS - RPFP_caching *gen_cands_rpfp; -#endif - - - typedef RPFP::Node Node; - typedef RPFP::Edge Edge; - - /** This struct represents a candidate for extending the - unwinding. It consists of an edge to instantiate - and a vector of children for the new instance. */ - - struct Candidate { - Edge *edge; std::vector - Children; - }; - - /** Comparison operator, allowing us to sort Nodes - by their number field. */ - - struct lnode - { - bool operator()(const Node* s1, const Node* s2) const - { - return s1->number < s2->number; - } - }; - - typedef std::set Unexpanded; // sorted set of Nodes - - /** This class provides a heuristic for expanding a derivation - tree. */ - - class Heuristic { - RPFP *rpfp; - - /** Heuristic score for unwinding nodes. Currently this - counts the number of updates. */ - struct score { - int updates; - score() : updates(0) {} - }; - hash_map scores; - - public: - Heuristic(RPFP *_rpfp){ - rpfp = _rpfp; - } - - virtual ~Heuristic(){} - - virtual void Update(RPFP::Node *node){ - scores[node].updates++; - } - - /** Heuristic choice of nodes to expand. Takes a set "choices" - and returns a subset "best". We currently choose the - nodes with the fewest updates. - */ -#if 0 - virtual void ChooseExpand(const std::set &choices, std::set &best){ - int best_score = INT_MAX; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it)->map; - int score = scores[node].updates; - best_score = std::min(best_score,score); - } - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) - if(scores[(*it)->map].updates == best_score) - best.insert(*it); - } -#else - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority=false, bool best_only=false){ - if(high_priority) return; - int best_score = INT_MAX; - int worst_score = 0; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it)->map; - int score = scores[node].updates; - best_score = std::min(best_score,score); - worst_score = std::max(worst_score,score); - } - int cutoff = best_only ? best_score : (best_score + (worst_score-best_score)/2); - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it) - if(scores[(*it)->map].updates <= cutoff) - best.insert(*it); - } -#endif - - /** Called when done expanding a tree */ - virtual void Done() {} - - /** Ask whether a node should be used/unused in the tree. Returns, - 1 if yes, -1 if no, and 0 if don't care. */ - - virtual int UseNode(Node *node){ - return 0; - } - }; - - /** The Proposer class proposes conjectures eagerly. These can come - from any source, including predicate abstraction, templates, or - previous solver runs. The proposed conjectures are checked - with low effort when the unwinding is expanded. - */ - - class Proposer { - public: - /** Given a node in the unwinding, propose some conjectures */ - virtual std::vector &Propose(Node *node) = 0; - virtual ~Proposer(){}; - }; - - - class Covering; // see below - - // These members represent the state of the algorithm. - - RPFP *rpfp; // the input RPFP - Reporter *reporter; // object for logging - Reporter *conj_reporter; // object for logging conjectures - Heuristic *heuristic; // expansion heuristic - context &ctx; // Z3 context - solver &slvr; // Z3 solver - std::vector &nodes; // Nodes of input RPFP - std::vector &edges; // Edges of input RPFP - std::vector leaves; // leaf nodes of unwinding (unused) - Unexpanded unexpanded; // unexpanded nodes - std::list candidates; // candidates for expansion - // maps children to edges in input RPFP - hash_map > edges_by_child; - // maps each node in input RPFP to its expanded instances - hash_map > insts_of_node; - // maps each node in input RPFP to all its instances - hash_map > all_of_node; - RPFP *unwinding; // the unwinding - Covering *indset; // proposed inductive subset - Counterexample cex; // counterexample - std::list to_expand; - hash_set updated_nodes; - hash_map underapprox_map; // maps underapprox nodes to the nodes they approximate - int last_decisions; - hash_set overapproxes; - std::vector proposers; - std::string ConjectureFile; - bool stratified_inlining_done; - -#ifdef BOUNDED - struct Counter { - unsigned val; - Counter(){val = 0;} - }; - typedef std::map NodeToCounter; - hash_map back_edges; // counts of back edges -#endif - - /** Solve the problem. */ - virtual bool Solve(){ - PreSolve(); - bool res = SolveMain(); // does the actual work - PostSolve(); - return res; - } - - void PreSolve(){ - reporter = Report ? CreateStdoutReporter(rpfp) : new Reporter(rpfp); - conj_reporter = ConjectureFile.empty() ? 0 : CreateConjectureFileReporter(rpfp,ConjectureFile); -#ifndef LOCALIZE_CONJECTURES - heuristic = !cex.get_tree() ? new Heuristic(rpfp) : new ReplayHeuristic(rpfp,cex); -#else - heuristic = !cex.get_tree() ? (Heuristic *)(new LocalHeuristic(rpfp)) - : (Heuristic *)(new ReplayHeuristic(rpfp,cex)); -#endif - // determine if we are recursion bounded - for(unsigned i = 0; i < rpfp->nodes.size(); i++) - if(rpfp->nodes[i]->recursion_bound < UINT_MAX) - RecursionBound = 0; - cex.clear(); // in case we didn't use it for heuristic - if(unwinding) delete unwinding; - unwinding = new RPFP(rpfp->ls); - unwinding->HornClauses = rpfp->HornClauses; - indset = new Covering(this); - last_decisions = 0; - CreateEdgesByChildMap(); -#ifndef TOP_DOWN - CreateInitialUnwinding(); -#else - CreateLeaves(); - for(unsigned i = 0; i < leaves.size(); i++) - if(!SatisfyUpperBound(leaves[i])) - return false; -#endif - StratifiedLeafCount = -1; - stratified_inlining_done = false; - } - - void PostSolve(){ - // print_profile(std::cout); - delete indset; - delete heuristic; - // delete unwinding; // keep the unwinding for future mining of predicates - delete reporter; - if(conj_reporter) - delete conj_reporter; - for(unsigned i = 0; i < proposers.size(); i++) - delete proposers[i]; - } - - bool RecheckBounds(){ - for(unsigned i = 0; i < unwinding->nodes.size(); i++){ - Node *node = unwinding->nodes[i]; - node->Bound = node->map->Bound; - if(!SatisfyUpperBound(node)) - return false; - } - return true; - } - - void CreateInitialUnwinding(){ - if(!StratifiedInlining){ - CreateLeaves(); - if(FeasibleEdges)NullaryCandidates(); - else InstantiateAllEdges(); - } - else { -#ifdef NEW_STRATIFIED_INLINING - -#else - CreateLeaves(); -#endif - } - - } - - void Cancel(){ - // TODO - } - -#if 0 - virtual void Restart(RPFP *_rpfp){ - rpfp = _rpfp; - delete unwinding; - nodes = _rpfp->nodes; - edges = _rpfp->edges; - leaves.clear(); - unexpanded.clear(); // unexpanded nodes - candidates.clear(); // candidates for expansion - edges_by_child.clear(); - insts_of_node.clear(); - all_of_node.clear(); - to_expand.clear(); - } -#endif - - virtual void LearnFrom(Solver *other_solver){ - // get the counterexample as a guide - cex.swap(other_solver->GetCounterexample()); - - // propose conjectures based on old unwinding - Duality *old_duality = dynamic_cast(other_solver); - if(old_duality) - proposers.push_back(new HistoryProposer(old_duality,this)); - } - - /** Return a reference to the counterexample */ - virtual Counterexample &GetCounterexample(){ - return cex; - } - - // options - bool FullExpand; // do not use partial expansion of derivation tree - bool NoConj; // do not use conjectures (no forced covering) - bool FeasibleEdges; // use only feasible edges in unwinding - bool UseUnderapprox; // use underapproximations - bool Report; // spew on stdout - bool StratifiedInlining; // Do stratified inlining as preprocessing step - int RecursionBound; // Recursion bound for bounded verification - bool BatchExpand; - bool EnableRestarts; - - bool SetBoolOption(bool &opt, const std::string &value){ - if(value == "0") { - opt = false; - return true; - } - if(value == "1") { - opt = true; - return true; - } - return false; - } - - bool SetIntOption(int &opt, const std::string &value){ - opt = atoi(value.c_str()); - return true; - } - - /** Set options (not currently used) */ - virtual bool SetOption(const std::string &option, const std::string &value){ - if(option == "full_expand"){ - return SetBoolOption(FullExpand,value); - } - if(option == "no_conj"){ - return SetBoolOption(NoConj,value); - } - if(option == "feasible_edges"){ - return SetBoolOption(FeasibleEdges,value); - } - if(option == "use_underapprox"){ - return SetBoolOption(UseUnderapprox,value); - } - if(option == "report"){ - return SetBoolOption(Report,value); - } - if(option == "stratified_inlining"){ - return SetBoolOption(StratifiedInlining,value); - } - if(option == "batch_expand"){ - return SetBoolOption(BatchExpand,value); - } - if(option == "recursion_bound"){ - return SetIntOption(RecursionBound,value); - } - if(option == "conjecture_file"){ - ConjectureFile = value; - } - if(option == "enable_restarts"){ - return SetBoolOption(EnableRestarts,value); - } - return false; - } - - /** Create an instance of a node in the unwinding. Set its - annotation to true, and mark it unexpanded. */ - Node* CreateNodeInstance(Node *node, int number = 0){ - RPFP::Node *inst = unwinding->CloneNode(node); - inst->Annotation.SetFull(); - if(number < 0) inst->number = number; - unexpanded.insert(inst); - all_of_node[node].push_back(inst); - return inst; - } - - /** Create an instance of an edge in the unwinding, with given - parent and children. */ - void CreateEdgeInstance(Edge *edge, Node *parent, const std::vector &children){ - RPFP::Edge *inst = unwinding->CreateEdge(parent,edge->F,children); - inst->map = edge; - } - - void MakeLeaf(Node *node, bool do_not_expand = false){ - node->Annotation.SetEmpty(); - Edge *e = unwinding->CreateLowerBoundEdge(node); -#ifdef TOP_DOWN - node->Annotation.SetFull(); // allow this node to cover others -#endif - if(StratifiedInlining) - node->Annotation.SetFull(); // allow this node to cover others - else - updated_nodes.insert(node); - e->map = 0; - reporter->Extend(node); -#ifdef EARLY_EXPAND - if(!do_not_expand) - TryExpandNode(node); -#endif - // e->F.SetEmpty(); - } - - void MakeOverapprox(Node *node){ - node->Annotation.SetFull(); - Edge *e = unwinding->CreateLowerBoundEdge(node); - overapproxes.insert(node); - e->map = 0; - } - - /** We start the unwinding with leaves that under-approximate - each relation with false. */ - void CreateLeaves(){ - unexpanded.clear(); - leaves.clear(); - for(unsigned i = 0; i < nodes.size(); i++){ - RPFP::Node *node = CreateNodeInstance(nodes[i]); - if(0 && nodes[i]->Outgoing->Children.size() == 0) - CreateEdgeInstance(nodes[i]->Outgoing,node,std::vector()); - else { - if(!StratifiedInlining) - MakeLeaf(node); - else { - MakeOverapprox(node); - LeafMap[nodes[i]] = node; - } - } - leaves.push_back(node); - } - } - - /** Create the map from children to edges in the input RPFP. This - is used to generate candidates for expansion. */ - void CreateEdgesByChildMap(){ - edges_by_child.clear(); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *e = edges[i]; - std::set done; - for(unsigned j = 0; j < e->Children.size(); j++){ - Node *c = e->Children[j]; - if(done.find(c) == done.end()) // avoid duplicates - edges_by_child[c].push_back(e); - done.insert(c); - } - } - } - - void NullaryCandidates(){ - for(unsigned i = 0; i < edges.size(); i++){ - RPFP::Edge *edge = edges[i]; - if(edge->Children.size() == 0){ - Candidate cand; - cand.edge = edge; - candidates.push_back(cand); - } - } - } - - void InstantiateAllEdges(){ - hash_map leaf_map; - for(unsigned i = 0; i < leaves.size(); i++){ - leaf_map[leaves[i]->map] = leaves[i]; - insts_of_node[leaves[i]->map].push_back(leaves[i]); - } - unexpanded.clear(); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - Candidate c; c.edge = edge; - c.Children.resize(edge->Children.size()); - for(unsigned j = 0; j < c.Children.size(); j++) - c.Children[j] = leaf_map[edge->Children[j]]; - Node *new_node; - Extend(c,new_node); -#ifdef EARLY_EXPAND - TryExpandNode(new_node); -#endif - } - for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it) - indset->Add(*it); - for(unsigned i = 0; i < leaves.size(); i++){ - std::vector &foo = insts_of_node[leaves[i]->map]; - foo.erase(foo.begin()); - } - } - - bool ProducedBySI(Edge *edge, std::vector &children){ - if(LeafMap.find(edge->Parent) == LeafMap.end()) return false; - Node *other = LeafMap[edge->Parent]; - if(other->Outgoing->map != edge) return false; - std::vector &ochs = other->Outgoing->Children; - for(unsigned i = 0; i < children.size(); i++) - if(ochs[i] != children[i]) return false; - return true; - } - - /** Add a candidate for expansion, but not if Stratified inlining has already - produced it */ - - void AddCandidate(Edge *edge, std::vector &children){ - if(StratifiedInlining && ProducedBySI(edge,children)) - return; - candidates.push_back(Candidate()); - candidates.back().edge = edge; - candidates.back().Children = children; - } - - /** Generate candidates for expansion, given a vector of candidate - sets for each argument position. This recursively produces - the cross product. - */ - void GenCandidatesRec(int pos, Edge *edge, - const std::vector > &vec, - std::vector &children){ - if(pos == (int)vec.size()){ - AddCandidate(edge,children); - } - else { - for(unsigned i = 0; i < vec[pos].size(); i++){ - children[pos] = vec[pos][i]; - GenCandidatesRec(pos+1,edge,vec,children); - } - } - } - - /** Setup for above recursion. */ - void GenCandidates(int pos, Edge *edge, - const std::vector > &vec){ - std::vector children(vec.size()); - GenCandidatesRec(0,edge,vec,children); - } - - /** Expand a node. We find all the candidates for expansion using - this node and other already expanded nodes. This is a little - tricky, since a node may be used for multiple argument - positions of an edge, and we don't want to produce duplicates. - */ - -#ifndef NEW_EXPAND - void ExpandNode(Node *node){ - std::vector &nedges = edges_by_child[node->map]; - for(unsigned i = 0; i < nedges.size(); i++){ - Edge *edge = nedges[i]; - for(unsigned npos = 0; npos < edge->Children.size(); ++npos){ - if(edge->Children[npos] == node->map){ - std::vector > vec(edge->Children.size()); - vec[npos].push_back(node); - for(unsigned j = 0; j < edge->Children.size(); j++){ - if(j != npos){ - std::vector &insts = insts_of_node[edge->Children[j]]; - for(unsigned k = 0; k < insts.size(); k++) - if(indset->Candidate(insts[k])) - vec[j].push_back(insts[k]); - } - if(j < npos && edge->Children[j] == node->map) - vec[j].push_back(node); - } - GenCandidates(0,edge,vec); - } - } - } - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } -#else - /** If the current proposed solution is not inductive, - use the induction failure to generate candidates for extension. */ - void ExpandNode(Node *node){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - timer_start("GenCandIndFailUsing"); - std::vector &nedges = edges_by_child[node->map]; - for(unsigned i = 0; i < nedges.size(); i++){ - Edge *edge = nedges[i]; - slvr.push(); - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerJustForEdge(edge,checker,true); - if(root){ - expr using_cond = ctx.bool_val(false); - for(unsigned npos = 0; npos < edge->Children.size(); ++npos) - if(edge->Children[npos] == node->map) - using_cond = using_cond || checker->Localize(root->Outgoing->Children[npos]->Outgoing,NodeMarker(node)); - slvr.add(using_cond); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - } - slvr.pop(1); - delete checker; - } - timer_stop("GenCandIndFailUsing"); - } -#endif - - void ExpandNodeFromOther(Node *node, Node *other){ - std::vector &in = other->Incoming; - for(unsigned i = 0; i < in.size(); i++){ - Edge *edge = in[i]; - Candidate cand; - cand.edge = edge->map; - cand.Children = edge->Children; - for(unsigned j = 0; j < cand.Children.size(); j++) - if(cand.Children[j] == other) - cand.Children[j] = node; - candidates.push_front(cand); - } - // unexpanded.erase(node); - // insts_of_node[node->map].push_back(node); - } - - /** Expand a node based on some uncovered node it dominates. - This pushes cahdidates onto the *front* of the candidate - queue, so these expansions are done depth-first. */ - bool ExpandNodeFromCoverFail(Node *node){ - if(!node->Outgoing || node->Outgoing->Children.size() == 0) - return false; - Node *other = indset->GetSimilarNode(node); - if(!other) - return false; -#ifdef UNDERAPPROX_NODES - Node *under_node = CreateUnderapproxNode(node); - underapprox_map[under_node] = node; - indset->CoverByNode(node,under_node); - ExpandNodeFromOther(under_node,other); - ExpandNode(under_node); -#else - ExpandNodeFromOther(node,other); - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); -#endif - return true; - } - - - /** Make a boolean variable to act as a "marker" for a node. */ - expr NodeMarker(Node *node){ - std::string name = std::string("@m_") + string_of_int(node->number); - return ctx.constant(name.c_str(),ctx.bool_sort()); - } - - /** Make a boolean variable to act as a "marker" for a pair of nodes. */ - expr NodeMarker(Node *node1, Node *node2){ - std::string name = std::string("@m_") + string_of_int(node1->number); - name += std::string("_") + string_of_int(node2->number); - return ctx.constant(name.c_str(),ctx.bool_sort()); - } - - /** Union the annotation of dst into src. If with_markers is - true, we conjoin the annotation formula of dst with its - marker. This allows us to discover which disjunct is - true in a satisfying assignment. */ - void UnionAnnotations(RPFP::Transformer &dst, Node *src, bool with_markers = false){ - if(!with_markers) - dst.UnionWith(src->Annotation); - else { - RPFP::Transformer t = src->Annotation; - t.Formula = t.Formula && NodeMarker(src); - dst.UnionWith(t); - } - } - - void GenNodeSolutionFromIndSet(Node *node, RPFP::Transformer &annot, bool with_markers = false){ - annot.SetEmpty(); - std::vector &insts = insts_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++) - if(indset->Contains(insts[j])) - UnionAnnotations(annot,insts[j],with_markers); - annot.Simplify(); - } - - bool NodeSolutionFromIndSetFull(Node *node){ - std::vector &insts = insts_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++) - if(indset->Contains(insts[j])) - if(insts[j]->Annotation.IsFull()) - return true; - return false; - } - - bool recursionBounded; - - /** See if the solution might be bounded. */ - void TestRecursionBounded(){ - recursionBounded = false; - if(RecursionBound == -1) - return; - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - std::vector &insts = insts_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++) - if(indset->Contains(insts[j])) - if(NodePastRecursionBound(insts[j],true)) - recursionBounded = true; - } - } - - bool IsResultRecursionBounded(){ - return recursionBounded; - } - - /** Generate a proposed solution of the input RPFP from - the unwinding, by unioning the instances of each node. */ - void GenSolutionFromIndSet(bool with_markers = false){ - for(unsigned i = 0; i < nodes.size(); i++){ - Node *node = nodes[i]; - GenNodeSolutionFromIndSet(node,node->Annotation,with_markers); - } - } - -#ifdef BOUNDED - bool NodePastRecursionBound(Node *node, bool report = false){ - if(RecursionBound < 0) return false; - NodeToCounter &backs = back_edges[node]; - for(NodeToCounter::iterator it = backs.begin(), en = backs.end(); it != en; ++it){ - if(it->second.val > it->first->recursion_bound){ - if(report){ - std::ostringstream os; - os << "cut off " << it->first->Name.name() << " at depth " << it->second.val; - reporter->Message(os.str()); - } - return true; - } - } - return false; - } -#endif - - /** Test whether a given extension candidate actually represents - an induction failure. Right now we approximate this: if - the resulting node in the unwinding could be labeled false, - it clearly is not an induction failure. */ - - bool CandidateFeasible(const Candidate &cand){ - if(!FeasibleEdges) return true; - timer_start("CandidateFeasible"); - RPFP *checker = new RPFP(rpfp->ls); - // std::cout << "Checking feasibility of extension " << cand.edge->Parent->number << std::endl; - checker->Push(); - std::vector chs(cand.Children.size()); - Node *root = checker->CloneNode(cand.edge->Parent); -#ifdef BOUNDED - for(unsigned i = 0; i < cand.Children.size(); i++) - if(NodePastRecursionBound(cand.Children[i])){ - timer_stop("CandidateFeasible"); - return false; - } -#endif -#ifdef NEW_CAND_SEL - GenNodeSolutionFromIndSet(cand.edge->Parent,root->Bound); -#else - root->Bound.SetEmpty(); -#endif - checker->AssertNode(root); - for(unsigned i = 0; i < cand.Children.size(); i++) - chs[i] = checker->CloneNode(cand.Children[i]); - Edge *e = checker->CreateEdge(root,cand.edge->F,chs); - checker->AssertEdge(e,0,true); - // std::cout << "Checking SAT: " << e->dual << std::endl; - bool res = checker->Check(root) != unsat; - // std::cout << "Result: " << res << std::endl; - if(!res)reporter->Reject(cand.edge,cand.Children); - checker->Pop(1); - delete checker; - timer_stop("CandidateFeasible"); - return res; - } - - - hash_map TopoSort; - int TopoSortCounter; - std::vector SortedEdges; - - void DoTopoSortRec(Node *node){ - if(TopoSort.find(node) != TopoSort.end()) - return; - TopoSort[node] = INT_MAX; // just to break cycles - Edge *edge = node->Outgoing; // note, this is just *one* outgoing edge - if(edge){ - std::vector &chs = edge->Children; - for(unsigned i = 0; i < chs.size(); i++) - DoTopoSortRec(chs[i]); - } - TopoSort[node] = TopoSortCounter++; - SortedEdges.push_back(edge); - } - - void DoTopoSort(){ - TopoSort.clear(); - SortedEdges.clear(); - TopoSortCounter = 0; - for(unsigned i = 0; i < nodes.size(); i++) - DoTopoSortRec(nodes[i]); - } - - - int StratifiedLeafCount; - -#ifdef NEW_STRATIFIED_INLINING - - /** Stratified inlining builds an initial layered unwinding before - switching to the LAWI strategy. Currently the number of layers - is one. Only nodes that are the targets of back edges are - consider to be leaves. This assumes we have already computed a - topological sort. - */ - - bool DoStratifiedInlining(){ - if(stratified_inlining_done) - return true; - stratified_inlining_done = true; - DoTopoSort(); - int depth = 1; // TODO: make this an option - std::vector > unfolding_levels(depth+1); - for(int level = 1; level <= depth; level++) - for(unsigned i = 0; i < SortedEdges.size(); i++){ - Edge *edge = SortedEdges[i]; - Node *parent = edge->Parent; - std::vector &chs = edge->Children; - std::vector my_chs(chs.size()); - for(unsigned j = 0; j < chs.size(); j++){ - Node *child = chs[j]; - int ch_level = TopoSort[child] >= TopoSort[parent] ? level-1 : level; - if(unfolding_levels[ch_level].find(child) == unfolding_levels[ch_level].end()){ - if(ch_level == 0) - unfolding_levels[0][child] = CreateLeaf(child); - else - throw InternalError("in levelized unwinding"); - } - my_chs[j] = unfolding_levels[ch_level][child]; - } - Candidate cand; cand.edge = edge; cand.Children = my_chs; - Node *new_node; - bool ok = Extend(cand,new_node); - MarkExpanded(new_node); // we don't expand here -- just mark it done - if(!ok) return false; // got a counterexample - unfolding_levels[level][parent] = new_node; - } - return true; - } - - Node *CreateLeaf(Node *node){ - RPFP::Node *nchild = CreateNodeInstance(node); - MakeLeaf(nchild, /* do_not_expand = */ true); - nchild->Annotation.SetEmpty(); - return nchild; - } - - void MarkExpanded(Node *node){ - if(unexpanded.find(node) != unexpanded.end()){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } - } - -#else - - /** In stratified inlining, we build the unwinding from the bottom - down, trying to satisfy the node bounds. We do this as a pre-pass, - limiting the expansion. If we get a counterexample, we are done, - else we continue as usual expanding the unwinding upward. - */ - - bool DoStratifiedInlining(){ - timer_start("StratifiedInlining"); - DoTopoSort(); - for(unsigned i = 0; i < leaves.size(); i++){ - Node *node = leaves[i]; - bool res = SatisfyUpperBound(node); - if(!res){ - timer_stop("StratifiedInlining"); - return false; - } - } - // don't leave any dangling nodes! -#ifndef EFFORT_BOUNDED_STRAT - for(unsigned i = 0; i < leaves.size(); i++) - if(!leaves[i]->Outgoing) - MakeLeaf(leaves[i],true); -#endif - timer_stop("StratifiedInlining"); - return true; - } - -#endif - - /** Here, we do the downward expansion for stratified inlining */ - - hash_map LeafMap, StratifiedLeafMap; - - Edge *GetNodeOutgoing(Node *node, int last_decs = 0){ - if(overapproxes.find(node) == overapproxes.end()) return node->Outgoing; /* already expanded */ - overapproxes.erase(node); -#ifdef EFFORT_BOUNDED_STRAT - if(last_decs > 5000){ - // RPFP::Transformer save = node->Annotation; - node->Annotation.SetEmpty(); - Edge *e = unwinding->CreateLowerBoundEdge(node); - // node->Annotation = save; - insts_of_node[node->map].push_back(node); - // std::cout << "made leaf: " << node->number << std::endl; - return e; - } -#endif - Edge *edge = node->map->Outgoing; - std::vector &chs = edge->Children; - - // make sure we don't create a covered node in this process! - - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - if(TopoSort[child] < TopoSort[node->map]){ - Node *leaf = LeafMap[child]; - if(!indset->Contains(leaf)){ - node->Outgoing->F.Formula = ctx.bool_val(false); // make this a proper leaf, else bogus cex - return node->Outgoing; - } - } - } - - std::vector nchs(chs.size()); - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - if(TopoSort[child] < TopoSort[node->map]){ - Node *leaf = LeafMap[child]; - nchs[i] = leaf; - if(unexpanded.find(leaf) != unexpanded.end()){ - unexpanded.erase(leaf); - insts_of_node[child].push_back(leaf); - } - } - else { - if(StratifiedLeafMap.find(child) == StratifiedLeafMap.end()){ - RPFP::Node *nchild = CreateNodeInstance(child,StratifiedLeafCount--); - MakeLeaf(nchild); - nchild->Annotation.SetEmpty(); - StratifiedLeafMap[child] = nchild; - indset->SetDominated(nchild); - } - nchs[i] = StratifiedLeafMap[child]; - } - } - CreateEdgeInstance(edge,node,nchs); - reporter->Extend(node); - return node->Outgoing; - } - - void SetHeuristicOldNode(Node *node){ - LocalHeuristic *h = dynamic_cast(heuristic); - if(h) - h->SetOldNode(node); - } - - bool SolveMain(){ - timer_start("SolveMain"); - bool res = SolveMainInt(); // does the actual work - timer_stop("SolveMain"); - return res; - } - - /** This does the actual solving work. We try to generate - candidates for extension. If we succed, we extend the - unwinding. If we fail, we have a solution. */ - bool SolveMainInt(){ - if(StratifiedInlining && !DoStratifiedInlining()) - return false; -#ifdef BOUNDED - DoTopoSort(); -#endif - while(true){ - timer_start("ProduceCandidatesForExtension"); - ProduceCandidatesForExtension(); - timer_stop("ProduceCandidatesForExtension"); - if(candidates.empty()){ - GenSolutionFromIndSet(); - TestRecursionBounded(); - return true; - } - Candidate cand = candidates.front(); - candidates.pop_front(); - if(CandidateFeasible(cand)){ - Node *new_node; - if(!Extend(cand,new_node)) - return false; -#ifdef EARLY_EXPAND - TryExpandNode(new_node); -#endif - } - } - } - - // hack: put something local into the underapproximation formula - // without this, interpolants can be pretty bad - void AddThing(expr &conj){ - std::string name = "@thing"; - expr thing = ctx.constant(name.c_str(),ctx.bool_sort()); - if(conj.is_app() && conj.decl().get_decl_kind() == And){ - std::vector conjs(conj.num_args()+1); - for(unsigned i = 0; i+1 < conjs.size(); i++) - conjs[i] = conj.arg(i); - conjs[conjs.size()-1] = thing; - conj = rpfp->conjoin(conjs); - } - } - - Node *CreateUnderapproxNode(Node *node){ - // cex.get_tree()->ComputeUnderapprox(cex.get_root(),0); - RPFP::Node *under_node = CreateNodeInstance(node->map /* ,StratifiedLeafCount-- */); - under_node->Annotation.IntersectWith(cex.get_root()->Underapprox); - AddThing(under_node->Annotation.Formula); - Edge *e = unwinding->CreateLowerBoundEdge(under_node); - under_node->Annotation.SetFull(); // allow this node to cover others - back_edges[under_node] = back_edges[node]; - e->map = 0; - reporter->Extend(under_node); - return under_node; - } - - /** Try to prove a conjecture about a node. If successful - update the unwinding annotation appropriately. */ - bool ProveConjecture(Node *node, const RPFP::Transformer &t,Node *other = 0, Counterexample *_cex = 0){ - reporter->Conjecture(node,t); - timer_start("ProveConjecture"); - RPFP::Transformer save = node->Bound; - node->Bound.IntersectWith(t); - -#ifndef LOCALIZE_CONJECTURES - bool ok = SatisfyUpperBound(node); -#else - SetHeuristicOldNode(other); - bool ok = SatisfyUpperBound(node); - SetHeuristicOldNode(0); -#endif - - if(ok){ - timer_stop("ProveConjecture"); - return true; - } -#ifdef UNDERAPPROX_NODES - if(UseUnderapprox && last_decisions > 500){ - std::cout << "making an underapprox\n"; - ExpandNodeFromCoverFail(node); - } -#endif - if(_cex) (*_cex).swap(cex); // return the cex if asked - cex.clear(); // throw away the useless cex - node->Bound = save; // put back original bound - timer_stop("ProveConjecture"); - return false; - } - - /** If a node is part of the inductive subset, expand it. - We ask the inductive subset to exclude the node if possible. - */ - void TryExpandNode(RPFP::Node *node){ - if(indset->Close(node)) return; - if(!NoConj && indset->Conjecture(node)){ -#ifdef UNDERAPPROX_NODES - /* TODO: temporary fix. this prevents an infinite loop in case - the node is covered by multiple others. This should be - removed when covering by a set is implemented. - */ - if(indset->Contains(node)){ - unexpanded.erase(node); - insts_of_node[node->map].push_back(node); - } -#endif - return; - } -#ifdef UNDERAPPROX_NODES - if(!indset->Contains(node)) - return; // could be covered by an underapprox node -#endif - indset->Add(node); -#if defined(CANDS_FROM_COVER_FAIL) && !defined(UNDERAPPROX_NODES) - if(ExpandNodeFromCoverFail(node)) - return; -#endif - ExpandNode(node); - } - - /** Make the conjunction of markers for all (expanded) instances of - a node in the input RPFP. */ - expr AllNodeMarkers(Node *node){ - expr res = ctx.bool_val(true); - std::vector &insts = insts_of_node[node]; - for(int k = insts.size()-1; k >= 0; k--) - res = res && NodeMarker(insts[k]); - return res; - } - - void RuleOutNodesPastBound(Node *node, RPFP::Transformer &t){ -#ifdef BOUNDED - if(RecursionBound < 0)return; - std::vector &insts = insts_of_node[node]; - for(unsigned i = 0; i < insts.size(); i++) - if(NodePastRecursionBound(insts[i])) - t.Formula = t.Formula && !NodeMarker(insts[i]); -#endif - } - - - void GenNodeSolutionWithMarkersAux(Node *node, RPFP::Transformer &annot, expr &marker_disjunction, Node *other_node){ -#ifdef BOUNDED - if(RecursionBound >= 0 && NodePastRecursionBound(node)) - return; -#endif - RPFP::Transformer temp = node->Annotation; - expr marker = (!other_node) ? NodeMarker(node) : NodeMarker(node, other_node); - temp.Formula = (!marker || temp.Formula); - annot.IntersectWith(temp); - marker_disjunction = marker_disjunction || marker; - } - - bool GenNodeSolutionWithMarkers(Node *node, RPFP::Transformer &annot, bool expanded_only = false, Node *other_node = 0){ - bool res = false; - annot.SetFull(); - expr marker_disjunction = ctx.bool_val(false); - std::vector &insts = expanded_only ? insts_of_node[node] : all_of_node[node]; - for(unsigned j = 0; j < insts.size(); j++){ - Node *node = insts[j]; - if(indset->Contains(insts[j])){ - GenNodeSolutionWithMarkersAux(node, annot, marker_disjunction, other_node); res = true; - } - } - annot.Formula = annot.Formula && marker_disjunction; - annot.Simplify(); - return res; - } - - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerJustForEdge(Edge *edge, RPFP *checker, bool expanded_only = false){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); - if(root->Bound.IsFull()) - return 0; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - if(!GenNodeSolutionWithMarkers(oc,nc->Annotation,expanded_only)) - return 0; - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - -#ifndef MINIMIZE_CANDIDATES_HARDER - -#if 0 - /** Make a checker to detheermine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - root->Bound = edge->Parent->Annotation; - root->Bound.Formula = (!AllNodeMarkers(edge->Parent)) || root->Bound.Formula; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - nc->Annotation = oc->Annotation; - RuleOutNodesPastBound(oc,nc->Annotation); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - -#else - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); -#if 0 - if(root->Bound.IsFull()) - return = 0; -#endif - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } -#endif - - /** If an edge is not satisfied, produce an extension candidate - using instances of its children that violate the parent annotation. - We find these using the marker predicates. */ - void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ - candidate.edge = edge; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *node = root->Outgoing->Children[j]; - Edge *lb = node->Outgoing; - std::vector &insts = insts_of_node[edge->Children[j]]; -#ifndef MINIMIZE_CANDIDATES - for(int k = insts.size()-1; k >= 0; k--) -#else - for(unsigned k = 0; k < insts.size(); k++) -#endif - { - Node *inst = insts[k]; - if(indset->Contains(inst)){ - if(checker->Empty(node) || - eq(lb ? checker->Eval(lb,NodeMarker(inst)) : checker->dualModel.eval(NodeMarker(inst,node)),ctx.bool_val(true))){ - candidate.Children.push_back(inst); - goto next_child; - } - } - } - throw InternalError("No candidate from induction failure"); - next_child:; - } - } -#else - - - /** Make a checker to determine if an edge in the input RPFP - is satisfied. */ - Node *CheckerForEdge(Edge *edge, RPFP *checker){ - Node *root = checker->CloneNode(edge->Parent); - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); - if(root->Bound.IsFull()) - return = 0; - checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - GenNodeSolutionWithMarkers(oc,nc->Annotation,true); - Edge *e = checker->CreateLowerBoundEdge(nc); - checker->AssertEdge(e); - cs.push_back(nc); - } - checker->AssertEdge(checker->CreateEdge(root,edge->F,cs)); - return root; - } - - /** If an edge is not satisfied, produce an extension candidate - using instances of its children that violate the parent annotation. - We find these using the marker predicates. */ - void ExtractCandidateFromCex(Edge *edge, RPFP *checker, Node *root, Candidate &candidate){ - candidate.edge = edge; - std::vector assumps; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Edge *lb = root->Outgoing->Children[j]->Outgoing; - std::vector &insts = insts_of_node[edge->Children[j]]; - for(unsigned k = 0; k < insts.size(); k++) - { - Node *inst = insts[k]; - expr marker = NodeMarker(inst); - if(indset->Contains(inst)){ - if(checker->Empty(lb->Parent) || - eq(checker->Eval(lb,marker),ctx.bool_val(true))){ - candidate.Children.push_back(inst); - assumps.push_back(checker->Localize(lb,marker)); - goto next_child; - } - assumps.push_back(checker->Localize(lb,marker)); - if(checker->CheckUpdateModel(root,assumps) != unsat){ - candidate.Children.push_back(inst); - goto next_child; - } - assumps.pop_back(); - } - } - throw InternalError("No candidate from induction failure"); - next_child:; - } - } - -#endif - - - Node *CheckerForEdgeClone(Edge *edge, RPFP_caching *checker){ - Edge *gen_cands_edge = checker->GetEdgeClone(edge); - Node *root = gen_cands_edge->Parent; - root->Outgoing = gen_cands_edge; - GenNodeSolutionFromIndSet(edge->Parent, root->Bound); -#if 0 - if(root->Bound.IsFull()) - return = 0; -#endif - checker->AssertNode(root); - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = gen_cands_edge->Children[j]; - GenNodeSolutionWithMarkers(oc,nc->Annotation,true,nc); - } - checker->AssertEdge(gen_cands_edge,1,true); - return root; - } - - /** If the current proposed solution is not inductive, - use the induction failure to generate candidates for extension. */ - void GenCandidatesFromInductionFailure(bool full_scan = false){ - timer_start("GenCandIndFail"); - GenSolutionFromIndSet(true /* add markers */); - for(unsigned i = 0; i < edges.size(); i++){ - Edge *edge = edges[i]; - if(!full_scan && updated_nodes.find(edge->Parent) == updated_nodes.end()) - continue; -#ifndef USE_NEW_GEN_CANDS - slvr.push(); - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerForEdge(edge,checker); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - slvr.pop(1); - delete checker; -#else - if(!NodeSolutionFromIndSetFull(edge->Parent)){ - RPFP_caching::scoped_solver_for_edge ssfe(gen_cands_rpfp,edge,true /* models */, true /*axioms*/); - gen_cands_rpfp->Push(); - Node *root = CheckerForEdgeClone(edge,gen_cands_rpfp); - if(gen_cands_rpfp->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,gen_cands_rpfp,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - gen_cands_rpfp->Pop(1); - } -#endif - } - updated_nodes.clear(); - timer_stop("GenCandIndFail"); -#ifdef CHECK_CANDS_FROM_IND_SET - for(std::list::iterator it = candidates.begin(), en = candidates.end(); it != en; ++it){ - if(!CandidateFeasible(*it)) - throw "produced infeasible candidate"; - } -#endif - if(!full_scan && candidates.empty()){ - reporter->Message("No candidates from updates. Trying full scan."); - GenCandidatesFromInductionFailure(true); - } - } - -#ifdef CANDS_FROM_UPDATES - /** If the given edge is not inductive in the current proposed solution, - use the induction failure to generate candidates for extension. */ - void GenCandidatesFromEdgeInductionFailure(RPFP::Edge *edge){ - GenSolutionFromIndSet(true /* add markers */); - for(unsigned i = 0; i < edges.size(); i++){ - slvr.push(); - Edge *edge = edges[i]; - RPFP *checker = new RPFP(rpfp->ls); - Node *root = CheckerForEdge(edge,checker); - if(checker->Check(root) != unsat){ - Candidate candidate; - ExtractCandidateFromCex(edge,checker,root,candidate); - reporter->InductionFailure(edge,candidate.Children); - candidates.push_back(candidate); - } - slvr.pop(1); - delete checker; - } - } -#endif - - /** Find the unexpanded nodes in the inductive subset. */ - void FindNodesToExpand(){ - for(Unexpanded::iterator it = unexpanded.begin(), en = unexpanded.end(); it != en; ++it){ - Node *node = *it; - if(indset->Candidate(node)) - to_expand.push_back(node); - } - } - - /** Try to create some extension candidates from the unexpanded - nodes. */ - void ProduceSomeCandidates(){ - while(candidates.empty() && !to_expand.empty()){ - Node *node = to_expand.front(); - to_expand.pop_front(); - TryExpandNode(node); - } - } - - std::list postponed_candidates; - - /** Try to produce some extension candidates, first from unexpanded - nides, and if this fails, from induction failure. */ - void ProduceCandidatesForExtension(){ - if(candidates.empty()) - ProduceSomeCandidates(); - while(candidates.empty()){ - FindNodesToExpand(); - if(to_expand.empty()) break; - ProduceSomeCandidates(); - } - if(candidates.empty()){ -#ifdef DEPTH_FIRST_EXPAND - if(postponed_candidates.empty()){ - GenCandidatesFromInductionFailure(); - postponed_candidates.swap(candidates); - } - if(!postponed_candidates.empty()){ - candidates.push_back(postponed_candidates.front()); - postponed_candidates.pop_front(); - } -#else - GenCandidatesFromInductionFailure(); -#endif - } - } - - bool Update(Node *node, const RPFP::Transformer &fact, bool eager=false){ - if(!node->Annotation.SubsetEq(fact)){ - reporter->Update(node,fact,eager); - if(conj_reporter) - conj_reporter->Update(node,fact,eager); - indset->Update(node,fact); - updated_nodes.insert(node->map); - node->Annotation.IntersectWith(fact); - return true; - } - return false; - } - - bool UpdateNodeToNode(Node *node, Node *top){ - return Update(node,top->Annotation); - } - - /** Update the unwinding solution, using an interpolant for the - derivation tree. */ - void UpdateWithInterpolant(Node *node, RPFP *tree, Node *top){ - if(top->Outgoing) - for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) - UpdateWithInterpolant(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); - UpdateNodeToNode(node, top); - heuristic->Update(node); - } - - /** Update unwinding lower bounds, using a counterexample. */ - - void UpdateWithCounterexample(Node *node, RPFP *tree, Node *top){ - if(top->Outgoing) - for(unsigned i = 0; i < top->Outgoing->Children.size(); i++) - UpdateWithCounterexample(node->Outgoing->Children[i],tree,top->Outgoing->Children[i]); - if(!top->Underapprox.SubsetEq(node->Underapprox)){ - reporter->UpdateUnderapprox(node,top->Underapprox); - // indset->Update(node,top->Annotation); - node->Underapprox.UnionWith(top->Underapprox); - heuristic->Update(node); - } - } - - /** Try to update the unwinding to satisfy the upper bound of a - node. */ - bool SatisfyUpperBound(Node *node){ - if(node->Bound.IsFull()) return true; -#ifdef PROPAGATE_BEFORE_CHECK - Propagate(); -#endif - reporter->Bound(node); - int start_decs = rpfp->CumulativeDecisions(); - DerivationTree *dtp = new DerivationTreeSlow(this,unwinding,reporter,heuristic,FullExpand); - DerivationTree &dt = *dtp; - bool res = dt.Derive(unwinding,node,UseUnderapprox); - int end_decs = rpfp->CumulativeDecisions(); - // std::cout << "decisions: " << (end_decs - start_decs) << std::endl; - last_decisions = end_decs - start_decs; - if(res){ - cex.set(dt.tree,dt.top); // note tree is now owned by cex - if(UseUnderapprox){ - UpdateWithCounterexample(node,dt.tree,dt.top); - } - } - else { - UpdateWithInterpolant(node,dt.tree,dt.top); - delete dt.tree; - } - delete dtp; - return !res; - } - - /* For a given nod in the unwinding, get conjectures from the - proposers and check them locally. Update the node with any true - conjectures. - */ - - void DoEagerDeduction(Node *node){ - for(unsigned i = 0; i < proposers.size(); i++){ - const std::vector &conjectures = proposers[i]->Propose(node); - for(unsigned j = 0; j < conjectures.size(); j++){ - const RPFP::Transformer &conjecture = conjectures[j]; - RPFP::Transformer bound(conjecture); - std::vector conj_vec; - unwinding->CollectConjuncts(bound.Formula,conj_vec); - for(unsigned k = 0; k < conj_vec.size(); k++){ - bound.Formula = conj_vec[k]; - if(CheckEdgeCaching(node->Outgoing,bound) == unsat) - Update(node,bound, /* eager = */ true); - //else - //std::cout << "conjecture failed\n"; - } - } - } - } - - - check_result CheckEdge(RPFP *checker, Edge *edge){ - Node *root = edge->Parent; - checker->Push(); - checker->AssertNode(root); - checker->AssertEdge(edge,1,true); - check_result res = checker->Check(root); - checker->Pop(1); - return res; - } - - check_result CheckEdgeCaching(Edge *unwinding_edge, const RPFP::Transformer &bound){ - - // use a dedicated solver for this edge - // TODO: can this mess be hidden somehow? - - RPFP_caching *checker = gen_cands_rpfp; // TODO: a good choice? - Edge *edge = unwinding_edge->map; // get the edge in the original RPFP - RPFP_caching::scoped_solver_for_edge ssfe(checker,edge,true /* models */, true /*axioms*/); - Edge *checker_edge = checker->GetEdgeClone(edge); - - // copy the annotations and bound to the clone - Node *root = checker_edge->Parent; - root->Bound = bound; - for(unsigned j = 0; j < checker_edge->Children.size(); j++){ - Node *oc = unwinding_edge->Children[j]; - Node *nc = checker_edge->Children[j]; - nc->Annotation = oc->Annotation; - } - - return CheckEdge(checker,checker_edge); - } - - - /* If the counterexample derivation is partial due to - use of underapproximations, complete it. */ - - void BuildFullCex(Node *node){ - DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); - bool res = dt.Derive(unwinding,node,UseUnderapprox,true); // build full tree - if(!res) throw "Duality internal error in BuildFullCex"; - cex.set(dt.tree,dt.top); - } - - void UpdateBackEdges(Node *node){ -#ifdef BOUNDED - std::vector &chs = node->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++){ - Node *child = chs[i]; - bool is_back = TopoSort[child->map] >= TopoSort[node->map]; - NodeToCounter &nov = back_edges[node]; - NodeToCounter chv = back_edges[child]; - if(is_back) - chv[child->map].val++; - for(NodeToCounter::iterator it = chv.begin(), en = chv.end(); it != en; ++it){ - Node *back = it->first; - Counter &c = nov[back]; - c.val = std::max(c.val,it->second.val); - } - } -#endif - } - - /** Extend the unwinding, keeping it solved. */ - bool Extend(Candidate &cand, Node *&node){ - timer_start("Extend"); - node = CreateNodeInstance(cand.edge->Parent); - CreateEdgeInstance(cand.edge,node,cand.Children); - UpdateBackEdges(node); - reporter->Extend(node); - DoEagerDeduction(node); // first be eager... - bool res = SatisfyUpperBound(node); // then be lazy - if(res) indset->CloseDescendants(node); - else { -#ifdef UNDERAPPROX_NODES - ExpandUnderapproxNodes(cex.get_tree(), cex.get_root()); -#endif - if(UseUnderapprox) BuildFullCex(node); - timer_stop("Extend"); - return res; - } - timer_stop("Extend"); - return res; - } - - void ExpandUnderapproxNodes(RPFP *tree, Node *root){ - Node *node = root->map; - if(underapprox_map.find(node) != underapprox_map.end()){ - RPFP::Transformer cnst = root->Annotation; - tree->EvalNodeAsConstraint(root, cnst); - cnst.Complement(); - Node *orig = underapprox_map[node]; - RPFP::Transformer save = orig->Bound; - orig->Bound = cnst; - DerivationTree dt(this,unwinding,reporter,heuristic,FullExpand); - bool res = dt.Derive(unwinding,orig,UseUnderapprox,true,tree); - if(!res){ - UpdateWithInterpolant(orig,dt.tree,dt.top); - throw "bogus underapprox!"; - } - ExpandUnderapproxNodes(tree,dt.top); - } - else if(root->Outgoing){ - std::vector &chs = root->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++) - ExpandUnderapproxNodes(tree,chs[i]); - } - } - - // Propagate conjuncts up the unwinding - void Propagate(){ - reporter->Message("beginning propagation"); - timer_start("Propagate"); - std::vector sorted_nodes = unwinding->nodes; - std::sort(sorted_nodes.begin(),sorted_nodes.end(),std::less()); // sorts by sequence number - hash_map > facts; - for(unsigned i = 0; i < sorted_nodes.size(); i++){ - Node *node = sorted_nodes[i]; - std::set &node_facts = facts[node->map]; - if(!(node->Outgoing && indset->Contains(node))) - continue; - std::vector conj_vec; - unwinding->CollectConjuncts(node->Annotation.Formula,conj_vec); - std::set conjs; - std::copy(conj_vec.begin(),conj_vec.end(),std::inserter(conjs,conjs.begin())); - if(!node_facts.empty()){ - RPFP *checker = new RPFP(rpfp->ls); - slvr.push(); - Node *root = checker->CloneNode(node); - Edge *edge = node->Outgoing; - // checker->AssertNode(root); - std::vector cs; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *oc = edge->Children[j]; - Node *nc = checker->CloneNode(oc); - nc->Annotation = oc->Annotation; // is this needed? - cs.push_back(nc); - } - Edge *checker_edge = checker->CreateEdge(root,edge->F,cs); - checker->AssertEdge(checker_edge, 0, true, false); - std::vector propagated; - for(std::set ::iterator it = node_facts.begin(), en = node_facts.end(); it != en;){ - const expr &fact = *it; - if(conjs.find(fact) == conjs.end()){ - root->Bound.Formula = fact; - slvr.push(); - checker->AssertNode(root); - check_result res = checker->Check(root); - slvr.pop(); - if(res != unsat){ - std::set ::iterator victim = it; - ++it; - node_facts.erase(victim); // if it ain't true, nix it - continue; - } - propagated.push_back(fact); - } - ++it; - } - slvr.pop(); - for(unsigned i = 0; i < propagated.size(); i++){ - root->Annotation.Formula = propagated[i]; - UpdateNodeToNode(node,root); - } - delete checker; - } - for(std::set ::iterator it = conjs.begin(), en = conjs.end(); it != en; ++it){ - expr foo = *it; - node_facts.insert(foo); - } - } - timer_stop("Propagate"); - } - - - /** This class represents a derivation tree. */ - class DerivationTree { - public: - - virtual ~DerivationTree(){} - - DerivationTree(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) - : slvr(rpfp->slvr()), - ctx(rpfp->ctx) - { - duality = _duality; - reporter = _reporter; - heuristic = _heuristic; - full_expand = _full_expand; - } - - Duality *duality; - Reporter *reporter; - Heuristic *heuristic; - solver &slvr; - context &ctx; - RPFP *tree; - RPFP::Node *top; - std::list leaves; - bool full_expand; - bool underapprox; - bool constrained; - bool false_approx; - std::vector underapprox_core; - int start_decs, last_decs; - - /* We build derivation trees in one of three modes: - - 1) In normal mode, we build the full tree without considering - underapproximations. - - 2) In underapprox mode, we use underapproximations to cut off - the tree construction. THis means the resulting tree may not - be complete. - - 3) In constrained mode, we build the full tree but use - underapproximations as upper bounds. This mode is used to - complete the partial derivation constructed in underapprox - mode. - */ - - bool Derive(RPFP *rpfp, RPFP::Node *root, bool _underapprox, bool _constrained = false, RPFP *_tree = 0){ - underapprox = _underapprox; - constrained = _constrained; - false_approx = true; - timer_start("Derive"); -#ifndef USE_CACHING_RPFP - tree = _tree ? _tree : new RPFP(rpfp->ls); -#else - RPFP::LogicSolver *cache_ls = new RPFP::iZ3LogicSolver(ctx); - cache_ls->slvr->push(); - tree = _tree ? _tree : new RPFP_caching(cache_ls); -#endif - tree->HornClauses = rpfp->HornClauses; - tree->Push(); // so we can clear out the solver later when finished - top = CreateApproximatedInstance(root); - tree->AssertNode(top); // assert the negation of the top-level spec - timer_start("Build"); - bool res = Build(); - heuristic->Done(); - timer_stop("Build"); - timer_start("Pop"); - tree->Pop(1); - timer_stop("Pop"); -#ifdef USE_CACHING_RPFP - cache_ls->slvr->pop(1); - delete cache_ls; - tree->ls = rpfp->ls; -#endif - timer_stop("Derive"); - return res; - } - -#define WITH_CHILDREN - - void InitializeApproximatedInstance(RPFP::Node *to){ - to->Annotation = to->map->Annotation; -#ifndef WITH_CHILDREN - tree->CreateLowerBoundEdge(to); -#endif - leaves.push_back(to); - } - - Node *CreateApproximatedInstance(RPFP::Node *from){ - Node *to = tree->CloneNode(from); - InitializeApproximatedInstance(to); - return to; - } - - bool CheckWithUnderapprox(){ - timer_start("CheckWithUnderapprox"); - std::vector leaves_vector(leaves.size()); - std::copy(leaves.begin(),leaves.end(),leaves_vector.begin()); - check_result res = tree->Check(top,leaves_vector); - timer_stop("CheckWithUnderapprox"); - return res != unsat; - } - - virtual bool Build(){ -#ifdef EFFORT_BOUNDED_STRAT - start_decs = tree->CumulativeDecisions(); -#endif - while(ExpandSomeNodes(true)); // do high-priority expansions - while (true) - { -#ifndef WITH_CHILDREN - timer_start("asserting leaves"); - timer_start("pushing"); - tree->Push(); - timer_stop("pushing"); - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - tree->AssertEdge((*it)->Outgoing,1); // assert the overapproximation, and keep it past pop - timer_stop("asserting leaves"); - lbool res = tree->Solve(top, 2); // incremental solve, keep interpolants for two pops - timer_start("popping leaves"); - tree->Pop(1); - timer_stop("popping leaves"); -#else - lbool res; - if((underapprox || false_approx) && top->Outgoing && CheckWithUnderapprox()){ - if(constrained) goto expand_some_nodes; // in constrained mode, keep expanding - goto we_are_sat; // else if underapprox is sat, we stop - } - // tree->Check(top); - res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop -#endif - if (res == l_false) - return false; - - expand_some_nodes: - if(ExpandSomeNodes()) - continue; - - we_are_sat: - if(underapprox && !constrained){ - timer_start("ComputeUnderapprox"); - tree->ComputeUnderapprox(top,1); - timer_stop("ComputeUnderapprox"); - } - else { -#ifdef UNDERAPPROX_NODES -#ifndef SKIP_UNDERAPPROX_NODES - timer_start("ComputeUnderapprox"); - tree->ComputeUnderapprox(top,1); - timer_stop("ComputeUnderapprox"); -#endif -#endif - } - return true; - } - } - - virtual void ExpandNode(RPFP::Node *p){ - // tree->RemoveEdge(p->Outgoing); - Edge *ne = p->Outgoing; - if(ne) { - // reporter->Message("Recycling edge..."); - std::vector &cs = ne->Children; - for(unsigned i = 0; i < cs.size(); i++) - InitializeApproximatedInstance(cs[i]); - // ne->dual = expr(); - } - else { - Edge *edge = duality->GetNodeOutgoing(p->map,last_decs); - std::vector &cs = edge->Children; - std::vector children(cs.size()); - for(unsigned i = 0; i < cs.size(); i++) - children[i] = CreateApproximatedInstance(cs[i]); - ne = tree->CreateEdge(p, p->map->Outgoing->F, children); - ne->map = p->map->Outgoing->map; - } -#ifndef WITH_CHILDREN - tree->AssertEdge(ne); // assert the edge in the solver -#else - tree->AssertEdge(ne,0,!full_expand,(underapprox || false_approx)); // assert the edge in the solver -#endif - reporter->Expand(ne); - } - -#define UNDERAPPROXCORE -#ifndef UNDERAPPROXCORE - void ExpansionChoices(std::set &best){ - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - if (!tree->Empty(*it)) // if used in the counter-model - choices.insert(*it); - heuristic->ChooseExpand(choices, best); - } -#else -#if 0 - - void ExpansionChoices(std::set &best){ - std::vector unused_set, used_set; - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ - Node *n = *it; - if (!tree->Empty(n)) - used_set.push_back(n); - else - unused_set.push_back(n); - } - if(tree->Check(top,unused_set) == unsat) - throw "error in ExpansionChoices"; - for(unsigned i = 0; i < used_set.size(); i++){ - Node *n = used_set[i]; - unused_set.push_back(n); - if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ - unused_set.pop_back(); - choices.insert(n); - } - else - std::cout << "Using underapprox of " << n->number << std::endl; - } - heuristic->ChooseExpand(choices, best); - } -#else - void ExpansionChoicesFull(std::set &best, bool high_priority, bool best_only = false){ - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it) - if (high_priority || !tree->Empty(*it)) // if used in the counter-model - choices.insert(*it); - heuristic->ChooseExpand(choices, best, high_priority, best_only); - } - - void ExpansionChoicesRec(std::vector &unused_set, std::vector &used_set, - std::set &choices, int from, int to){ - if(from == to) return; - int orig_unused = unused_set.size(); - unused_set.resize(orig_unused + (to - from)); - std::copy(used_set.begin()+from,used_set.begin()+to,unused_set.begin()+orig_unused); - if(!top->Outgoing || tree->Check(top,unused_set) == unsat){ - unused_set.resize(orig_unused); - if(to - from == 1){ -#if 1 - std::cout << "Not using underapprox of " << used_set[from] ->number << std::endl; -#endif - choices.insert(used_set[from]); - } - else { - int mid = from + (to - from)/2; - ExpansionChoicesRec(unused_set, used_set, choices, from, mid); - ExpansionChoicesRec(unused_set, used_set, choices, mid, to); - } - } - else { -#if 1 - std::cout << "Using underapprox of "; - for(int i = from; i < to; i++){ - std::cout << used_set[i]->number << " "; - if(used_set[i]->map->Underapprox.IsEmpty()) - std::cout << "(false!) "; - } - std::cout << std::endl; -#endif - } - } - - std::set old_choices; - - void ExpansionChoices(std::set &best, bool high_priority, bool best_only = false){ - if(!underapprox || constrained || high_priority){ - ExpansionChoicesFull(best, high_priority,best_only); - return; - } - std::vector unused_set, used_set; - std::set choices; - for(std::list::iterator it = leaves.begin(), en = leaves.end(); it != en; ++it){ - Node *n = *it; - if (!tree->Empty(n)){ - if(old_choices.find(n) != old_choices.end() || n->map->Underapprox.IsEmpty()) - choices.insert(n); - else - used_set.push_back(n); - } - else - unused_set.push_back(n); - } - if(tree->Check(top,unused_set) == unsat) - throw "error in ExpansionChoices"; - ExpansionChoicesRec(unused_set, used_set, choices, 0, used_set.size()); - old_choices = choices; - heuristic->ChooseExpand(choices, best, high_priority); - } -#endif -#endif - - bool ExpandSomeNodes(bool high_priority = false, int max = INT_MAX){ -#ifdef EFFORT_BOUNDED_STRAT - last_decs = tree->CumulativeDecisions() - start_decs; -#endif - timer_start("ExpandSomeNodes"); - timer_start("ExpansionChoices"); - std::set choices; - ExpansionChoices(choices,high_priority,max != INT_MAX); - timer_stop("ExpansionChoices"); - std::list leaves_copy = leaves; // copy so can modify orig - leaves.clear(); - int count = 0; - for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ - if(choices.find(*it) != choices.end() && count < max){ - count++; - ExpandNode(*it); - } - else leaves.push_back(*it); - } - timer_stop("ExpandSomeNodes"); - return !choices.empty(); - } - - void RemoveExpansion(RPFP::Node *p){ - Edge *edge = p->Outgoing; - Node *parent = edge->Parent; -#ifndef KEEP_EXPANSIONS - std::vector cs = edge->Children; - tree->DeleteEdge(edge); - for(unsigned i = 0; i < cs.size(); i++) - tree->DeleteNode(cs[i]); -#endif - leaves.push_back(parent); - } - - // remove all the descendants of tree root (but not root itself) - void RemoveTree(RPFP *tree, RPFP::Node *root){ - Edge *edge = root->Outgoing; - std::vector cs = edge->Children; - tree->DeleteEdge(edge); - for(unsigned i = 0; i < cs.size(); i++){ - RemoveTree(tree,cs[i]); - tree->DeleteNode(cs[i]); - } - } - }; - - class DerivationTreeSlow : public DerivationTree { - public: - - struct stack_entry { - unsigned level; // SMT solver stack level - std::vector expansions; - }; - - std::vector stack; - - hash_map updates; - - int restart_interval; - - DerivationTreeSlow(Duality *_duality, RPFP *rpfp, Reporter *_reporter, Heuristic *_heuristic, bool _full_expand) - : DerivationTree(_duality, rpfp, _reporter, _heuristic, _full_expand) { - stack.push_back(stack_entry()); - } - - struct DoRestart {}; - - virtual bool Build(){ - restart_interval = 3; - while (true) { - try { - return BuildMain(); - } - catch (const DoRestart &) { - // clear the statck and try again - updated_nodes.clear(); - while(stack.size() > 1) - PopLevel(); - reporter->Message("restarted"); - restart_interval += 1; - } - } - } - - - // When we check, try to use the same children that were used in the - // previous counterexample. - check_result Check(){ -#if 0 - std::vector posnodes, negnodes; - std::vector &expansions = stack.back().expansions; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - std::vector &chs = node->Outgoing->Children; - for(unsigned j = 0; j < chs.size(); j++){ - Node *ch = chs[j]; - int use = heuristic->UseNode(ch); - if(use == 1) - posnodes.push_back(ch); - else if (use == -1) - negnodes.push_back(ch); - } - } - if(!(posnodes.empty() && negnodes.empty())){ - check_result res = tree->CheckWithConstrainedNodes(posnodes,negnodes); - if(res != unsat){ - reporter->Message("matched previous counterexample"); - return res; - } - } -#endif - return tree->Check(top); - } - - bool BuildMain(){ - - stack.back().level = tree->slvr().get_scope_level(); - bool was_sat = true; - int update_failures = 0; - int total_updates = 0; - - while (true) - { - lbool res; - - unsigned slvr_level = tree->slvr().get_scope_level(); - if(slvr_level != stack.back().level) - throw "stacks out of sync!"; - reporter->Depth(stack.size()); - - // res = tree->Solve(top, 1); // incremental solve, keep interpolants for one pop - check_result foo = Check(); - res = foo == unsat ? l_false : l_true; - - if (res == l_false) { - if (stack.empty()) // should never happen - return false; - - { - std::vector &expansions = stack.back().expansions; - int update_count = 0; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - try { - tree->SolveSingleNode(top,node); -#ifdef NO_GENERALIZE - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); -#else - if(expansions.size() == 1 && NodeTooComplicated(node)) - SimplifyNode(node); - else - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - Generalize(node); -#endif - } - catch(const RPFP::Bad &){ - // bad interpolants can get us here - throw DoRestart(); - } - catch(const RPFP::ReallyBad &){ - // this could be caused by incompleteness - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - node->map->Annotation.SetFull(); - std::vector &chs = node->map->Outgoing->Children; - for(unsigned j = 0; j < chs.size(); j++) - chs[j]->Annotation.SetFull(); - reporter->Message("incompleteness: cleared annotation and child annotations"); - } - throw DoRestart(); - } - catch(char const *msg){ - // bad interpolants can get us here - reporter->Message(std::string("interpolation failure:") + msg); - throw DoRestart(); - } - catch(const RPFP::greedy_reduce_failed &){ - // if we couldn't reduce, just continue (maybe should restart?) - reporter->Message("interpolant verification failed"); - } - if(RecordUpdate(node)){ - update_count++; - total_updates++; - } - else - heuristic->Update(node->map); // make it less likely to expand this node in future - } -#if 1 - if(duality->EnableRestarts) - if(total_updates >= restart_interval) - throw DoRestart(); -#endif - if(update_count == 0){ - if(was_sat){ - update_failures++; - if(update_failures > 10){ - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - node->map->Annotation.SetFull(); - reporter->Message("incompleteness: cleared annotation"); - } - throw DoRestart(); - } - } - reporter->Message("backtracked without learning"); - } - else update_failures = 0; - } - tree->ComputeProofCore(); // need to compute the proof core before popping solver - bool propagated = false; - while(1) { - bool prev_level_used = LevelUsedInProof(stack.size()-2); // need to compute this before pop - PopLevel(); - if(stack.size() == 1)break; - if(prev_level_used){ - Node *node = stack.back().expansions[0]; -#ifndef NO_PROPAGATE - if(!Propagate(node)) break; -#endif - if(!RecordUpdate(node)) break; // shouldn't happen! - RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list - propagated = true; - continue; - } - if(propagated) break; // propagation invalidates the proof core, so disable non-chron backtrack - RemoveUpdateNodesAtCurrentLevel(); // this level is about to be deleted -- remove its children from update list - std::vector &unused_ex = stack.back().expansions; - for(unsigned i = 0; i < unused_ex.size(); i++) - heuristic->Update(unused_ex[i]->map); // make it less likely to expand this node in future - } - HandleUpdatedNodes(); - if(stack.size() == 1){ - if(top->Outgoing) - tree->DeleteEdge(top->Outgoing); // in case we kept the tree - return false; - } - was_sat = false; - } - else { - was_sat = true; - tree->Push(); - std::vector &expansions = stack.back().expansions; -#ifndef NO_DECISIONS -#if 0 - if(expansions.size() > 0) - tree->GreedyReduceNodes(expansions[0]->Outgoing->Children); // try to reduce number of children -#endif - for(unsigned i = 0; i < expansions.size(); i++){ - tree->FixCurrentState(expansions[i]->Outgoing); - } -#endif -#if 0 - if(tree->slvr().check() == unsat) - throw "help!"; -#endif - int expand_max = 1; - if(0&&duality->BatchExpand){ - int thing = stack.size() / 10; // * 0.1; - expand_max = std::max(1,thing); - if(expand_max > 1) - std::cout << "foo!\n"; - } - - if(ExpandSomeNodes(false,expand_max)) - continue; - tree->Pop(1); - node_order.clear(); - while(stack.size() > 1){ - tree->Pop(1); - std::vector &expansions = stack.back().expansions; - for(unsigned i = 0; i < expansions.size(); i++) - node_order.push_back(expansions[i]); - stack.pop_back(); - } -#if 0 - Reduce(); -#endif - return true; - } - } - } - - std::vector node_order; - - void Reduce(){ - tree->Push(); - // tree->AssertNode(top); // assert the negation of the top-level spec - for(int i = node_order.size()-1; i >= 0; --i){ - Edge *edge = node_order[i]->Outgoing; - if(edge){ - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *ch = edge->Children[j]; - if(!ch->Outgoing) - ch->Annotation.SetEmpty(); - } - tree->AssertEdge(edge,0,true); - } - } - tree->GreedyReduceNodes(node_order); // try to reduce the counterexample size - tree->Pop(1); - } - - void PopLevel(){ - std::vector &expansions = stack.back().expansions; - tree->Pop(1); - hash_set leaves_to_remove; - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - // if(node != top) - //tree->ConstrainParent(node->Incoming[0],node); - std::vector &cs = node->Outgoing->Children; - for(unsigned i = 0; i < cs.size(); i++){ - leaves_to_remove.insert(cs[i]); - UnmapNode(cs[i]); - if(std::find(updated_nodes.begin(),updated_nodes.end(),cs[i]) != updated_nodes.end()) - throw "help!"; - } - } - RemoveLeaves(leaves_to_remove); // have to do this before actually deleting the children - for(unsigned i = 0; i < expansions.size(); i++){ - Node *node = expansions[i]; - RemoveExpansion(node); - } - stack.pop_back(); - } - - bool NodeTooComplicated(Node *node){ - int ops = tree->CountOperators(node->Annotation.Formula); - if(ops > 10) return true; - node->Annotation.Formula = tree->RemoveRedundancy(node->Annotation.Formula).simplify(); - return tree->CountOperators(node->Annotation.Formula) > 3; - } - - void SimplifyNode(Node *node){ - // have to destroy the old proof to get a new interpolant - timer_start("SimplifyNode"); - tree->PopPush(); - try { - tree->InterpolateByCases(top,node); - } - catch(const RPFP::Bad&){ - timer_stop("SimplifyNode"); - throw RPFP::Bad(); - } - timer_stop("SimplifyNode"); - } - - bool LevelUsedInProof(unsigned level){ - std::vector &expansions = stack[level].expansions; - for(unsigned i = 0; i < expansions.size(); i++) - if(tree->EdgeUsedInProof(expansions[i]->Outgoing)) - return true; - return false; - } - - void RemoveUpdateNodesAtCurrentLevel() { - for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ - Node *node = *it; - if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ - std::list::iterator victim = it; - ++it; - updated_nodes.erase(victim); - } - else - ++it; - } - } - - void RemoveLeaves(hash_set &leaves_to_remove){ - std::list leaves_copy; - leaves_copy.swap(leaves); - for(std::list::iterator it = leaves_copy.begin(), en = leaves_copy.end(); it != en; ++it){ - if(leaves_to_remove.find(*it) == leaves_to_remove.end()) - leaves.push_back(*it); - } - } - - hash_map > node_map; - std::list updated_nodes; - - virtual void ExpandNode(RPFP::Node *p){ - stack.push_back(stack_entry()); - stack.back().level = tree->slvr().get_scope_level(); - stack.back().expansions.push_back(p); - DerivationTree::ExpandNode(p); - std::vector &new_nodes = p->Outgoing->Children; - for(unsigned i = 0; i < new_nodes.size(); i++){ - Node *n = new_nodes[i]; - node_map[n->map].push_back(n); - } - } - - bool RecordUpdate(Node *node){ - bool res = duality->UpdateNodeToNode(node->map,node); - if(res){ - std::vector to_update = node_map[node->map]; - for(unsigned i = 0; i < to_update.size(); i++){ - Node *node2 = to_update[i]; - // maintain invariant that no nodes on updated list are created at current stack level - if(node2 == node || !(node->Incoming.size() > 0 && AtCurrentStackLevel(node2->Incoming[0]->Parent))){ - updated_nodes.push_back(node2); - if(node2 != node) - node2->Annotation = node->Annotation; - } - } - } - return res; - } - - void HandleUpdatedNodes(){ - for(std::list::iterator it = updated_nodes.begin(), en = updated_nodes.end(); it != en;){ - Node *node = *it; - node->Annotation = node->map->Annotation; - if(node->Incoming.size() > 0) - tree->ConstrainParent(node->Incoming[0],node); - if(AtCurrentStackLevel(node->Incoming[0]->Parent)){ - std::list::iterator victim = it; - ++it; - updated_nodes.erase(victim); - } - else - ++it; - } - } - - bool AtCurrentStackLevel(Node *node){ - std::vector vec = stack.back().expansions; - for(unsigned i = 0; i < vec.size(); i++) - if(vec[i] == node) - return true; - return false; - } - - void UnmapNode(Node *node){ - std::vector &vec = node_map[node->map]; - for(unsigned i = 0; i < vec.size(); i++){ - if(vec[i] == node){ - std::swap(vec[i],vec.back()); - vec.pop_back(); - return; - } - } - throw "can't unmap node"; - } - - void Generalize(Node *node){ -#ifndef USE_RPFP_CLONE - tree->Generalize(top,node); -#else - RPFP_caching *clone_rpfp = duality->clone_rpfp; - if(!node->Outgoing->map) return; - Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); - Node *clone_node = clone_edge->Parent; - clone_node->Annotation = node->Annotation; - for(unsigned i = 0; i < clone_edge->Children.size(); i++) - clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; - clone_rpfp->GeneralizeCache(clone_edge); - node->Annotation = clone_node->Annotation; -#endif - } - - bool Propagate(Node *node){ -#ifdef USE_RPFP_CLONE - RPFP_caching *clone_rpfp = duality->clone_rpfp; - Edge *clone_edge = clone_rpfp->GetEdgeClone(node->Outgoing->map); - Node *clone_node = clone_edge->Parent; - clone_node->Annotation = node->map->Annotation; - for(unsigned i = 0; i < clone_edge->Children.size(); i++) - clone_edge->Children[i]->Annotation = node->map->Outgoing->Children[i]->Annotation; - bool res = clone_rpfp->PropagateCache(clone_edge); - if(res) - node->Annotation = clone_node->Annotation; - return res; -#else - return false; -#endif - } - - }; - - - class Covering { - - struct cover_info { - Node *covered_by; - std::list covers; - bool dominated; - std::set dominates; - cover_info(){ - covered_by = 0; - dominated = false; - } - }; - - typedef hash_map cover_map; - cover_map cm; - Duality *parent; - bool some_updates; - -#define NO_CONJ_ON_SIMPLE_LOOPS -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - hash_set simple_loops; -#endif - - Node *&covered_by(Node *node){ - return cm[node].covered_by; - } - - std::list &covers(Node *node){ - return cm[node].covers; - } - - std::vector &insts_of_node(Node *node){ - return parent->insts_of_node[node]; - } - - Reporter *reporter(){ - return parent->reporter; - } - - std::set &dominates(Node *x){ - return cm[x].dominates; - } - - bool dominates(Node *x, Node *y){ - std::set &d = cm[x].dominates; - return d.find(y) != d.end(); - } - - bool &dominated(Node *x){ - return cm[x].dominated; - } - - public: - - Covering(Duality *_parent){ - parent = _parent; - some_updates = false; - -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - hash_map > outgoing; - for(unsigned i = 0; i < parent->rpfp->edges.size(); i++) - outgoing[parent->rpfp->edges[i]->Parent].push_back(parent->rpfp->edges[i]); - for(unsigned i = 0; i < parent->rpfp->nodes.size(); i++){ - Node * node = parent->rpfp->nodes[i]; - std::vector &outs = outgoing[node]; - if(outs.size() == 2){ - for(int j = 0; j < 2; j++){ - Edge *loop_edge = outs[j]; - if(loop_edge->Children.size() == 1 && loop_edge->Children[0] == loop_edge->Parent) - simple_loops.insert(node); - } - } - } -#endif - - } - - bool IsCoveredRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return false; - memo.insert(node); - if(covered_by(node)) return true; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) - if(IsCoveredRec(memo,node->Outgoing->Children[i])) - return true; - return false; - } - - bool IsCovered(Node *node){ - hash_set memo; - return IsCoveredRec(memo,node); - } - -#ifndef UNDERAPPROX_NODES - void RemoveCoveringsBy(Node *node){ - std::list &cs = covers(node); - for(std::list::iterator it = cs.begin(), en = cs.end(); it != en; it++){ - covered_by(*it) = 0; - reporter()->RemoveCover(*it,node); - } - cs.clear(); - } -#else - void RemoveCoveringsBy(Node *node){ - std::vector &cs = parent->all_of_node[node->map]; - for(std::vector::iterator it = cs.begin(), en = cs.end(); it != en; it++){ - Node *other = *it; - if(covered_by(other) && CoverOrder(node,other)){ - covered_by(other) = 0; - reporter()->RemoveCover(*it,node); - } - } - } -#endif - - void RemoveAscendantCoveringsRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return; - memo.insert(node); - RemoveCoveringsBy(node); - for(std::vector::iterator it = node->Incoming.begin(), en = node->Incoming.end(); it != en; ++it) - RemoveAscendantCoveringsRec(memo,(*it)->Parent); - } - - void RemoveAscendantCoverings(Node *node){ - hash_set memo; - RemoveAscendantCoveringsRec(memo,node); - } - - bool CoverOrder(Node *covering, Node *covered){ -#ifdef UNDERAPPROX_NODES - if(parent->underapprox_map.find(covered) != parent->underapprox_map.end()) - return false; - if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) - return covering->number < covered->number || parent->underapprox_map[covering] == covered; -#endif - return covering->number < covered->number; - } - - bool CheckCover(Node *covered, Node *covering){ - return - CoverOrder(covering,covered) - && covered->Annotation.SubsetEq(covering->Annotation) - && !IsCovered(covering); - } - - bool CoverByNode(Node *covered, Node *covering){ - if(CheckCover(covered,covering)){ - covered_by(covered) = covering; - covers(covering).push_back(covered); - std::vector others; others.push_back(covering); - reporter()->AddCover(covered,others); - RemoveAscendantCoverings(covered); - return true; - } - else - return false; - } - -#ifdef UNDERAPPROX_NODES - bool CoverByAll(Node *covered){ - RPFP::Transformer all = covered->Annotation; - all.SetEmpty(); - std::vector &insts = parent->insts_of_node[covered->map]; - std::vector others; - for(unsigned i = 0; i < insts.size(); i++){ - Node *covering = insts[i]; - if(CoverOrder(covering,covered) && !IsCovered(covering)){ - others.push_back(covering); - all.UnionWith(covering->Annotation); - } - } - if(others.size() && covered->Annotation.SubsetEq(all)){ - covered_by(covered) = covered; // anything non-null will do - reporter()->AddCover(covered,others); - RemoveAscendantCoverings(covered); - return true; - } - else - return false; - } -#endif - - bool Close(Node *node){ - if(covered_by(node)) - return true; -#ifndef UNDERAPPROX_NODES - std::vector &insts = insts_of_node(node->map); - for(unsigned i = 0; i < insts.size(); i++) - if(CoverByNode(node,insts[i])) - return true; -#else - if(CoverByAll(node)) - return true; -#endif - return false; - } - - bool CloseDescendantsRec(hash_set &memo, Node *node){ - if(memo.find(node) != memo.end()) - return false; - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++) - if(CloseDescendantsRec(memo,node->Outgoing->Children[i])) - return true; - if(Close(node)) - return true; - memo.insert(node); - return false; - } - - bool CloseDescendants(Node *node){ - timer_start("CloseDescendants"); - hash_set memo; - bool res = CloseDescendantsRec(memo,node); - timer_stop("CloseDescendants"); - return res; - } - - bool Contains(Node *node){ - timer_start("Contains"); - bool res = !IsCovered(node); - timer_stop("Contains"); - return res; - } - - bool Candidate(Node *node){ - timer_start("Candidate"); - bool res = !IsCovered(node) && !dominated(node); - timer_stop("Candidate"); - return res; - } - - void SetDominated(Node *node){ - dominated(node) = true; - } - - bool CouldCover(Node *covered, Node *covering){ -#ifdef NO_CONJ_ON_SIMPLE_LOOPS - // Forsimple loops, we rely on propagation, not covering - if(simple_loops.find(covered->map) != simple_loops.end()) - return false; -#endif -#ifdef UNDERAPPROX_NODES - // if(parent->underapprox_map.find(covering) != parent->underapprox_map.end()) - // return parent->underapprox_map[covering] == covered; -#endif - if(CoverOrder(covering,covered) - && !IsCovered(covering)){ - RPFP::Transformer f(covering->Annotation); f.SetEmpty(); -#if defined(TOP_DOWN) || defined(EFFORT_BOUNDED_STRAT) - if(parent->StratifiedInlining) - return true; -#endif - return !covering->Annotation.SubsetEq(f); - } - return false; - } - - bool ContainsCex(Node *node, Counterexample &cex){ - expr val = cex.get_tree()->Eval(cex.get_root()->Outgoing,node->Annotation.Formula); - return eq(val,parent->ctx.bool_val(true)); - } - - /** We conjecture that the annotations of similar nodes may be - true of this one. We start with later nodes, on the - principle that their annotations are likely weaker. We save - a counterexample -- if annotations of other nodes are true - in this counterexample, we don't need to check them. - */ - -#ifndef UNDERAPPROX_NODES - bool Conjecture(Node *node){ - std::vector &insts = insts_of_node(node->map); - Counterexample cex; - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CouldCover(node,other)){ - reporter()->Forcing(node,other); - if(cex.get_tree() && !ContainsCex(other,cex)) - continue; - cex.clear(); - if(parent->ProveConjecture(node,other->Annotation,other,&cex)) - if(CloseDescendants(node)) - return true; - } - } - cex.clear(); - return false; - } -#else - bool Conjecture(Node *node){ - std::vector &insts = insts_of_node(node->map); - Counterexample cex; - RPFP::Transformer Bound = node->Annotation; - Bound.SetEmpty(); - bool some_other = false; - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CouldCover(node,other)){ - reporter()->Forcing(node,other); - Bound.UnionWith(other->Annotation); - some_other = true; - } - } - if(some_other && parent->ProveConjecture(node,Bound)){ - CloseDescendants(node); - return true; - } - return false; - } -#endif - - void Update(Node *node, const RPFP::Transformer &update){ - RemoveCoveringsBy(node); - some_updates = true; - } - -#ifndef UNDERAPPROX_NODES - Node *GetSimilarNode(Node *node){ - if(!some_updates) - return 0; - std::vector &insts = insts_of_node(node->map); - for(int i = insts.size()-1; i >= 0; i--){ - Node *other = insts[i]; - if(dominates(node,other)) - if(CoverOrder(other,node) - && !IsCovered(other)) - return other; - } - return 0; - } -#else - Node *GetSimilarNode(Node *node){ - if(!some_updates) - return 0; - std::vector &insts = insts_of_node(node->map); - for(int i = insts.size() - 1; i >= 0; i--){ - Node *other = insts[i]; - if(CoverOrder(other,node) - && !IsCovered(other)) - return other; - } - return 0; - } -#endif - - bool Dominates(Node * node, Node *other){ - if(node == other) return false; - if(other->Outgoing->map == 0) return true; - if(node->Outgoing->map == other->Outgoing->map){ - assert(node->Outgoing->Children.size() == other->Outgoing->Children.size()); - for(unsigned i = 0; i < node->Outgoing->Children.size(); i++){ - Node *nc = node->Outgoing->Children[i]; - Node *oc = other->Outgoing->Children[i]; - if(!(nc == oc || oc->Outgoing->map ==0 || dominates(nc,oc))) - return false; - } - return true; - } - return false; - } - - void Add(Node *node){ - std::vector &insts = insts_of_node(node->map); - for(unsigned i = 0; i < insts.size(); i++){ - Node *other = insts[i]; - if(Dominates(node,other)){ - cm[node].dominates.insert(other); - cm[other].dominated = true; - reporter()->Dominates(node, other); - } - } - } - - }; - - /* This expansion heuristic makes use of a previuosly obtained - counterexample as a guide. This is for use in abstraction - refinement schemes.*/ - - class ReplayHeuristic : public Heuristic { - - Counterexample old_cex; - public: - ReplayHeuristic(RPFP *_rpfp, Counterexample &_old_cex) - : Heuristic(_rpfp) - { - old_cex.swap(_old_cex); // take ownership from caller - } - - ~ReplayHeuristic(){ - } - - // Maps nodes of derivation tree into old cex - hash_map cex_map; - - void Done() { - cex_map.clear(); - old_cex.clear(); - } - - void ShowNodeAndChildren(Node *n){ - std::cout << n->Name.name() << ": "; - std::vector &chs = n->Outgoing->Children; - for(unsigned i = 0; i < chs.size(); i++) - std::cout << chs[i]->Name.name() << " " ; - std::cout << std::endl; - } - - // HACK: When matching relation names, we drop suffixes used to - // make the names unique between runs. For compatibility - // with boggie, we drop suffixes beginning with @@ - std::string BaseName(const std::string &name){ - int pos = name.find("@@"); - if(pos >= 1) - return name.substr(0,pos); - return name; - } - - Node *MatchNode(Node *node){ - if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node - Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! - if(cex_map.find(parent) == cex_map.end()) - throw "catastrophe in ReplayHeuristic::ChooseExpand"; - Node *old_parent = cex_map[parent]; - std::vector &chs = parent->Outgoing->Children; - if(old_parent && old_parent->Outgoing){ - std::vector &old_chs = old_parent->Outgoing->Children; - for(unsigned i = 0, j=0; i < chs.size(); i++){ - if(j < old_chs.size() && BaseName(chs[i]->Name.name().str()) == BaseName(old_chs[j]->Name.name().str())) - cex_map[chs[i]] = old_chs[j++]; - else { - std::cerr << "WARNING: duality: unmatched child: " << chs[i]->Name.name() << std::endl; - cex_map[chs[i]] = 0; - } - } - goto matching_done; - } - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = 0; - } - matching_done: - return cex_map[node]; - } - - int UseNode(Node *node){ - if (!old_cex.get_tree()) - return 0; - Node *old_node = MatchNode(node); - if(!old_node) - return 0; - return old_cex.get_tree()->Empty(old_node) ? -1 : 1; - } - - virtual void ChooseExpand(const std::set &choices, std::set &best, bool high_priority, bool best_only){ - if(cex_map.empty()) - cex_map[*(choices.begin())] = old_cex.get_root(); // match the root nodes - if(!high_priority || !old_cex.get_tree()){ - Heuristic::ChooseExpand(choices,best,false); - return; - } - // first, try to match the derivatino tree nodes to the old cex - std::set matched, unmatched; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it); - Node *old_node = MatchNode(node); - if(!old_node) - unmatched.insert(node); - else if(old_cex.get_tree()->Empty(old_node)) - unmatched.insert(node); - else - matched.insert(node); - } - if (matched.empty() && !high_priority) - Heuristic::ChooseExpand(unmatched,best,false); - else - Heuristic::ChooseExpand(matched,best,false); - } - }; - - - class LocalHeuristic : public Heuristic { - - RPFP::Node *old_node; - public: - LocalHeuristic(RPFP *_rpfp) - : Heuristic(_rpfp) - { - old_node = 0; - } - - void SetOldNode(RPFP::Node *_old_node){ - old_node = _old_node; - cex_map.clear(); - } - - // Maps nodes of derivation tree into old subtree - hash_map cex_map; - - virtual void ChooseExpand(const std::set &choices, std::set &best, bool, bool){ - if(old_node == 0){ - Heuristic::ChooseExpand(choices,best); - return; - } - // first, try to match the derivatino tree nodes to the old cex - std::set matched, unmatched; - for(std::set::iterator it = choices.begin(), en = choices.end(); it != en; ++it){ - Node *node = (*it); - if(cex_map.empty()) - cex_map[node] = old_node; // match the root nodes - if(cex_map.find(node) == cex_map.end()){ // try to match an unmatched node - Node *parent = node->Incoming[0]->Parent; // assumes we are a tree! - if(cex_map.find(parent) == cex_map.end()) - throw "catastrophe in ReplayHeuristic::ChooseExpand"; - Node *old_parent = cex_map[parent]; - std::vector &chs = parent->Outgoing->Children; - if(old_parent && old_parent->Outgoing){ - std::vector &old_chs = old_parent->Outgoing->Children; - if(chs.size() == old_chs.size()){ - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = old_chs[i]; - goto matching_done; - } - else - std::cout << "derivation tree does not match old cex" << std::endl; - } - for(unsigned i = 0; i < chs.size(); i++) - cex_map[chs[i]] = 0; - } - matching_done: - Node *old_node = cex_map[node]; - if(!old_node) - unmatched.insert(node); - else if(old_node != node->map) - unmatched.insert(node); - else - matched.insert(node); - } - Heuristic::ChooseExpand(unmatched,best); - } - }; - - /** This proposer class generates conjectures based on the - unwinding generated by a previous solver. The assumption is - that the provious solver was working on a different - abstraction of the same system. The trick is to adapt the - annotations in the old unwinding to the new predicates. We - start by generating a map from predicates and parameters in - the old problem to the new. - - HACK: mapping is done by cheesy name comparison. - */ - - class HistoryProposer : public Proposer - { - Duality *old_solver; - Duality *new_solver; - hash_map > conjectures; - - public: - /** Construct a history solver. */ - HistoryProposer(Duality *_old_solver, Duality *_new_solver) - : old_solver(_old_solver), new_solver(_new_solver) { - - // tricky: names in the axioms may have changed -- map them - hash_set &old_constants = old_solver->unwinding->ls->get_constants(); - hash_set &new_constants = new_solver->rpfp->ls->get_constants(); - hash_map cmap; - for(hash_set::iterator it = new_constants.begin(), en = new_constants.end(); it != en; ++it) - cmap[GetKey(*it)] = *it; - hash_map bckg_map; - for(hash_set::iterator it = old_constants.begin(), en = old_constants.end(); it != en; ++it){ - func_decl f = new_solver->ctx.translate(*it); // move to new context - if(cmap.find(GetKey(f)) != cmap.end()) - bckg_map[f] = cmap[GetKey(f)]; - // else - // std::cout << "constant not matched\n"; - } - - RPFP *old_unwinding = old_solver->unwinding; - hash_map > pred_match; - - // index all the predicates in the old unwinding - for(unsigned i = 0; i < old_unwinding->nodes.size(); i++){ - Node *node = old_unwinding->nodes[i]; - std::string key = GetKey(node); - pred_match[key].push_back(node); - } - - // match with predicates in the new RPFP - RPFP *rpfp = new_solver->rpfp; - for(unsigned i = 0; i < rpfp->nodes.size(); i++){ - Node *node = rpfp->nodes[i]; - std::string key = GetKey(node); - std::vector &matches = pred_match[key]; - for(unsigned j = 0; j < matches.size(); j++) - MatchNodes(node,matches[j],bckg_map); - } - } - - virtual std::vector &Propose(Node *node){ - return conjectures[node->map]; - } - - virtual ~HistoryProposer(){ - }; - - private: - void MatchNodes(Node *new_node, Node *old_node, hash_map &bckg_map){ - if(old_node->Annotation.IsFull()) - return; // don't conjecture true! - hash_map var_match; - std::vector &new_params = new_node->Annotation.IndParams; - // Index the new parameters by their keys - for(unsigned i = 0; i < new_params.size(); i++) - var_match[GetKey(new_params[i])] = new_params[i]; - RPFP::Transformer &old = old_node->Annotation; - std::vector from_params = old.IndParams; - for(unsigned j = 0; j < from_params.size(); j++) - from_params[j] = new_solver->ctx.translate(from_params[j]); // get in new context - std::vector to_params = from_params; - for(unsigned j = 0; j < to_params.size(); j++){ - std::string key = GetKey(to_params[j]); - if(var_match.find(key) == var_match.end()){ - // std::cout << "unmatched parameter!\n"; - return; - } - to_params[j] = var_match[key]; - } - expr fmla = new_solver->ctx.translate(old.Formula); // get in new context - fmla = new_solver->rpfp->SubstParams(old.IndParams,to_params,fmla); // substitute parameters - hash_map memo; - fmla = new_solver->rpfp->SubstRec(memo,bckg_map,fmla); // substitute background constants - RPFP::Transformer new_annot = new_node->Annotation; - new_annot.Formula = fmla; - conjectures[new_node].push_back(new_annot); - } - - // We match names by removing suffixes beginning with double at sign - - std::string GetKey(Node *node){ - return GetKey(node->Name); - } - - std::string GetKey(const expr &var){ - return GetKey(var.decl()); - } - - std::string GetKey(const func_decl &f){ - std::string name = f.name().str(); - int idx = name.find("@@"); - if(idx >= 0) - name.erase(idx); - return name; - } - }; - }; - - static int stop_event = -1; - - class StreamReporter : public Reporter { - std::ostream &s; - public: - StreamReporter(RPFP *_rpfp, std::ostream &_s) - : Reporter(_rpfp), s(_s) {event = 0; depth = -1;} - int event; - int depth; - void ev(){ - if(stop_event == event){ - std::cout << "stop!\n"; - } - s << "[" << event++ << "]" ; - } - virtual void Extend(RPFP::Node *node){ - ev(); s << "node " << node->number << ": " << node->Name.name(); - std::vector &rps = node->Outgoing->Children; - for(unsigned i = 0; i < rps.size(); i++) - s << " " << rps[i]->number; - s << std::endl; - } - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ - ev(); s << "update " << node->number << " " << node->Name.name() << ": "; - rpfp->Summarize(update.Formula); - if(depth > 0) s << " (depth=" << depth << ")"; - if(eager) s << " (eager)"; - s << std::endl; - } - virtual void Bound(RPFP::Node *node){ - ev(); s << "check " << node->number << std::endl; - } - virtual void Expand(RPFP::Edge *edge){ - RPFP::Node *node = edge->Parent; - ev(); s << "expand " << node->map->number << " " << node->Name.name(); - if(depth > 0) s << " (depth=" << depth << ")"; - s << std::endl; - } - virtual void Depth(int d){ - depth = d; - } - virtual void AddCover(RPFP::Node *covered, std::vector &covering){ - ev(); s << "cover " << covered->Name.name() << ": " << covered->number << " by "; - for(unsigned i = 0; i < covering.size(); i++) - s << covering[i]->number << " "; - s << std::endl; - } - virtual void RemoveCover(RPFP::Node *covered, RPFP::Node *covering){ - ev(); s << "uncover " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; - } - virtual void Forcing(RPFP::Node *covered, RPFP::Node *covering){ - ev(); s << "forcing " << covered->Name.name() << ": " << covered->number << " by " << covering->number << std::endl; - } - virtual void Conjecture(RPFP::Node *node, const RPFP::Transformer &t){ - ev(); s << "conjecture " << node->number << " " << node->Name.name() << ": "; - rpfp->Summarize(t.Formula); - s << std::endl; - } - virtual void Dominates(RPFP::Node *node, RPFP::Node *other){ - ev(); s << "dominates " << node->Name.name() << ": " << node->number << " > " << other->number << std::endl; - } - virtual void InductionFailure(RPFP::Edge *edge, const std::vector &children){ - ev(); s << "induction failure: " << edge->Parent->Name.name() << ", children ="; - for(unsigned i = 0; i < children.size(); i++) - s << " " << children[i]->number; - s << std::endl; - } - virtual void UpdateUnderapprox(RPFP::Node *node, const RPFP::Transformer &update){ - ev(); s << "underapprox " << node->number << " " << node->Name.name() << ": " << update.Formula << std::endl; - } - virtual void Reject(RPFP::Edge *edge, const std::vector &children){ - ev(); s << "reject " << edge->Parent->number << " " << edge->Parent->Name.name() << ": "; - for(unsigned i = 0; i < children.size(); i++) - s << " " << children[i]->number; - s << std::endl; - } - virtual void Message(const std::string &msg){ - ev(); s << "msg " << msg << std::endl; - } - - }; - - - class DualityDepthBounded : public Solver { - - Duality *duality; - context &ctx; // Z3 context - solver &slvr; // Z3 solver - - public: - DualityDepthBounded(RPFP *_rpfp) : - ctx(_rpfp->ctx), - slvr(_rpfp->slvr()){ - rpfp = _rpfp; - DepthBoundRPFP(); - duality = alloc(Duality,drpfp); - } - - ~DualityDepthBounded(){ - dealloc(duality); - delete drpfp; - } - - bool Solve(){ - int depth_bound = 10; - bool res; - SetMaxDepthRPFP(depth_bound); - duality->PreSolve(); - while(true){ - res = duality->SolveMain(); - if(!res || GetSolution()) - break; - depth_bound++; - SetMaxDepthRPFP(depth_bound); - res = duality->RecheckBounds(); - if(!res) - break; - } - duality->PostSolve(); - if(!res) - ConvertCex(); - return res; - } - - Counterexample &GetCounterexample(){ - return cex; - } - - bool SetOption(const std::string &option, const std::string &value){ - return duality->SetOption(option,value); - } - - virtual void LearnFrom(Solver *old_solver){ - DualityDepthBounded *old = dynamic_cast(old_solver); - if(old){ - duality->LearnFrom(old->duality); - } - } - - bool IsResultRecursionBounded(){ - return duality->IsResultRecursionBounded(); - } - - void Cancel(){ - duality->Cancel(); - } - - typedef RPFP::Node Node; - typedef RPFP::Edge Edge; - RPFP *rpfp, *drpfp; - hash_map db_map, db_rev_map; - hash_map db_edge_rev_map; - std::vector db_saved_bounds; - Counterexample cex; - - expr AddParamToRels(hash_map &memo, hash_map &rmap, const expr &p, const expr &t) { - std::pair foo(t,expr(ctx)); - std::pair::iterator, bool> bar = memo.insert(foo); - expr &res = bar.first->second; - if(!bar.second) return res; - - if (t.is_app()) - { - func_decl f = t.decl(); - std::vector args; - int nargs = t.num_args(); - for(int i = 0; i < nargs; i++) - args.push_back(AddParamToRels(memo, rmap, p, t.arg(i))); - hash_map::iterator rit = rmap.find(f); - if(rit != rmap.end()){ - args.push_back(p); - res = (rit->second)(args); - res = ctx.make(And,res,ctx.make(Geq,p,ctx.int_val(0))); - } - else - res = f(args); - } - else if (t.is_quantifier()) - { - expr body = AddParamToRels(memo, rmap, p, t.body()); - res = clone_quantifier(t, body); - } - else res = t; - return res; - } - - - void DepthBoundRPFP(){ - drpfp = new RPFP(rpfp->ls); - expr dvar = ctx.int_const("@depth"); - expr dmax = ctx.int_const("@depth_max"); - for(unsigned i = 0; i < rpfp->nodes.size(); i++){ - Node *node = rpfp->nodes[i]; - std::vector arg_sorts; - const std::vector ¶ms = node->Annotation.IndParams; - for(unsigned j = 0; j < params.size(); j++) - arg_sorts.push_back(params[j].get_sort()); - arg_sorts.push_back(ctx.int_sort()); - std::string new_name = std::string("@db@") + node->Name.name().str(); - func_decl f = ctx.function(new_name.c_str(),arg_sorts.size(), VEC2PTR(arg_sorts),ctx.bool_sort()); - std::vector args = params; - args.push_back(dvar); - expr pat = f(args); - Node *dnode = drpfp->CreateNode(pat); - db_map[node] = dnode; - db_rev_map[dnode] = node; - expr bound_fmla = node->Bound.Formula; - if(!eq(bound_fmla,ctx.bool_val(true))){ - bound_fmla = implies(dvar == dmax,bound_fmla); - dnode->Bound.Formula = bound_fmla; - } - db_saved_bounds.push_back(bound_fmla); - // dnode->Annotation.Formula = ctx.make(And,node->Annotation.Formula,ctx.make(Geq,dvar,ctx.int_val(0))); - } - for(unsigned i = 0; i < rpfp->edges.size(); i++){ - Edge *edge = rpfp->edges[i]; - std::vector new_children; - std::vector new_relparams; - hash_map rmap; - for(unsigned j = 0; j < edge->Children.size(); j++){ - Node *ch = edge->Children[j]; - Node *nch = db_map[ch]; - func_decl f = nch->Name; - func_decl sf = drpfp->NumberPred(f,j); - new_children.push_back(nch); - new_relparams.push_back(sf); - rmap[edge->F.RelParams[j]] = sf; - } - std::vector new_indparams = edge->F.IndParams; - new_indparams.push_back(dvar); - hash_map memo; - expr new_fmla = AddParamToRels(memo,rmap,ctx.make(Sub,dvar,ctx.int_val(1)),edge->F.Formula); - RPFP::Transformer new_t = drpfp->CreateTransformer(new_relparams,new_indparams,new_fmla); - Node *new_parent = db_map[edge->Parent]; - db_edge_rev_map[drpfp->CreateEdge(new_parent,new_t,new_children)] = edge; - } - } - - void SetMaxDepthRPFP(int depth){ - hash_map subst; - expr dmax = ctx.int_const("@depth_max"); - subst[dmax] = ctx.int_val(depth); - for(unsigned i = 0; i < drpfp->nodes.size(); i++){ - Node *node = drpfp->nodes[i]; - expr fmla = db_saved_bounds[i]; - fmla = drpfp->SubstRec(subst,fmla); - node->Bound.Formula = fmla; - } - } - - void ConvertCex(){ - cex.clear(); - RPFP *tree = new RPFP(rpfp->ls); - Node *root; - Counterexample &dctx = duality->GetCounterexample(); - hash_map ctx_node_map; - for(unsigned i = 0; i < dctx.get_tree()->nodes.size(); i++){ - Node *dnode = dctx.get_tree()->nodes[i]; - Node *onode = db_rev_map[dnode->map->map]; - Node *node = tree->CloneNode(onode); - node->number = dnode->number; // numbers have to match for model to make sense - ctx_node_map[dnode] = node; - if(dnode == dctx.get_root()) - root = node; - } - for(unsigned i = 0; i < dctx.get_tree()->edges.size(); i++){ - Edge *dedge = dctx.get_tree()->edges[i]; - Edge *oedge = db_edge_rev_map[dedge->map]; - Node *parent = ctx_node_map[dedge->Parent]; - std::vector chs; - for(unsigned j = 0; j < dedge->Children.size(); j++) - chs.push_back(ctx_node_map[dedge->Children[j]]); - Edge *edge = tree->CreateEdge(parent,oedge->F,chs); - edge->number = dedge->number; // numbers have to match for model to make sense - edge->map = oedge; - } - tree->dualModel = dctx.get_tree()->dualModel; - cex.set(tree,root); - } - - bool GetSolution(){ - for(unsigned i = 0; i < rpfp->nodes.size(); i++) - if(!drpfp->nodes[i]->Annotation.SubsetEq(rpfp->nodes[i]->Bound)) - return false; - expr dvar = ctx.int_const("@depth"); - hash_map subst; - subst[dvar] = ctx.int_val(INT_MAX); - for(unsigned i = 0; i < rpfp->nodes.size(); i++){ - expr fmla = drpfp->nodes[i]->Annotation.Formula; - fmla = drpfp->SubstRec(subst,fmla); - fmla = fmla.simplify(); - rpfp->nodes[i]->Annotation.Formula = fmla; - } - return true; - } - - void UndoDepthBoundRPFP(){ -#if 0 - if(cex.get_tree()){ - // here, need to map the cex back... - } - // also need to map the proof back, but we don't... -#endif - } - }; - - Solver *Solver::Create(const std::string &solver_class, RPFP *rpfp){ - // Solver *s = alloc(DualityDepthBounded,rpfp); - Solver *s = alloc(Duality,rpfp); - return s; - } - - Reporter *CreateStdoutReporter(RPFP *rpfp){ - return new StreamReporter(rpfp, std::cout); - } - - class ConjectureFileReporter : public Reporter { - std::ofstream s; - public: - ConjectureFileReporter(RPFP *_rpfp, const std::string &fname) - : Reporter(_rpfp), s(fname.c_str()) {} - virtual void Update(RPFP::Node *node, const RPFP::Transformer &update, bool eager){ - s << "(define-fun " << node->Name.name() << " ("; - for(unsigned i = 0; i < update.IndParams.size(); i++){ - if(i != 0) - s << " "; - s << "(" << update.IndParams[i] << " " << update.IndParams[i].get_sort() << ")"; - } - s << ") Bool \n"; - s << update.Formula << ")\n"; - s << std::endl; - } - }; - - Reporter *CreateConjectureFileReporter(RPFP *rpfp, const std::string &fname){ - return new ConjectureFileReporter(rpfp, fname); - } - -} - diff --git a/src/duality/duality_wrapper.cpp b/src/duality/duality_wrapper.cpp deleted file mode 100755 index 4493beddf..000000000 --- a/src/duality/duality_wrapper.cpp +++ /dev/null @@ -1,744 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - wrapper.cpp - - Abstract: - - wrap various objects in the style expected by duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "duality/duality_wrapper.h" -#include -#include "smt/smt_solver.h" -#include "interp/iz3interp.h" -#include "util/statistics.h" -#include "ast/expr_abstract.h" -#include "util/stopwatch.h" -#include "model/model_smt2_pp.h" -#include "qe/qe_lite.h" - -namespace Duality { - - solver::solver(Duality::context& c, bool _extensional, bool models) : object(c), the_model(c) { - params_ref p; - p.set_bool("proof", true); // this is currently useless - if(models) - p.set_bool("model", true); - p.set_bool("unsat_core", true); - bool mbqi = c.get_config().get().get_bool("mbqi",true); - p.set_bool("mbqi",mbqi); // just to test - p.set_str("mbqi.id","itp"); // use mbqi for quantifiers in interpolants - p.set_uint("mbqi.max_iterations",1); // use mbqi for quantifiers in interpolants - extensional = mbqi && (true || _extensional); - if(extensional) - p.set_bool("array.extensional",true); - scoped_ptr sf = mk_smt_solver_factory(); - m_solver = (*sf)(m(), p, true, true, true, ::symbol::null); - m_solver->updt_params(p); // why do we have to do this? - canceled = false; - m_mode = m().proof_mode(); - } - - expr context::constant(const std::string &name, const sort &ty){ - symbol s = str_symbol(name.c_str()); - return cook(m().mk_const(m().mk_const_decl(s, ty))); - } - - expr context::make(decl_kind op, int n, ::expr **args){ - switch(op) { - case True: return mki(m_basic_fid,OP_TRUE,n,args); - case False: return mki(m_basic_fid,OP_FALSE,n,args); - case Equal: return mki(m_basic_fid,OP_EQ,n,args); - case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args); - case Ite: return mki(m_basic_fid,OP_ITE,n,args); - case And: return mki(m_basic_fid,OP_AND,n,args); - case Or: return mki(m_basic_fid,OP_OR,n,args); - case Iff: return mki(m_basic_fid,OP_IFF,n,args); - case Xor: return mki(m_basic_fid,OP_XOR,n,args); - case Not: return mki(m_basic_fid,OP_NOT,n,args); - case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args); - case Oeq: return mki(m_basic_fid,OP_OEQ,n,args); - case Interp: return mki(m_basic_fid,OP_INTERP,n,args); - case Leq: return mki(m_arith_fid,OP_LE,n,args); - case Geq: return mki(m_arith_fid,OP_GE,n,args); - case Lt: return mki(m_arith_fid,OP_LT,n,args); - case Gt: return mki(m_arith_fid,OP_GT,n,args); - case Plus: return mki(m_arith_fid,OP_ADD,n,args); - case Sub: return mki(m_arith_fid,OP_SUB,n,args); - case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args); - case Times: return mki(m_arith_fid,OP_MUL,n,args); - case Div: return mki(m_arith_fid,OP_DIV,n,args); - case Idiv: return mki(m_arith_fid,OP_IDIV,n,args); - case Rem: return mki(m_arith_fid,OP_REM,n,args); - case Mod: return mki(m_arith_fid,OP_MOD,n,args); - case Power: return mki(m_arith_fid,OP_POWER,n,args); - case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args); - case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args); - case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args); - case Store: return mki(m_array_fid,OP_STORE,n,args); - case Select: return mki(m_array_fid,OP_SELECT,n,args); - case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args); - case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args); - case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args); - case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args); - case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args); - case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args); - case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args); - case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args); - case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args); - default: - assert(0); - return expr(*this); - } - } - - expr context::mki(family_id fid, ::decl_kind dk, int n, ::expr **args){ - return cook(m().mk_app(fid, dk, 0, 0, n, (::expr **)args)); - } - - expr context::make(decl_kind op, const std::vector &args){ - static std::vector< ::expr*> a(10); - if(a.size() < args.size()) - a.resize(args.size()); - for(unsigned i = 0; i < args.size(); i++) - a[i] = to_expr(args[i].raw()); - return make(op,args.size(), args.size() ? VEC2PTR(a) : 0); - } - - expr context::make(decl_kind op){ - return make(op,0,0); - } - - expr context::make(decl_kind op, const expr &arg0){ - ::expr *a = to_expr(arg0.raw()); - return make(op,1,&a); - } - - expr context::make(decl_kind op, const expr &arg0, const expr &arg1){ - ::expr *args[2]; - args[0] = to_expr(arg0.raw()); - args[1] = to_expr(arg1.raw()); - return make(op,2,args); - } - - expr context::make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2){ - ::expr *args[3]; - args[0] = to_expr(arg0.raw()); - args[1] = to_expr(arg1.raw()); - args[2] = to_expr(arg2.raw()); - return make(op,3,args); - } - - expr context::make_quant(decl_kind op, const std::vector &bvs, const expr &body){ - if(bvs.size() == 0) return body; - std::vector< ::expr *> foo(bvs.size()); - - - std::vector< ::symbol> names; - std::vector< ::sort *> types; - std::vector< ::expr *> bound_asts; - unsigned num_bound = bvs.size(); - - for (unsigned i = 0; i < num_bound; ++i) { - app* a = to_app(bvs[i].raw()); - ::symbol s(to_app(a)->get_decl()->get_name()); - names.push_back(s); - types.push_back(m().get_sort(a)); - bound_asts.push_back(a); - } - expr_ref abs_body(m()); - expr_abstract(m(), 0, num_bound, VEC2PTR(bound_asts), to_expr(body.raw()), abs_body); - expr_ref result(m()); - result = m().mk_quantifier( - op == Forall, - names.size(), VEC2PTR(types), VEC2PTR(names), abs_body.get(), - 0, - ::symbol(), - ::symbol(), - 0, 0, - 0, 0 - ); - return cook(result.get()); - } - - expr context::make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body){ - if(_sorts.size() == 0) return body; - - - std::vector< ::symbol> names; - std::vector< ::sort *> types; - std::vector< ::expr *> bound_asts; - unsigned num_bound = _sorts.size(); - - for (unsigned i = 0; i < num_bound; ++i) { - names.push_back(_names[i]); - types.push_back(to_sort(_sorts[i].raw())); - } - expr_ref result(m()); - result = m().mk_quantifier( - op == Forall, - names.size(), VEC2PTR(types), VEC2PTR(names), to_expr(body.raw()), - 0, - ::symbol(), - ::symbol(), - 0, 0, - 0, 0 - ); - return cook(result.get()); - } - - - decl_kind func_decl::get_decl_kind() const { - return ctx().get_decl_kind(*this); - } - - decl_kind context::get_decl_kind(const func_decl &t){ - ::func_decl *d = to_func_decl(t.raw()); - if (null_family_id == d->get_family_id()) - return Uninterpreted; - // return (opr)d->get_decl_kind(); - if (m_basic_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_TRUE: return True; - case OP_FALSE: return False; - case OP_EQ: return Equal; - case OP_DISTINCT: return Distinct; - case OP_ITE: return Ite; - case OP_AND: return And; - case OP_OR: return Or; - case OP_IFF: return Iff; - case OP_XOR: return Xor; - case OP_NOT: return Not; - case OP_IMPLIES: return Implies; - case OP_OEQ: return Oeq; - case OP_INTERP: return Interp; - default: - return OtherBasic; - } - } - if (m_arith_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_LE: return Leq; - case OP_GE: return Geq; - case OP_LT: return Lt; - case OP_GT: return Gt; - case OP_ADD: return Plus; - case OP_SUB: return Sub; - case OP_UMINUS: return Uminus; - case OP_MUL: return Times; - case OP_DIV: return Div; - case OP_IDIV: return Idiv; - case OP_REM: return Rem; - case OP_MOD: return Mod; - case OP_POWER: return Power; - case OP_TO_REAL: return ToReal; - case OP_TO_INT: return ToInt; - case OP_IS_INT: return IsInt; - default: - return OtherArith; - } - } - if (m_array_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_STORE: return Store; - case OP_SELECT: return Select; - case OP_CONST_ARRAY: return ConstArray; - case OP_ARRAY_DEFAULT: return ArrayDefault; - case OP_ARRAY_MAP: return ArrayMap; - case OP_SET_UNION: return SetUnion; - case OP_SET_INTERSECT: return SetIntersect; - case OP_SET_DIFFERENCE: return SetDifference; - case OP_SET_COMPLEMENT: return SetComplement; - case OP_SET_SUBSET: return SetSubSet; - case OP_AS_ARRAY: return AsArray; - default: - return OtherArray; - } - } - - return Other; - } - - - sort_kind context::get_sort_kind(const sort &s){ - family_id fid = to_sort(s.raw())->get_family_id(); - ::decl_kind k = to_sort(s.raw())->get_decl_kind(); - if (m().is_uninterp(to_sort(s.raw()))) { - return UninterpretedSort; - } - else if (fid == m_basic_fid && k == BOOL_SORT) { - return BoolSort; - } - else if (fid == m_arith_fid && k == INT_SORT) { - return IntSort; - } - else if (fid == m_arith_fid && k == REAL_SORT) { - return RealSort; - } - else if (fid == m_array_fid && k == ARRAY_SORT) { - return ArraySort; - } - else { - return UnknownSort; - } - } - - expr func_decl::operator()(unsigned n, expr const * args) const { - std::vector< ::expr *> _args(n); - for(unsigned i = 0; i < n; i++) - _args[i] = to_expr(args[i].raw()); - return ctx().cook(m().mk_app(to_func_decl(raw()),n,VEC2PTR(_args))); - } - - int solver::get_num_decisions(){ - ::statistics st; - m_solver->collect_statistics(st); - std::ostringstream ss; - st.display(ss); - std::string stats = ss.str(); - int pos = stats.find("decisions:"); - if(pos < 0) return 0; // for some reason, decisions are not reported if there are none - pos += 10; - int end = stats.find('\n',pos); - std::string val = stats.substr(pos,end-pos); - return atoi(val.c_str()); - } - - void context::print_expr(std::ostream &s, const ast &e){ - s << mk_pp(e.raw(), m()); - } - - - expr expr::simplify(const params &_p) const { - ::expr * a = to_expr(raw()); - params_ref p = _p.get(); - th_rewriter m_rw(m(), p); - expr_ref result(m()); - m_rw(a, result); - return ctx().cook(result); - } - - expr expr::simplify() const { - params p; - return simplify(p); - } - - expr context::make_var(int idx, const sort &s){ - ::sort * a = to_sort(s.raw()); - return cook(m().mk_var(idx,a)); - } - - - expr expr::qe_lite() const { - ::qe_lite qe(m(), params_ref()); - expr_ref result(to_expr(raw()),m()); - proof_ref pf(m()); - qe(result,pf); - return ctx().cook(result); - } - - expr expr::qe_lite(const std::set &idxs, bool index_of_bound) const { - ::qe_lite qe(m(), params_ref()); - expr_ref result(to_expr(raw()),m()); - proof_ref pf(m()); - uint_set uis; - for(std::set::const_iterator it=idxs.begin(), en = idxs.end(); it != en; ++it) - uis.insert(*it); - qe(uis,index_of_bound,result); - return ctx().cook(result); - } - - expr clone_quantifier(const expr &q, const expr &b){ - return q.ctx().cook(q.m().update_quantifier(to_quantifier(q.raw()), to_expr(b.raw()))); - } - - expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns){ - quantifier *thing = to_quantifier(q.raw()); - bool is_forall = thing->is_forall(); - unsigned num_patterns = patterns.size(); - std::vector< ::expr *> _patterns(num_patterns); - for(unsigned i = 0; i < num_patterns; i++) - _patterns[i] = to_expr(patterns[i].raw()); - return q.ctx().cook(q.m().update_quantifier(thing, is_forall, num_patterns, VEC2PTR(_patterns), to_expr(b.raw()))); - } - - expr clone_quantifier(decl_kind dk, const expr &q, const expr &b){ - quantifier *thing = to_quantifier(q.raw()); - bool is_forall = dk == Forall; - return q.ctx().cook(q.m().update_quantifier(thing, is_forall, to_expr(b.raw()))); - } - - void expr::get_patterns(std::vector &pats) const { - quantifier *thing = to_quantifier(raw()); - unsigned num_patterns = thing->get_num_patterns(); - :: expr * const *it = thing->get_patterns(); - pats.resize(num_patterns); - for(unsigned i = 0; i < num_patterns; i++) - pats[i] = expr(ctx(),it[i]); - } - - - unsigned func_decl::arity() const { - return (to_func_decl(raw())->get_arity()); - } - - sort func_decl::domain(unsigned i) const { - return sort(ctx(),(to_func_decl(raw())->get_domain(i))); - } - - sort func_decl::range() const { - return sort(ctx(),(to_func_decl(raw())->get_range())); - } - - func_decl context::fresh_func_decl(char const * prefix, const std::vector &domain, sort const & range){ - std::vector < ::sort * > _domain(domain.size()); - for(unsigned i = 0; i < domain.size(); i++) - _domain[i] = to_sort(domain[i].raw()); - ::func_decl* d = m().mk_fresh_func_decl(prefix, - _domain.size(), - VEC2PTR(_domain), - to_sort(range.raw())); - return func_decl(*this,d); - } - - func_decl context::fresh_func_decl(char const * prefix, sort const & range){ - ::func_decl* d = m().mk_fresh_func_decl(prefix, - 0, - 0, - to_sort(range.raw())); - return func_decl(*this,d); - } - - - -#if 0 - - - lbool interpolating_solver::interpolate( - const std::vector &assumptions, - std::vector &interpolants, - model &model, - Z3_literals &labels, - bool incremental) - { - Z3_model _model = 0; - Z3_literals _labels = 0; - Z3_lbool lb; - std::vector _assumptions(assumptions.size()); - std::vector _interpolants(assumptions.size()-1); - for(unsigned i = 0; i < assumptions.size(); i++) - _assumptions[i] = assumptions[i]; - std::vector _theory(theory.size()); - for(unsigned i = 0; i < theory.size(); i++) - _theory[i] = theory[i]; - - lb = Z3_interpolate( - ctx(), - _assumptions.size(), - VEC2PTR(_assumptions), - 0, - 0, - VEC2PTR(_interpolants), - &_model, - &_labels, - incremental, - _theory.size(), - VEC2PTR(_theory)); - - if(lb == Z3_L_FALSE){ - interpolants.resize(_interpolants.size()); - for (unsigned i = 0; i < _interpolants.size(); ++i) { - interpolants[i] = expr(ctx(),_interpolants[i]); - } - } - - if (_model) { - model = iz3wrapper::model(ctx(), _model); - } - - if(_labels){ - labels = _labels; - } - - return lb; - } - -#endif - - static int linearize_assumptions(int num, - TermTree *assumptions, - std::vector > &linear_assumptions, - std::vector &parents){ - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - num = linearize_assumptions(num, assumptions->getChildren()[i], linear_assumptions, parents); - // linear_assumptions[num].push_back(assumptions->getTerm()); - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - parents[assumptions->getChildren()[i]->getNumber()] = num; - parents[num] = SHRT_MAX; // in case we have no parent - linear_assumptions[num].push_back(assumptions->getTerm()); - std::vector &ts = assumptions->getTerms(); - for(unsigned i = 0; i < ts.size(); i++) - linear_assumptions[num].push_back(ts[i]); - return num + 1; - } - - static int unlinearize_interpolants(int num, - TermTree* assumptions, - const std::vector &interpolant, - TermTree * &tree_interpolant) - { - std::vector chs(assumptions->getChildren().size()); - for(unsigned i = 0; i < assumptions->getChildren().size(); i++) - num = unlinearize_interpolants(num, assumptions->getChildren()[i], interpolant,chs[i]); - expr f; - if(num < (int)interpolant.size()) // last interpolant is missing, presumed false - f = interpolant[num]; - tree_interpolant = new TermTree(f,chs); - return num + 1; - } - - - lbool interpolating_solver::interpolate_tree(TermTree *assumptions, - TermTree *&interpolant, - model &model, - literals &labels, - bool incremental - ) - - { - int size = assumptions->number(0); - std::vector > linear_assumptions(size); - std::vector parents(size); - linearize_assumptions(0,assumptions,linear_assumptions,parents); - - ptr_vector< ::ast> _interpolants(size-1); - vector >_assumptions(size); - for(int i = 0; i < size; i++) - for(unsigned j = 0; j < linear_assumptions[i].size(); j++) - _assumptions[i].push_back(linear_assumptions[i][j]); - ::vector _parents; _parents.resize(parents.size()); - for(unsigned i = 0; i < parents.size(); i++) - _parents[i] = parents[i]; - ptr_vector< ::ast> _theory(theory.size()); - for(unsigned i = 0; i < theory.size(); i++) - _theory[i] = theory[i]; - - - if(!incremental){ - push(); - for(unsigned i = 0; i < linear_assumptions.size(); i++) - for(unsigned j = 0; j < linear_assumptions[i].size(); j++) - add(linear_assumptions[i][j]); - } - - check_result res = unsat; - - if(!m_solver->get_proof()) - res = check(); - - if(res == unsat){ - - interpolation_options_struct opts; - if(weak_mode) - opts.set("weak","1"); - - ::ast *proof = m_solver->get_proof(); - try { - iz3interpolate(m(),proof,_assumptions,_parents,_interpolants,_theory,&opts); - } - // If there's an interpolation bug, throw a char * - // exception so duality can catch it and restart. - catch (const interpolation_failure &f) { - throw f.msg(); - } - - std::vector linearized_interpolants(_interpolants.size()); - for(unsigned i = 0; i < _interpolants.size(); i++) - linearized_interpolants[i] = expr(ctx(),_interpolants[i]); - - // since iz3interpolant returns interpolants with one ref count, we decrement here - for(unsigned i = 0; i < _interpolants.size(); i++) - m().dec_ref(_interpolants[i]); - - unlinearize_interpolants(0,assumptions,linearized_interpolants,interpolant); - interpolant->setTerm(ctx().bool_val(false)); - } - - model_ref _m; - m_solver->get_model(_m); - model = Duality::model(ctx(),_m.get()); - -#if 0 - if(_labels){ - labels = _labels; - } -#endif - - if(!incremental) - pop(); - - return (res == unsat) ? l_false : ((res == sat) ? l_true : l_undef); - - } - - - void interpolating_solver::SetWeakInterpolants(bool weak){ - weak_mode = weak; - } - - - void interpolating_solver::SetPrintToFile(const std::string &filename){ - print_filename = filename; - } - - void interpolating_solver::AssertInterpolationAxiom(const expr & t){ - add(t); - theory.push_back(t); - } - - - void interpolating_solver::RemoveInterpolationAxiom(const expr & t){ - // theory.remove(t); - } - - - const char *interpolating_solver::profile(){ - // return Z3_interpolation_profile(ctx()); - return ""; - } - - - static void get_assumptions_rec(stl_ext::hash_set &memo, const proof &pf, std::vector &assumps){ - if(memo.find(pf) != memo.end())return; - memo.insert(pf); - pfrule dk = pf.rule(); - if(dk == PR_ASSERTED){ - expr con = pf.conc(); - assumps.push_back(con); - } - else { - unsigned nprems = pf.num_prems(); - for(unsigned i = 0; i < nprems; i++){ - proof arg = pf.prem(i); - get_assumptions_rec(memo,arg,assumps); - } - } - } - - void proof::get_assumptions(std::vector &assumps){ - stl_ext::hash_set memo; - get_assumptions_rec(memo,*this,assumps); - } - - - void ast::show() const{ - std::cout << mk_pp(raw(), m()) << std::endl; - } - - void model::show() const { - model_smt2_pp(std::cout, m(), *m_model, 0); - std::cout << std::endl; - } - - void model::show_hash() const { - std::ostringstream ss; - model_smt2_pp(ss, m(), *m_model, 0); - hash_space::hash hasher; - unsigned h = hasher(ss.str()); - std::cout << "model hash: " << h << "\n"; - } - - void solver::show() { - unsigned n = m_solver->get_num_assertions(); - if(!n) - return; - ast_smt_pp pp(m()); - for (unsigned i = 0; i < n-1; ++i) - pp.add_assumption(m_solver->get_assertion(i)); - pp.display_smt2(std::cout, m_solver->get_assertion(n-1)); - } - - void solver::print(const char *filename) { - std::ofstream f(filename); - unsigned n = m_solver->get_num_assertions(); - if(!n) - return; - ast_smt_pp pp(m()); - for (unsigned i = 0; i < n-1; ++i) - pp.add_assumption(m_solver->get_assertion(i)); - pp.display_smt2(f, m_solver->get_assertion(n-1)); - } - - - void solver::show_assertion_ids() { -#if 0 - unsigned n = m_solver->get_num_assertions(); - std::cerr << "assertion ids: "; - for (unsigned i = 0; i < n-1; ++i) - std::cerr << " " << m_solver->get_assertion(i)->get_id(); - std::cerr << "\n"; -#else - unsigned n = m_solver->get_num_assertions(); - std::cerr << "assertion ids hash: "; - unsigned h = 0; - for (unsigned i = 0; i < n-1; ++i) - h += m_solver->get_assertion(i)->get_id(); - std::cerr << h << "\n"; -#endif - } - - void include_ast_show(ast &a){ - a.show(); - } - - void include_model_show(model &a){ - a.show(); - } - - void show_ast(::ast *a, ast_manager &m) { - std::cout << mk_pp(a, m) << std::endl; - } - - bool expr::is_label (bool &pos,std::vector &names) const { - buffer< ::symbol> _names; - bool res = m().is_label(to_expr(raw()),pos,_names); - if(res) - for(unsigned i = 0; i < _names.size(); i++) - names.push_back(symbol(ctx(),_names[i])); - return res; - } - - double current_time() - { - static stopwatch sw; - static bool started = false; - if(!started){ - sw.start(); - started = true; - } - return sw.get_current_seconds(); - } - -} - - - - diff --git a/src/duality/duality_wrapper.h b/src/duality/duality_wrapper.h deleted file mode 100644 index 96c49b36b..000000000 --- a/src/duality/duality_wrapper.h +++ /dev/null @@ -1,1489 +0,0 @@ -/*++ - Copyright (c) 2012 Microsoft Corporation - - Module Name: - - duality_wrapper.h - - Abstract: - - wrap various Z3 classes in the style expected by duality - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - - --*/ -#ifndef DUALITY_WRAPPER_H_ -#define DUALITY_WRAPPER_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "util/version.h" -#include - -#include "interp/iz3hash.h" -#include "model/model.h" -#include "solver/solver.h" - -#include "ast/well_sorted.h" -#include "ast/arith_decl_plugin.h" -#include "ast/bv_decl_plugin.h" -#include "ast/datatype_decl_plugin.h" -#include "ast/array_decl_plugin.h" -#include "ast/ast_translation.h" -#include "ast/ast_pp.h" -#include "ast/ast_ll_pp.h" -#include "ast/ast_smt_pp.h" -#include "ast/ast_smt2_pp.h" -#include "ast/rewriter/th_rewriter.h" -#include "ast/rewriter/var_subst.h" -#include "ast/expr_substitution.h" -#include "ast/pp.h" -#include "util/scoped_ctrl_c.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" -#include "ast/scoped_proof.h" - -namespace Duality { - - class exception; - class config; - class context; - class symbol; - class params; - class ast; - class sort; - class func_decl; - class expr; - class solver; - class goal; - class tactic; - class probe; - class model; - class func_interp; - class func_entry; - class statistics; - class apply_result; - class fixedpoint; - class literals; - - /** - Duality global configuration object. - */ - - class config { - params_ref m_params; - config & operator=(config const & s); - public: - config(config const & s) : m_params(s.m_params) {} - config(const params_ref &_params) : m_params(_params) {} - config() { } // TODO: create a new params object here - ~config() { } - void set(char const * param, char const * value) { m_params.set_str(param,value); } - void set(char const * param, bool value) { m_params.set_bool(param,value); } - void set(char const * param, int value) { m_params.set_uint(param,value); } - params_ref &get() {return m_params;} - const params_ref &get() const {return m_params;} - }; - - enum decl_kind { - True, - False, - And, - Or, - Not, - Iff, - Ite, - Equal, - Implies, - Distinct, - Xor, - Oeq, - Interp, - Leq, - Geq, - Lt, - Gt, - Plus, - Sub, - Uminus, - Times, - Div, - Idiv, - Rem, - Mod, - Power, - ToReal, - ToInt, - IsInt, - Select, - Store, - ConstArray, - ArrayDefault, - ArrayMap, - SetUnion, - SetIntersect, - SetDifference, - SetComplement, - SetSubSet, - AsArray, - Numeral, - Forall, - Exists, - Variable, - Uninterpreted, - OtherBasic, - OtherArith, - OtherArray, - Other - }; - - enum sort_kind {BoolSort,IntSort,RealSort,ArraySort,UninterpretedSort,UnknownSort}; - - /** - A context has an ast manager global configuration options, etc. - */ - - class context { - protected: - ast_manager &mgr; - config m_config; - - family_id m_basic_fid; - family_id m_array_fid; - family_id m_arith_fid; - family_id m_bv_fid; - family_id m_dt_fid; - family_id m_datalog_fid; - arith_util m_arith_util; - - public: - context(ast_manager &_manager, const config &_config) : mgr(_manager), m_config(_config), m_arith_util(_manager) { - m_basic_fid = m().get_basic_family_id(); - m_arith_fid = m().mk_family_id("arith"); - m_bv_fid = m().mk_family_id("bv"); - m_array_fid = m().mk_family_id("array"); - m_dt_fid = m().mk_family_id("datatype"); - m_datalog_fid = m().mk_family_id("datalog_relation"); - } - ~context() { } - - ast_manager &m() const {return *(ast_manager *)&mgr;} - - void set(char const * param, char const * value) { m_config.set(param,value); } - void set(char const * param, bool value) { m_config.set(param,value); } - void set(char const * param, int value) { m_config.set(param,value); } - config &get_config() {return m_config;} - - symbol str_symbol(char const * s); - symbol int_symbol(int n); - - sort bool_sort(); - sort int_sort(); - sort real_sort(); - sort bv_sort(unsigned sz); - sort array_sort(sort d, sort r); - - func_decl function(symbol const & name, unsigned arity, sort const * domain, sort const & range); - func_decl function(char const * name, unsigned arity, sort const * domain, sort const & range); - func_decl function(char const * name, sort const & domain, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range); - func_decl function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range); - func_decl fresh_func_decl(char const * name, const std::vector &domain, sort const & range); - func_decl fresh_func_decl(char const * name, sort const & range); - - expr constant(symbol const & name, sort const & s); - expr constant(char const * name, sort const & s); - expr constant(const std::string &name, sort const & s); - expr bool_const(char const * name); - expr int_const(char const * name); - expr real_const(char const * name); - expr bv_const(char const * name, unsigned sz); - - expr bool_val(bool b); - - expr int_val(int n); - expr int_val(unsigned n); - expr int_val(char const * n); - - expr real_val(int n, int d); - expr real_val(int n); - expr real_val(unsigned n); - expr real_val(char const * n); - - expr bv_val(int n, unsigned sz); - expr bv_val(unsigned n, unsigned sz); - expr bv_val(char const * n, unsigned sz); - - expr num_val(int n, sort const & s); - - expr mki(family_id fid, ::decl_kind dk, int n, ::expr **args); - expr make(decl_kind op, int n, ::expr **args); - expr make(decl_kind op, const std::vector &args); - expr make(decl_kind op); - expr make(decl_kind op, const expr &arg0); - expr make(decl_kind op, const expr &arg0, const expr &arg1); - expr make(decl_kind op, const expr &arg0, const expr &arg1, const expr &arg2); - - expr make_quant(decl_kind op, const std::vector &bvs, const expr &body); - expr make_quant(decl_kind op, const std::vector &_sorts, const std::vector &_names, const expr &body); - expr make_var(int idx, const sort &s); - - decl_kind get_decl_kind(const func_decl &t); - - sort_kind get_sort_kind(const sort &s); - - expr translate(const expr &e); - func_decl translate(const func_decl &); - - void print_expr(std::ostream &s, const ast &e); - - fixedpoint mk_fixedpoint(); - - expr cook(::expr *a); - std::vector cook(ptr_vector< ::expr> v); - ::expr *uncook(const expr &a); - }; - - template - class z3array { - T * m_array; - unsigned m_size; - z3array(z3array const & s); - z3array & operator=(z3array const & s); - public: - z3array(unsigned sz):m_size(sz) { m_array = new T[sz]; } - ~z3array() { delete[] m_array; } - unsigned size() const { return m_size; } - T & operator[](unsigned i) { assert(i < m_size); return m_array[i]; } - T const & operator[](unsigned i) const { assert(i < m_size); return m_array[i]; } - T const * ptr() const { return m_array; } - T * ptr() { return m_array; } - }; - - class object { - protected: - context * m_ctx; - public: - object(): m_ctx((context *)0) {} - object(context & c):m_ctx(&c) {} - object(object const & s):m_ctx(s.m_ctx) {} - context & ctx() const { return *m_ctx; } - friend void check_context(object const & a, object const & b) { assert(a.m_ctx == b.m_ctx); } - ast_manager &m() const {return m_ctx->m();} - }; - - class symbol : public object { - ::symbol m_sym; - public: - symbol(context & c, ::symbol s):object(c), m_sym(s) {} - symbol(symbol const & s):object(s), m_sym(s.m_sym) {} - symbol & operator=(symbol const & s) { m_ctx = s.m_ctx; m_sym = s.m_sym; return *this; } - operator ::symbol() const {return m_sym;} - std::string str() const { - if (m_sym.is_numerical()) { - std::ostringstream buffer; - buffer << m_sym.get_num(); - return buffer.str(); - } - else { - return m_sym.bare_str(); - } - } - friend std::ostream & operator<<(std::ostream & out, symbol const & s) { - return out << s.str(); - } - friend bool operator==(const symbol &x, const symbol &y) { - return x.m_sym == y.m_sym; - } - }; - - class params : public config {}; - - /** Wrapper around an ast pointer */ - class ast_i : public object { - protected: - ::ast *_ast; - public: - ::ast * const &raw() const {return _ast;} - ast_i(context & c, ::ast *a = 0) : object(c) {_ast = a;} - - ast_i(){_ast = 0;} - bool eq(const ast_i &other) const { - return _ast == other._ast; - } - bool lt(const ast_i &other) const { - return _ast < other._ast; - } - friend bool operator==(const ast_i &x, const ast_i&y){ - return x.eq(y); - } - friend bool operator!=(const ast_i &x, const ast_i&y){ - return !x.eq(y); - } - friend bool operator<(const ast_i &x, const ast_i&y){ - return x.lt(y); - } - size_t hash() const {return (size_t)_ast;} - bool null() const {return !_ast;} - }; - - /** Reference counting verison of above */ - class ast : public ast_i { - public: - operator ::ast*() const { return raw(); } - friend bool eq(ast const & a, ast const & b) { return a.raw() == b.raw(); } - - - ast(context &c, ::ast *a = 0) : ast_i(c,a) { - if(_ast) - m().inc_ref(a); - } - - ast() {} - - ast(const ast &other) : ast_i(other) { - if(_ast) - m().inc_ref(_ast); - } - - ast &operator=(const ast &other) { - if(_ast) - m().dec_ref(_ast); - _ast = other._ast; - m_ctx = other.m_ctx; - if(_ast) - m().inc_ref(_ast); - return *this; - } - - ~ast(){ - if(_ast) - m().dec_ref(_ast); - } - - void show() const; - - }; - - class sort : public ast { - public: - sort(context & c):ast(c) {} - sort(context & c, ::sort *s):ast(c, s) {} - sort(sort const & s):ast(s) {} - operator ::sort*() const { return to_sort(raw()); } - sort & operator=(sort const & s) { return static_cast(ast::operator=(s)); } - - bool is_bool() const { return m().is_bool(*this); } - bool is_int() const { return ctx().get_sort_kind(*this) == IntSort; } - bool is_real() const { return ctx().get_sort_kind(*this) == RealSort; } - bool is_arith() const; - bool is_array() const { return ctx().get_sort_kind(*this) == ArraySort; } - bool is_datatype() const; - bool is_relation() const; - bool is_finite_domain() const; - - - sort array_domain() const; - sort array_range() const; - - friend std::ostream & operator<<(std::ostream & out, sort const & m){ - m.ctx().print_expr(out,m); - return out; - } - }; - - - class func_decl : public ast { - public: - func_decl() : ast() {} - func_decl(context & c):ast(c) {} - func_decl(context & c, ::func_decl *n):ast(c, n) {} - func_decl(func_decl const & s):ast(s) {} - operator ::func_decl*() const { return to_func_decl(*this); } - func_decl & operator=(func_decl const & s) { return static_cast(ast::operator=(s)); } - - unsigned arity() const; - sort domain(unsigned i) const; - sort range() const; - symbol name() const {return symbol(ctx(),to_func_decl(raw())->get_name());} - decl_kind get_decl_kind() const; - - bool is_const() const { return arity() == 0; } - - expr operator()(unsigned n, expr const * args) const; - expr operator()(const std::vector &args) const; - expr operator()() const; - expr operator()(expr const & a) const; - expr operator()(int a) const; - expr operator()(expr const & a1, expr const & a2) const; - expr operator()(expr const & a1, int a2) const; - expr operator()(int a1, expr const & a2) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const; - expr operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const; - - func_decl get_func_decl_parameter(unsigned idx){ - return func_decl(ctx(),to_func_decl(to_func_decl(raw())->get_parameters()[idx].get_ast())); - } - - }; - - class expr : public ast { - public: - expr() : ast() {} - expr(context & c):ast(c) {} - expr(context & c, ::ast *n):ast(c, n) {} - expr(expr const & n):ast(n) {} - expr & operator=(expr const & n) { return static_cast(ast::operator=(n)); } - operator ::expr*() const { return to_expr(raw()); } - unsigned get_id() const {return to_expr(raw())->get_id();} - - sort get_sort() const { return sort(ctx(),m().get_sort(to_expr(raw()))); } - - bool is_bool() const { return get_sort().is_bool(); } - bool is_int() const { return get_sort().is_int(); } - bool is_real() const { return get_sort().is_real(); } - bool is_arith() const { return get_sort().is_arith(); } - bool is_array() const { return get_sort().is_array(); } - bool is_datatype() const { return get_sort().is_datatype(); } - bool is_relation() const { return get_sort().is_relation(); } - bool is_finite_domain() const { return get_sort().is_finite_domain(); } - bool is_true() const {return is_app() && decl().get_decl_kind() == True; } - - bool is_numeral() const { - return is_app() && decl().get_decl_kind() == OtherArith && m().is_unique_value(to_expr(raw())); - } - bool is_app() const {return raw()->get_kind() == AST_APP;} - bool is_quantifier() const {return raw()->get_kind() == AST_QUANTIFIER;} - bool is_var() const {return raw()->get_kind() == AST_VAR;} - bool is_label (bool &pos,std::vector &names) const ; - bool is_ground() const {return to_app(raw())->is_ground();} - bool has_quantifiers() const {return to_app(raw())->has_quantifiers();} - bool has_free(int idx) const { - used_vars proc; - proc.process(to_expr(raw())); - return proc.contains(idx); - } - unsigned get_max_var_idx_plus_1() const { - used_vars proc; - proc.process(to_expr(raw())); - return proc.get_max_found_var_idx_plus_1(); - } - - // operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } - func_decl decl() const {return func_decl(ctx(),to_app(raw())->get_decl());} - unsigned num_args() const { - ast_kind dk = raw()->get_kind(); - switch(dk){ - case AST_APP: - return to_app(raw())->get_num_args(); - case AST_QUANTIFIER: - return 1; - case AST_VAR: - return 0; - default:; - } - SASSERT(0); - return 0; - } - expr arg(unsigned i) const { - ast_kind dk = raw()->get_kind(); - switch(dk){ - case AST_APP: - return ctx().cook(to_app(raw())->get_arg(i)); - case AST_QUANTIFIER: - return ctx().cook(to_quantifier(raw())->get_expr()); - default:; - } - assert(0); - return expr(); - } - - expr body() const { - return ctx().cook(to_quantifier(raw())->get_expr()); - } - - friend expr operator!(expr const & a) { - // ::expr *e = a; - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_NOT,a)); - } - - friend expr operator&&(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_AND,a,b)); - } - - friend expr operator||(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_OR,a,b)); - } - - friend expr implies(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_IMPLIES,a,b)); - } - - friend expr operator==(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_EQ,a,b)); - } - - friend expr operator!=(expr const & a, expr const & b) { - return expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DISTINCT,a,b)); - } - - friend expr operator+(expr const & a, expr const & b) { - return a.ctx().make(Plus,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_ADD,a,b)); - } - - friend expr operator*(expr const & a, expr const & b) { - return a.ctx().make(Times,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_MUL,a,b)); - } - - friend expr operator/(expr const & a, expr const & b) { - return a.ctx().make(Div,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_DIV,a,b)); - } - - friend expr operator-(expr const & a) { - return a.ctx().make(Uminus,a); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_UMINUS,a)); - } - - friend expr operator-(expr const & a, expr const & b) { - return a.ctx().make(Sub,a,b); // expr(a.ctx(),a.m().mk_app(a.ctx().m_arith_fid,OP_SUB,a,b)); - } - - friend expr operator<=(expr const & a, expr const & b) { - return a.ctx().make(Leq,a,b); // expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LE,a,b)); - } - - friend expr operator>=(expr const & a, expr const & b) { - return a.ctx().make(Geq,a,b); //expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GE,a,b)); - } - - friend expr operator<(expr const & a, expr const & b) { - return a.ctx().make(Lt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_LT,a,b)); - } - - friend expr operator>(expr const & a, expr const & b) { - return a.ctx().make(Gt,a,b); expr(a.ctx(),a.m().mk_app(a.m().get_basic_family_id(),OP_GT,a,b)); - } - - expr simplify() const; - - expr simplify(params const & p) const; - - expr qe_lite() const; - - expr qe_lite(const std::set &idxs, bool index_of_bound) const; - - friend expr clone_quantifier(const expr &, const expr &); - - friend expr clone_quantifier(const expr &q, const expr &b, const std::vector &patterns); - - friend expr clone_quantifier(decl_kind, const expr &, const expr &); - - friend std::ostream & operator<<(std::ostream & out, expr const & m){ - m.ctx().print_expr(out,m); - return out; - } - - void get_patterns(std::vector &pats) const ; - - unsigned get_quantifier_num_bound() const { - return to_quantifier(raw())->get_num_decls(); - } - - unsigned get_index_value() const { - var* va = to_var(raw()); - return va->get_idx(); - } - - bool is_quantifier_forall() const { - return to_quantifier(raw())->is_forall(); - } - - sort get_quantifier_bound_sort(unsigned n) const { - return sort(ctx(),to_quantifier(raw())->get_decl_sort(n)); - } - - symbol get_quantifier_bound_name(unsigned n) const { - return symbol(ctx(),to_quantifier(raw())->get_decl_names()[n]); - } - - friend expr forall(const std::vector &quants, const expr &body); - - friend expr exists(const std::vector &quants, const expr &body); - - }; - - - typedef ::decl_kind pfrule; - - class proof : public ast { - public: - proof(context & c):ast(c) {} - proof(context & c, ::proof *s):ast(c, s) {} - proof(proof const & s):ast(s) {} - operator ::proof*() const { return to_app(raw()); } - proof & operator=(proof const & s) { return static_cast(ast::operator=(s)); } - - pfrule rule() const { - ::func_decl *d = to_app(raw())->get_decl(); - return d->get_decl_kind(); - } - - unsigned num_prems() const { - return to_app(raw())->get_num_args() - 1; - } - - expr conc() const { - return ctx().cook(to_app(raw())->get_arg(num_prems())); - } - - proof prem(unsigned i) const { - return proof(ctx(),to_app(to_app(raw())->get_arg(i))); - } - - void get_assumptions(std::vector &assumps); - }; - -#if 0 - -#if Z3_MAJOR_VERSION > 4 || Z3_MAJOR_VERSION == 4 && Z3_MINOR_VERSION >= 3 - template - class ast_vector_tpl : public object { - Z3_ast_vector m_vector; - void init(Z3_ast_vector v) { Z3_ast_vector_inc_ref(ctx(), v); m_vector = v; } - public: - ast_vector_tpl(context & c):object(c) { init(Z3_mk_ast_vector(c)); } - ast_vector_tpl(context & c, Z3_ast_vector v):object(c) { init(v); } - ast_vector_tpl(ast_vector_tpl const & s):object(s), m_vector(s.m_vector) { Z3_ast_vector_inc_ref(ctx(), m_vector); } - ~ast_vector_tpl() { /* Z3_ast_vector_dec_ref(ctx(), m_vector); */ } - operator Z3_ast_vector() const { return m_vector; } - unsigned size() const { return Z3_ast_vector_size(ctx(), m_vector); } - T operator[](unsigned i) const { Z3_ast r = Z3_ast_vector_get(ctx(), m_vector, i); check_error(); return cast_ast()(ctx(), r); } - void push_back(T const & e) { Z3_ast_vector_push(ctx(), m_vector, e); check_error(); } - void resize(unsigned sz) { Z3_ast_vector_resize(ctx(), m_vector, sz); check_error(); } - T back() const { return operator[](size() - 1); } - void pop_back() { assert(size() > 0); resize(size() - 1); } - bool empty() const { return size() == 0; } - ast_vector_tpl & operator=(ast_vector_tpl const & s) { - Z3_ast_vector_inc_ref(s.ctx(), s.m_vector); - // Z3_ast_vector_dec_ref(ctx(), m_vector); - m_ctx = s.m_ctx; - m_vector = s.m_vector; - return *this; - } - friend std::ostream & operator<<(std::ostream & out, ast_vector_tpl const & v) { out << Z3_ast_vector_to_string(v.ctx(), v); return out; } - }; - - typedef ast_vector_tpl ast_vector; - typedef ast_vector_tpl expr_vector; - typedef ast_vector_tpl sort_vector; - typedef ast_vector_tpl func_decl_vector; - -#endif - -#endif - - class func_interp : public object { - ::func_interp * m_interp; - void init(::func_interp * e) { - m_interp = e; - } - public: - func_interp(context & c, ::func_interp * e):object(c) { init(e); } - func_interp(func_interp const & s):object(s) { init(s.m_interp); } - ~func_interp() { } - operator ::func_interp *() const { return m_interp; } - func_interp & operator=(func_interp const & s) { - m_ctx = s.m_ctx; - m_interp = s.m_interp; - return *this; - } - unsigned num_entries() const { return m_interp->num_entries(); } - expr get_arg(unsigned ent, unsigned arg) const { - return expr(ctx(),m_interp->get_entry(ent)->get_arg(arg)); - } - expr get_value(unsigned ent) const { - return expr(ctx(),m_interp->get_entry(ent)->get_result()); - } - expr else_value() const { - return expr(ctx(),m_interp->get_else()); - } - }; - - - - class model : public object { - model_ref m_model; - void init(::model *m) { - m_model = m; - } - public: - model(context & c, ::model * m = 0):object(c), m_model(m) { } - model(model const & s):object(s), m_model(s.m_model) { } - ~model() { } - operator ::model *() const { return m_model.get(); } - model & operator=(model const & s) { - // ::model *_inc_ref(s.ctx(), s.m_model); - // ::model *_dec_ref(ctx(), m_model); - m_ctx = s.m_ctx; - m_model = s.m_model.get(); - return *this; - } - model & operator=(::model *s) { - m_model = s; - return *this; - } - bool null() const {return !m_model;} - - expr eval(expr const & n, bool model_completion=true) const { - ::model * _m = m_model.get(); - expr_ref result(ctx().m()); - _m->eval(n, result, model_completion); - return expr(ctx(), result); - } - - void show() const; - void show_hash() const; - - unsigned num_consts() const {return m_model.get()->get_num_constants();} - unsigned num_funcs() const {return m_model.get()->get_num_functions();} - func_decl get_const_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_constant(i));} - func_decl get_func_decl(unsigned i) const {return func_decl(ctx(),m_model.get()->get_function(i));} - unsigned size() const; - func_decl operator[](unsigned i) const; - - expr get_const_interp(func_decl f) const { - return ctx().cook(m_model->get_const_interp(to_func_decl(f.raw()))); - } - - func_interp get_func_interp(func_decl f) const { - return func_interp(ctx(),m_model->get_func_interp(to_func_decl(f.raw()))); - } - -#if 0 - friend std::ostream & operator<<(std::ostream & out, model const & m) { out << Z3_model_to_string(m.ctx(), m); return out; } -#endif - }; - -#if 0 - class stats : public object { - Z3_stats m_stats; - void init(Z3_stats e) { - m_stats = e; - Z3_stats_inc_ref(ctx(), m_stats); - } - public: - stats(context & c):object(c), m_stats(0) {} - stats(context & c, Z3_stats e):object(c) { init(e); } - stats(stats const & s):object(s) { init(s.m_stats); } - ~stats() { if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); } - operator Z3_stats() const { return m_stats; } - stats & operator=(stats const & s) { - Z3_stats_inc_ref(s.ctx(), s.m_stats); - if (m_stats) Z3_stats_dec_ref(ctx(), m_stats); - m_ctx = s.m_ctx; - m_stats = s.m_stats; - return *this; - } - unsigned size() const { return Z3_stats_size(ctx(), m_stats); } - std::string key(unsigned i) const { Z3_string s = Z3_stats_get_key(ctx(), m_stats, i); check_error(); return s; } - 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; } - friend std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } - }; -#endif - - enum check_result { - unsat, sat, unknown - }; - - class fixedpoint : public object { - - public: - void get_rules(std::vector &rules); - void get_assertions(std::vector &rules); - void register_relation(const func_decl &rela); - void add_rule(const expr &clause, const symbol &name); - void assert_cnst(const expr &cnst); - }; - - inline std::ostream & operator<<(std::ostream & out, check_result r) { - if (r == unsat) out << "unsat"; - else if (r == sat) out << "sat"; - else out << "unknown"; - return out; - } - - inline check_result to_check_result(lbool l) { - if (l == l_true) return sat; - else if (l == l_false) return unsat; - return unknown; - } - - class solver : public object { - protected: - ::solver *m_solver; - model the_model; - bool canceled; - proof_gen_mode m_mode; - bool extensional; - public: - solver(context & c, bool extensional = false, bool models = true); - solver(context & c, ::solver *s):object(c),the_model(c) { m_solver = s; canceled = false;} - solver(solver const & s):object(s), the_model(s.the_model) { m_solver = s.m_solver; canceled = false;} - ~solver() { - if(m_solver) - dealloc(m_solver); - } - operator ::solver*() const { return m_solver; } - solver & operator=(solver const & s) { - m_ctx = s.m_ctx; - m_solver = s.m_solver; - the_model = s.the_model; - m_mode = s.m_mode; - return *this; - } - struct cancel_exception {}; - void checkpoint(){ - if(canceled) - throw(cancel_exception()); - } - // void set(params const & p) { Z3_solver_set_params(ctx(), m_solver, p); check_error(); } - void push() { scoped_proof_mode spm(m(),m_mode); m_solver->push(); } - void pop(unsigned n = 1) { scoped_proof_mode spm(m(),m_mode); m_solver->pop(n); } - // void reset() { Z3_solver_reset(ctx(), m_solver); check_error(); } - void add(expr const & e) { scoped_proof_mode spm(m(),m_mode); m_solver->assert_expr(e); } - check_result check() { - scoped_proof_mode spm(m(),m_mode); - checkpoint(); - lbool r = m_solver->check_sat(0,0); - model_ref m; - m_solver->get_model(m); - the_model = m.get(); - return to_check_result(r); - } - check_result check_keep_model(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { - scoped_proof_mode spm(m(),m_mode); - model old_model(the_model); - check_result res = check(n,assumptions,core_size,core); - if(the_model == 0) - the_model = old_model; - return res; - } - check_result check(unsigned n, expr * const assumptions, unsigned *core_size = 0, expr *core = 0) { - scoped_proof_mode spm(m(),m_mode); - checkpoint(); - std::vector< ::expr *> _assumptions(n); - for (unsigned i = 0; i < n; i++) { - _assumptions[i] = to_expr(assumptions[i]); - } - the_model = 0; - lbool r = m_solver->check_sat(n, VEC2PTR(_assumptions)); - - if(core_size && core){ - ptr_vector< ::expr> _core; - m_solver->get_unsat_core(_core); - *core_size = _core.size(); - for(unsigned i = 0; i < *core_size; i++) - core[i] = expr(ctx(),_core[i]); - } - - model_ref m; - m_solver->get_model(m); - the_model = m.get(); - - return to_check_result(r); - } -#if 0 - check_result check(expr_vector assumptions) { - scoped_proof_mode spm(m(),m_mode); - unsigned n = assumptions.size(); - z3array _assumptions(n); - for (unsigned i = 0; i < n; i++) { - check_context(*this, assumptions[i]); - _assumptions[i] = assumptions[i]; - } - Z3_lbool r = Z3_check_assumptions(ctx(), m_solver, n, _assumptions.ptr()); - check_error(); - return to_check_result(r); - } -#endif - model get_model() const { return model(ctx(), the_model); } - // std::string reason_unknown() const { Z3_string r = Z3_solver_get_reason_unknown(ctx(), m_solver); check_error(); return r; } - // stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } -#if 0 - 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); } -#endif - // expr proof() const { Z3_ast r = Z3_solver_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } - // friend std::ostream & operator<<(std::ostream & out, solver const & s) { out << Z3_solver_to_string(s.ctx(), s); return out; } - int get_num_decisions(); - - void cancel(){ - scoped_proof_mode spm(m(),m_mode); - canceled = true; - m().limit().cancel(); - } - - unsigned get_scope_level(){ scoped_proof_mode spm(m(),m_mode); return m_solver->get_scope_level();} - - void show(); - void print(const char *filename); - void show_assertion_ids(); - - proof get_proof(){ - scoped_proof_mode spm(m(),m_mode); - return proof(ctx(),m_solver->get_proof()); - } - - bool extensional_array_theory() {return extensional;} - }; - -#if 0 - class goal : public object { - Z3_goal m_goal; - void init(Z3_goal s) { - m_goal = s; - Z3_goal_inc_ref(ctx(), s); - } - public: - goal(context & c, bool models=true, bool unsat_cores=false, bool proofs=false):object(c) { init(Z3_mk_goal(c, models, unsat_cores, proofs)); } - goal(context & c, Z3_goal s):object(c) { init(s); } - goal(goal const & s):object(s) { init(s.m_goal); } - ~goal() { Z3_goal_dec_ref(ctx(), m_goal); } - operator Z3_goal() const { return m_goal; } - goal & operator=(goal const & s) { - Z3_goal_inc_ref(s.ctx(), s.m_goal); - Z3_goal_dec_ref(ctx(), m_goal); - m_ctx = s.m_ctx; - m_goal = s.m_goal; - return *this; - } - void add(expr const & f) { check_context(*this, f); Z3_goal_assert(ctx(), m_goal, f); check_error(); } - unsigned size() const { return Z3_goal_size(ctx(), m_goal); } - expr operator[](unsigned i) const { Z3_ast r = Z3_goal_formula(ctx(), m_goal, i); check_error(); return expr(ctx(), r); } - Z3_goal_prec precision() const { return Z3_goal_precision(ctx(), m_goal); } - bool inconsistent() const { return Z3_goal_inconsistent(ctx(), m_goal) != 0; } - unsigned depth() const { return Z3_goal_depth(ctx(), m_goal); } - void reset() { Z3_goal_reset(ctx(), m_goal); } - unsigned num_exprs() const { Z3_goal_num_exprs(ctx(), m_goal); } - bool is_decided_sat() const { return Z3_goal_is_decided_sat(ctx(), m_goal) != 0; } - bool is_decided_unsat() const { return Z3_goal_is_decided_unsat(ctx(), m_goal) != 0; } - friend std::ostream & operator<<(std::ostream & out, goal const & g) { out << Z3_goal_to_string(g.ctx(), g); return out; } - }; - - class apply_result : public object { - Z3_apply_result m_apply_result; - void init(Z3_apply_result s) { - m_apply_result = s; - Z3_apply_result_inc_ref(ctx(), s); - } - public: - apply_result(context & c, Z3_apply_result s):object(c) { init(s); } - apply_result(apply_result const & s):object(s) { init(s.m_apply_result); } - ~apply_result() { Z3_apply_result_dec_ref(ctx(), m_apply_result); } - operator Z3_apply_result() const { return m_apply_result; } - apply_result & operator=(apply_result const & s) { - Z3_apply_result_inc_ref(s.ctx(), s.m_apply_result); - Z3_apply_result_dec_ref(ctx(), m_apply_result); - m_ctx = s.m_ctx; - m_apply_result = s.m_apply_result; - return *this; - } - unsigned size() const { return Z3_apply_result_get_num_subgoals(ctx(), m_apply_result); } - goal operator[](unsigned i) const { Z3_goal r = Z3_apply_result_get_subgoal(ctx(), m_apply_result, i); check_error(); return goal(ctx(), r); } - goal operator[](int i) const { assert(i >= 0); return this->operator[](static_cast(i)); } - model convert_model(model const & m, unsigned i = 0) const { - check_context(*this, m); - Z3_model new_m = Z3_apply_result_convert_model(ctx(), m_apply_result, i, m); - check_error(); - return model(ctx(), new_m); - } - friend std::ostream & operator<<(std::ostream & out, apply_result const & r) { out << Z3_apply_result_to_string(r.ctx(), r); return out; } - }; - - class tactic : public object { - Z3_tactic m_tactic; - void init(Z3_tactic s) { - m_tactic = s; - Z3_tactic_inc_ref(ctx(), s); - } - public: - tactic(context & c, char const * name):object(c) { Z3_tactic r = Z3_mk_tactic(c, name); check_error(); init(r); } - tactic(context & c, Z3_tactic s):object(c) { init(s); } - tactic(tactic const & s):object(s) { init(s.m_tactic); } - ~tactic() { Z3_tactic_dec_ref(ctx(), m_tactic); } - operator Z3_tactic() const { return m_tactic; } - tactic & operator=(tactic const & s) { - Z3_tactic_inc_ref(s.ctx(), s.m_tactic); - Z3_tactic_dec_ref(ctx(), m_tactic); - m_ctx = s.m_ctx; - m_tactic = s.m_tactic; - return *this; - } - solver mk_solver() const { Z3_solver r = Z3_mk_solver_from_tactic(ctx(), m_tactic); check_error(); return solver(ctx(), r); } - apply_result apply(goal const & g) const { - check_context(*this, g); - Z3_apply_result r = Z3_tactic_apply(ctx(), m_tactic, g); - check_error(); - return apply_result(ctx(), r); - } - apply_result operator()(goal const & g) const { - return apply(g); - } - std::string help() const { char const * r = Z3_tactic_get_help(ctx(), m_tactic); check_error(); return r; } - friend tactic operator&(tactic const & t1, tactic const & t2) { - check_context(t1, t2); - Z3_tactic r = Z3_tactic_and_then(t1.ctx(), t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - friend tactic operator|(tactic const & t1, tactic const & t2) { - check_context(t1, t2); - Z3_tactic r = Z3_tactic_or_else(t1.ctx(), t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - friend tactic repeat(tactic const & t, unsigned max=UINT_MAX) { - Z3_tactic r = Z3_tactic_repeat(t.ctx(), t, max); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic with(tactic const & t, params const & p) { - Z3_tactic r = Z3_tactic_using_params(t.ctx(), t, p); - t.check_error(); - return tactic(t.ctx(), r); - } - friend tactic try_for(tactic const & t, unsigned ms) { - Z3_tactic r = Z3_tactic_try_for(t.ctx(), t, ms); - t.check_error(); - return tactic(t.ctx(), r); - } - }; - - class probe : public object { - Z3_probe m_probe; - void init(Z3_probe s) { - m_probe = s; - Z3_probe_inc_ref(ctx(), s); - } - public: - probe(context & c, char const * name):object(c) { Z3_probe r = Z3_mk_probe(c, name); check_error(); init(r); } - probe(context & c, double val):object(c) { Z3_probe r = Z3_probe_const(c, val); check_error(); init(r); } - probe(context & c, Z3_probe s):object(c) { init(s); } - probe(probe const & s):object(s) { init(s.m_probe); } - ~probe() { Z3_probe_dec_ref(ctx(), m_probe); } - operator Z3_probe() const { return m_probe; } - probe & operator=(probe const & s) { - Z3_probe_inc_ref(s.ctx(), s.m_probe); - Z3_probe_dec_ref(ctx(), m_probe); - m_ctx = s.m_ctx; - m_probe = s.m_probe; - return *this; - } - double apply(goal const & g) const { double r = Z3_probe_apply(ctx(), m_probe, g); check_error(); return r; } - double operator()(goal const & g) const { return apply(g); } - friend probe operator<=(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_le(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator<=(probe const & p1, double p2) { return p1 <= probe(p1.ctx(), p2); } - friend probe operator<=(double p1, probe const & p2) { return probe(p2.ctx(), p1) <= p2; } - friend probe operator>=(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_ge(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator>=(probe const & p1, double p2) { return p1 >= probe(p1.ctx(), p2); } - friend probe operator>=(double p1, probe const & p2) { return probe(p2.ctx(), p1) >= p2; } - friend probe operator<(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_lt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator<(probe const & p1, double p2) { return p1 < probe(p1.ctx(), p2); } - friend probe operator<(double p1, probe const & p2) { return probe(p2.ctx(), p1) < p2; } - friend probe operator>(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_gt(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator>(probe const & p1, double p2) { return p1 > probe(p1.ctx(), p2); } - friend probe operator>(double p1, probe const & p2) { return probe(p2.ctx(), p1) > p2; } - friend probe operator==(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_eq(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator==(probe const & p1, double p2) { return p1 == probe(p1.ctx(), p2); } - friend probe operator==(double p1, probe const & p2) { return probe(p2.ctx(), p1) == p2; } - friend probe operator&&(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_and(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator||(probe const & p1, probe const & p2) { - check_context(p1, p2); Z3_probe r = Z3_probe_or(p1.ctx(), p1, p2); p1.check_error(); return probe(p1.ctx(), r); - } - friend probe operator!(probe const & p) { - Z3_probe r = Z3_probe_not(p.ctx(), p); p.check_error(); return probe(p.ctx(), r); - } - }; - - inline tactic fail_if(probe const & p) { - Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p); - p.check_error(); - return tactic(p.ctx(), r); - } - inline tactic when(probe const & p, tactic const & t) { - check_context(p, t); - Z3_tactic r = Z3_tactic_when(t.ctx(), p, t); - t.check_error(); - return tactic(t.ctx(), r); - } - inline tactic cond(probe const & p, tactic const & t1, tactic const & t2) { - check_context(p, t1); check_context(p, t2); - Z3_tactic r = Z3_tactic_cond(t1.ctx(), p, t1, t2); - t1.check_error(); - return tactic(t1.ctx(), r); - } - -#endif - - inline expr context::bool_val(bool b){return b ? make(True) : make(False);} - - inline symbol context::str_symbol(char const * s) { ::symbol r = ::symbol(s); return symbol(*this, r); } - inline symbol context::int_symbol(int n) { ::symbol r = ::symbol(n); return symbol(*this, r); } - - inline sort context::bool_sort() { - ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); - return sort(*this, s); - } - inline sort context::int_sort() { - ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); - return sort(*this, s); - } - inline sort context::real_sort() { - ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); - return sort(*this, s); - } - inline sort context::array_sort(sort d, sort r) { - parameter params[2] = { parameter(d), parameter(to_sort(r)) }; - ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); - return sort(*this, s); - } - - - inline func_decl context::function(symbol const & name, unsigned arity, sort const * domain, sort const & range) { - std::vector< ::sort *> sv(arity); - for(unsigned i = 0; i < arity; i++) - sv[i] = domain[i]; - ::func_decl* d = m().mk_func_decl(name,arity, VEC2PTR(sv),range); - return func_decl(*this,d); - } - - inline func_decl context::function(char const * name, unsigned arity, sort const * domain, sort const & range) { - return function(str_symbol(name), arity, domain, range); - } - - inline func_decl context::function(char const * name, sort const & domain, sort const & range) { - sort args[1] = { domain }; - return function(name, 1, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & range) { - sort args[2] = { d1, d2 }; - return function(name, 2, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & range) { - sort args[3] = { d1, d2, d3 }; - return function(name, 3, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & range) { - sort args[4] = { d1, d2, d3, d4 }; - return function(name, 4, args, range); - } - - inline func_decl context::function(char const * name, sort const & d1, sort const & d2, sort const & d3, sort const & d4, sort const & d5, sort const & range) { - sort args[5] = { d1, d2, d3, d4, d5 }; - return function(name, 5, args, range); - } - - - inline expr context::constant(symbol const & name, sort const & s) { - ::expr *r = m().mk_const(m().mk_const_decl(name, s)); - return expr(*this, r); - } - inline expr context::constant(char const * name, sort const & s) { return constant(str_symbol(name), s); } - inline expr context::bool_const(char const * name) { return constant(name, bool_sort()); } - 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 func_decl::operator()(const std::vector &args) const { - return operator()(args.size(), VEC2PTR(args)); - } - inline expr func_decl::operator()() const { - return operator()(0,0); - } - inline expr func_decl::operator()(expr const & a) const { - return operator()(1,&a); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2) const { - expr args[2] = {a1,a2}; - return operator()(2,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3) const { - expr args[3] = {a1,a2,a3}; - return operator()(3,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4) const { - expr args[4] = {a1,a2,a3,a4}; - return operator()(4,args); - } - inline expr func_decl::operator()(expr const & a1, expr const & a2, expr const & a3, expr const & a4, expr const & a5) const { - expr args[5] = {a1,a2,a3,a4,a5}; - return operator()(5,args); - } - - - inline expr select(expr const & a, expr const & i) { return a.ctx().make(Select,a,i); } - inline expr store(expr const & a, expr const & i, expr const & v) { return a.ctx().make(Store,a,i,v); } - - inline expr forall(const std::vector &quants, const expr &body){ - return body.ctx().make_quant(Forall,quants,body); - } - - inline expr exists(const std::vector &quants, const expr &body){ - return body.ctx().make_quant(Exists,quants,body); - } - - inline expr context::int_val(int n){ - :: sort *r = m().mk_sort(m_arith_fid, INT_SORT); - return cook(m_arith_util.mk_numeral(rational(n),r)); - } - - - class literals : public object { - }; - - class TermTree { - public: - - TermTree(expr _term){ - term = _term; - } - - TermTree(expr _term, const std::vector &_children){ - term = _term; - children = _children; - } - - inline expr getTerm(){return term;} - - inline std::vector &getTerms(){return terms;} - - inline std::vector &getChildren(){ - return children; - } - - inline int number(int from){ - for(unsigned i = 0; i < children.size(); i++) - from = children[i]->number(from); - num = from; - return from + 1; - } - - inline int getNumber(){ - return num; - } - - inline void setTerm(expr t){term = t;} - - inline void addTerm(expr t){terms.push_back(t);} - - inline void setChildren(const std::vector & _children){ - children = _children; - } - - inline void setNumber(int _num){ - num = _num; - } - - ~TermTree(){ - for(unsigned i = 0; i < children.size(); i++) - delete children[i]; - } - - private: - expr term; - std::vector terms; - std::vector children; - int num; - }; - - typedef context interpolating_context; - - class interpolating_solver : public solver { - public: - interpolating_solver(context &ctx, bool models = true) - : solver(ctx, true, models) - { - weak_mode = false; - } - - public: - lbool interpolate(const std::vector &assumptions, - std::vector &interpolants, - model &_model, - literals &lits, - bool incremental - ); - - lbool interpolate_tree(TermTree *assumptions, - TermTree *&interpolants, - model &_model, - literals &lits, - bool incremental - ); - - bool read_interpolation_problem(const std::string &file_name, - std::vector &assumptions, - std::vector &theory, - std::string &error_message - ); - - void write_interpolation_problem(const std::string &file_name, - const std::vector &assumptions, - const std::vector &theory - ); - - void AssertInterpolationAxiom(const expr &expr); - void RemoveInterpolationAxiom(const expr &expr); - - void SetWeakInterpolants(bool weak); - void SetPrintToFile(const std::string &file_name); - - const std::vector &GetInterpolationAxioms() {return theory;} - const char *profile(); - - private: - bool weak_mode; - std::string print_filename; - std::vector theory; - }; - - - inline expr context::cook(::expr *a) {return expr(*this,a);} - - inline std::vector context::cook(ptr_vector< ::expr> v) { - std::vector _v(v.size()); - for(unsigned i = 0; i < v.size(); i++) - _v[i] = cook(v[i]); - return _v; - } - - inline ::expr *context::uncook(const expr &a) { - m().inc_ref(a.raw()); - return to_expr(a.raw()); - } - - inline expr context::translate(const expr &e) { - ::expr *f = to_expr(e.raw()); - if(&e.ctx().m() != &m()) // same ast manager -> no translation - throw "ast manager mismatch"; - return cook(f); - } - - inline func_decl context::translate(const func_decl &e) { - ::func_decl *f = to_func_decl(e.raw()); - if(&e.ctx().m() != &m()) // same ast manager -> no translation - throw "ast manager mismatch"; - return func_decl(*this,f); - } - - typedef double clock_t; - clock_t current_time(); - inline void output_time(std::ostream &os, clock_t time){os << time;} - - template class uptr { - public: - X *ptr; - uptr(){ptr = 0;} - void set(X *_ptr){ - if(ptr) delete ptr; - ptr = _ptr; - } - X *get(){ return ptr;} - ~uptr(){ - if(ptr) delete ptr; - } - }; - -}; - -// to make Duality::ast hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::ast &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make Duality::ast usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::ast &s, const Duality::ast &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// to make Duality::ast usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::expr &s, const Duality::expr &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -// to make Duality::func_decl hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Duality::func_decl &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make Duality::func_decl usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const Duality::func_decl &s, const Duality::func_decl &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - -#endif diff --git a/src/interp/CMakeLists.txt b/src/interp/CMakeLists.txt deleted file mode 100644 index c3d8e3d5e..000000000 --- a/src/interp/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -z3_add_component(interp - SOURCES - iz3base.cpp - iz3checker.cpp - iz3interp.cpp - iz3mgr.cpp - iz3pp.cpp - iz3profiling.cpp - iz3proof.cpp - iz3proof_itp.cpp - iz3scopes.cpp - iz3translate.cpp - iz3translate_direct.cpp - COMPONENT_DEPENDENCIES - solver - PYG_FILES - interp_params.pyg -) diff --git a/src/interp/interp_params.pyg b/src/interp/interp_params.pyg deleted file mode 100644 index 3116a18db..000000000 --- a/src/interp/interp_params.pyg +++ /dev/null @@ -1,6 +0,0 @@ -def_module_params('interp', - description='interpolation parameters', - export=True, - params=(('profile', BOOL, False, '(INTERP) profile interpolation'), - ('check', BOOL, False, '(INTERP) check interpolants'), - )) diff --git a/src/interp/iz3base.cpp b/src/interp/iz3base.cpp deleted file mode 100755 index b9284b869..000000000 --- a/src/interp/iz3base.cpp +++ /dev/null @@ -1,372 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3base.cpp - - Abstract: - - Base class for interpolators. Includes an AST manager and a scoping - object as bases. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3base.h" -#include -#include -#include -#include -#include "solver/solver.h" -#include "../smt/smt_solver.h" - - -using namespace stl_ext; - - -iz3base::range &iz3base::ast_range(ast t){ - return ast_ranges_hash[t].rng; -} - -iz3base::range &iz3base::sym_range(symb d){ - return sym_range_hash[d]; -} - -void iz3base::add_frame_range(int frame, ast t){ - range &rng = ast_range(t); - if(!in_range(frame,rng)){ - range_add(frame,rng); - for(int i = 0, n = num_args(t); i < n; ++i) - add_frame_range(frame,arg(t,i)); - if(op(t) == Uninterpreted) - range_add(frame,sym_range(sym(t))); - } -} - -#if 1 -iz3base::range &iz3base::ast_scope(ast t){ - ranges &rngs = ast_ranges_hash[t]; - range &rng = rngs.scp; - if(!rngs.scope_computed){ // not computed yet - rng = range_full(); - for(int i = 0, n = num_args(t); i < n; ++i) - rng = range_glb(rng,ast_scope(arg(t,i))); - if(op(t) == Uninterpreted) - if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global - rng = range_glb(rng,sym_range(sym(t))); - rngs.scope_computed = true; - } - return rng; -} -#else -iz3base::range &iz3base::ast_scope(ast t){ - ranges &rngs = ast_ranges_hash[t]; - if(rngs.scope_computed) return rngs.scp; - range rng = range_full(); - for(int i = 0, n = num_args(t); i < n; ++i) - rng = range_glb(rng,ast_scope(arg(t,i))); - if(op(t) == Uninterpreted) - if(parents.empty() || num_args(t) == 0) // in tree mode, all function syms are global - rng = range_glb(rng,sym_range(sym(t))); - rngs = ast_ranges_hash[t]; - rngs.scope_computed = true; - rngs.scp = rng; - return rngs.scp; -} -#endif - -void iz3base::print(const std::string &filename){ - ast t = make(And,cnsts); - std::ofstream f(filename.c_str()); - print_sat_problem(f,t); - f.close(); -} - - -void iz3base::gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo){ - if(memo.find(n) == memo.end()){ - memo.insert(n); - if(op(n) == And){ - int nargs = num_args(n); - for(int i = 0; i < nargs; i++) - gather_conjuncts_rec(arg(n,i),conjuncts,memo); - } - else - conjuncts.push_back(n); - } -} - -void iz3base::gather_conjuncts(ast n, std::vector &conjuncts){ - hash_set memo; - gather_conjuncts_rec(n,conjuncts,memo); -} - -bool iz3base::is_literal(ast n){ - if(is_not(n))n = arg(n,0); - if(is_true(n) || is_false(n)) return false; - if(op(n) == And) return false; - return true; -} - -iz3base::ast iz3base::simplify_and(std::vector &conjuncts){ - hash_set memo; - for(unsigned i = 0; i < conjuncts.size(); i++){ - if(is_false(conjuncts[i])) - return conjuncts[i]; - if(is_true(conjuncts[i]) || memo.find(conjuncts[i]) != memo.end()){ - std::swap(conjuncts[i],conjuncts.back()); - conjuncts.pop_back(); - } - else if(memo.find(mk_not(conjuncts[i])) != memo.end()) - return mk_false(); // contradiction! - else - memo.insert(conjuncts[i]); - } - if(conjuncts.empty())return mk_true(); - return make(And,conjuncts); -} - -iz3base::ast iz3base::simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth){ - if(is_not(n))return mk_not(simplify_with_lit_rec(mk_not(n),lit,memo,depth)); - if(n == lit) return mk_true(); - ast not_lit = mk_not(lit); - if(n == not_lit) return mk_false(); - if(op(n) != And || depth <= 0) return n; - std::pair foo(n,ast()); - std::pair::iterator,bool> bar = memo.insert(foo); - ast &res = bar.first->second; - if(!bar.second) return res; - int nargs = num_args(n); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = simplify_with_lit_rec(arg(n,i),lit,memo,depth-1); - res = simplify_and(args); - return res; -} - -iz3base::ast iz3base::simplify_with_lit(ast n, ast lit){ - hash_map memo; - return simplify_with_lit_rec(n,lit,memo,1); -} - -iz3base::ast iz3base::simplify(ast n){ - if(is_not(n)) return mk_not(simplify(mk_not(n))); - std::pair memo_obj(n,ast()); - std::pair::iterator,bool> memo = simplify_memo.insert(memo_obj); - ast &res = memo.first->second; - if(!memo.second) return res; - switch(op(n)){ - case And: { - std::vector conjuncts; - gather_conjuncts(n,conjuncts); - for(unsigned i = 0; i < conjuncts.size(); i++) - conjuncts[i] = simplify(conjuncts[i]); -#if 0 - for(unsigned i = 0; i < conjuncts.size(); i++) - if(is_literal(conjuncts[i])) - for(unsigned j = 0; j < conjuncts.size(); j++) - if(j != i) - conjuncts[j] = simplify_with_lit(conjuncts[j],conjuncts[i]); -#endif - res = simplify_and(conjuncts); - } - break; - case Equal: { - ast x = arg(n,0); - ast y = arg(n,1); - if(ast_id(x) > ast_id(y)) - std::swap(x,y); - res = make(Equal,x,y); - break; - } - default: - res = n; - } - return res; -} - -void iz3base::initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory){ - cnsts = _parts; - theory = _theory; - for(unsigned i = 0; i < cnsts.size(); i++) - add_frame_range(i, cnsts[i]); - for(unsigned i = 0; i < _theory.size(); i++){ - add_frame_range(SHRT_MIN, _theory[i]); - add_frame_range(SHRT_MAX, _theory[i]); - } - for(unsigned i = 0; i < cnsts.size(); i++) - frame_map[cnsts[i]] = i; - for(unsigned i = 0; i < theory.size(); i++) - frame_map[theory[i]] = INT_MAX; -} - -void iz3base::initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory){ - cnsts.resize(_parts.size()); - theory = _theory; - for(unsigned i = 0; i < _parts.size(); i++) - for(unsigned j = 0; j < _parts[i].size(); j++){ - cnsts[i] = make(And,_parts[i]); - add_frame_range(i, _parts[i][j]); - frame_map[_parts[i][j]] = i; - } - for(unsigned i = 0; i < _theory.size(); i++){ - add_frame_range(SHRT_MIN, _theory[i]); - add_frame_range(SHRT_MAX, _theory[i]); - frame_map[theory[i]] = INT_MAX; - } -} - -void iz3base::check_interp(const std::vector &itps, std::vector &theory){ -#if 0 - Z3_config config = Z3_mk_config(); - Z3_context vctx = Z3_mk_context(config); - int frames = cnsts.size(); - std::vector foocnsts(cnsts); - for(unsigned i = 0; i < frames; i++) - foocnsts[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),cnsts[i]); - Z3_write_interpolation_problem(ctx,frames,&foocnsts[0],0, "temp_lemma.smt", theory.size(), &theory[0]); - int vframes,*vparents; - Z3_ast *vcnsts; - const char *verror; - bool ok = Z3_read_interpolation_problem(vctx,&vframes,&vcnsts,0,"temp_lemma.smt",&verror); - assert(ok); - std::vector vvcnsts(vframes); - std::copy(vcnsts,vcnsts+vframes,vvcnsts.begin()); - std::vector vitps(itps.size()); - for(unsigned i = 0; i < itps.size(); i++) - vitps[i] = Z3_mk_implies(ctx,Z3_mk_true(ctx),itps[i]); - Z3_write_interpolation_problem(ctx,itps.size(),&vitps[0],0,"temp_interp.smt"); - int iframes,*iparents; - Z3_ast *icnsts; - const char *ierror; - ok = Z3_read_interpolation_problem(vctx,&iframes,&icnsts,0,"temp_interp.smt",&ierror); - assert(ok); - const char *error = 0; - bool iok = Z3_check_interpolant(vctx, frames, &vvcnsts[0], parents.size() ? &parents[0] : 0, icnsts, &error); - assert(iok); -#endif -} - -bool iz3base::is_sat(const std::vector &q, ast &_proof, std::vector &vars){ - - params_ref p; - p.set_bool("proof", true); // this is currently useless - p.set_bool("model", true); - p.set_bool("unsat_core", true); - scoped_ptr sf = mk_smt_solver_factory(); - scoped_ptr< ::solver > solver = (*sf)(m(), p, true, true, true, ::symbol::null); - ::solver &s = *solver.get(); - - for(unsigned i = 0; i < q.size(); i++) - s.assert_expr(to_expr(q[i].raw())); - lbool res = s.check_sat(0,0); - if (m().canceled()) { - throw iz3_exception(Z3_CANCELED_MSG); - } - if(res == l_false){ - ::ast *proof = s.get_proof(); - _proof = cook(proof); - } - else if(vars.size()) { - model_ref(_m); - s.get_model(_m); - if (!_m.get()) { - SASSERT(l_undef == res); - throw iz3_exception("interpolation cannot proceed without a model"); - } - for(unsigned i = 0; i < vars.size(); i++){ - expr_ref r(m()); - _m.get()->eval(to_expr(vars[i].raw()),r,true); - vars[i] = cook(r.get()); - } - } - solver = 0; - return res != l_false; -} - - -void iz3base::find_children(const stl_ext::hash_set &cnsts_set, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &conjuncts, - std::vector &children, - std::vector &pos_map, - bool merge - ){ - std::vector my_children; - std::vector my_conjuncts; - if(op(tree) == Interp){ // if we've hit an interpolation position... - find_children(cnsts_set,arg(tree,0),cnsts,parents,my_conjuncts,my_children,pos_map,merge); - if(my_conjuncts.empty()) - my_conjuncts.push_back(mk_true()); // need at least one conjunct - int root = cnsts.size() + my_conjuncts.size() - 1; - for(unsigned i = 0; i < my_conjuncts.size(); i++){ - parents.push_back(root); - cnsts.push_back(my_conjuncts[i]); - } - for(unsigned i = 0; i < my_children.size(); i++) - parents[my_children[i]] = root; - children.push_back(root); - pos_map.push_back(root); - } - else { - if(op(tree) == And){ - int nargs = num_args(tree); - for(int i = 0; i < nargs; i++) - find_children(cnsts_set,arg(tree,i),cnsts,parents,my_conjuncts,my_children,pos_map,merge); - } - if(cnsts_set.find(tree) != cnsts_set.end()){ - if(merge && !my_conjuncts.empty()) - my_conjuncts.back() = mk_and(my_conjuncts.back(),tree); - else - my_conjuncts.push_back(tree); - } - for(unsigned i = 0; i < my_children.size(); i++) - children.push_back(my_children[i]); - for(unsigned i = 0; i < my_conjuncts.size(); i++) - conjuncts.push_back(my_conjuncts[i]); - } -} - -void iz3base::to_parents_vec_representation(const std::vector &_cnsts, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &theory, - std::vector &pos_map, - bool merge - ){ - std::vector my_children; - std::vector my_conjuncts; - hash_set cnsts_set; - for(unsigned i = 0; i < _cnsts.size(); i++) - cnsts_set.insert(_cnsts[i]); - ast _tree = (op(tree) != Interp) ? make(Interp,tree) : tree; - find_children(cnsts_set,_tree,cnsts,parents,my_conjuncts,my_children,pos_map,merge); - if(op(tree) != Interp) pos_map.pop_back(); - parents[parents.size()-1] = SHRT_MAX; - - // rest of the constraints are the background theory - - hash_set used_set; - for(unsigned i = 0; i < cnsts.size(); i++) - used_set.insert(cnsts[i]); - for(unsigned i = 0; i < _cnsts.size(); i++) - if(used_set.find(_cnsts[i]) == used_set.end()) - theory.push_back(_cnsts[i]); -} - diff --git a/src/interp/iz3base.h b/src/interp/iz3base.h deleted file mode 100755 index 15f613730..000000000 --- a/src/interp/iz3base.h +++ /dev/null @@ -1,195 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3base.h - - Abstract: - - Base class for interpolators. Includes an AST manager and a scoping - object as bases. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3BASE_H -#define IZ3BASE_H - -#include "interp/iz3mgr.h" -#include "interp/iz3scopes.h" - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(func_decl * const &s) const { - return (size_t) s; - } - }; -} - -/* Base class for interpolators. Includes an AST manager and a scoping - object as bases. */ - -class iz3base : public iz3mgr, public scopes { - - public: - - /** Get the range in which an expression occurs. This is the - smallest subtree containing all occurrences of the - expression. */ - range &ast_range(ast); - - /** Get the scope of an expression. This is the set of tree nodes in - which all of the expression's symbols are in scope. */ - range &ast_scope(ast); - - /** Get the range of a symbol. This is the smallest subtree containing - all occurrences of the symbol. */ - range &sym_range(symb); - - /** Is an expression local (in scope in some frame)? */ - - bool is_local(ast node){ - return !range_is_empty(ast_scope(node)); - } - - /** Simplify an expression */ - - ast simplify(ast); - - /** Constructor */ - - iz3base(ast_manager &_m_manager, - const std::vector &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(_m_manager), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other, - const std::vector &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(other), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other, - const std::vector > &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3mgr(other), scopes(_parents) { - initialize(_cnsts,_parents,_theory); - weak = false; - } - - iz3base(const iz3mgr& other) - : iz3mgr(other), scopes() { - weak = false; - } - - /* Set our options */ - void set_option(const std::string &name, const std::string &value){ - if(name == "weak" && value == "1") weak = true; - } - - /* Are we doing weak interpolants? */ - bool weak_mode(){return weak;} - - /** Print interpolation problem to an SMTLIB format file */ - void print(const std::string &filename); - - /** Check correctness of a solutino to this problem. */ - void check_interp(const std::vector &itps, std::vector &theory); - - /** For convenience -- is this formula SAT? */ - bool is_sat(const std::vector &consts, ast &_proof, std::vector &vars); - - /** Interpolator for clauses, to be implemented */ - virtual void interpolate_clause(std::vector &lits, std::vector &itps){ - throw iz3_exception("no interpolator"); - } - - ast get_proof_check_assump(range &rng){ - std::vector cs(theory); - cs.push_back(cnsts[rng.hi]); - return make(And,cs); - } - - int frame_of_assertion(const ast &ass){ - stl_ext::hash_map::iterator it = frame_map.find(ass); - if(it == frame_map.end()) - throw iz3_exception("unknown assertion"); - return it->second; - } - - - void to_parents_vec_representation(const std::vector &_cnsts, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &theory, - std::vector &pos_map, - bool merge = false - ); - - protected: - std::vector cnsts; - std::vector theory; - - private: - - struct ranges { - range rng; - range scp; - bool scope_computed; - ranges(){scope_computed = false;} - }; - - stl_ext::hash_map sym_range_hash; - stl_ext::hash_map ast_ranges_hash; - stl_ext::hash_map simplify_memo; - stl_ext::hash_map frame_map; // map assertions to frames - - // int frames; // number of frames - - protected: - void add_frame_range(int frame, ast t); - - private: - void initialize(const std::vector &_parts, const std::vector &_parents, const std::vector &_theory); - - void initialize(const std::vector > &_parts, const std::vector &_parents, const std::vector &_theory); - - bool is_literal(ast n); - void gather_conjuncts_rec(ast n, std::vector &conjuncts, stl_ext::hash_set &memo); - void gather_conjuncts(ast n, std::vector &conjuncts); - ast simplify_and(std::vector &conjuncts); - ast simplify_with_lit_rec(ast n, ast lit, stl_ext::hash_map &memo, int depth); - ast simplify_with_lit(ast n, ast lit); - void find_children(const stl_ext::hash_set &cnsts_set, - const ast &tree, - std::vector &cnsts, - std::vector &parents, - std::vector &conjuncts, - std::vector &children, - std::vector &pos_map, - bool merge - ); - bool weak; - -}; - - - -#endif diff --git a/src/interp/iz3checker.cpp b/src/interp/iz3checker.cpp deleted file mode 100755 index 8fa99fe10..000000000 --- a/src/interp/iz3checker.cpp +++ /dev/null @@ -1,227 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3checker.cpp - - Abstract: - - check correctness of interpolant - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3base.h" -#include "interp/iz3checker.h" - -#include -#include -#include -#include -#include -#include -#include - - -using namespace stl_ext; - -struct iz3checker : iz3base { - - /* HACK: for tree interpolants, we assume that uninterpreted functions - are global. This is because in the current state of the tree interpolation - code, symbols that appear in sibling sub-trees have to be global, and - we have no way to eliminate such function symbols. When tree interpoaltion is - fixed, we can tree function symbols the same as constant symbols. */ - - bool is_tree; - - void support(const ast &t, std::set &res, hash_set &memo){ - if(memo.find(t) != memo.end()) return; - memo.insert(t); - - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - support(arg(t,i),res,memo); - - switch(op(t)){ - case Uninterpreted: - if(nargs == 0 || !is_tree) { - std::string name = string_of_symbol(sym(t)); - res.insert(name); - } - break; - case Forall: - case Exists: - support(get_quantifier_body(t),res,memo); - break; - default:; - } - } - - bool check(solver *s, std::ostream &err, - const std::vector &cnsts, - const std::vector &parents, - const std::vector &itp, - const std::vector &theory){ - - is_tree = !parents.empty(); - int num = cnsts.size(); - std::vector > children(num); - - for(int i = 0; i < num-1; i++){ - if(parents.size()) - children[parents[i]].push_back(i); - else - children[i+1].push_back(i); - } - - for(int i = 0; i < num; i++){ - s->push(); - for(unsigned j = 0; j < theory.size(); j++) - s->assert_expr(to_expr(theory[j].raw())); - s->assert_expr(to_expr(cnsts[i].raw())); - std::vector &cs = children[i]; - for(unsigned j = 0; j < cs.size(); j++) - s->assert_expr(to_expr(itp[cs[j]].raw())); - if(i != num-1) - s->assert_expr(to_expr(mk_not(itp[i]).raw())); - lbool result = s->check_sat(0,0); - if(result != l_false){ - err << "interpolant " << i << " is incorrect"; - - s->pop(1); - for(unsigned j = 0; j < theory.size(); j++) - s->assert_expr(to_expr(theory[j].raw())); - for(unsigned j = 0; j < cnsts.size(); j++) - if(in_range(j,range_downward(i))) - s->assert_expr(to_expr(cnsts[j].raw())); - if(i != num-1) - s->assert_expr(to_expr(mk_not(itp[i]).raw())); - lbool result = s->check_sat(0,0); - if(result != l_false) - err << "interpolant " << i << " is not implied by its downeard closurn"; - - return false; - } - s->pop(1); - } - - std::vector > supports(num); - for(int i = 0; i < num; i++){ - hash_set memo; - support(cnsts[i],supports[i],memo); - } - for(int i = 0; i < num-1; i++){ - std::vector Bside(num); - for(int j = num-1; j >= 0; j--) - Bside[j] = j != i; - for(int j = num-1; j >= 0; j--) - if(!Bside[j]){ - std::vector &cs = children[i]; - for(unsigned k = 0; k < cs.size(); k++) - Bside[cs[k]] = false; - } - std::set Asup, Bsup,common,Isup,bad; - for(int j = num-1; j >= 0; j--){ - std::set &side = Bside[j] ? Bsup : Asup; - side.insert(supports[j].begin(),supports[j].end()); - } - std::set_intersection(Asup.begin(),Asup.end(),Bsup.begin(),Bsup.end(),std::inserter(common,common.begin())); - { - hash_set tmemo; - for(unsigned j = 0; j < theory.size(); j++) - support(theory[j],common,tmemo); // all theory symbols allowed in interps - } - hash_set memo; - support(itp[i],Isup,memo); - std::set_difference(Isup.begin(),Isup.end(),common.begin(),common.end(),std::inserter(bad,bad.begin())); - if(!bad.empty()){ - err << "bad symbols in interpolant " << i << ":"; - std::copy(bad.begin(),bad.end(),std::ostream_iterator(err,",")); - return false; - } - } - return true; - } - - bool check(solver *s, std::ostream &err, - const std::vector &_cnsts, - const ast &tree, - const std::vector &itp){ - - std::vector pos_map; - - // convert to the parents vector representation - - to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map); - - //use the parents vector representation to compute interpolant - return check(s,err,cnsts,parents,itp,theory); - } - - iz3checker(ast_manager &_m) - : iz3base(_m) { - } - - iz3checker(iz3mgr &_m) - : iz3base(_m) { - } - -}; - -template -std::vector to_std_vector(const ::vector &v){ - std::vector _v(v.size()); - for(unsigned i = 0; i < v.size(); i++) - _v[i] = v[i]; - return _v; -} - - -bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &cnsts, - const ::vector &parents, - const ptr_vector &interps, - const ptr_vector &theory) -{ - iz3checker chk(_m_manager); - return chk.check(s,err,chk.cook(cnsts),to_std_vector(parents),chk.cook(interps),chk.cook(theory)); -} - -bool iz3check(iz3mgr &mgr, - solver *s, - std::ostream &err, - const std::vector &cnsts, - const std::vector &parents, - const std::vector &interps, - const std::vector &theory) -{ - iz3checker chk(mgr); - return chk.check(s,err,cnsts,parents,interps,theory); -} - -bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &_cnsts, - ast *tree, - const ptr_vector &interps) -{ - iz3checker chk(_m_manager); - return chk.check(s,err,chk.cook(_cnsts),chk.cook(tree),chk.cook(interps)); -} diff --git a/src/interp/iz3checker.h b/src/interp/iz3checker.h deleted file mode 100644 index d89db3011..000000000 --- a/src/interp/iz3checker.h +++ /dev/null @@ -1,49 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3checker.h - - Abstract: - - check correctness of an interpolant - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3_CHECKER_H -#define IZ3_CHECKER_H - -#include "interp/iz3mgr.h" -#include "solver/solver.h" - -bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &cnsts, - const ::vector &parents, - const ptr_vector &interps, - const ptr_vector &theory); - -bool iz3check(ast_manager &_m_manager, - solver *s, - std::ostream &err, - const ptr_vector &cnsts, - ast *tree, - const ptr_vector &interps); - -bool iz3check(iz3mgr &mgr, - solver *s, - std::ostream &err, - const std::vector &cnsts, - const std::vector &parents, - const std::vector &interps, - const ptr_vector &theory); - -#endif diff --git a/src/interp/iz3exception.h b/src/interp/iz3exception.h deleted file mode 100644 index b3f841565..000000000 --- a/src/interp/iz3exception.h +++ /dev/null @@ -1,28 +0,0 @@ -/*++ -Copyright (c) 2015 Microsoft Corporation - -Module Name: - - iz3exception.h - -Abstract: - - Base class for exceptions raised by interpolation routines - -Author: - -Notes: - ---*/ -#ifndef _IZ3EXCEPTION_H_ -#define _IZ3EXCEPTION_H_ - -#include "util/z3_exception.h" -#include "util/error_codes.h" - -class iz3_exception: public default_exception { -public: - iz3_exception(const std::string &msg): default_exception(msg) {} -}; - -#endif diff --git a/src/interp/iz3hash.h b/src/interp/iz3hash.h deleted file mode 100644 index c796a247b..000000000 --- a/src/interp/iz3hash.h +++ /dev/null @@ -1,479 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3hash.h - - Abstract: - - Simple implementation of bucket-list hash tables conforming to SGI - hash_map and hash_set interfaces. Just enough members are - implemented to support iz3 and duality. - - iz3 and duality need this package because they assume that insert - preserves iterators and references to elements, which is not true - of the hashtable packages in util. - - This package lives in namespace hash_space. Specializations of - class "hash" should be made in this namespace. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3_HASH_H -#define IZ3_HASH_H - -#ifdef _WINDOWS -#pragma warning(disable:4267) -#endif - -#include -#include -#include -#include "util/hash.h" - -#define stl_ext hash_space - -namespace hash_space { - - template class hash {}; - - - template <> - class hash { - public: - size_t operator()(const int &s) const { - return s; - } - }; - - template <> - class hash { - public: - size_t operator()(const std::string &s) const { - return string_hash(s.c_str(), s.size(), 0); - } - }; - - template <> - class hash > { - public: - size_t operator()(const std::pair &p) const { - return p.first + p.second; - } - }; - - template - class hash > { - public: - size_t operator()(const std::pair &p) const { - return (size_t)p.first + (size_t)p.second; - } - }; - - enum { num_primes = 29 }; - - static const unsigned long primes[num_primes] = - { - 7ul, - 53ul, - 97ul, - 193ul, - 389ul, - 769ul, - 1543ul, - 3079ul, - 6151ul, - 12289ul, - 24593ul, - 49157ul, - 98317ul, - 196613ul, - 393241ul, - 786433ul, - 1572869ul, - 3145739ul, - 6291469ul, - 12582917ul, - 25165843ul, - 50331653ul, - 100663319ul, - 201326611ul, - 402653189ul, - 805306457ul, - 1610612741ul, - 3221225473ul, - 4294967291ul - }; - - inline unsigned long next_prime(unsigned long n) { - const unsigned long* to = primes + (int)num_primes; - for(const unsigned long* p = primes; p < to; p++) - if(*p >= n) return *p; - return primes[num_primes-1]; - } - - template - class hashtable - { - public: - - typedef Value &reference; - typedef const Value &const_reference; - - struct Entry - { - Entry* next; - Value val; - - Entry(const Value &_val) : val(_val) {next = 0;} - }; - - - struct iterator - { - Entry* ent; - hashtable* tab; - - typedef std::forward_iterator_tag iterator_category; - typedef Value value_type; - typedef std::ptrdiff_t difference_type; - typedef size_t size_type; - typedef Value& reference; - typedef Value* pointer; - - iterator(Entry* _ent, hashtable* _tab) : ent(_ent), tab(_tab) { } - - iterator() { } - - Value &operator*() const { return ent->val; } - - Value *operator->() const { return &(operator*()); } - - iterator &operator++() { - Entry *old = ent; - ent = ent->next; - if (!ent) { - size_t bucket = tab->get_bucket(old->val); - while (!ent && ++bucket < tab->buckets.size()) - ent = tab->buckets[bucket]; - } - return *this; - } - - iterator operator++(int) { - iterator tmp = *this; - operator++(); - return tmp; - } - - - bool operator==(const iterator& it) const { - return ent == it.ent; - } - - bool operator!=(const iterator& it) const { - return ent != it.ent; - } - }; - - struct const_iterator - { - const Entry* ent; - const hashtable* tab; - - typedef std::forward_iterator_tag iterator_category; - typedef Value value_type; - typedef std::ptrdiff_t difference_type; - typedef size_t size_type; - typedef const Value& reference; - typedef const Value* pointer; - - const_iterator(const Entry* _ent, const hashtable* _tab) : ent(_ent), tab(_tab) { } - - const_iterator() { } - - const Value &operator*() const { return ent->val; } - - const Value *operator->() const { return &(operator*()); } - - const_iterator &operator++() { - Entry *old = ent; - ent = ent->next; - if (!ent) { - size_t bucket = tab->get_bucket(old->val); - while (!ent && ++bucket < tab->buckets.size()) - ent = tab->buckets[bucket]; - } - return *this; - } - - const_iterator operator++(int) { - const_iterator tmp = *this; - operator++(); - return tmp; - } - - - bool operator==(const const_iterator& it) const { - return ent == it.ent; - } - - bool operator!=(const const_iterator& it) const { - return ent != it.ent; - } - }; - - private: - - typedef std::vector Table; - - Table buckets; - size_t entries; - HashFun hash_fun ; - GetKey get_key; - KeyEqFun key_eq_fun; - - public: - - hashtable(size_t init_size) : buckets(init_size,(Entry *)0) { - entries = 0; - } - - hashtable(const hashtable& other) { - dup(other); - } - - hashtable& operator= (const hashtable& other) { - if (&other != this) - dup(other); - return *this; - } - - ~hashtable() { - clear(); - } - - size_t size() const { - return entries; - } - - bool empty() const { - return size() == 0; - } - - void swap(hashtable& other) { - buckets.swap(other.buckets); - std::swap(entries, other.entries); - } - - iterator begin() { - for (size_t i = 0; i < buckets.size(); ++i) - if (buckets[i]) - return iterator(buckets[i], this); - return end(); - } - - iterator end() { - return iterator(0, this); - } - - const_iterator begin() const { - for (size_t i = 0; i < buckets.size(); ++i) - if (buckets[i]) - return const_iterator(buckets[i], this); - return end(); - } - - const_iterator end() const { - return const_iterator(0, this); - } - - size_t get_bucket(const Value& val, size_t n) const { - return hash_fun(get_key(val)) % n; - } - - size_t get_key_bucket(const Key& key) const { - return hash_fun(key) % buckets.size(); - } - - size_t get_bucket(const Value& val) const { - return get_bucket(val,buckets.size()); - } - - Entry *lookup(const Value& val, bool ins = false) - { - resize(entries + 1); - - size_t n = get_bucket(val); - Entry* from = buckets[n]; - - for (Entry* ent = from; ent; ent = ent->next) - if (key_eq_fun(get_key(ent->val), get_key(val))) - return ent; - - if(!ins) return 0; - - Entry* tmp = new Entry(val); - tmp->next = from; - buckets[n] = tmp; - ++entries; - return tmp; - } - - Entry *lookup_key(const Key& key) const - { - size_t n = get_key_bucket(key); - Entry* from = buckets[n]; - - for (Entry* ent = from; ent; ent = ent->next) - if (key_eq_fun(get_key(ent->val), key)) - return ent; - - return 0; - } - - const_iterator find(const Key& key) const { - return const_iterator(lookup_key(key),this); - } - - iterator find(const Key& key) { - return iterator(lookup_key(key),this); - } - - std::pair insert(const Value& val){ - size_t old_entries = entries; - Entry *ent = lookup(val,true); - return std::pair(iterator(ent,this),entries > old_entries); - } - - iterator insert(const iterator &it, const Value& val){ - Entry *ent = lookup(val,true); - return iterator(ent,this); - } - - size_t erase(const Key& key) - { - Entry** p = &(buckets[get_key_bucket(key)]); - size_t count = 0; - while(*p){ - Entry *q = *p; - if (key_eq_fun(get_key(q->val), key)) { - ++count; - *p = q->next; - delete q; - } - else - p = &(q->next); - } - entries -= count; - return count; - } - - void resize(size_t new_size) { - const size_t old_n = buckets.size(); - if (new_size <= old_n) return; - const size_t n = next_prime(new_size); - if (n <= old_n) return; - Table tmp(n, (Entry*)(0)); - for (size_t i = 0; i < old_n; ++i) { - Entry* ent = buckets[i]; - while (ent) { - size_t new_bucket = get_bucket(ent->val, n); - buckets[i] = ent->next; - ent->next = tmp[new_bucket]; - tmp[new_bucket] = ent; - ent = buckets[i]; - } - } - buckets.swap(tmp); - } - - void clear() - { - for (size_t i = 0; i < buckets.size(); ++i) { - for (Entry* ent = buckets[i]; ent != 0;) { - Entry* next = ent->next; - delete ent; - ent = next; - } - buckets[i] = 0; - } - entries = 0; - } - - void dup(const hashtable& other) - { - buckets.resize(other.buckets.size()); - for (size_t i = 0; i < other.buckets.size(); ++i) { - Entry** to = &buckets[i]; - for (Entry* from = other.buckets[i]; from; from = from->next) - to = &((*to = new Entry(from->val))->next); - } - entries = other.entries; - } - }; - - template - class equal { - public: - bool operator()(const T& x, const T &y) const { - return x == y; - } - }; - - template - class identity { - public: - const T &operator()(const T &x) const { - return x; - } - }; - - template - class proj1 { - public: - const T &operator()(const std::pair &x) const { - return x.first; - } - }; - - template , - class EqFun = equal > - class hash_set - : public hashtable,EqFun> { - - public: - - typedef Element value_type; - - hash_set() - : hashtable,EqFun>(7) {} - }; - - template , - class EqFun = equal > - class hash_map - : public hashtable,Key,HashFun,proj1,EqFun> { - - public: - - hash_map() - : hashtable,Key,HashFun,proj1,EqFun>(7) {} - - Value &operator[](const Key& key) { - std::pair kvp(key,Value()); - return - hashtable,Key,HashFun,proj1,EqFun>:: - lookup(kvp,true)->val.second; - } - }; - -} -#endif diff --git a/src/interp/iz3interp.cpp b/src/interp/iz3interp.cpp deleted file mode 100755 index c1c4588cc..000000000 --- a/src/interp/iz3interp.cpp +++ /dev/null @@ -1,573 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3interp.cpp - - Abstract: - - Interpolation based on proof translation. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -/* Copyright 2011 Microsoft Research. */ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include -#include -#include -#include -#include -#include - -#include "interp/iz3profiling.h" -#include "interp/iz3translate.h" -#include "interp/iz3proof.h" -#include "interp/iz3hash.h" -#include "interp/iz3interp.h" - -#include "ast/scoped_proof.h" - - -using namespace stl_ext; - - - -#if 1 - -struct frame_reducer : public iz3mgr { - - int frames; - hash_map frame_map; - std::vector assertions_map; - std::vector orig_parents_copy; - std::vector used_frames; - - - frame_reducer(const iz3mgr &other) - : iz3mgr(other) {} - - void get_proof_assumptions_rec(z3pf proof, hash_set &memo, std::vector &used_frames){ - if(memo.find(proof) != memo.end())return; - memo.insert(proof); - pfrule dk = pr(proof); - if(dk == PR_ASSERTED){ - ast con = conc(proof); - if(frame_map.find(con) != frame_map.end()){ // false for theory facts - int frame = frame_map[con]; - used_frames[frame] = true; - } - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - z3pf arg = prem(proof,i); - get_proof_assumptions_rec(arg,memo,used_frames); - } - } - } - - void get_frames(const std::vector >&z3_preds, - const std::vector &orig_parents, - std::vector >&assertions, - std::vector &parents, - z3pf proof){ - frames = z3_preds.size(); - orig_parents_copy = orig_parents; - for(unsigned i = 0; i < z3_preds.size(); i++) - for(unsigned j = 0; j < z3_preds[i].size(); j++) - frame_map[z3_preds[i][j]] = i; - used_frames.resize(frames); - hash_set memo; - get_proof_assumptions_rec(proof,memo,used_frames); - std::vector assertions_back_map(frames); - - // if multiple children of a tree node are used, we can't delete it - std::vector used_children; - for(int i = 0; i < frames; i++) - used_children.push_back(0); - for(int i = 0; i < frames; i++) - if(orig_parents[i] != SHRT_MAX) - if(used_frames[i] || used_children[i]){ - if(used_children[i] > 1) - used_frames[i] = true; - used_children[orig_parents[i]]++; - } - - for(unsigned i = 0; i < z3_preds.size(); i++) - if(used_frames[i] || i == z3_preds.size() - 1){ - assertions.push_back(z3_preds[i]); - assertions_map.push_back(i); - assertions_back_map[i] = assertions.size() - 1; - } - - if(orig_parents.size()){ - parents.resize(assertions.size()); - for(unsigned i = 0; i < assertions.size(); i++){ - int p = orig_parents[assertions_map[i]]; - while(p != SHRT_MAX && !used_frames[p]) - p = orig_parents[p]; - parents[i] = p == SHRT_MAX ? p : assertions_back_map[p]; - } - } - - // std::cout << "used frames = " << frames << "\n"; - } - - void fix_interpolants(std::vector &interpolants){ - std::vector unfixed = interpolants; - interpolants.resize(frames - 1); - for(int i = 0; i < frames - 1; i++) - interpolants[i] = mk_true(); - for(unsigned i = 0; i < unfixed.size(); i++) - interpolants[assertions_map[i]] = unfixed[i]; - for(int i = 0; i < frames-2; i++){ - int p = orig_parents_copy.size() == 0 ? i+1 : orig_parents_copy[i]; - if(p < frames - 1 && !used_frames[p]) - interpolants[p] = mk_and(interpolants[i],interpolants[p]); - } - } -}; - -#else -struct frame_reducer { - - - - frame_reducer(context _ctx){ - } - - void get_frames(const std::vector &z3_preds, - const std::vector &orig_parents, - std::vector &assertions, - std::vector &parents, - ast proof){ - assertions = z3_preds; - parents = orig_parents; - } - - void fix_interpolants(std::vector &interpolants){ - } -}; - -#endif - - - -template -struct killme { - T *p; - killme(){p = 0;} - void set(T *_p) {p = _p;} - ~killme(){ - if(p) - delete p; - } -}; - - -class iz3interp : public iz3base { -public: - - killme sp_killer; - killme tr_killer; - - bool is_linear(std::vector &parents){ - for(int i = 0; i < ((int)parents.size())-1; i++) - if(parents[i] != i+1) - return false; - return true; - } - - void test_secondary(const std::vector &cnsts, - const std::vector &parents, - std::vector &interps - ){ - throw iz3_exception("secondary interpolating prover not supported"); - } - - void proof_to_interpolant(z3pf proof, - const std::vector > &cnsts, - const std::vector &parents, - std::vector &interps, - const std::vector &theory, - interpolation_options_struct *options = 0 - ){ -#if 0 - test_secondary(cnsts,parents,interps); - return; -#endif - - profiling::timer_start("Interpolation prep"); - - // get rid of frames not used in proof - - std::vector > cnsts_vec; - std::vector parents_vec; - frame_reducer fr(*this); - fr.get_frames(cnsts,parents,cnsts_vec,parents_vec,proof); - - int num = cnsts_vec.size(); - std::vector interps_vec(num-1); - - // if this is really a sequence problem, we can make it easier - if(is_linear(parents_vec)) - parents_vec.clear(); - - // secondary prover no longer supported - iz3secondary *sp = NULL; - -#define BINARY_INTERPOLATION -#ifndef BINARY_INTERPOLATION - // create a translator - iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec,parents_vec,theory); - tr_killer.set(tr); - - // set the translation options, if needed - if(options) - for(hash_map::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it) - tr->set_option(it->first, it->second); - - // create a proof object to hold the translation - iz3proof pf(tr); - - profiling::timer_stop("Interpolation prep"); - - // translate into an interpolatable proof - profiling::timer_start("Proof translation"); - try { - tr->translate(proof,pf); - } - catch (const char *msg) { - throw interpolation_failure(msg); - } - catch (const iz3translation::unsupported &) { - throw interpolation_error(); - } - catch (const iz3proof::proof_error &) { - throw interpolation_error(); - } - profiling::timer_stop("Proof translation"); - - // translate the proof into interpolants - profiling::timer_start("Proof interpolation"); - for(int i = 0; i < num-1; i++){ - interps_vec[i] = pf.interpolate(tr->range_downward(i),tr->weak_mode()); - interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(i)); - } - profiling::timer_stop("Proof interpolation"); -#else - iz3base the_base(*this,cnsts_vec,parents_vec,theory); - - profiling::timer_stop("Interpolation prep"); - - for(int i = 0; i < num-1; i++){ - range rng = the_base.range_downward(i); - std::vector > cnsts_vec_vec(2); - for(unsigned j = 0; j < cnsts_vec.size(); j++){ - bool is_A = the_base.in_range(j,rng); - for(unsigned k = 0; k < cnsts_vec[j].size(); k++) - cnsts_vec_vec[is_A ? 0 : 1].push_back(cnsts_vec[j][k]); - } - - killme tr_killer_i; - iz3translation *tr = iz3translation::create(*this,sp,cnsts_vec_vec,std::vector(),theory); - tr_killer_i.set(tr); - - // set the translation options, if needed - if(options) - for(hash_map::iterator it = options->map.begin(), en = options->map.end(); it != en; ++it) - tr->set_option(it->first, it->second); - - // create a proof object to hold the translation - iz3proof pf(tr); - - // translate into an interpolatable proof - profiling::timer_start("Proof translation"); - try { - tr->translate(proof,pf); - } - catch (const char *msg) { - throw interpolation_failure(msg); - } - catch (const iz3translation::unsupported &) { - throw interpolation_error(); - } - catch (const iz3proof::proof_error &) { - throw interpolation_error(); - } - profiling::timer_stop("Proof translation"); - - // translate the proof into interpolants - profiling::timer_start("Proof interpolation"); - interps_vec[i] = pf.interpolate(tr->range_downward(0),tr->weak_mode()); - interps_vec[i] = tr->quantify(interps_vec[i],tr->range_downward(0)); - profiling::timer_stop("Proof interpolation"); - } -#endif - // put back in the removed frames - fr.fix_interpolants(interps_vec); - - interps = interps_vec; - - } - - - void proof_to_interpolant(z3pf proof, - std::vector &cnsts, - const std::vector &parents, - std::vector &interps, - const std::vector &theory, - interpolation_options_struct *options = 0 - ){ - std::vector > cnsts_vec(cnsts.size()); - for(unsigned i = 0; i < cnsts.size(); i++) - cnsts_vec[i].push_back(cnsts[i]); - proof_to_interpolant(proof,cnsts_vec,parents,interps,theory,options); - } - - // same as above, but represents the tree using an ast - - void proof_to_interpolant(const z3pf &proof, - const std::vector &_cnsts, - const ast &tree, - std::vector &interps, - interpolation_options_struct *options = 0 - ){ - std::vector pos_map; - - // convert to the parents vector representation - - to_parents_vec_representation(_cnsts, tree, cnsts, parents, theory, pos_map); - - //use the parents vector representation to compute interpolant - proof_to_interpolant(proof,cnsts,parents,interps,theory,options); - - // get the interps for the tree positions - std::vector _interps = interps; - interps.resize(pos_map.size()); - for(unsigned i = 0; i < pos_map.size(); i++){ - unsigned j = pos_map[i]; - interps[i] = j < _interps.size() ? _interps[j] : mk_false(); - } - } - - bool has_interp(hash_map &memo, const ast &t){ - if(memo.find(t) != memo.end()) - return memo[t]; - bool res = false; - if(op(t) == Interp) - res = true; - else if(op(t) == And){ - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - res |= has_interp(memo, arg(t,i)); - } - memo[t] = res; - return res; - } - - void collect_conjuncts(std::vector &cnsts, hash_map &memo, const ast &t){ - if(!has_interp(memo,t)) - cnsts.push_back(t); - else { - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - collect_conjuncts(cnsts, memo, arg(t,i)); - } - } - - void assert_conjuncts(solver &s, std::vector &cnsts, const ast &t){ - hash_map memo; - collect_conjuncts(cnsts,memo,t); - for(unsigned i = 0; i < cnsts.size(); i++) - s.assert_expr(to_expr(cnsts[i].raw())); - } - - void get_proof_assumptions(z3pf proof, std::vector &cnsts, hash_set &memo){ - if(memo.find(proof) != memo.end())return; - memo.insert(proof); - pfrule dk = pr(proof); - if(dk == PR_ASSERTED) - cnsts.push_back(conc(proof)); - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - z3pf arg = prem(proof,i); - get_proof_assumptions(arg,cnsts,memo); - } - } - } - - iz3interp(ast_manager &_m_manager) - : iz3base(_m_manager) {} -}; - - - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options) -{ - iz3interp itp(_m_manager); - if(options) - options->apply(itp); - std::vector _cnsts(cnsts.size()); - std::vector _parents(parents.size()); - std::vector _interps; - std::vector _theory(theory.size()); - for(unsigned i = 0; i < cnsts.size(); i++) - _cnsts[i] = itp.cook(cnsts[i]); - for(unsigned i = 0; i < parents.size(); i++) - _parents[i] = parents[i]; - for(unsigned i = 0; i < theory.size(); i++) - _theory[i] = itp.cook(theory[i]); - iz3mgr::ast _proof = itp.cook(proof); - itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options); - interps.resize(_interps.size()); - for(unsigned i = 0; i < interps.size(); i++) - interps[i] = itp.uncook(_interps[i]); -} - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ::vector > &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options) -{ - iz3interp itp(_m_manager); - if(options) - options->apply(itp); - std::vector > _cnsts(cnsts.size()); - std::vector _parents(parents.size()); - std::vector _interps; - std::vector _theory(theory.size()); - for(unsigned i = 0; i < cnsts.size(); i++) - for(unsigned j = 0; j < cnsts[i].size(); j++) - _cnsts[i].push_back(itp.cook(cnsts[i][j])); - for(unsigned i = 0; i < parents.size(); i++) - _parents[i] = parents[i]; - for(unsigned i = 0; i < theory.size(); i++) - _theory[i] = itp.cook(theory[i]); - iz3mgr::ast _proof = itp.cook(proof); - itp.proof_to_interpolant(_proof,_cnsts,_parents,_interps,_theory,options); - interps.resize(_interps.size()); - for(unsigned i = 0; i < interps.size(); i++) - interps[i] = itp.uncook(_interps[i]); -} - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - ast *tree, - ptr_vector &interps, - interpolation_options_struct * options) -{ - iz3interp itp(_m_manager); - if(options) - options->apply(itp); - std::vector _cnsts(cnsts.size()); - std::vector _interps; - for(unsigned i = 0; i < cnsts.size(); i++) - _cnsts[i] = itp.cook(cnsts[i]); - iz3mgr::ast _proof = itp.cook(proof); - iz3mgr::ast _tree = itp.cook(tree); - - // if consts isn't provided, we can reconstruct it - if(_cnsts.empty()){ - hash_set memo; - itp.get_proof_assumptions(_proof,_cnsts,memo); - } - - itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options); - interps.resize(_interps.size()); - for(unsigned i = 0; i < interps.size(); i++) - interps[i] = itp.uncook(_interps[i]); -} - -lbool iz3interpolate(ast_manager &_m_manager, - solver &s, - ast *tree, - ptr_vector &cnsts, - ptr_vector &interps, - model_ref &m, - interpolation_options_struct * options) -{ - iz3interp itp(_m_manager); - if(options) - options->apply(itp); - iz3mgr::ast _tree = itp.cook(tree); - std::vector _cnsts; - itp.assert_conjuncts(s,_cnsts,_tree); - profiling::timer_start("solving"); - lbool res = s.check_sat(0,0); - profiling::timer_stop("solving"); - if(res == l_false){ - ast *proof = s.get_proof(); - iz3mgr::ast _proof = itp.cook(proof); - std::vector _interps; - itp.proof_to_interpolant(_proof,_cnsts,_tree,_interps,options); - interps.resize(_interps.size()); - for(unsigned i = 0; i < interps.size(); i++) - interps[i] = itp.uncook(_interps[i]); - } - else if(m){ - s.get_model(m); - } - cnsts.resize(_cnsts.size()); - for(unsigned i = 0; i < cnsts.size(); i++) - cnsts[i] = itp.uncook(_cnsts[i]); - return res; -} - - - -void interpolation_options_struct::apply(iz3base &b){ - for(stl_ext::hash_map::iterator it = map.begin(), en = map.end(); - it != en; - ++it) - b.set_option((*it).first,(*it).second); -} - -// On linux and mac, unlimit stack space so we get recursion - -#if defined(_WINDOWS) || defined(_CYGWIN) || defined(_MINGW) - -#else - -#include -#include - -class iz3stack_unlimiter { -public: - iz3stack_unlimiter() { - struct rlimit rl = {RLIM_INFINITY, RLIM_INFINITY}; - setrlimit(RLIMIT_STACK, &rl); - // nothing to be done if above fails - } -}; - -// initializing this will unlimit stack - -iz3stack_unlimiter the_iz3stack_unlimiter; - -#endif diff --git a/src/interp/iz3interp.h b/src/interp/iz3interp.h deleted file mode 100644 index a4e1024a9..000000000 --- a/src/interp/iz3interp.h +++ /dev/null @@ -1,123 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3interp.h - - Abstract: - - Interpolation based on proof translation. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3_INTERP_H -#define IZ3_INTERP_H - -#include "interp/iz3hash.h" -#include "interp/iz3exception.h" -#include "solver/solver.h" - -class iz3base; - -struct interpolation_options_struct { - stl_ext::hash_map map; -public: - void set(const std::string &name, const std::string &value){ - map[name] = value; - } - void apply(iz3base &b); -}; - -/** This object is thrown if a tree interpolation problem is mal-formed */ -struct iz3_bad_tree: public iz3_exception { - iz3_bad_tree(): iz3_exception("iz3_bad_tree") {} -}; - -/** This object is thrown when iz3 fails due to an incompleteness in - the secondary solver. */ -struct iz3_incompleteness: public iz3_exception { - iz3_incompleteness(): iz3_exception("iz3_incompleteness") {} -}; - -// This is thrown if there is some bug in the -// interpolation procedure -class interpolation_failure : public default_exception { - public: - interpolation_failure(const char *msg) - : default_exception(msg) - { - } -}; - -// This is thrown if we cannot derive an interpolant from a proof -// because it contains unsupported theories or if the proof contains -// errors -class interpolation_error : public default_exception { - public: - interpolation_error() - : default_exception("theory not supported by interpolation or bad proof" ) - { - } -}; - -typedef interpolation_options_struct *interpolation_options; - -/* Compute an interpolant from a proof. This version uses the parents vector - representation, for compatibility with the old API. */ - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options = 0); - -/* Same as above, but each constraint is a vector of formulas. */ - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const vector > &cnsts, - const ::vector &parents, - ptr_vector &interps, - const ptr_vector &theory, - interpolation_options_struct * options = 0); - -/* Compute an interpolant from a proof. This version uses the ast - representation, for compatibility with the new API. Here, cnsts is - a vector of all the assertions in the proof. This can be - over-approximated by the set of all assertions in the - solver. However, if it is empty it will be reconstructed from the - proof, so it can be considered a hint. */ - -void iz3interpolate(ast_manager &_m_manager, - ast *proof, - const ptr_vector &cnsts, - ast *tree, - ptr_vector &interps, - interpolation_options_struct * options); - - -/* Compute an interpolant from an ast representing an interpolation - problem, if unsat, else return a model (if enabled). Uses the - given solver to produce the proof/model. Also returns a vector - of the constraints in the problem, helpful for checking correctness. -*/ - -lbool iz3interpolate(ast_manager &_m_manager, - solver &s, - ast *tree, - ptr_vector &cnsts, - ptr_vector &interps, - model_ref &m, - interpolation_options_struct * options); - - -#endif diff --git a/src/interp/iz3mgr.cpp b/src/interp/iz3mgr.cpp deleted file mode 100755 index 7314403b0..000000000 --- a/src/interp/iz3mgr.cpp +++ /dev/null @@ -1,969 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3mgr.cpp - - Abstract: - - A wrapper around an ast manager, providing convenience methods. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#pragma warning(disable:4805) -#pragma warning(disable:4800) -#endif - -#include "interp/iz3mgr.h" - -#include -#include -#include -#include -#include - -#include "ast/expr_abstract.h" -#include "util/params.h" -#include "ast/used_vars.h" - - -using namespace stl_ext; - - -std::ostream &operator <<(std::ostream &s, const iz3mgr::ast &a){ - return s; -} - - -iz3mgr::ast iz3mgr::make_var(const std::string &name, type ty){ - symbol s = symbol(name.c_str()); - return cook(m().mk_const(m().mk_const_decl(s, ty))); -} - -iz3mgr::ast iz3mgr::make(opr op, int n, raw_ast **args){ - switch(op) { - case True: return mki(m_basic_fid,OP_TRUE,n,args); - case False: return mki(m_basic_fid,OP_FALSE,n,args); - case Equal: return mki(m_basic_fid,OP_EQ,n,args); - case Distinct: return mki(m_basic_fid,OP_DISTINCT,n,args); - case Ite: return mki(m_basic_fid,OP_ITE,n,args); - case And: return mki(m_basic_fid,OP_AND,n,args); - case Or: return mki(m_basic_fid,OP_OR,n,args); - case Iff: return mki(m_basic_fid,OP_IFF,n,args); - case Xor: return mki(m_basic_fid,OP_XOR,n,args); - case Not: return mki(m_basic_fid,OP_NOT,n,args); - case Implies: return mki(m_basic_fid,OP_IMPLIES,n,args); - case Oeq: return mki(m_basic_fid,OP_OEQ,n,args); - case Interp: return mki(m_basic_fid,OP_INTERP,n,args); - case Leq: return mki(m_arith_fid,OP_LE,n,args); - case Geq: return mki(m_arith_fid,OP_GE,n,args); - case Lt: return mki(m_arith_fid,OP_LT,n,args); - case Gt: return mki(m_arith_fid,OP_GT,n,args); - case Plus: return mki(m_arith_fid,OP_ADD,n,args); - case Sub: return mki(m_arith_fid,OP_SUB,n,args); - case Uminus: return mki(m_arith_fid,OP_UMINUS,n,args); - case Times: return mki(m_arith_fid,OP_MUL,n,args); - case Div: return mki(m_arith_fid,OP_DIV,n,args); - case Idiv: return mki(m_arith_fid,OP_IDIV,n,args); - case Rem: return mki(m_arith_fid,OP_REM,n,args); - case Mod: return mki(m_arith_fid,OP_MOD,n,args); - case Power: return mki(m_arith_fid,OP_POWER,n,args); - case ToReal: return mki(m_arith_fid,OP_TO_REAL,n,args); - case ToInt: return mki(m_arith_fid,OP_TO_INT,n,args); - case IsInt: return mki(m_arith_fid,OP_IS_INT,n,args); - case Store: return mki(m_array_fid,OP_STORE,n,args); - case Select: return mki(m_array_fid,OP_SELECT,n,args); - case ConstArray: return mki(m_array_fid,OP_CONST_ARRAY,n,args); - case ArrayDefault: return mki(m_array_fid,OP_ARRAY_DEFAULT,n,args); - case ArrayMap: return mki(m_array_fid,OP_ARRAY_MAP,n,args); - case SetUnion: return mki(m_array_fid,OP_SET_UNION,n,args); - case SetIntersect: return mki(m_array_fid,OP_SET_INTERSECT,n,args); - case SetDifference: return mki(m_array_fid,OP_SET_DIFFERENCE,n,args); - case SetComplement: return mki(m_array_fid,OP_SET_COMPLEMENT,n,args); - case SetSubSet: return mki(m_array_fid,OP_SET_SUBSET,n,args); - case AsArray: return mki(m_array_fid,OP_AS_ARRAY,n,args); - default: - assert(0); - return ast(); - } -} - -iz3mgr::ast iz3mgr::mki(family_id fid, decl_kind dk, int n, raw_ast **args){ - return cook(m().mk_app(fid, dk, 0, 0, n, (expr **)args)); -} - -iz3mgr::ast iz3mgr::make(opr op, const std::vector &args){ - static std::vector a(10); - if(a.size() < args.size()) - a.resize(args.size()); - for(unsigned i = 0; i < args.size(); i++) - a[i] = args[i].raw(); - return make(op,args.size(), args.size() ? &a[0] : 0); -} - -iz3mgr::ast iz3mgr::make(opr op){ - return make(op,0,0); -} - -iz3mgr::ast iz3mgr::make(opr op, const ast &arg0){ - raw_ast *a = arg0.raw(); - return make(op,1,&a); -} - -iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1){ - raw_ast *args[2]; - args[0] = arg0.raw(); - args[1] = arg1.raw(); - return make(op,2,args); -} - -iz3mgr::ast iz3mgr::make(opr op, const ast &arg0, const ast &arg1, const ast &arg2){ - raw_ast *args[3]; - args[0] = arg0.raw(); - args[1] = arg1.raw(); - args[2] = arg2.raw(); - return make(op,3,args); -} - -iz3mgr::ast iz3mgr::make(symb sym, int n, raw_ast **args){ - return cook(m().mk_app(sym, n, (expr **) args)); -} - -iz3mgr::ast iz3mgr::make(symb sym, const std::vector &args){ - static std::vector a(10); - if(a.size() < args.size()) - a.resize(args.size()); - for(unsigned i = 0; i < args.size(); i++) - a[i] = args[i].raw(); - return make(sym,args.size(), args.size() ? &a[0] : 0); -} - -iz3mgr::ast iz3mgr::make(symb sym){ - return make(sym,0,0); -} - -iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0){ - raw_ast *a = arg0.raw(); - return make(sym,1,&a); -} - -iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1){ - raw_ast *args[2]; - args[0] = arg0.raw(); - args[1] = arg1.raw(); - return make(sym,2,args); -} - -iz3mgr::ast iz3mgr::make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2){ - raw_ast *args[3]; - args[0] = arg0.raw(); - args[1] = arg1.raw(); - args[2] = arg2.raw(); - return make(sym,3,args); -} - -iz3mgr::ast iz3mgr::make_quant(opr op, const std::vector &bvs, ast &body){ - if(bvs.size() == 0) return body; - std::vector foo(bvs.size()); - - - std::vector names; - std::vector types; - std::vector bound_asts; - unsigned num_bound = bvs.size(); - - for (unsigned i = 0; i < num_bound; ++i) { - app* a = to_app(bvs[i].raw()); - symbol s(to_app(a)->get_decl()->get_name()); - names.push_back(s); - types.push_back(m().get_sort(a)); - bound_asts.push_back(a); - } - expr_ref abs_body(m()); - expr_abstract(m(), 0, num_bound, &bound_asts[0], to_expr(body.raw()), abs_body); - expr_ref result(m()); - result = m().mk_quantifier( - op == Forall, - names.size(), &types[0], &names[0], abs_body.get(), - 0, - symbol("itp"), - symbol(), - 0, 0, - 0, 0 - ); - return cook(result.get()); -} - -// FIXME replace this with existing Z3 functionality - -iz3mgr::ast iz3mgr::clone(const ast &t, const std::vector &_args){ - if(_args.size() == 0) - return t; - - ast_manager& m = m_manager; - expr* a = to_expr(t.raw()); - static std::vector rargs(10); - if(rargs.size() < _args.size()) - rargs.resize(_args.size()); - for(unsigned i = 0; i < _args.size(); i++) - rargs[i] = _args[i].raw(); - expr* const* args = (expr **)&rargs[0]; - switch(a->get_kind()) { - case AST_APP: { - app* e = to_app(a); - if (e->get_num_args() != _args.size()) { - assert(0); - } - else { - a = m.mk_app(e->get_decl(), _args.size(), args); - } - break; - } - case AST_QUANTIFIER: { - if (_args.size() != 1) { - assert(0); - } - else { - a = m.update_quantifier(to_quantifier(a), args[0]); - } - break; - } - default: - break; - } - return cook(a); -} - - -void iz3mgr::show(ast t){ - if(t.null()){ - std::cout << "(null)" << std::endl; - } - params_ref p; - p.set_bool("flat_assoc",false); - std::cout << mk_pp(t.raw(), m(), p) << std::endl; -} - -void iz3mgr::show_symb(symb s){ - std::cout << mk_pp(s, m()) << std::endl; -} - -void iz3mgr::print_expr(std::ostream &s, const ast &e){ - params_ref p; - p.set_bool("flat_assoc",false); - s << mk_pp(e.raw(), m(), p); -} - - -void iz3mgr::print_clause(std::ostream &s, std::vector &cls){ - s << "("; - for(unsigned i = 0; i < cls.size(); i++){ - if(i > 0) s << ","; - print_expr(s,cls[i]); - } - s << ")"; -} - -void iz3mgr::show_clause(std::vector &cls){ - print_clause(std::cout,cls); - std::cout << std::endl; -} - -void iz3mgr::print_lit(ast lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - int f = op(abslit); - if(f == And || f == Or || f == Iff){ - if(is_not(lit)) std::cout << "~"; - std::cout << "[" << abslit << "]"; - } - else - std::cout << lit; -} - - -static int pretty_cols = 79; -static int pretty_indent_chars = 2; - -static int pretty_find_delim(const std::string &s, int pos){ - int level = 0; - int end = s.size(); - for(; pos < end; pos++){ - int ch = s[pos]; - if(ch == '(')level++; - if(ch == ')')level--; - if(level < 0 || (level == 0 && ch == ','))break; - } - return pos; -} - -static void pretty_newline(std::ostream &f, int indent){ - f << std::endl; - for(int i = 0; i < indent; i++) - f << " "; -} - -void iz3mgr::pretty_print(std::ostream &f, const std::string &s){ - int cur_indent = 0; - int indent = 0; - int col = 0; - int pos = 0; - while(pos < (int)s.size()){ - int delim = pretty_find_delim(s,pos); - if(s[pos] != ')' && s[pos] != ',' && cur_indent > indent){ - pretty_newline(f,indent); - cur_indent = indent; - col = indent; - continue; - } - if (col + delim - pos > pretty_cols) { - if (col > indent) { - pretty_newline(f,indent); - cur_indent = indent; - col = indent; - continue; - } - int paren = s.find('(',pos); - if(paren != (int)std::string::npos){ - int chars = paren - pos + 1; - f << s.substr(pos,chars); - indent += pretty_indent_chars; - if(col) pretty_newline(f,indent); - cur_indent = indent; - pos += chars; - col = indent; - continue; - } - } - int chars = delim - pos + 1; - f << s.substr(pos,chars); - pos += chars; - col += chars; - if(s[delim] == ')') - indent -= pretty_indent_chars; - } -} - - -iz3mgr::opr iz3mgr::op(const ast &t){ - ast_kind dk = t.raw()->get_kind(); - switch(dk){ - case AST_APP: { - expr * e = to_expr(t.raw()); - func_decl *d = to_app(t.raw())->get_decl(); - if (null_family_id == d->get_family_id()) - return Uninterpreted; - // return (opr)d->get_decl_kind(); - if (m_basic_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_TRUE: return True; - case OP_FALSE: return False; - case OP_EQ: return Equal; - case OP_DISTINCT: return Distinct; - case OP_ITE: return Ite; - case OP_AND: return And; - case OP_OR: return Or; - case OP_IFF: return Iff; - case OP_XOR: return Xor; - case OP_NOT: return Not; - case OP_IMPLIES: return Implies; - case OP_OEQ: return Oeq; - case OP_INTERP: return Interp; - default: - return Other; - } - } - if (m_arith_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_LE: return Leq; - case OP_GE: return Geq; - case OP_LT: return Lt; - case OP_GT: return Gt; - case OP_ADD: return Plus; - case OP_SUB: return Sub; - case OP_UMINUS: return Uminus; - case OP_MUL: return Times; - case OP_DIV: return Div; - case OP_IDIV: return Idiv; - case OP_REM: return Rem; - case OP_MOD: return Mod; - case OP_POWER: return Power; - case OP_TO_REAL: return ToReal; - case OP_TO_INT: return ToInt; - case OP_IS_INT: return IsInt; - default: - if (m().is_unique_value(e)) - return Numeral; - return Other; - } - } - if (m_array_fid == d->get_family_id()) { - switch(d->get_decl_kind()) { - case OP_STORE: return Store; - case OP_SELECT: return Select; - case OP_CONST_ARRAY: return ConstArray; - case OP_ARRAY_DEFAULT: return ArrayDefault; - case OP_ARRAY_MAP: return ArrayMap; - case OP_SET_UNION: return SetUnion; - case OP_SET_INTERSECT: return SetIntersect; - case OP_SET_DIFFERENCE: return SetDifference; - case OP_SET_COMPLEMENT: return SetComplement; - case OP_SET_SUBSET: return SetSubSet; - case OP_AS_ARRAY: return AsArray; - default: - return Other; - } - } - - return Other; - } - - - case AST_QUANTIFIER: - return to_quantifier(t.raw())->is_forall() ? Forall : Exists; - case AST_VAR: - return Variable; - default:; - } - return Other; -} - - -iz3mgr::pfrule iz3mgr::pr(const ast &t){ - func_decl *d = to_app(t.raw())->get_decl(); - assert(m_basic_fid == d->get_family_id()); - return d->get_decl_kind(); -} - -void iz3mgr::print_sat_problem(std::ostream &out, const ast &t){ - ast_smt_pp pp(m()); - pp.set_simplify_implies(false); - pp.display_smt2(out, to_expr(t.raw())); -} - -iz3mgr::ast iz3mgr::z3_simplify(const ast &e){ - ::expr * a = to_expr(e.raw()); - params_ref p; - th_rewriter m_rw(m(), p); - expr_ref result(m()); - m_rw(a, result); - return cook(result); -} - -iz3mgr::ast iz3mgr::z3_really_simplify(const ast &e){ - ::expr * a = to_expr(e.raw()); - params_ref simp_params; - simp_params.set_bool(":som",true); - simp_params.set_bool(":sort-sums",true); - th_rewriter m_rw(m(), simp_params); - expr_ref result(m()); - m_rw(a, result); - return cook(result); -} - - -#if 0 -static rational lcm(const rational &x, const rational &y){ - int a = x.numerator(); - int b = y.numerator(); - return rational(a * b / gcd(a, b)); -} -#endif - -static rational extract_lcd(std::vector &res){ - if(res.size() == 0) return rational(1); // shouldn't happen - rational lcd = denominator(res[0]); - for(unsigned i = 1; i < res.size(); i++) - lcd = lcm(lcd,denominator(res[i])); - for(unsigned i = 0; i < res.size(); i++) - res[i] *= lcd; - return lcd; -} - -void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& coeffs){ - std::vector rats; - get_farkas_coeffs(proof,rats); - coeffs.resize(rats.size()); - for(unsigned i = 0; i < rats.size(); i++){ - class sort *is = m().mk_sort(m_arith_fid, INT_SORT); - ast coeff = cook(m_arith_util.mk_numeral(rats[i],is)); - coeffs[i] = coeff; - } -} - -static void abs_rat(std::vector &rats){ - // check that they are all non-neg -- if neg, take abs val and warn! - for(unsigned i = 0; i < rats.size(); i++) - if(rats[i].is_neg()){ - // std::cout << "negative Farkas coeff!\n"; - rats[i] = -rats[i]; - } -} - -bool iz3mgr::is_farkas_coefficient_negative(const ast &proof, int n){ - rational r; - symb s = sym(proof); - bool ok = s->get_parameter(n+2).is_rational(r); - if(!ok) - throw iz3_exception("Bad Farkas coefficient"); - return r.is_neg(); -} - -void iz3mgr::get_farkas_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-2); -#if 0 - if(num_prems(proof) < numps-2){ - std::cout << "bad farkas rule: " << num_prems(proof) << " premises should be " << numps-2 << "\n"; - } -#endif - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw iz3_exception("Bad Farkas coefficient"); -#if 0 - { - ast con = conc(prem(proof,i-2)); - ast temp = make_real(r); // for debugging - opr o = is_not(con) ? op(arg(con,0)) : op(con); - if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) - r = -r; - } -#endif - rats[i-2] = r; - } -#if 0 - if(rats.size() != 0 && rats[0].is_neg()){ - for(unsigned i = 0; i < rats.size(); i++){ - assert(rats[i].is_neg()); - rats[i] = -rats[i]; - } - } -#endif - abs_rat(rats); - extract_lcd(rats); -} - -void iz3mgr::get_broken_gcd_test_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-2); - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw "Bad Farkas coefficient"; - rats[i-2] = r; - } - extract_lcd(rats); -} - -void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& coeffs){ - std::vector rats; - get_assign_bounds_coeffs(proof,rats); - coeffs.resize(rats.size()); - for(unsigned i = 0; i < rats.size(); i++){ - coeffs[i] = make_int(rats[i]); - } -} - -void iz3mgr::get_assign_bounds_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-1); - rats[0] = rational(1); - ast conseq = arg(conc(proof),0); - opr conseq_o = is_not(conseq) ? op(arg(conseq,0)) : op(conseq); - bool conseq_neg = is_not(conseq) ? (conseq_o == Leq || conseq_o == Lt) : (conseq_o == Geq || conseq_o == Gt); - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw iz3_exception("Bad Farkas coefficient"); - { - ast con = arg(conc(proof),i-1); - ast temp = make_real(r); // for debugging - opr o = is_not(con) ? op(arg(con,0)) : op(con); - if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) - r = -r; - if(conseq_neg) - r = -r; - } - rats[i-1] = r; - } -#if 0 - if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them - for(unsigned i = 1; i < rats.size(); i++){ - if(!rats[i].is_neg()) - throw iz3_exception("Bad Farkas coefficients"); - rats[i] = -rats[i]; - } - } -#endif - abs_rat(rats); - extract_lcd(rats); -} - -void iz3mgr::get_gomory_cut_coeffs(const ast &proof, std::vector& coeffs){ - std::vector rats; - get_gomory_cut_coeffs(proof,rats); - coeffs.resize(rats.size()); - for(unsigned i = 0; i < rats.size(); i++){ - coeffs[i] = make_int(rats[i]); - } -} - -void iz3mgr::get_gomory_cut_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-2); - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw "Bad Farkas coefficient"; - rats[i-2] = r; - } - abs_rat(rats); - extract_lcd(rats); -} - -void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector& coeffs){ - std::vector rats; - get_assign_bounds_rule_coeffs(proof,rats); - coeffs.resize(rats.size()); - for(unsigned i = 0; i < rats.size(); i++){ - coeffs[i] = make_int(rats[i]); - } -} - -void iz3mgr::get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats){ - symb s = sym(proof); - int numps = s->get_num_parameters(); - rats.resize(numps-1); - rats[0] = rational(1); - ast conseq = arg(conc(proof),0); - opr conseq_o = is_not(conseq) ? op(arg(conseq,0)) : op(conseq); - bool conseq_neg = is_not(conseq) ? (conseq_o == Leq || conseq_o == Lt) : (conseq_o == Geq || conseq_o == Gt); - for(int i = 2; i < numps; i++){ - rational r; - bool ok = s->get_parameter(i).is_rational(r); - if(!ok) - throw iz3_exception("Bad Farkas coefficient"); - { - ast con = conc(prem(proof,i-2)); - ast temp = make_real(r); // for debugging - opr o = is_not(con) ? op(arg(con,0)) : op(con); - if(is_not(con) ? (o == Leq || o == Lt) : (o == Geq || o == Gt)) - r = -r; - if(conseq_neg) - r = -r; - } - rats[i-1] = r; - } -#if 0 - if(rats[1].is_neg()){ // work around bug -- if all coeffs negative, negate them - for(unsigned i = 1; i < rats.size(); i++){ - if(!rats[i].is_neg()) - throw iz3_exception("Bad Farkas coefficients"); - rats[i] = -rats[i]; - } - } -#endif - abs_rat(rats); - extract_lcd(rats); -} - -/** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */ - -void iz3mgr::linear_comb(ast &P, const ast &c, const ast &Q, bool round_off){ - ast Qrhs; - bool qstrict = false; - if(is_not(Q)){ - ast nQ = arg(Q,0); - switch(op(nQ)){ - case Gt: - Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); - break; - case Lt: - Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); - break; - case Geq: - Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); - qstrict = true; - break; - case Leq: - Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); - qstrict = true; - break; - default: - throw iz3_exception("not an inequality"); - } - } - else { - switch(op(Q)){ - case Leq: - Qrhs = make(Sub,arg(Q,1),arg(Q,0)); - break; - case Geq: - Qrhs = make(Sub,arg(Q,0),arg(Q,1)); - break; - case Lt: - Qrhs = make(Sub,arg(Q,1),arg(Q,0)); - qstrict = true; - break; - case Gt: - Qrhs = make(Sub,arg(Q,0),arg(Q,1)); - qstrict = true; - break; - default: - throw iz3_exception("not an inequality"); - } - } - bool pstrict = op(P) == Lt; - if (round_off && get_type(Qrhs) != int_type()) - round_off = false; - if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ - Qrhs = make(Sub,Qrhs,make_int(rational(1))); - qstrict = false; - } - Qrhs = make(Times,c,Qrhs); - bool strict = pstrict || qstrict; - if(strict) - P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); - else - P = make(Leq,arg(P,0),make(Plus,arg(P,1),Qrhs)); -} - -iz3mgr::ast iz3mgr::sum_inequalities(const std::vector &coeffs, const std::vector &ineqs, bool round_off){ - ast zero = make_int("0"); - ast thing = make(Leq,zero,zero); - for(unsigned i = 0; i < ineqs.size(); i++){ - linear_comb(thing,coeffs[i],ineqs[i], round_off); - } - thing = simplify_ineq(thing); - return thing; -} - -void iz3mgr::mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac){ - opr o = op(t); - if(o == Plus){ - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - mk_idiv(arg(t,i),d,whole,frac); - return; - } - else if(o == Times){ - rational coeff; - if(is_numeral(arg(t,0),coeff)){ - if(gcd(coeff,d) == d){ - whole = make(Plus,whole,make(Times,make_int(coeff/d),arg(t,1))); - return; - } - } - } - frac = make(Plus,frac,t); -} - -iz3mgr::ast iz3mgr::mk_idiv(const ast& q, const rational &d){ - ast t = z3_simplify(q); - if(d == rational(1)) - return t; - else { - ast whole = make_int("0"); - ast frac = whole; - mk_idiv(t,d,whole,frac); - return z3_simplify(make(Plus,whole,make(Idiv,z3_simplify(frac),make_int(d)))); - } -} - -iz3mgr::ast iz3mgr::mk_idiv(const ast& t, const ast &d){ - rational r; - if(is_numeral(d,r)) - return mk_idiv(t,r); - return make(Idiv,t,d); -} - - -// does variable occur in expression? -int iz3mgr::occurs_in1(stl_ext::hash_map &occurs_in_memo,ast var, ast e){ - std::pair foo(e,false); - std::pair::iterator,bool> bar = occurs_in_memo.insert(foo); - bool &res = bar.first->second; - if(bar.second){ - if(e == var) res = true; - int nargs = num_args(e); - for(int i = 0; i < nargs; i++) - res |= occurs_in1(occurs_in_memo,var,arg(e,i)); - } - return res; -} - -int iz3mgr::occurs_in(ast var, ast e){ - hash_map memo; - return occurs_in1(memo,var,e); -} - - -bool iz3mgr::solve_arith(const ast &v, const ast &x, const ast &y, ast &res){ - if(occurs_in(v,y)) - return false; - if(op(x) == Plus){ - int n = num_args(x); - for(int i = 0; i < n; i++){ - if(arg(x,i) == v){ - res = z3_simplify(make(Sub, y, make(Sub, x, v))); - return true; - } - } - } - return false; -} - -// find a controlling equality for a given variable v in a term -// a controlling equality is of the form v = t, which, being -// false would force the formula to have the specifid truth value -// returns t, or null if no such - -iz3mgr::ast iz3mgr::cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, ast v, ast e){ - if(is_not(e)) return cont_eq(cont_eq_memo, !truth,v,arg(e,0)); - if(cont_eq_memo.find(e) != cont_eq_memo.end()) - return ast(); - cont_eq_memo.insert(e); - if(!truth && op(e) == Equal){ - if(arg(e,0) == v && !occurs_in(v,arg(e,1))) return(arg(e,1)); - if(arg(e,1) == v && !occurs_in(v,arg(e,0))) return(arg(e,0)); - ast res; - if(solve_arith(v,arg(e,0),arg(e,1),res)) return res; - if(solve_arith(v,arg(e,1),arg(e,0),res)) return res; - } - if((!truth && op(e) == And) || (truth && op(e) == Or)){ - int nargs = num_args(e); - for(int i = 0; i < nargs; i++){ - ast res = cont_eq(cont_eq_memo, truth, v, arg(e,i)); - if(!res.null()) return res; - } - } - if(truth && op(e) == Implies){ - ast res = cont_eq(cont_eq_memo, !truth, v, arg(e,0)); - if(!res.null()) return res; - res = cont_eq(cont_eq_memo, truth, v, arg(e,1)); - if(!res.null()) return res; - } - return ast(); -} - -// substitute a term t for unbound occurrences of variable v in e - -iz3mgr::ast iz3mgr::subst(stl_ext::hash_map &subst_memo, ast var, ast t, ast e){ - if(e == var) return t; - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = subst_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - int nargs = num_args(e); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = subst(subst_memo,var,t,arg(e,i)); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else res = clone(e,args); - } - return res; -} - -iz3mgr::ast iz3mgr::subst(ast var, ast t, ast e){ - hash_map memo; - return subst(memo,var,t,e); -} - -iz3mgr::ast iz3mgr::subst(stl_ext::hash_map &subst_memo,ast e){ - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = subst_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - int nargs = num_args(e); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = subst(subst_memo,arg(e,i)); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else res = clone(e,args); - } - return res; -} - -// apply a quantifier to a formula, with some optimizations -// 1) bound variable does not occur -> no quantifier -// 2) bound variable must be equal to some term -> substitute - -iz3mgr::ast iz3mgr::apply_quant(opr quantifier, ast var, ast e){ - if((quantifier == Forall && op(e) == And) - || (quantifier == Exists && op(e) == Or)){ - int n = num_args(e); - std::vector args(n); - for(int i = 0; i < n; i++) - args[i] = apply_quant(quantifier,var,arg(e,i)); - return make(op(e),args); - } - if(!occurs_in(var,e))return e; - hash_set cont_eq_memo; - ast cterm = cont_eq(cont_eq_memo, quantifier == Forall, var, e); - if(!cterm.null()){ - return subst(var,cterm,e); - } - std::vector bvs; bvs.push_back(var); - return make_quant(quantifier,bvs,e); -} - -#if 0 -void iz3mgr::get_bound_substitutes(stl_ext::hash_map &memo, const ast &e, const ast &var, std::vector &substs){ - std::pair foo(e,false); - std::pair::iterator,bool> bar = memo.insert(foo); - if(bar.second){ - if(op(e) == - } - - } -#endif - -unsigned iz3mgr::num_free_variables(const ast &e){ - used_vars uv; - uv(to_expr(e.raw())); - return uv.get_num_vars(); -} - -iz3mgr::ast iz3mgr::close_universally (ast e){ - used_vars uv; - uv(to_expr(e.raw())); - std::vector bvs; - stl_ext::hash_map subst_memo; - for (unsigned i = 0; i < uv.get_max_found_var_idx_plus_1(); i++){ - if (uv.get(i)) { - std::ostringstream os; - os << "%%" << i; - ast c = make_var(os.str(),uv.get(i)); - ast v = cook(m().mk_var(i,uv.get(i))); - subst_memo[v] = c; - bvs.push_back(c); - } - } - e = subst(subst_memo,e); - for (unsigned i = 0; i < bvs.size(); i++) - e = apply_quant(Forall,bvs[i],e); - return e; -} diff --git a/src/interp/iz3mgr.h b/src/interp/iz3mgr.h deleted file mode 100755 index 0ad751326..000000000 --- a/src/interp/iz3mgr.h +++ /dev/null @@ -1,738 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3mgr.h - - Abstract: - - A wrapper around an ast manager, providing convenience methods. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3MGR_H -#define IZ3MGR_H - - -#include -#include -#include - -#include "interp/iz3hash.h" -#include "interp/iz3exception.h" - -#include "ast/well_sorted.h" -#include "ast/arith_decl_plugin.h" -#include "ast/bv_decl_plugin.h" -#include "ast/datatype_decl_plugin.h" -#include "ast/array_decl_plugin.h" -#include "ast/ast_translation.h" -#include "ast/ast_pp.h" -#include "ast/ast_ll_pp.h" -#include "ast/ast_smt_pp.h" -#include "ast/ast_smt2_pp.h" -#include "ast/rewriter/th_rewriter.h" -#include "ast/rewriter/var_subst.h" -#include "ast/expr_substitution.h" -#include "ast/pp.h" -#include "util/scoped_ctrl_c.h" -#include "util/cancel_eh.h" -#include "util/scoped_timer.h" - -/* A wrapper around an ast manager, providing convenience methods. */ - -/** Shorthands for some built-in operators. */ - - - -// rename this to keep it accessible, as we use ast for something else -typedef ast raw_ast; - -/** Wrapper around an ast pointer */ -class ast_i { -protected: - raw_ast *_ast; -public: - raw_ast * const &raw() const {return _ast;} - ast_i(raw_ast *a){_ast = a;} - - ast_i(){_ast = 0;} - bool eq(const ast_i &other) const { - return _ast == other._ast; - } - bool lt(const ast_i &other) const { - return _ast->get_id() < other._ast->get_id(); - } - friend bool operator==(const ast_i &x, const ast_i&y){ - return x.eq(y); - } - friend bool operator!=(const ast_i &x, const ast_i&y){ - return !x.eq(y); - } - friend bool operator<(const ast_i &x, const ast_i&y){ - return x.lt(y); - } - size_t hash() const {return _ast->get_id();} - bool null() const {return !_ast;} -}; - -/** Reference counting verison of above */ -class ast_r : public ast_i { - ast_manager *_m; -public: - ast_r(ast_manager *m, raw_ast *a) : ast_i(a) { - _m = m; - m->inc_ref(a); - } - - ast_r() {_m = 0;} - - ast_r(const ast_r &other) : ast_i(other) { - _m = other._m; - if (_m) _m->inc_ref(_ast); - } - - ast_r &operator=(const ast_r &other) { - if(_ast) - _m->dec_ref(_ast); - _ast = other._ast; - _m = other._m; - if (_m) _m->inc_ref(_ast); - return *this; - } - - ~ast_r() { - if(_ast) - _m->dec_ref(_ast); - } - - ast_manager *mgr() const {return _m;} - -}; - -// to make ast_r hashable -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const ast_r &s) const { - return s.raw()->get_id(); - } - }; -} - - -// to make ast_r usable in ordered collections -namespace std { - template <> - class less { - public: - bool operator()(const ast_r &s, const ast_r &t) const { - // return s.raw() < t.raw(); - return s.raw()->get_id() < t.raw()->get_id(); - } - }; -} - - -/** Wrapper around an AST manager, providing convenience methods. */ - -class iz3mgr { - - public: - typedef ast_r ast; - // typedef decl_kind opr; - typedef func_decl *symb; - typedef sort *type; - typedef ast_r z3pf; - typedef decl_kind pfrule; - - enum opr { - True, - False, - And, - Or, - Not, - Iff, - Ite, - Equal, - Implies, - Distinct, - Xor, - Oeq, - Interp, - Leq, - Geq, - Lt, - Gt, - Plus, - Sub, - Uminus, - Times, - Div, - Idiv, - Rem, - Mod, - Power, - ToReal, - ToInt, - IsInt, - Select, - Store, - ConstArray, - ArrayDefault, - ArrayMap, - SetUnion, - SetIntersect, - SetDifference, - SetComplement, - SetSubSet, - AsArray, - Numeral, - Forall, - Exists, - Variable, - Uninterpreted, - Other - }; - - opr op(const ast &t); - - unsigned ast_id(const ast &x) - { - return to_expr(x.raw())->get_id(); - } - - /** Overloads for constructing ast. */ - - ast make_var(const std::string &name, type ty); - ast make(opr op, const std::vector &args); - ast make(opr op); - ast make(opr op, const ast &arg0); - ast make(opr op, const ast &arg0, const ast &arg1); - ast make(opr op, const ast &arg0, const ast &arg1, const ast &arg2); - ast make(symb sym, const std::vector &args); - ast make(symb sym); - ast make(symb sym, const ast &arg0); - ast make(symb sym, const ast &arg0, const ast &arg1); - ast make(symb sym, const ast &arg0, const ast &arg1, const ast &arg2); - ast make_quant(opr op, const std::vector &bvs, ast &body); - ast clone(const ast &t, const std::vector &args); - - ast_manager &m() {return m_manager;} - - ast cook(raw_ast *a) {return ast(&m_manager,a);} - - std::vector cook(ptr_vector v) { - std::vector _v(v.size()); - for(unsigned i = 0; i < v.size(); i++) - _v[i] = cook(v[i]); - return _v; - } - - raw_ast *uncook(const ast &a) { - m_manager.inc_ref(a.raw()); - return a.raw(); - } - - /** Methods for destructing ast. */ - - - int num_args(ast t){ - ast_kind dk = t.raw()->get_kind(); - switch(dk){ - case AST_APP: - return to_app(t.raw())->get_num_args(); - case AST_QUANTIFIER: - return 1; - case AST_VAR: - return 0; - default:; - } - assert(0); - return 0; - } - - ast arg(const ast &t, int i){ - ast_kind dk = t.raw()->get_kind(); - switch(dk){ - case AST_APP: - return cook(to_app(t.raw())->get_arg(i)); - case AST_QUANTIFIER: - return cook(to_quantifier(t.raw())->get_expr()); - default:; - } - assert(0); - return ast(); - } - - void get_args(const ast &t, std::vector &res){ - res.resize(num_args(t)); - for(unsigned i = 0; i < res.size(); i++) - res[i] = arg(t,i); - } - - std::vector args(const ast &t){ - std::vector res; - get_args(t,res); - return res; - } - - symb sym(ast t){ - raw_ast *_ast = t.raw(); - return is_app(_ast) ? to_app(_ast)->get_decl() : 0; - } - - std::string string_of_symbol(symb s){ - symbol _s = s->get_name(); - if (_s.is_numerical()) { - std::ostringstream buffer; - buffer << _s.get_num(); - return buffer.str(); - } - else { - return _s.bare_str(); - } - } - - type get_type(ast t){ - return m().get_sort(to_expr(t.raw())); - } - - std::string string_of_numeral(const ast& t){ - rational r; - expr* e = to_expr(t.raw()); - assert(e); - if (m_arith_util.is_numeral(e, r)) - return r.to_string(); - assert(0); - return "NaN"; - } - - bool is_numeral(const ast& t, rational &r){ - expr* e = to_expr(t.raw()); - assert(e); - return m_arith_util.is_numeral(e, r); - } - - rational get_coeff(const ast& t){ - rational res; - if(op(t) == Times && is_numeral(arg(t,0),res)) - return res; - return rational(1); - } - - ast get_linear_var(const ast& t){ - rational res; - if(op(t) == Times && is_numeral(arg(t,0),res)) - return arg(t,1); - return t; - } - - int get_quantifier_num_bound(const ast &t) { - return to_quantifier(t.raw())->get_num_decls(); - } - - std::string get_quantifier_bound_name(const ast &t, unsigned i) { - return to_quantifier(t.raw())->get_decl_names()[i].bare_str(); - } - - type get_quantifier_bound_type(const ast &t, unsigned i) { - return to_quantifier(t.raw())->get_decl_sort(i); - } - - ast get_quantifier_body(const ast &t) { - return cook(to_quantifier(t.raw())->get_expr()); - } - - unsigned get_variable_index_value(const ast &t) { - var* va = to_var(t.raw()); - return va->get_idx(); - } - - bool is_bool_type(type t){ - family_id fid = to_sort(t)->get_family_id(); - decl_kind k = to_sort(t)->get_decl_kind(); - return fid == m().get_basic_family_id() && k == BOOL_SORT; - } - - bool is_array_type(type t){ - family_id fid = to_sort(t)->get_family_id(); - decl_kind k = to_sort(t)->get_decl_kind(); - return fid == m_array_fid && k == ARRAY_SORT; - } - - type get_range_type(symb s){ - return to_func_decl(s)->get_range(); - } - - int get_num_parameters(const symb &s){ - return to_func_decl(s)->get_num_parameters(); - } - - ast get_ast_parameter(const symb &s, int idx){ - return cook(to_func_decl(s)->get_parameters()[idx].get_ast()); - } - - enum lemma_theory {ArithTheory,ArrayTheory,UnknownTheory}; - - lemma_theory get_theory_lemma_theory(const ast &proof){ - symb s = sym(proof); - ::symbol p0; - bool ok = s->get_parameter(0).is_symbol(p0); - if(!ok) return UnknownTheory; - std::string foo(p0.bare_str()); - if(foo == "arith") - return ArithTheory; - if(foo == "array") - return ArrayTheory; - return UnknownTheory; - } - - enum lemma_kind {FarkasKind,Leq2EqKind,Eq2LeqKind,GCDTestKind,AssignBoundsKind,EqPropagateKind,GomoryCutKind,ArithMysteryKind,UnknownKind}; - - lemma_kind get_theory_lemma_kind(const ast &proof){ - symb s = sym(proof); - if(s->get_num_parameters() < 2) { - return ArithMysteryKind; // Bad -- Z3 hasn't told us - } - ::symbol p0; - bool ok = s->get_parameter(1).is_symbol(p0); - if(!ok) return UnknownKind; - std::string foo(p0.bare_str()); - if(foo == "farkas") - return FarkasKind; - if(foo == "triangle-eq") - return is_not(arg(conc(proof),0)) ? Eq2LeqKind : Leq2EqKind; - if(foo == "gcd-test") - return GCDTestKind; - if(foo == "assign-bounds") - return AssignBoundsKind; - if(foo == "eq-propagate") - return EqPropagateKind; - if(foo == "gomory-cut") - return GomoryCutKind; - return UnknownKind; - } - - void get_farkas_coeffs(const ast &proof, std::vector& coeffs); - - void get_farkas_coeffs(const ast &proof, std::vector& rats); - - void get_broken_gcd_test_coeffs(const ast &proof, std::vector& rats); - - void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); - - void get_assign_bounds_coeffs(const ast &proof, std::vector& rats); - - void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); - - void get_assign_bounds_rule_coeffs(const ast &proof, std::vector& rats); - - void get_gomory_cut_coeffs(const ast &proof, std::vector& rats); - - void get_gomory_cut_coeffs(const ast &proof, std::vector& rats); - - bool is_farkas_coefficient_negative(const ast &proof, int n); - - bool is_true(ast t){ - return op(t) == True; - } - - bool is_false(ast t){ - return op(t) == False; - } - - bool is_iff(ast t){ - return op(t) == Iff; - } - - bool is_or(ast t){ - return op(t) == Or; - } - - bool is_not(ast t){ - return op(t) == Not; - } - - /** Simplify an expression using z3 simplifier */ - - ast z3_simplify(const ast& e); - - /** Simplify, sorting sums */ - ast z3_really_simplify(const ast &e); - - - // Some constructors that simplify things - - ast mk_not(ast x){ - opr o = op(x); - if(o == True) return make(False); - if(o == False) return make(True); - if(o == Not) return arg(x,0); - return make(Not,x); - } - - ast mk_and(ast x, ast y){ - opr ox = op(x); - opr oy = op(y); - if(ox == True) return y; - if(oy == True) return x; - if(ox == False) return x; - if(oy == False) return y; - if(x == y) return x; - return make(And,x,y); - } - - ast mk_or(ast x, ast y){ - opr ox = op(x); - opr oy = op(y); - if(ox == False) return y; - if(oy == False) return x; - if(ox == True) return x; - if(oy == True) return y; - if(x == y) return x; - return make(Or,x,y); - } - - ast mk_implies(ast x, ast y){ - opr ox = op(x); - opr oy = op(y); - if(ox == True) return y; - if(oy == False) return mk_not(x); - if(ox == False) return mk_true(); - if(oy == True) return y; - if(x == y) return mk_true(); - return make(Implies,x,y); - } - - ast mk_or(const std::vector &x){ - ast res = mk_false(); - for(unsigned i = 0; i < x.size(); i++) - res = mk_or(res,x[i]); - return res; - } - - ast mk_and(const std::vector &x){ - std::vector conjs; - for(unsigned i = 0; i < x.size(); i++){ - const ast &e = x[i]; - opr o = op(e); - if(o == False) - return mk_false(); - if(o != True) - conjs.push_back(e); - } - if(conjs.size() == 0) - return mk_true(); - if(conjs.size() == 1) - return conjs[0]; - return make(And,conjs); - } - - ast mk_equal(ast x, ast y){ - if(x == y) return make(True); - opr ox = op(x); - opr oy = op(y); - if(ox == True) return y; - if(oy == True) return x; - if(ox == False) return mk_not(y); - if(oy == False) return mk_not(x); - if(ox == False && oy == True) return make(False); - if(oy == False && ox == True) return make(False); - return make(Equal,x,y); - } - - ast z3_ite(ast x, ast y, ast z){ - opr ox = op(x); - opr oy = op(y); - opr oz = op(z); - if(ox == True) return y; - if(ox == False) return z; - if(y == z) return y; - if(oy == True && oz == False) return x; - if(oz == True && oy == False) return mk_not(x); - return make(Ite,x,y,z); - } - - ast make_int(const std::string &s) { - sort *r = m().mk_sort(m_arith_fid, INT_SORT); - return cook(m_arith_util.mk_numeral(rational(s.c_str()),r)); - } - - ast make_int(const rational &s) { - sort *r = m().mk_sort(m_arith_fid, INT_SORT); - return cook(m_arith_util.mk_numeral(s,r)); - } - - ast make_real(const std::string &s) { - sort *r = m().mk_sort(m_arith_fid, REAL_SORT); - return cook(m_arith_util.mk_numeral(rational(s.c_str()),r)); - } - - ast make_real(const rational &s) { - sort *r = m().mk_sort(m_arith_fid, REAL_SORT); - return cook(m_arith_util.mk_numeral(s,r)); - } - - ast mk_false() { return make(False); } - - ast mk_true() { return make(True); } - - ast mk_fresh_constant(char const * prefix, type s){ - return cook(m().mk_fresh_const(prefix, s)); - } - - type bool_type() { - ::sort *s = m().mk_sort(m_basic_fid, BOOL_SORT); - return s; - } - - type int_type() { - ::sort *s = m().mk_sort(m_arith_fid, INT_SORT); - return s; - } - - type real_type() { - ::sort *s = m().mk_sort(m_arith_fid, REAL_SORT); - return s; - } - - type array_type(type d, type r) { - parameter params[2] = { parameter(d), parameter(to_sort(r)) }; - ::sort * s = m().mk_sort(m_array_fid, ARRAY_SORT, 2, params); - return s; - } - - symb function(const std::string &str_name, unsigned arity, type *domain, type range) { - ::symbol name = ::symbol(str_name.c_str()); - std::vector< ::sort *> sv(arity); - for(unsigned i = 0; i < arity; i++) - sv[i] = domain[i]; - ::func_decl* d = m().mk_func_decl(name,arity,&sv[0],range); - return d; - } - - void linear_comb(ast &P, const ast &c, const ast &Q, bool round_off = false); - - ast sum_inequalities(const std::vector &coeffs, const std::vector &ineqs, bool round_off = false); - - ast simplify_ineq(const ast &ineq){ - ast res = make(op(ineq),arg(ineq,0),z3_simplify(arg(ineq,1))); - return res; - } - - void mk_idiv(const ast& t, const rational &d, ast &whole, ast &frac); - - ast mk_idiv(const ast& t, const rational &d); - - ast mk_idiv(const ast& t, const ast &d); - - /** methods for destructing proof terms */ - - pfrule pr(const z3pf &t); - - int num_prems(const z3pf &t){return to_app(t.raw())->get_num_args()-1;} - - z3pf prem(const z3pf &t, int n){return arg(t,n);} - - z3pf conc(const z3pf &t){return arg(t,num_prems(t));} - - - /* quantifier handling */ - - // substitute a term t for unbound occurrences of variable v in e - - ast subst(ast var, ast t, ast e); - - // apply a substitution defined by a map - ast subst(stl_ext::hash_map &map, ast e); - - // apply a quantifier to a formula, with some optimizations - // 1) bound variable does not occur -> no quantifier - // 2) bound variable must be equal to some term -> substitute - - ast apply_quant(opr quantifier, ast var, ast e); - - // Universally quantify all the free variables in a formula. - // Makes up names for the quntifiers. - - ast close_universally (ast e); - - unsigned num_free_variables(const ast &e); - - /** For debugging */ - void show(ast); - - void show_symb(symb s); - - /** Constructor */ - - void print_lit(ast lit); - - void print_expr(std::ostream &s, const ast &e); - - void print_clause(std::ostream &s, std::vector &cls); - - void print_sat_problem(std::ostream &out, const ast &t); - - void show_clause(std::vector &cls); - - static void pretty_print(std::ostream &f, const std::string &s); - - iz3mgr(ast_manager &_m_manager) - : m_manager(_m_manager), - m_arith_util(_m_manager) - { - m_basic_fid = m().get_basic_family_id(); - m_arith_fid = m().mk_family_id("arith"); - m_bv_fid = m().mk_family_id("bv"); - m_array_fid = m().mk_family_id("array"); - m_dt_fid = m().mk_family_id("datatype"); - m_datalog_fid = m().mk_family_id("datalog_relation"); - } - - iz3mgr(const iz3mgr& other) - : m_manager(other.m_manager), - m_arith_util(other.m_manager) - { - m_basic_fid = m().get_basic_family_id(); - m_arith_fid = m().mk_family_id("arith"); - m_bv_fid = m().mk_family_id("bv"); - m_array_fid = m().mk_family_id("array"); - m_dt_fid = m().mk_family_id("datatype"); - m_datalog_fid = m().mk_family_id("datalog_relation"); - } - - protected: - ast_manager &m_manager; - int occurs_in(ast var, ast e); - - private: - ast mki(family_id fid, decl_kind sk, int n, raw_ast **args); - ast make(opr op, int n, raw_ast **args); - ast make(symb sym, int n, raw_ast **args); - int occurs_in1(stl_ext::hash_map &occurs_in_memo, ast var, ast e); - bool solve_arith(const ast &v, const ast &x, const ast &y, ast &res); - ast cont_eq(stl_ext::hash_set &cont_eq_memo, bool truth, ast v, ast e); - ast subst(stl_ext::hash_map &subst_memo, ast var, ast t, ast e); - - - family_id m_basic_fid; - family_id m_array_fid; - family_id m_arith_fid; - family_id m_bv_fid; - family_id m_dt_fid; - family_id m_datalog_fid; - arith_util m_arith_util; -}; - -#endif - diff --git a/src/interp/iz3pp.cpp b/src/interp/iz3pp.cpp deleted file mode 100644 index 787fa4ec7..000000000 --- a/src/interp/iz3pp.cpp +++ /dev/null @@ -1,185 +0,0 @@ -/*++ - Copyright (c) 2013 Microsoft Corporation - - Module Name: - - iz3pp.cpp - - Abstract: - - Pretty-print interpolation problems - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -/* Copyright 2011 Microsoft Research. */ -#include -#include -#include -#include -#include -#include -#include - -#include "interp/iz3mgr.h" -#include "interp/iz3pp.h" -#include "ast/func_decl_dependencies.h" -#include "ast/for_each_expr.h" -#include "ast/ast_smt_pp.h" -#include "ast/ast_smt2_pp.h" -#include "ast/expr_functors.h" -#include "ast/expr_abstract.h" - - -using namespace stl_ext; - -// We promise not to use this for hash_map with range destructor -namespace stl_ext { - template <> - class hash { - public: - size_t operator()(const expr *p) const { - return (size_t) p; - } - }; -} - - -// TBD: algebraic data-types declarations will not be printed. -class free_func_visitor { - ast_manager& m; - func_decl_set m_funcs; - obj_hashtable m_sorts; -public: - free_func_visitor(ast_manager& m): m(m) {} - void operator()(var * n) { } - void operator()(app * n) { - m_funcs.insert(n->get_decl()); - class sort* s = m.get_sort(n); - if (s->get_family_id() == null_family_id) { - m_sorts.insert(s); - } - } - void operator()(quantifier * n) { } - func_decl_set& funcs() { return m_funcs; } - obj_hashtable& sorts() { return m_sorts; } -}; - -class iz3pp_helper : public iz3mgr { -public: - - void print_tree(const ast &tree, hash_map &cnames, std::ostream &out){ - hash_map::iterator foo = cnames.find(to_expr(tree.raw())); - if(foo != cnames.end()){ - symbol nm = foo->second; - if (is_smt2_quoted_symbol(nm)) { - out << mk_smt2_quoted_symbol(nm); - } - else { - out << nm; - } - } - else if(op(tree) == And){ - out << "(and"; - int nargs = num_args(tree); - for(int i = 0; i < nargs; i++){ - out << " "; - print_tree(arg(tree,i), cnames, out); - } - out << ")"; - } - else if(op(tree) == Interp){ - out << "(interp "; - print_tree(arg(tree,0), cnames, out); - out << ")"; - } - else throw iz3pp_bad_tree(); - } - - - iz3pp_helper(ast_manager &_m_manager) - : iz3mgr(_m_manager) {} -}; - -void iz3pp(ast_manager &m, - const ptr_vector &cnsts_vec, - expr *tree, - std::ostream& out) { - - unsigned sz = cnsts_vec.size(); - expr* const* cnsts = &cnsts_vec[0]; - - out << "(set-option :produce-interpolants true)\n"; - - free_func_visitor visitor(m); - expr_mark visited; - bool print_low_level = true; // m_params.print_low_level_smt2(); - -#define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env); - - smt2_pp_environment_dbg env(m); - - for (unsigned i = 0; i < sz; ++i) { - expr* e = cnsts[i]; - for_each_expr(visitor, visited, e); - } - - // name all the constraints - hash_map cnames; - int ctr = 1; - for(unsigned i = 0; i < sz; i++){ - symbol nm; - std::ostringstream s; - s << "f!" << (ctr++); - cnames[cnsts[i]] = symbol(s.str().c_str()); - } - - func_decl_set &funcs = visitor.funcs(); - func_decl_set::iterator it = funcs.begin(), end = funcs.end(); - - obj_hashtable& sorts = visitor.sorts(); - obj_hashtable::iterator sit = sorts.begin(), send = sorts.end(); - - - - for (; sit != send; ++sit) { - PP(*sit); - } - - for (; it != end; ++it) { - func_decl* f = *it; - if(f->get_family_id() == null_family_id){ - PP(f); - out << "\n"; - } - } - - for (unsigned i = 0; i < sz; ++i) { - out << "(assert "; - expr* r = cnsts[i]; - symbol nm = cnames[r]; - out << "(! "; - PP(r); - out << " :named "; - if (is_smt2_quoted_symbol(nm)) { - out << mk_smt2_quoted_symbol(nm); - } - else { - out << nm; - } - out << ")"; - out << ")\n"; - } - out << "(check-sat)\n"; - out << "(get-interpolant "; - iz3pp_helper pp(m); - pp.print_tree(pp.cook(tree),cnames,out); - out << ")\n"; -} - - diff --git a/src/interp/iz3pp.h b/src/interp/iz3pp.h deleted file mode 100644 index 7b3405f9b..000000000 --- a/src/interp/iz3pp.h +++ /dev/null @@ -1,36 +0,0 @@ -/*++ - Copyright (c) 2013 Microsoft Corporation - - Module Name: - - iz3pp.cpp - - Abstract: - - Pretty-print interpolation problems - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3_PP_H -#define IZ3_PP_H - -#include "interp/iz3mgr.h" - -/** Exception thrown in case of mal-formed tree interpoloation - specification */ - -struct iz3pp_bad_tree: public iz3_exception { - iz3pp_bad_tree(): iz3_exception("iz3pp_bad_tree") {} -}; - -void iz3pp(ast_manager &m, - const ptr_vector &cnsts_vec, - expr *tree, - std::ostream& out); -#endif diff --git a/src/interp/iz3profiling.cpp b/src/interp/iz3profiling.cpp deleted file mode 100755 index df3126e4f..000000000 --- a/src/interp/iz3profiling.cpp +++ /dev/null @@ -1,153 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3profiling.h - - Abstract: - - Some routines for measuring performance. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3profiling.h" - -#include -#include -#include -#include -#include -#include "util/stopwatch.h" - - -// FIXME fill in these stubs - -#define clock_t double - -static double current_time() -{ - static stopwatch sw; - static bool started = false; - if(!started){ - sw.start(); - started = true; - } - return sw.get_current_seconds(); -} - -static void output_time(std::ostream &os, clock_t time){ - os << time; -} - - -namespace profiling { - - void show_time(){ - output_time(std::cout,current_time()); - std::cout << "\n"; - } - - typedef std::map nmap; - - struct node { - std::string name; - clock_t time; - clock_t start_time; - nmap sub; - struct node *parent; - - node(); - } top; - - node::node(){ - time = 0; - parent = 0; - } - - struct node *current; - - struct init { - init(){ - top.name = "TOTAL"; - current = ⊤ - } - } initializer; - - struct time_entry { - clock_t t; - time_entry(){t = 0;}; - void add(clock_t incr){t += incr;} - }; - - struct ltstr - { - bool operator()(const char* s1, const char* s2) const - { - return strcmp(s1, s2) < 0; - } - }; - - typedef std::map tmap; - - static std::ostream *pfs; - - void print_node(node &top, int indent, tmap &totals){ - for(int i = 0; i < indent; i++) (*pfs) << " "; - (*pfs) << top.name; - int dots = 70 - 2 * indent - top.name.size(); - for(int i = 0; i second,indent+1,totals); - } - - void print(std::ostream &os) { - pfs = &os; - top.time = 0; - for(nmap::iterator it = top.sub.begin(); it != top.sub.end(); it++) - top.time += it->second.time; - tmap totals; - print_node(top,0,totals); - (*pfs) << "TOTALS:" << std::endl; - for(tmap::iterator it = totals.begin(); it != totals.end(); it++){ - (*pfs) << (it->first) << " "; - output_time(*pfs, it->second.t); - (*pfs) << std::endl; - } - } - - void timer_start(const char *name){ - node &child = current->sub[name]; - if(child.name.empty()){ // a new node - child.parent = current; - child.name = name; - } - child.start_time = current_time(); - current = &child; - } - - void timer_stop(const char *name){ - if(current->name != name || !current->parent){ - std::cerr << "imbalanced timer_start and timer_stop"; - exit(1); - } - current->time += (current_time() - current->start_time); - current = current->parent; - } -} diff --git a/src/interp/iz3profiling.h b/src/interp/iz3profiling.h deleted file mode 100755 index 6b9b07f25..000000000 --- a/src/interp/iz3profiling.h +++ /dev/null @@ -1,37 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3profiling.h - - Abstract: - - Some routines for measuring performance. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3PROFILING_H -#define IZ3PROFILING_H - -#include - -namespace profiling { - /** Start a timer with given name */ - void timer_start(const char *); - /** Stop a timer with given name */ - void timer_stop(const char *); - /** Print out timings */ - void print(std::ostream &s); - /** Show the current time. */ - void show_time(); -} - -#endif - diff --git a/src/interp/iz3proof.cpp b/src/interp/iz3proof.cpp deleted file mode 100755 index bc046ceff..000000000 --- a/src/interp/iz3proof.cpp +++ /dev/null @@ -1,628 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3proof.cpp - - Abstract: - - This class defines a simple interpolating proof system. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3proof.h" -#include "interp/iz3profiling.h" - -#include -#include -#include -#include - -// #define FACTOR_INTERPS -// #define CHECK_PROOFS - - -void iz3proof::resolve(ast pivot, std::vector &cls1, const std::vector &cls2){ -#ifdef CHECK_PROOFS - std::vector orig_cls1 = cls1; -#endif - ast neg_pivot = pv->mk_not(pivot); - bool found_pivot1 = false, found_pivot2 = false; - for(unsigned i = 0; i < cls1.size(); i++){ - if(cls1[i] == neg_pivot){ - cls1[i] = cls1.back(); - cls1.pop_back(); - found_pivot1 = true; - break; - } - } - { - std::set memo; - memo.insert(cls1.begin(),cls1.end()); - for(unsigned j = 0; j < cls2.size(); j++){ - if(cls2[j] == pivot) - found_pivot2 = true; - else - if(memo.find(cls2[j]) == memo.end()) - cls1.push_back(cls2[j]); - } - } - if(found_pivot1 && found_pivot2) - return; - -#ifdef CHECK_PROOFS - std::cerr << "resolution anomaly: " << nodes.size()-1 << "\n"; -#if 0 - std::cerr << "pivot: "; {pv->print_lit(pivot); std::cout << "\n";} - std::cerr << "left clause:\n"; - for(unsigned i = 0; i < orig_cls1.size(); i++) - {pv->print_lit(orig_cls1[i]); std::cout << "\n";} - std::cerr << "right clause:\n"; - for(unsigned i = 0; i < cls2.size(); i++) - {pv->print_lit(cls2[i]); std::cout << "\n";} - throw proof_error(); -#endif -#endif -} - -iz3proof::node iz3proof::make_resolution(ast pivot, node premise1, node premise2) -{ - if(nodes[premise1].rl == Hypothesis) return premise2; // resolve with hyp is noop - if(nodes[premise2].rl == Hypothesis) return premise1; - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Resolution; - n.aux = pivot; - n.premises.resize(2); - n.premises[0] = (premise1); - n.premises[1] = (premise2); -#ifdef CHECK_PROOFS - n.conclusion = nodes[premise1].conclusion; - resolve(pivot,n.conclusion,nodes[premise2].conclusion); - n.frame = 1; -#else - n.frame = 0; // compute conclusion lazily -#endif - return res; -} - -iz3proof::node iz3proof::resolve_lemmas(ast pivot, node premise1, node premise2) -{ - std::vector lits(nodes[premise1].conclusion), itp; // no interpolant - resolve(pivot,lits,nodes[premise2].conclusion); - return make_lemma(lits,itp); -} - - -iz3proof::node iz3proof::make_assumption(int frame, const std::vector &assumption){ -#if 0 - std::cout << "assumption: \n"; - for(unsigned i = 0; i < assumption.size(); i++) - pv->show(assumption[i]); - std::cout << "\n"; -#endif - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Assumption; - n.conclusion.resize(1); - n.conclusion = assumption; - n.frame = frame; - return res; -} - -iz3proof::node iz3proof::make_hypothesis(ast hypothesis){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Hypothesis; - n.conclusion.resize(2); - n.conclusion[0] = hypothesis; - n.conclusion[1] = pv->mk_not(hypothesis); - return res; -} - -iz3proof::node iz3proof::make_theory(const std::vector &conclusion, std::vector premises){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Theory; - n.conclusion = conclusion; - n.premises = premises; - return res; -} - -iz3proof::node iz3proof::make_axiom(const std::vector &conclusion){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Axiom; - n.conclusion = conclusion; - return res; -} - -iz3proof::node iz3proof::make_contra(node prem, const std::vector &conclusion){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Contra; - n.conclusion = conclusion; -#ifdef CHECK_PROOFS - //if(!(conclusion == nodes[prem].conclusion)){ - //std::cerr << "internal error: proof error\n"; - //assert(0 && "proof error"); - //} -#endif - n.premises.push_back(prem); - return res; -} - - -iz3proof::node iz3proof::make_lemma(const std::vector &conclusion, const std::vector &interpolation){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Lemma; - n.conclusion = conclusion; - n.frame = interps.size(); - interps.push_back(interpolation); - return res; -} - -/** Make a Reflexivity node. This rule produces |- x = x */ - -iz3proof::node iz3proof::make_reflexivity(ast con){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Reflexivity; - n.conclusion.push_back(con); - return res; -} - -/** Make a Symmetry node. This takes a derivation of |- x = y and - produces | y = x */ - -iz3proof::node iz3proof::make_symmetry(ast con, node prem){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Reflexivity; - n.conclusion.push_back(con); - n.premises.push_back(prem); - return res; -} - -/** Make a transitivity node. This takes derivations of |- x = y - and |- y = z produces | x = z */ - -iz3proof::node iz3proof::make_transitivity(ast con, node prem1, node prem2){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Transitivity; - n.conclusion.push_back(con); - n.premises.push_back(prem1); - n.premises.push_back(prem2); - return res; -} - - -/** Make a congruence node. This takes derivations of |- x_i = y_i - and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ - -iz3proof::node iz3proof::make_congruence(ast con, const std::vector &prems){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = Congruence; - n.conclusion.push_back(con); - n.premises = prems; - return res; -} - - -/** Make an equality contradicition node. This takes |- x = y - and |- !(x = y) and produces false. */ - -iz3proof::node iz3proof::make_eqcontra(node prem1, node prem2){ - node res = make_node(); - node_struct &n = nodes[res]; - n.rl = EqContra; - n.premises.push_back(prem1); - n.premises.push_back(prem2); - return res; -} - -iz3proof::node iz3proof::copy_rec(stl_ext::hash_map &memo, iz3proof &src, node n){ - stl_ext::hash_map::iterator it = memo.find(n); - if(it != memo.end()) return (*it).second; - node_struct &ns = src.nodes[n]; - std::vector prems(ns.premises.size()); - for(unsigned i = 0; i < prems.size(); i++) - prems[i] = copy_rec(memo,src,ns.premises[i]); - nodes.push_back(ns); - nodes.back().premises.swap(prems); - if(ns.rl == Lemma){ - nodes.back().frame = interps.size(); - interps.push_back(src.interps[ns.frame]); - } - int res = nodes.size()-1; - memo[n] = res; - return res; -} - -iz3proof::node iz3proof::copy(iz3proof &src, node n){ - stl_ext::hash_map memo; - return copy_rec(memo, src, n); -} - -bool iz3proof::pred_in_A(ast id){ - return weak - ? pv->ranges_intersect(pv->ast_range(id),rng) : - pv->range_contained(pv->ast_range(id),rng); -} - -bool iz3proof::term_in_B(ast id){ - prover::range r = pv->ast_scope(id); - if(weak) { - if(pv->range_min(r) == SHRT_MIN) - return !pv->range_contained(r,rng); - else - return !pv->ranges_intersect(r,rng); - } - else - return !pv->range_contained(r,rng); -} - -bool iz3proof::frame_in_A(int frame){ - return pv->in_range(frame,rng); -} - -bool iz3proof::lit_in_B(ast lit){ - return - b_lits.find(lit) != b_lits.end() - || b_lits.find(pv->mk_not(lit)) != b_lits.end(); -} - -iz3proof::ast iz3proof::my_or(ast x, ast y){ - return pv->mk_not(pv->mk_and(pv->mk_not(x),pv->mk_not(y))); -} - -iz3proof::ast iz3proof::get_A_lits(std::vector &cls){ - ast foo = pv->mk_false(); - for(unsigned i = 0; i < cls.size(); i++){ - ast lit = cls[i]; - if(b_lits.find(pv->mk_not(lit)) == b_lits.end()){ - if(pv->range_max(pv->ast_scope(lit)) == pv->range_min(pv->ast_scope(lit))){ - std::cout << "bad lit: " << pv->range_max(rng) << " : " << pv->range_max(pv->ast_scope(lit)) << " : " << (pv->ast_id(lit)) << " : "; - pv->show(lit); - } - foo = my_or(foo,lit); - } - } - return foo; -} - -iz3proof::ast iz3proof::get_B_lits(std::vector &cls){ - ast foo = pv->mk_false(); - for(unsigned i = 0; i < cls.size(); i++){ - ast lit = cls[i]; - if(b_lits.find(pv->mk_not(lit)) != b_lits.end()) - foo = my_or(foo,lit); - } - return foo; -} - -void iz3proof::set_of_B_lits(std::vector &cls, std::set &res){ - for(unsigned i = 0; i < cls.size(); i++){ - ast lit = cls[i]; - if(b_lits.find(pv->mk_not(lit)) != b_lits.end()) - res.insert(lit); - } -} - -void iz3proof::set_of_A_lits(std::vector &cls, std::set &res){ - for(unsigned i = 0; i < cls.size(); i++){ - ast lit = cls[i]; - if(b_lits.find(pv->mk_not(lit)) == b_lits.end()) - res.insert(lit); - } -} - -void iz3proof::find_B_lits(){ - b_lits.clear(); - for(unsigned i = 0; i < nodes.size(); i++){ - node_struct &n = nodes[i]; - std::vector &cls = n.conclusion; - if(n.rl == Assumption){ - if(weak) goto lemma; - if(!frame_in_A(n.frame)) - for(unsigned j = 0; j < cls.size(); j++) - b_lits.insert(cls[j]); - } - else if(n.rl == Lemma) { - lemma: - for(unsigned j = 0; j < cls.size(); j++) - if(term_in_B(cls[j])) - b_lits.insert(cls[j]); - } - } -} - -iz3proof::ast iz3proof::disj_of_set(std::set &s){ - ast res = pv->mk_false(); - for(std::set::iterator it = s.begin(), en = s.end(); it != en; ++it) - res = my_or(*it,res); - return res; -} - -void iz3proof::mk_and_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs){ -#ifdef FACTOR_INTERPS - std::set &d1 = disjs[p1]; - std::set &d2 = disjs[p2]; - if(!weak){ - if(pv->is_true(itps[p1])){ - itps[i] = itps[p2]; - disjs[i] = disjs[p2]; - } - else if(pv->is_true(itps[p2])){ - itps[i] = itps[p1]; - disjs[i] = disjs[p1]; - } - else { - std::set p1only,p2only; - std::insert_iterator > p1o(p1only,p1only.begin()); - std::insert_iterator > p2o(p2only,p2only.begin()); - std::insert_iterator > dio(disjs[i],disjs[i].begin()); - std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o); - std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o); - std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); - ast p1i = my_or(itps[p1],disj_of_set(p1only)); - ast p2i = my_or(itps[p2],disj_of_set(p2only)); - itps[i] = pv->mk_and(p1i,p2i); - } - } - else { - itps[i] = pv->mk_and(itps[p1],itps[p2]); - std::insert_iterator > dio(disjs[i],disjs[i].begin()); - std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); - } -#endif -} - -void iz3proof::mk_or_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs){ -#ifdef FACTOR_INTERPS - std::set &d1 = disjs[p1]; - std::set &d2 = disjs[p2]; - if(weak){ - if(pv->is_false(itps[p1])){ - itps[i] = itps[p2]; - disjs[i] = disjs[p2]; - } - else if(pv->is_false(itps[p2])){ - itps[i] = itps[p1]; - disjs[i] = disjs[p1]; - } - else { - std::set p1only,p2only; - std::insert_iterator > p1o(p1only,p1only.begin()); - std::insert_iterator > p2o(p2only,p2only.begin()); - std::insert_iterator > dio(disjs[i],disjs[i].begin()); - std::set_difference(d1.begin(),d1.end(),d2.begin(),d2.end(),p1o); - std::set_difference(d2.begin(),d2.end(),d1.begin(),d1.end(),p2o); - std::set_intersection(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); - ast p1i = pv->mk_and(itps[p1],pv->mk_not(disj_of_set(p1only))); - ast p2i = pv->mk_and(itps[p2],pv->mk_not(disj_of_set(p2only))); - itps[i] = my_or(p1i,p2i); - } - } - else { - itps[i] = my_or(itps[p1],itps[p2]); - std::insert_iterator > dio(disjs[i],disjs[i].begin()); - std::set_union(d1.begin(),d1.end(),d2.begin(),d2.end(),dio); - } -#endif -} - -void iz3proof::interpolate_lemma(node_struct &n){ - if(interps[n.frame].size()) - return; // already computed - pv->interpolate_clause(n.conclusion,interps[n.frame]); -} - -iz3proof::ast iz3proof::interpolate(const prover::range &_rng, bool _weak -#ifdef CHECK_PROOFS - , ast assump - , std::vector *parents -#endif - ){ - // std::cout << "proof size: " << nodes.size() << "\n"; - rng = _rng; - weak = _weak; -#ifdef CHECK_PROOFS - if(nodes[nodes.size()-1].conclusion.size() != 0) - std::cerr << "internal error: proof conclusion is not empty clause\n"; - if(!child_interps.size()){ - child_interps.resize(nodes.size()); - for(unsigned j = 0; j < nodes.size(); j++) - child_interps[j] = pv->mk_true(); - } -#endif - std::vector itps(nodes.size()); -#ifdef FACTOR_INTERPS - std::vector > disjs(nodes.size()); -#endif - profiling::timer_start("Blits"); - find_B_lits(); - profiling::timer_stop("Blits"); - profiling::timer_start("interp_proof"); - // strengthen(); - for(unsigned i = 0; i < nodes.size(); i++){ - node_struct &n = nodes[i]; - ast &q = itps[i]; - switch(n.rl){ - case Assumption: { - - if(frame_in_A(n.frame)){ - /* HypC-A */ - if(!weak) -#ifdef FACTOR_INTERPS - { - q = pv->mk_false(); - set_of_B_lits(n.conclusion,disjs[i]); - } -#else - q = get_B_lits(n.conclusion); -#endif - else - q = pv->mk_false(); - } - else { - /* HypEq-B */ - if(!weak) - q = pv->mk_true(); - else -#ifdef FACTOR_INTERPS - { - q = pv->mk_true(); - set_of_A_lits(n.conclusion,disjs[i]); - } -#else - q = pv->mk_not(get_A_lits(n.conclusion)); -#endif - } - break; - } - case Resolution: { - ast p = n.aux; - p = pv->is_not(p) ? pv->mk_not(p) : p; // should be positive, but just in case - if(lit_in_B(p)) -#ifdef FACTOR_INTERPS - mk_and_factor(n.premises[0],n.premises[1],i,itps,disjs); -#else - q = pv->mk_and(itps[n.premises[0]],itps[n.premises[1]]); -#endif - else -#ifdef FACTOR_INTERPS - mk_or_factor(n.premises[0],n.premises[1],i,itps,disjs); -#else - q = my_or(itps[n.premises[0]],itps[n.premises[1]]); -#endif - break; - } - case Lemma: { - interpolate_lemma(n); // make sure lemma interpolants have been computed - q = interps[n.frame][pv->range_max(rng)]; - break; - } - case Contra: { - q = itps[n.premises[0]]; -#ifdef FACTOR_INTERPS - disjs[i] = disjs[n.premises[0]]; -#endif - break; - } - default: - assert(0 && "rule not allowed in interpolated proof"); - } -#ifdef CHECK_PROOFS - int this_frame = pv->range_max(rng); - if(0 && this_frame == 39) { - std::vector alits; - ast s = pv->mk_true(); - for(unsigned j = 0; j < n.conclusion.size(); j++) - if(pred_in_A(n.conclusion[j])){ - int scpmax = pv->range_max(pv->ast_scope(n.conclusion[j])); - if(scpmax == this_frame) - s = pv->mk_and(s,pv->mk_not(n.conclusion[j])); - } - ast ci = child_interps[i]; - s = pv->mk_and(pv->mk_and(s,pv->mk_and(assump,pv->mk_not(q))),ci); - if(pv->is_sat(s)){ - std::cout << "interpolation invariant violated at step " << i << "\n"; - assert(0 && "interpolation invariant violated"); - } - } - if((*parents)[this_frame] == 39) - child_interps[i] = pv->mk_and(child_interps[i],q); -#endif - } - ast &bar = itps[nodes.size()-1]; -#ifdef FACTOR_INTERPS - if(!weak) - bar = my_or(bar,disj_of_set(disjs[nodes.size()-1])); - else - bar = pv->mk_and(bar,pv->mk_not(disj_of_set(disjs[nodes.size()-1]))); -#endif - profiling::timer_stop("interp_proof"); - profiling::timer_start("simplifying"); - bar = pv->simplify(bar); - profiling::timer_stop("simplifying"); - return bar; -} - - -void iz3proof::print(std::ostream &s, int id){ - node_struct &n = nodes[id]; - switch(n.rl){ - case Assumption: - s << "Assumption("; - pv->print_clause(s,n.conclusion); - s << ")"; - break; - case Hypothesis: - s << "Hyp("; pv->print_expr(s,n.conclusion[0]); s << ")"; break; - case Reflexivity: - s << "Refl("; pv->print_expr(s,n.conclusion[0]); s << ")"; break; - case Symmetry: - s << "Symm("; print(s,n.premises[0]); s << ")"; break; - case Transitivity: - s << "Trans("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break; - case Congruence: - s << "Cong("; pv->print_expr(s,n.conclusion[0]); - for(unsigned i = 0; i < n.premises.size(); i++){ - s << ","; - print(s,n.premises[i]); - } - s << ")"; break; - case EqContra: - s << "EqContra("; print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; break; - case Resolution: - s << "Res("; - pv->print_expr(s,n.aux); s << ","; - print(s,n.premises[0]); s << ","; print(s,n.premises[1]); s << ")"; - break; - case Lemma: - s << "Lemma("; - pv->print_clause(s,n.conclusion); - for(unsigned i = 0; i < n.premises.size(); i++){ - s << ","; - print(s,n.premises[i]); - } - s << ")"; - break; - case Contra: - s << "Contra("; - print(s,n.premises[0]); - s << ")"; - break; - default:; - } -} - - -void iz3proof::show(int id){ - std::ostringstream ss; - print(ss,id); - iz3base::pretty_print(std::cout,ss.str()); - // std::cout << ss.str(); - std::cout << "\n"; -} - - diff --git a/src/interp/iz3proof.h b/src/interp/iz3proof.h deleted file mode 100755 index ba4507ba2..000000000 --- a/src/interp/iz3proof.h +++ /dev/null @@ -1,274 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3proof.h - - Abstract: - - This class defines a simple interpolating proof system. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3PROOF_H -#define IZ3PROOF_H - -#include - -#include "interp/iz3base.h" -#include "interp/iz3secondary.h" - -// #define CHECK_PROOFS - -/** This class defines a simple proof system. - - A proof is a dag consisting of "nodes". The children of each node - are its "premises". Each node has a "conclusion" that is a clause, - represented as a vector of literals. - - The literals are represented by abstract syntax trees. Operations - on these, including computation of scopes are provided by iz3base. - - A proof can be interpolated, provided it is restricted to the - rules Resolution, Assumption, Contra and Lemma, and that all - clauses are strict (i.e., each literal in each clause is local). - -*/ - -class iz3proof { - public: - /** The type of proof nodes (nodes in the derivation tree). */ - typedef int node; - - /** Enumeration of proof rules. */ - enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra}; - - /** Interface to prover. */ - typedef iz3base prover; - - /** Ast type. */ - typedef prover::ast ast; - - /** Object thrown in case of a proof error. */ - struct proof_error: public iz3_exception { - proof_error(): iz3_exception("proof_error") {} - }; - - /* Null proof node */ - static const node null = -1; - - /** Make a resolution node with given pivot liter and premises. - The conclusion of premise1 should contain the negation of the - pivot literal, while the conclusion of premise2 should containe the - pivot literal. - */ - node make_resolution(ast pivot, node premise1, node premise2); - - /** Make an assumption node. The given clause is assumed in the given frame. */ - node make_assumption(int frame, const std::vector &assumption); - - /** Make a hypothesis node. If phi is the hypothesis, this is - effectively phi |- phi. */ - node make_hypothesis(ast hypothesis); - - /** Make a theory node. This can be any inference valid in the theory. */ - node make_theory(const std::vector &conclusion, std::vector premises); - - /** Make an axiom node. The conclusion must be an instance of an axiom. */ - node make_axiom(const std::vector &conclusion); - - /** Make a Contra node. This rule takes a derivation of the form - Gamma |- False and produces |- \/~Gamma. */ - - node make_contra(node prem, const std::vector &conclusion); - - /** Make a lemma node. A lemma node must have an interpolation. */ - node make_lemma(const std::vector &conclusion, const std::vector &interpolation); - - /** Make a Reflexivity node. This rule produces |- x = x */ - - node make_reflexivity(ast con); - - /** Make a Symmetry node. This takes a derivation of |- x = y and - produces | y = x */ - - node make_symmetry(ast con, node prem); - - /** Make a transitivity node. This takes derivations of |- x = y - and |- y = z produces | x = z */ - - node make_transitivity(ast con, node prem1, node prem2); - - /** Make a congruence node. This takes derivations of |- x_i = y_i - and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ - - node make_congruence(ast con, const std::vector &prems); - - /** Make an equality contradicition node. This takes |- x = y - and |- !(x = y) and produces false. */ - - node make_eqcontra(node prem1, node prem2); - - /** Get the rule of a node in a proof. */ - rule get_rule(node n){ - return nodes[n].rl; - } - - /** Get the pivot of a resolution node. */ - ast get_pivot(node n){ - return nodes[n].aux; - } - - /** Get the frame of an assumption node. */ - int get_frame(node n){ - return nodes[n].frame; - } - - /** Get the number of literals of the conclusion of a node. */ - int get_num_conclusion_lits(node n){ - return get_conclusion(n).size(); - } - - /** Get the nth literal of the conclusion of a node. */ - ast get_nth_conclusion_lit(node n, int i){ - return get_conclusion(n)[i]; - } - - /** Get the conclusion of a node. */ - void get_conclusion(node n, std::vector &result){ - result = get_conclusion(n); - } - - /** Get the number of premises of a node. */ - int get_num_premises(node n){ - return nodes[n].premises.size(); - } - - /** Get the nth premise of a node. */ - int get_nth_premise(node n, int i){ - return nodes[n].premises[i]; - } - - /** Get all the premises of a node. */ - void get_premises(node n, std::vector &result){ - result = nodes[n].premises; - } - - /** Create a new proof node, replacing the premises of an old - one. */ - - node clone(node n, std::vector &premises){ - if(premises == nodes[n].premises) - return n; - nodes.push_back(nodes[n]); - nodes.back().premises = premises; - return nodes.size()-1; - } - - /** Copy a proof node from src */ - node copy(iz3proof &src, node n); - - /** Resolve two lemmas on a given literal. */ - - node resolve_lemmas(ast pivot, node left, node right); - - /** Swap two proofs. */ - void swap(iz3proof &other){ - std::swap(pv,other.pv); - nodes.swap(other.nodes); - interps.swap(other.interps); - } - - /** Compute an interpolant for a proof, where the "A" side is defined by - the given range of frames. Parameter "weak", when true, uses different - interpolation system that resutls in generally weaker interpolants. - */ - ast interpolate(const prover::range &_rng, bool weak = false -#ifdef CHECK_PROOFS - , Z3_ast assump = (Z3_ast)0, std::vector *parents = 0 - -#endif - ); - - /** print proof node to a stream */ - - void print(std::ostream &s, node n); - - /** show proof node on stdout */ - void show(node n); - - /** Construct a proof, with a given prover. */ - iz3proof(prover *p){ - pv = p; - } - - /** Default constructor */ - iz3proof(){pv = 0;} - - - protected: - - struct node_struct { - rule rl; - ast aux; - int frame; - std::vector conclusion; - std::vector premises; - }; - - std::vector nodes; - std::vector > interps; // interpolations of lemmas - prover *pv; - - node make_node(){ - nodes.push_back(node_struct()); - return nodes.size()-1; - } - - void resolve(ast pivot, std::vector &cls1, const std::vector &cls2); - - node copy_rec(stl_ext::hash_map &memo, iz3proof &src, node n); - - void interpolate_lemma(node_struct &n); - - // lazily compute the result of resolution - // the node member "frame" indicates result is computed - const std::vector &get_conclusion(node x){ - node_struct &n = nodes[x]; - if(n.rl == Resolution && !n.frame){ - n.conclusion = get_conclusion(n.premises[0]); - resolve(n.aux,n.conclusion,get_conclusion(n.premises[1])); - n.frame = 1; - } - return n.conclusion; - } - - prover::range rng; - bool weak; - stl_ext::hash_set b_lits; - ast my_or(ast x, ast y); -#ifdef CHECK_PROOFS - std::vector child_interps; -#endif - bool pred_in_A(ast id); - bool term_in_B(ast id); - bool frame_in_A(int frame); - bool lit_in_B(ast lit); - ast get_A_lits(std::vector &cls); - ast get_B_lits(std::vector &cls); - void find_B_lits(); - ast disj_of_set(std::set &s); - void mk_or_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs); - void mk_and_factor(int p1, int p2, int i, std::vector &itps, std::vector > &disjs); - void set_of_B_lits(std::vector &cls, std::set &res); - void set_of_A_lits(std::vector &cls, std::set &res); -}; - -#endif diff --git a/src/interp/iz3proof_itp.cpp b/src/interp/iz3proof_itp.cpp deleted file mode 100755 index fc9d0fac6..000000000 --- a/src/interp/iz3proof_itp.cpp +++ /dev/null @@ -1,3117 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3proof.cpp - - Abstract: - - This class defines a simple interpolating proof system. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3proof_itp.h" - -using namespace stl_ext; - -// #define INVARIANT_CHECKING - -class iz3proof_itp_impl : public iz3proof_itp { - - prover *pv; - prover::range rng; - bool weak; - - enum LitType {LitA,LitB,LitMixed}; - - hash_map placeholders; - - // These symbols represent deduction rules - - /* This symbol represents a proof by contradiction. That is, - contra(p,l1 /\ ... /\ lk) takes a proof p of - - l1,...,lk |- false - - and returns a proof of - - |- ~l1,...,~l2 - */ - symb contra; - - /* The summation rule. The term sum(p,c,i) takes a proof p of an - inequality i', an integer coefficient c and an inequality i, and - yieds a proof of i' + ci. */ - symb sum; - - /* Proof rotation. The proof term rotate(q,p) takes a - proof p of: - - Gamma, q |- false - - and yields a proof of: - - Gamma |- ~q - */ - symb rotate_sum; - - /* Inequalities to equality. leq2eq(p, q, r) takes a proof - p of ~x=y, a proof q of x <= y and a proof r of y <= x - and yields a proof of false. */ - symb leq2eq; - - /* Equality to inequality. eq2leq(p, q) takes a proof p of x=y, and - a proof q ~(x <= y) and and yields a proof of false. */ - symb eq2leq; - - /* Proof term cong(p,q) takes a proof p of x=y and a proof - q of t != t and returns a proof of false. */ - symb cong; - - - /* Excluded middle. exmid(phi,p,q) takes a proof p of phi and a - proof q of ~\phi and returns a proof of false. */ - symb exmid; - - /* Symmetry. symm(p) takes a proof p of x=y and produces - a proof of y=x. */ - symb symm; - - /* Modus ponens. modpon(p,e,q) takes proofs p of P, e of P=Q - and q of ~Q and returns a proof of false. */ - symb modpon; - - /* This oprerator represents a concatenation of rewrites. The term - a=b;c=d represents an A rewrite from a to b, followed by a B - rewrite fron b to c, followed by an A rewrite from c to d. - */ - symb concat; - - /* This represents a lack of a proof */ - ast no_proof; - - // This is used to represent an infinitessimal value - ast epsilon; - - // Represents the top position of a term - ast top_pos; - - // add_pos(i,pos) represents position pos if the ith argument - symb add_pos; - - // rewrite proof rules - - /* rewrite_A(pos,cond,x=y) derives A |- cond => t[x]_p = t[y]_p - where t is an arbitrary term */ - symb rewrite_A; - - /* rewrite_B(pos,cond,x=y) derives B |- cond => t[x]_p = t[y]_p, - where t is an arbitrary term */ - symb rewrite_B; - - /* a normalization step is of the form (lhs=rhs) : proof, where "proof" - is a proof of lhs=rhs and lhs is a mixed term. If rhs is a mixed term - then it must have a greater index than lhs. */ - symb normal_step; - - /* A chain of normalization steps is either "true" (the null chain) - or normal_chain( ), where step is a normalization step - and tail is a normalization chain. The lhs of must have - a less term index than any lhs in the chain. Moreover, the rhs of - may not occur as the lhs of step in . If we wish to - add lhs=rhs to the beginning of and rhs=rhs' occurs in - we must apply transitivity, transforming to lhs=rhs'. */ - - symb normal_chain; - - /* If p is a proof of Q and c is a normalization chain, then normal(p,c) - is a proof of Q(c) (that is, Q with all substitutions in c performed). */ - - symb normal; - - /** Stand-ins for quantifiers */ - - symb sforall, sexists; - - - ast get_placeholder(ast t){ - hash_map::iterator it = placeholders.find(t); - if(it != placeholders.end()) - return it->second; - ast &res = placeholders[t]; - res = mk_fresh_constant("@p",get_type(t)); -#if 0 - std::cout << "placeholder "; - print_expr(std::cout,res); - std::cout << " = "; - print_expr(std::cout,t); - std::cout << std::endl; -#endif - return res; - } - - ast make_contra_node(const ast &pf, const std::vector &lits, int pfok = -1){ - if(lits.size() == 0) - return pf; - std::vector reslits; - reslits.push_back(make(contra,pf,mk_false())); - for(unsigned i = 0; i < lits.size(); i++){ - ast bar; - if(pfok & (1 << i)) bar = make(rotate_sum,lits[i],pf); - else bar = no_proof; - ast foo = make(contra,bar,lits[i]); - reslits.push_back(foo); - } - return make(And,reslits); - } - - LitType get_term_type(const ast &lit){ - prover::range r = pv->ast_scope(lit); - if(pv->range_is_empty(r)) - return LitMixed; - if(weak) { - if(pv->range_min(r) == SHRT_MIN) - return pv->range_contained(r,rng) ? LitA : LitB; - else - return pv->ranges_intersect(r,rng) ? LitA : LitB; - } - else - return pv->range_contained(r,rng) ? LitA : LitB; - } - - bool term_common(const ast &t){ - prover::range r = pv->ast_scope(t); - return pv->ranges_intersect(r,rng) && !pv->range_contained(r,rng); - } - - bool term_in_vocab(LitType ty, const ast &lit){ - prover::range r = pv->ast_scope(lit); - if(ty == LitA){ - return pv->ranges_intersect(r,rng); - } - return !pv->range_contained(r,rng); - } - - /** Make a resolution node with given pivot literal and premises. - The conclusion of premise1 should contain the negation of the - pivot literal, while the conclusion of premise2 should contain the - pivot literal. - */ - node make_resolution(ast pivot, const std::vector &conc, node premise1, node premise2) { - LitType lt = get_term_type(pivot); - if(lt == LitA) - return my_or(premise1,premise2); - if(lt == LitB) - return my_and(premise1,premise2); - - /* the mixed case is a bit complicated */ - - static int non_local_count = 0; - ast res = resolve_arith(pivot,conc,premise1,premise2); -#ifdef INVARIANT_CHECKING - check_contra(conc,res); -#endif - non_local_count++; - return res; - } - - - /* Handles the case of resolution on a mixed arith atom. */ - - ast resolve_arith(const ast &pivot, const std::vector &conc, node premise1, node premise2){ - ast atom = get_lit_atom(pivot); - hash_map memo; - ast neg_pivot_lit = mk_not(atom); - if(op(pivot) != Not) - std::swap(premise1,premise2); - if(op(pivot) == Equal && op(arg(pivot,0)) == Select && op(arg(pivot,1)) == Select){ - neg_pivot_lit = mk_not(neg_pivot_lit); - std::swap(premise1,premise2); - } - return resolve_arith_rec1(memo, neg_pivot_lit, premise1, premise2); - } - - - ast apply_coeff(const ast &coeff, const ast &t){ -#if 0 - rational r; - if(!is_integer(coeff,r)) - throw iz3_exception("ack!"); - ast n = make_int(r.numerator()); - ast res = make(Times,n,t); - if(!r.is_int()) { - ast d = make_int(r.numerator()); - res = mk_idiv(res,d); - } - return res; -#endif - return make(Times,coeff,t); - } - - ast sum_ineq(const ast &coeff1, const ast &ineq1, const ast &coeff2, const ast &ineq2){ - opr sum_op = Leq; - if(op(ineq1) == Lt || op(ineq2) == Lt) - sum_op = Lt; - ast sum_sides[2]; - for(int i = 0; i < 2; i++){ - sum_sides[i] = make(Plus,apply_coeff(coeff1,arg(ineq1,i)),apply_coeff(coeff2,arg(ineq2,i))); - sum_sides[i] = z3_simplify(sum_sides[i]); - } - return make(sum_op,sum_sides[0],sum_sides[1]); - } - - - void collect_contra_resolvents(int from, const ast &pivot1, const ast &pivot, const ast &conj, std::vector &res){ - int nargs = num_args(conj); - for(int i = from; i < nargs; i++){ - ast f = arg(conj,i); - if(!(f == pivot)){ - ast ph = get_placeholder(mk_not(arg(pivot1,1))); - ast pf = arg(pivot1,0); - ast thing = pf == no_proof ? no_proof : subst_term_and_simp(ph,pf,arg(f,0)); - ast newf = make(contra,thing,arg(f,1)); - res.push_back(newf); - } - } - } - - bool is_negative_equality(const ast &e){ - if(op(e) == Not){ - opr o = op(arg(e,0)); - return o == Equal || o == Iff; - } - return false; - } - - int count_negative_equalities(const std::vector &resolvent){ - int res = 0; - for(unsigned i = 0; i < resolvent.size(); i++) - if(is_negative_equality(arg(resolvent[i],1))) - res++; - return res; - } - - ast resolve_contra_nf(const ast &pivot1, const ast &conj1, - const ast &pivot2, const ast &conj2){ - std::vector resolvent; - collect_contra_resolvents(0,pivot1,pivot2,conj2,resolvent); - collect_contra_resolvents(1,pivot2,pivot1,conj1,resolvent); - if(count_negative_equalities(resolvent) > 1) - throw proof_error(); - if(resolvent.size() == 1) // we have proved a contradiction - return simplify(arg(resolvent[0],0)); // this is the proof -- get interpolant - return make(And,resolvent); - } - - ast resolve_contra(const ast &pivot1, const ast &conj1, - const ast &pivot2, const ast &conj2){ - if(arg(pivot1,0) != no_proof) - return resolve_contra_nf(pivot1, conj1, pivot2, conj2); - if(arg(pivot2,0) != no_proof) - return resolve_contra_nf(pivot2, conj2, pivot1, conj1); - return resolve_with_quantifier(pivot1, conj1, pivot2, conj2); - } - - - bool is_contra_itp(const ast &pivot1, ast itp2, ast &pivot2){ - if(op(itp2) == And){ - int nargs = num_args(itp2); - for(int i = 1; i < nargs; i++){ - ast foo = arg(itp2,i); - if(op(foo) == Uninterpreted && sym(foo) == contra){ - if(arg(foo,1) == pivot1){ - pivot2 = foo; - return true; - } - } - else break; - } - } - return false; - } - - ast resolve_arith_rec2(hash_map &memo, const ast &pivot1, const ast &conj1, const ast &itp2){ - ast &res = memo[itp2]; - if(!res.null()) - return res; - - ast pivot2; - if(is_contra_itp(mk_not(arg(pivot1,1)),itp2,pivot2)) - res = resolve_contra(pivot1,conj1,pivot2,itp2); - else { - switch(op(itp2)){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(itp2); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - args[i] = resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,i)); - ast foo = itp2; // get rid of const - res = clone(foo,args); - break; - } - default: - { - opr o = op(itp2); - if(o == Uninterpreted){ - symb s = sym(itp2); - if(s == sforall || s == sexists) - res = make(s,arg(itp2,0),resolve_arith_rec2(memo, pivot1, conj1, arg(itp2,1))); - else - res = itp2; - } - else { - res = itp2; - } - } - } - } - return res; - } - - - ast resolve_arith_rec1(hash_map &memo, const ast &neg_pivot_lit, const ast &itp1, const ast &itp2){ - ast &res = memo[itp1]; - if(!res.null()) - return res; - ast pivot1; - if(is_contra_itp(neg_pivot_lit,itp1,pivot1)){ - hash_map memo2; - res = resolve_arith_rec2(memo2,pivot1,itp1,itp2); - } - else { - switch(op(itp1)){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(itp1); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - args[i] = resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,i), itp2); - ast foo = itp1; // get rid of const - res = clone(foo,args); - break; - } - default: - { - opr o = op(itp1); - if(o == Uninterpreted){ - symb s = sym(itp1); - if(s == sforall || s == sexists) - res = make(s,arg(itp1,0),resolve_arith_rec1(memo, neg_pivot_lit, arg(itp1,1), itp2)); - else - res = itp1; - } - else { - res = itp1; - } - } - } - } - return res; - } - - void check_contra(hash_set &memo, hash_set &neg_lits, const ast &foo){ - if(memo.find(foo) != memo.end()) - return; - memo.insert(foo); - if(op(foo) == Uninterpreted && sym(foo) == contra){ - ast neg_lit = arg(foo,1); - if(!is_false(neg_lit) && neg_lits.find(neg_lit) == neg_lits.end()) - throw iz3_exception("lost a literal"); - return; - } - else { - switch(op(foo)){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(foo); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - check_contra(memo, neg_lits, arg(foo,i)); - break; - } - default: break; - } - } - } - - void check_contra(const std::vector &neg_lits, const ast &foo){ - hash_set memo; - hash_set neg_lits_set; - for(unsigned i = 0; i < neg_lits.size(); i++) - if(get_term_type(neg_lits[i]) == LitMixed) - neg_lits_set.insert(mk_not(neg_lits[i])); - check_contra(memo,neg_lits_set,foo); - } - - hash_map subst_memo; // memo of subst function - - ast subst_term_and_simp(const ast &var, const ast &t, const ast &e){ - subst_memo.clear(); - return subst_term_and_simp_rec(var,t,e); - } - - ast subst_term_and_simp_rec(const ast &var, const ast &t, const ast &e){ - if(e == var) return t; - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = subst_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - if(op(e) == Uninterpreted){ - symb g = sym(e); - if(g == rotate_sum){ - if(var == get_placeholder(arg(e,0))){ - res = e; - } - else - res = make(rotate_sum,arg(e,0),subst_term_and_simp_rec(var,t,arg(e,1))); - return res; - } - if(g == concat){ - res = e; - return res; - } - } - int nargs = num_args(e); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = subst_term_and_simp_rec(var,t,arg(e,i)); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else if(f == And) res = my_and(args); - else if(f == Or) res = my_or(args); - else if(f == Idiv) res = mk_idiv(args[0],args[1]); - else res = clone(e,args); - } - return res; - } - - /* This is where the real work happens. Here, we simplify the - proof obtained by cut elimination, obtaining an interpolant. */ - - struct cannot_simplify: public iz3_exception { - cannot_simplify(): iz3_exception("cannot_simplify") {} - }; - hash_map simplify_memo; - - ast simplify(const ast &t){ - ast res = normalize(simplify_rec(t)); -#ifdef BOGUS_QUANTS - if(localization_vars.size()) - res = add_quants(z3_simplify(res)); -#endif - return res; - } - - ast simplify_rec(const ast &e){ - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = simplify_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - int nargs = num_args(e); - std::vector args(nargs); - bool placeholder_arg = false; - symb g = sym(e); - if(g == concat){ - res = e; - return res; - } - for(int i = 0; i < nargs; i++){ - if(i == 0 && g == rotate_sum) - args[i] = arg(e,i); - else - args[i] = simplify_rec(arg(e,i)); - placeholder_arg |= is_placeholder(args[i]); - } - try { - TRACE("duality", print_expr(tout, e); tout << "\n";); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else if(f == And) res = my_and(args); - else if(f == Or) - res = my_or(args); - else if(f == Idiv) res = mk_idiv(args[0],args[1]); - else if(f == Uninterpreted && !placeholder_arg){ - if(g == rotate_sum) res = simplify_rotate(args); - else if(g == symm) res = simplify_symm(args); - else if(g == modpon) res = simplify_modpon(args); - else if(g == sum) res = simplify_sum(args); - else if(g == exmid) res = simplify_exmid(args); - else if(g == cong) res = simplify_cong(args); -#if 0 - else if(g == modpon) res = simplify_modpon(args); - else if(g == leq2eq) res = simplify_leq2eq(args); - else if(g == eq2leq) res = simplify_eq2leq(args); -#endif - else res = clone(e,args); - } - else res = clone(e,args); - } - catch (const cannot_simplify &){ - if(g == sum) - res = clone(e,args); - else - throw "interpolation failure"; - } - } - return res; - } - - - ast simplify_rotate(const std::vector &args){ - const ast &pf = args[1]; - ast pl = get_placeholder(args[0]); - if(op(pf) == Uninterpreted){ - symb g = sym(pf); - if(g == sum) return simplify_rotate_sum(pl,pf); - if(g == leq2eq) return simplify_rotate_leq2eq(pl,args[0],pf); - if(g == eq2leq) return simplify_rotate_eq2leq(pl,args[0],pf); - if(g == cong) return simplify_rotate_cong(pl,args[0],pf); - if(g == modpon) return simplify_rotate_modpon(pl,args[0],pf); - // if(g == symm) return simplify_rotate_symm(pl,args[0],pf); - } - if(op(pf) == Leq) - throw iz3_exception("foo!"); - throw cannot_simplify(); - } - - bool is_normal_ineq(const ast &ineq){ - if(sym(ineq) == normal) - return is_ineq(arg(ineq,0)); - return is_ineq(ineq); - } - - ast destruct_cond_ineq(const ast &ineq, ast &Aproves, ast &Bproves){ - ast res = ineq; - opr o = op(res); - if(o == And){ - Aproves = my_and(Aproves,arg(res,0)); - res = arg(res,1); - o = op(res); - } - if(o == Implies){ - Bproves = my_and(Bproves,arg(res,0)); - res = arg(res,1); - } - return res; - } - - ast distribute_coeff(const ast &coeff, const ast &s){ - if(sym(s) == sum){ - if(sym(arg(s,2)) == sum) - return make(sum, - distribute_coeff(coeff,arg(s,0)), - make_int(rational(1)), - distribute_coeff(make(Times,coeff,arg(s,1)), arg(s,2))); - else - return make(sum, - distribute_coeff(coeff,arg(s,0)), - make(Times,coeff,arg(s,1)), - arg(s,2)); - } - if(op(s) == Leq && arg(s,1) == make_int(rational(0)) && arg(s,2) == make_int(rational(0))) - return s; - return make(sum,make(Leq,make_int(rational(0)),make_int(rational(0))),coeff,s); - } - - ast simplify_sum(std::vector &args){ - if(args[1] != make_int(rational(1))){ - if(sym(args[2]) == sum) - return make(sum,args[0],make_int(rational(1)),distribute_coeff(args[1],args[2])); - } - ast Aproves = mk_true(), Bproves = mk_true(); - ast ineq = destruct_cond_ineq(args[0],Aproves,Bproves); - if(!is_normal_ineq(ineq)) throw cannot_simplify(); - sum_cond_ineq(ineq,args[1],args[2],Aproves,Bproves); - return my_and(Aproves,my_implies(Bproves,ineq)); - } - - ast simplify_rotate_sum(const ast &pl, const ast &pf){ - ast Aproves = mk_true(), Bproves = mk_true(); - ast ineq = make(Leq,make_int("0"),make_int("0")); - ineq = rotate_sum_rec(pl,pf,Aproves,Bproves,ineq); - if(is_true(Aproves) && is_true(Bproves)) - return ineq; - return my_and(Aproves,my_implies(Bproves,ineq)); - } - - bool is_rewrite_chain(const ast &chain){ - return sym(chain) == concat; - } - -#if 0 - ast ineq_from_chain_simple(const ast &chain, ast &cond){ - if(is_true(chain)) - return chain; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_true(rest) && is_rewrite_side(LitA,last) - && is_true(rewrite_lhs(last))){ - cond = my_and(cond,rewrite_cond(last)); - return rewrite_rhs(last); - } - if(is_rewrite_side(LitB,last) && is_true(rewrite_cond(last))) - return ineq_from_chain_simple(rest,cond); - return chain; - } -#endif - - ast ineq_from_chain(const ast &chain, ast &Aproves, ast &Bproves){ - if(is_rewrite_chain(chain)) - return rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); - return chain; - } - - - void sum_cond_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ - opr o = op(ineq2); - if(o == And){ - sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); - Aproves = my_and(Aproves,arg(ineq2,0)); - } - else if(o == Implies){ - sum_cond_ineq(ineq,coeff2,arg(ineq2,1),Aproves,Bproves); - Bproves = my_and(Bproves,arg(ineq2,0)); - } - else { - ast the_ineq = ineq_from_chain(ineq2,Aproves,Bproves); - if(sym(ineq) == normal || sym(the_ineq) == normal){ - sum_normal_ineq(ineq,coeff2,the_ineq,Aproves,Bproves); - return; - } - if(is_ineq(the_ineq)) - linear_comb(ineq,coeff2,the_ineq); - else - throw cannot_simplify(); - } - } - - void destruct_normal(const ast &pf, ast &p, ast &n){ - if(sym(pf) == normal){ - p = arg(pf,0); - n = arg(pf,1); - } - else { - p = pf; - n = mk_true(); - } - } - - void sum_normal_ineq(ast &ineq, const ast &coeff2, const ast &ineq2, ast &Aproves, ast &Bproves){ - ast in1,in2,n1,n2; - destruct_normal(ineq,in1,n1); - destruct_normal(ineq2,in2,n2); - ast dummy1, dummy2; - sum_cond_ineq(in1,coeff2,in2,dummy1,dummy2); - n1 = merge_normal_chains(n1,n2, Aproves, Bproves); - ineq = is_true(n1) ? in1 : make_normal(in1,n1); - } - - bool is_ineq(const ast &ineq){ - opr o = op(ineq); - if(o == Not) o = op(arg(ineq,0)); - return o == Leq || o == Lt || o == Geq || o == Gt; - } - - // divide both sides of inequality by a non-negative integer divisor - ast idiv_ineq(const ast &ineq1, const ast &divisor){ - if(sym(ineq1) == normal){ - ast in1,n1; - destruct_normal(ineq1,in1,n1); - in1 = idiv_ineq(in1,divisor); - return make_normal(in1,n1); - } - if(divisor == make_int(rational(1))) - return ineq1; - ast ineq = ineq1; - if(op(ineq) == Lt) - ineq = simplify_ineq(make(Leq,arg(ineq,0),make(Sub,arg(ineq,1),make_int("1")))); - return make(op(ineq),mk_idiv(arg(ineq,0),divisor),mk_idiv(arg(ineq,1),divisor)); - } - - ast rotate_sum_rec(const ast &pl, const ast &pf, ast &Aproves, ast &Bproves, ast &ineq){ - if(pf == pl){ - if(sym(ineq) == normal) - return ineq; - return simplify_ineq(ineq); - } - if(op(pf) == Uninterpreted && sym(pf) == sum){ - if(arg(pf,2) == pl){ - sum_cond_ineq(ineq,make_int("1"),arg(pf,0),Aproves,Bproves); - ineq = idiv_ineq(ineq,arg(pf,1)); - return ineq; - } - sum_cond_ineq(ineq,arg(pf,1),arg(pf,2),Aproves,Bproves); - return rotate_sum_rec(pl,arg(pf,0),Aproves,Bproves,ineq); - } - throw cannot_simplify(); - } - - ast simplify_rotate_leq2eq(const ast &pl, const ast &neg_equality, const ast &pf){ - if(pl == arg(pf,0)){ - ast equality = arg(neg_equality,0); - ast x = arg(equality,0); - ast y = arg(equality,1); - ast Aproves1 = mk_true(), Bproves1 = mk_true(); - ast pf1 = destruct_cond_ineq(arg(pf,1), Aproves1, Bproves1); - ast pf2 = destruct_cond_ineq(arg(pf,2), Aproves1, Bproves1); - ast xleqy = round_ineq(ineq_from_chain(pf1,Aproves1,Bproves1)); - ast yleqx = round_ineq(ineq_from_chain(pf2,Aproves1,Bproves1)); - ast ineq1 = make(Leq,make_int("0"),make_int("0")); - sum_cond_ineq(ineq1,make_int("-1"),xleqy,Aproves1,Bproves1); - sum_cond_ineq(ineq1,make_int("-1"),yleqx,Aproves1,Bproves1); - ast Acond = my_implies(Aproves1,my_and(Bproves1,z3_simplify(ineq1))); - ast Aproves2 = mk_true(), Bproves2 = mk_true(); - ast ineq2 = make(Leq,make_int("0"),make_int("0")); - sum_cond_ineq(ineq2,make_int("1"),xleqy,Aproves2,Bproves2); - sum_cond_ineq(ineq2,make_int("1"),yleqx,Aproves2,Bproves2); - ast Bcond = my_implies(Bproves1,my_and(Aproves1,z3_simplify(ineq2))); - // if(!is_true(Aproves1) || !is_true(Bproves1)) - // std::cout << "foo!\n";; - if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ - if(get_term_type(arg(x,0)) == LitA){ - ast iter = z3_simplify(make(Plus,arg(x,0),get_ineq_rhs(xleqy))); - ast rewrite1 = make_rewrite(LitA,pos_add(0,top_pos),Acond,make(Equal,arg(x,0),iter)); - iter = make(Plus,iter,arg(x,1)); - ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); - return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); - } - if(get_term_type(arg(x,1)) == LitA){ - ast iter = z3_simplify(make(Plus,arg(x,1),get_ineq_rhs(xleqy))); - ast rewrite1 = make_rewrite(LitA,pos_add(1,top_pos),Acond,make(Equal,arg(x,1),iter)); - iter = make(Plus,arg(x,0),iter); - ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); - return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); - } - } - if(get_term_type(x) == LitA){ - ast iter = z3_simplify(make(Plus,x,get_ineq_rhs(xleqy))); - ast rewrite1 = make_rewrite(LitA,top_pos,Acond,make(Equal,x,iter)); - ast rewrite2 = make_rewrite(LitB,top_pos,Bcond,make(Equal,iter,y)); - return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); - } - if(get_term_type(y) == LitA){ - ast iter = z3_simplify(make(Plus,y,get_ineq_rhs(yleqx))); - ast rewrite2 = make_rewrite(LitA,top_pos,Acond,make(Equal,iter,y)); - ast rewrite1 = make_rewrite(LitB,top_pos,Bcond,make(Equal,x,iter)); - return chain_cons(chain_cons(mk_true(),rewrite1),rewrite2); - } - throw cannot_simplify(); - } - throw cannot_simplify(); - } - - ast round_ineq(const ast &ineq){ - if(sym(ineq) == normal) - return make_normal(round_ineq(arg(ineq,0)),arg(ineq,1)); - if(!is_ineq(ineq)) - throw cannot_simplify(); - ast res = simplify_ineq(ineq); - if(op(res) == Lt) - res = make(Leq,arg(res,0),make(Sub,arg(res,1),make_int("1"))); - return res; - } - - ast unmixed_eq2ineq(const ast &lhs, const ast &rhs, opr comp_op, const ast &equa, ast &cond){ - ast ineqs= chain_ineqs(comp_op,LitA,equa,lhs,rhs); // chain must be from lhs to rhs - cond = my_and(cond,chain_conditions(LitA,equa)); - ast Bconds = z3_simplify(chain_conditions(LitB,equa)); - if(is_true(Bconds) && op(ineqs) != And) - return my_implies(cond,ineqs); - if(op(ineqs) != And) - return my_and(Bconds,my_implies(cond,ineqs)); - throw iz3_exception("help!"); - } - - ast add_mixed_eq2ineq(const ast &lhs, const ast &rhs, const ast &equa, const ast &itp){ - if(is_true(equa)) - return itp; - std::vector args(3); - args[0] = itp; - args[1] = make_int("1"); - ast ineq = make(Leq,make_int(rational(0)),make_int(rational(0))); - args[2] = make_normal(ineq,cons_normal(fix_normal(lhs,rhs,equa),mk_true())); - return simplify_sum(args); - } - - - ast simplify_rotate_eq2leq(const ast &pl, const ast &neg_equality, const ast &pf){ - if(pl == arg(pf,1)){ - TRACE("duality", print_expr(tout, pl); print_expr(tout << "\n", neg_equality); print_expr(tout << "\n", pf); tout << "\n";); - ast cond = mk_true(); - ast equa = sep_cond(arg(pf,0),cond); - if(is_equivrel_chain(equa)){ - ast lhs,rhs; eq_from_ineq(arg(neg_equality,0),lhs,rhs); // get inequality we need to prove - if(!rewrites_from_to(equa,lhs,rhs)){ - lhs = arg(arg(neg_equality,0),0); // the equality proved is ambiguous, sadly - rhs = arg(arg(neg_equality,0),1); - } - LitType lhst = get_term_type(lhs), rhst = get_term_type(rhs); - if(lhst != LitMixed && rhst != LitMixed) - return unmixed_eq2ineq(lhs, rhs, op(arg(neg_equality,0)), equa, cond); - else { - ast left, left_term, middle, right_term, right; - left = get_left_movers(equa,lhs,middle,left_term); - middle = get_right_movers(middle,rhs,right,right_term); - ast itp = unmixed_eq2ineq(left_term, right_term, op(arg(neg_equality,0)), middle, cond); - // itp = my_implies(cond,itp); - itp = add_mixed_eq2ineq(lhs, left_term, left, itp); - itp = add_mixed_eq2ineq(right_term, rhs, right, itp); - return itp; - } - } - } - throw iz3_exception("help!"); - } - - void reverse_modpon(std::vector &args){ - std::vector sargs(1); sargs[0] = args[1]; - args[1] = simplify_symm(sargs); - if(is_equivrel_chain(args[2])) - args[1] = down_chain(args[1]); - std::swap(args[0],args[2]); - } - - ast simplify_rotate_modpon(const ast &pl, const ast &neg_equality, const ast &pf){ - std::vector args; args.resize(3); - args[0] = arg(pf,0); - args[1] = arg(pf,1); - args[2] = arg(pf,2); - if(pl == args[0]) - reverse_modpon(args); - if(pl == args[2]){ - ast cond = mk_true(); - ast chain = simplify_modpon_fwd(args, cond); - return my_implies(cond,chain); - } - throw cannot_simplify(); - } - - ast get_ineq_rhs(const ast &ineq2){ - opr o = op(ineq2); - if(o == Implies) - return get_ineq_rhs(arg(ineq2,1)); - else if(o == Leq || o == Lt) - return arg(ineq2,1); - throw cannot_simplify(); - } - - ast simplify_rotate_cong(const ast &pl, const ast &neg_equality, const ast &pf){ - if(pl == arg(pf,2)){ - if(op(arg(pf,0)) == True) - return mk_true(); - rational pos; - if(is_numeral(arg(pf,1),pos)){ - int ipos = pos.get_unsigned(); - ast cond = mk_true(); - ast equa = sep_cond(arg(pf,0),cond); -#if 0 - if(op(equa) == Equal){ - ast pe = mk_not(neg_equality); - ast lhs = subst_in_arg_pos(ipos,arg(equa,0),arg(pe,0)); - ast rhs = subst_in_arg_pos(ipos,arg(equa,1),arg(pe,1)); - ast res = make(Equal,lhs,rhs); - return my_implies(cond,res); - } -#endif - ast res = chain_pos_add(ipos,equa); - return my_implies(cond,res); - } - } - throw cannot_simplify(); - } - - ast simplify_symm(const std::vector &args){ - if(op(args[0]) == True) - return mk_true(); - ast cond = mk_true(); - ast equa = sep_cond(args[0],cond); - if(is_equivrel_chain(equa)) - return my_implies(cond,reverse_chain(equa)); - if(is_negation_chain(equa)) - return commute_negation_chain(equa); - throw cannot_simplify(); - } - - ast simplify_modpon_fwd(const std::vector &args, ast &cond){ - ast P = sep_cond(args[0],cond); - ast PeqQ = sep_cond(args[1],cond); - ast chain; - if(is_equivrel_chain(P)){ - try { - ast split[2]; - split_chain(PeqQ,split); - chain = reverse_chain(split[0]); - chain = concat_rewrite_chain(chain,P); - chain = concat_rewrite_chain(chain,split[1]); - } - catch(const cannot_split &){ - static int this_count = 0; - this_count++; - ast tail, pref = get_head_chain(PeqQ,tail,false); // pref is x=y, tail is x=y -> x'=y' - ast split[2]; split_chain(tail,split); // rewrites from x to x' and y to y' - ast head = chain_last(pref); - ast prem = make_rewrite(rewrite_side(head),top_pos,rewrite_cond(head),make(Iff,mk_true(),mk_not(rewrite_lhs(head)))); - ast back_chain = chain_cons(mk_true(),prem); - back_chain = concat_rewrite_chain(back_chain,chain_pos_add(0,reverse_chain(chain_rest(pref)))); - ast cond = contra_chain(back_chain,P); - if(is_rewrite_side(LitA,head)) - cond = mk_not(cond); - ast fwd_rewrite = make_rewrite(rewrite_side(head),top_pos,cond,rewrite_rhs(head)); - P = chain_cons(mk_true(),fwd_rewrite); - chain = reverse_chain(split[0]); - chain = concat_rewrite_chain(chain,P); - chain = concat_rewrite_chain(chain,split[1]); - } - } - else { // if not an equivalence, must be of form T <-> pred - chain = concat_rewrite_chain(P,PeqQ); - } - return chain; - } - - struct subterm_normals_failed: public iz3_exception { - subterm_normals_failed(): iz3_exception("subterm_normals_failed") {} - }; - - void get_subterm_normals(const ast &ineq1, const ast &ineq2, const ast &chain, ast &normals, - const ast &pos, hash_set &memo, ast &Aproves, ast &Bproves){ - opr o1 = op(ineq1); - opr o2 = op(ineq2); - if(o1 == Not || o1 == Leq || o1 == Lt || o1 == Geq || o1 == Gt || o1 == Plus || o1 == Times){ - int n = num_args(ineq1); - if(o2 != o1 || num_args(ineq2) != n) - throw iz3_exception("bad inequality rewriting"); - for(int i = 0; i < n; i++){ - ast new_pos = add_pos_to_end(pos,i); - get_subterm_normals(arg(ineq1,i), arg(ineq2,i), chain, normals, new_pos, memo, Aproves, Bproves); - } - } - else if(get_term_type(ineq2) == LitMixed){ - if(memo.find(ineq2) == memo.end()){ - memo.insert(ineq2); - ast sub_chain = extract_rewrites(chain,pos); - if(is_true(sub_chain)) - throw iz3_exception("bad inequality rewriting"); - ast new_normal = make_normal_step(ineq2,ineq1,reverse_chain(sub_chain)); - normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); - } - } - else if(!(ineq1 == ineq2)) - throw subterm_normals_failed(); - } - - ast rewrites_to_normals(const ast &ineq1, const ast &chain, ast &normals, ast &Aproves, ast &Bproves, ast &Aineqs){ - if(is_true(chain)) - return ineq1; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast new_ineq1 = rewrites_to_normals(ineq1, rest, normals, Aproves, Bproves, Aineqs); - ast p1 = rewrite_pos(last); - ast term1; - ast coeff = arith_rewrite_coeff(new_ineq1,p1,term1); - ast res = subst_in_pos(new_ineq1,rewrite_pos(last),rewrite_rhs(last)); - ast rpos; - pos_diff(p1,rewrite_pos(last),rpos); - ast term2 = subst_in_pos(term1,rpos,rewrite_rhs(last)); - if(get_term_type(term1) != LitMixed && get_term_type(term2) != LitMixed){ - if(is_rewrite_side(LitA,last)) - linear_comb(Aineqs,coeff,make(Leq,make_int(rational(0)),make(Sub,term2,term1))); - } - else { - ast pf = extract_rewrites(make(concat,mk_true(),last),p1); - ast new_normal = fix_normal(term1,term2,pf); - normals = merge_normal_chains(normals,cons_normal(new_normal,mk_true()), Aproves, Bproves); - } - return res; - } - - ast arith_rewrite_coeff(const ast &ineq, ast &p1, ast &term){ - ast coeff = make_int(rational(1)); - if(p1 == top_pos){ - term = ineq; - return coeff; - } - int argpos = pos_arg(p1); - opr o = op(ineq); - switch(o){ - case Leq: - case Lt: - coeff = argpos ? make_int(rational(1)) : make_int(rational(-1)); - break; - case Geq: - case Gt: - coeff = argpos ? make_int(rational(-1)) : make_int(rational(1)); - break; - case Not: - coeff = make_int(rational(-1)); - case Plus: - break; - case Times: - coeff = arg(ineq,0); - break; - default: - p1 = top_pos; - term = ineq; - return coeff; - } - p1 = arg(p1,1); - ast res = arith_rewrite_coeff(arg(ineq,argpos),p1,term); - p1 = pos_add(argpos,p1); - return coeff == make_int(rational(1)) ? res : make(Times,coeff,res); - } - - ast rewrite_chain_to_normal_ineq(const ast &chain, ast &Aproves, ast &Bproves){ - ast tail, pref = get_head_chain(chain,tail,false); // pref is x=y, tail is x=y -> x'=y' - ast head = chain_last(pref); - ast ineq1 = rewrite_rhs(head); - ast ineq2 = apply_rewrite_chain(ineq1,tail); - ast nc = mk_true(); - hash_set memo; - ast itp = make(Leq,make_int(rational(0)),make_int(rational(0))); - ast Aproves_save = Aproves, Bproves_save = Bproves; try { - get_subterm_normals(ineq1,ineq2,tail,nc,top_pos,memo, Aproves, Bproves); - } - catch (const subterm_normals_failed &){ Aproves = Aproves_save; Bproves = Bproves_save; nc = mk_true(); - rewrites_to_normals(ineq1, tail, nc, Aproves, Bproves, itp); - } - if(is_rewrite_side(LitA,head)){ - linear_comb(itp,make_int("1"),ineq1); // make sure it is normal form - //itp = ineq1; - ast mc = z3_simplify(chain_side_proves(LitB,pref)); - Bproves = my_and(Bproves,mc); - } - else { - ast mc = z3_simplify(chain_side_proves(LitA,pref)); - Aproves = my_and(Aproves,mc); - } - if(is_true(nc)) - return itp; - return make_normal(itp,nc); - } - - /* Given a chain rewrite chain deriving not P and a rewrite chain deriving P, return an interpolant. */ - ast contra_chain(const ast &neg_chain, const ast &pos_chain){ - // equality is a special case. we use the derivation of x=y to rewrite not(x=y) to not(y=y) - if(is_equivrel_chain(pos_chain)){ - ast tail, pref = get_head_chain(neg_chain,tail); // pref is not(x=y), tail is not(x,y) -> not(x',y') - ast split[2]; split_chain(down_chain(tail),split); // rewrites from x to x' and y to y' - ast chain = split[0]; - chain = concat_rewrite_chain(chain,pos_chain); // rewrites from x to y' - chain = concat_rewrite_chain(chain,reverse_chain(split[1])); // rewrites from x to y - chain = concat_rewrite_chain(pref,chain_pos_add(0,chain_pos_add(0,chain))); // rewrites t -> not(y=y) - ast head = chain_last(pref); - if(is_rewrite_side(LitB,head)){ - ast condition = chain_conditions(LitB,chain); - return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),condition); - } - else { - ast condition = chain_conditions(LitA,chain); - return my_and(chain_conditions(LitB,chain),my_implies(condition,mk_not(chain_formulas(LitB,chain)))); - } - // ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,chain_pos_add(0,pos_chain))); - // return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); - } - // otherwise, we reverse the derivation of t = P and use it to rewrite not(P) to not(t) - ast chain = concat_rewrite_chain(neg_chain,chain_pos_add(0,reverse_chain(pos_chain))); - return my_and(my_implies(chain_conditions(LitA,chain),chain_formulas(LitA,chain)),chain_conditions(LitB,chain)); - } - - ast simplify_modpon(const std::vector &args){ - ast Aproves = mk_true(), Bproves = mk_true(); - ast chain = simplify_modpon_fwd(args,Bproves); - ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); - ast interp; - if(is_normal_ineq(Q2)){ // inequalities are special - ast nQ2 = rewrite_chain_to_normal_ineq(chain,Aproves,Bproves); - sum_cond_ineq(nQ2,make_int(rational(1)),Q2,Aproves,Bproves); - interp = normalize(nQ2); - } - else - interp = is_negation_chain(chain) ? contra_chain(chain,Q2) : contra_chain(Q2,chain); - return my_and(Aproves,my_implies(Bproves,interp)); - } - - - ast simplify_exmid(const std::vector &args){ - if(is_equivrel(args[0])){ - ast Aproves = mk_true(), Bproves = mk_true(); - ast chain = destruct_cond_ineq(args[1],Aproves,Bproves); - ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); - ast interp = contra_chain(Q2,chain); - return my_and(Aproves,my_implies(Bproves,interp)); - } - throw iz3_exception("bad exmid"); - } - - ast simplify_cong(const std::vector &args){ - ast Aproves = mk_true(), Bproves = mk_true(); - ast chain = destruct_cond_ineq(args[0],Aproves,Bproves); - rational pos; - if(is_numeral(args[1],pos)){ - int ipos = pos.get_unsigned(); - chain = chain_pos_add(ipos,chain); - ast Q2 = destruct_cond_ineq(args[2],Aproves,Bproves); - ast interp = contra_chain(Q2,chain); - return my_and(Aproves,my_implies(Bproves,interp)); - } - throw iz3_exception("bad cong"); - } - - bool is_equivrel(const ast &p){ - opr o = op(p); - return o == Equal || o == Iff; - } - - struct rewrites_failed: public iz3_exception { - rewrites_failed(): iz3_exception("rewrites_failed") {} - }; - - /* Suppose p in Lang(B) and A |- p -> q and B |- q -> r. Return a z in Lang(B) such that - B |- p -> z and A |- z -> q. Collect any side conditions in "rules". */ - - ast commute_rewrites(const ast &p, const ast &q, const ast &r, ast &rules){ - if(q == r) - return p; - if(p == q) - return r; - else { - ast rew = make(Equal,q,r); - if(get_term_type(rew) == LitB){ - apply_common_rewrites(p,p,q,rules); // A rewrites must be over comon vocab - return r; - } - } - if(sym(p) != sym(q) || sym(q) != sym(r)) - throw rewrites_failed(); - int nargs = num_args(p); - if(nargs != num_args(q) || nargs != num_args(r)) - throw rewrites_failed(); - std::vector args; args.resize(nargs); - for(int i = 0; i < nargs; i++) - args[i] = commute_rewrites(arg(p,i),arg(q,i),arg(r,i),rules); - return clone(p,args); - } - - ast apply_common_rewrites(const ast &p, const ast &q, const ast &r, ast &rules){ - if(q == r) - return p; - ast rew = make(Equal,q,r); - if(term_common(rew)){ - if(p != q) - throw rewrites_failed(); - rules = my_and(rules,rew); - return r; - } - if(sym(p) != sym(q) || sym(q) != sym(r)) - return p; - int nargs = num_args(p); - if(nargs != num_args(q) || nargs != num_args(r)) - return p; - std::vector args; args.resize(nargs); - for(int i = 0; i < nargs; i++) - args[i] = apply_common_rewrites(arg(p,i),arg(q,i),arg(r,i),rules); - return clone(p,args); - } - - ast apply_all_rewrites(const ast &p, const ast &q, const ast &r){ - if(q == r) - return p; - if(p == q) - return r; - if(sym(p) != sym(q) || sym(q) != sym(r)) - throw rewrites_failed(); - int nargs = num_args(p); - if(nargs != num_args(q) || nargs != num_args(r)) - throw rewrites_failed(); - std::vector args; args.resize(nargs); - for(int i = 0; i < nargs; i++) - args[i] = apply_all_rewrites(arg(p,i),arg(q,i),arg(r,i)); - return clone(p,args); - } - - ast delta(const ast &x, const ast &y){ - if(op(x) != op(y) || (op(x) == Uninterpreted && sym(x) != sym(y)) || num_args(x) != num_args(y)) - return make(Equal,x,y); - ast res = mk_true(); - int nargs = num_args(x); - for(int i = 0; i < nargs; i++) - res = my_and(res,delta(arg(x,i),arg(y,i))); - return res; - } - - bool diff_rec(LitType t, const ast &p, const ast &q, ast &pd, ast &qd){ - if(p == q) - return false; - if(term_in_vocab(t,p) && term_in_vocab(t,q)){ - pd = p; - qd = q; - return true; - } - else { - if(sym(p) != sym(q)) return false; - int nargs = num_args(p); - if(num_args(q) != nargs) return false; - for(int i = 0; i < nargs; i++) - if(diff_rec(t,arg(p,i),arg(q,i),pd,qd)) - return true; - return false; - } - } - - void diff(LitType t, const ast &p, const ast &q, ast &pd, ast &qd){ - if(!diff_rec(t,p,q,pd,qd)) - throw cannot_simplify(); - } - - bool apply_diff_rec(LitType t, const ast &inp, const ast &p, const ast &q, ast &out){ - if(p == q) - return false; - if(term_in_vocab(t,p) && term_in_vocab(t,q)){ - if(inp != p) - throw cannot_simplify(); - out = q; - return true; - } - else { - int nargs = num_args(p); - if(sym(p) != sym(q)) throw cannot_simplify(); - if(num_args(q) != nargs) throw cannot_simplify(); - if(sym(p) != sym(inp)) throw cannot_simplify(); - if(num_args(inp) != nargs) throw cannot_simplify(); - for(int i = 0; i < nargs; i++) - if(apply_diff_rec(t,arg(inp,i),arg(p,i),arg(q,i),out)) - return true; - return false; - } - } - - ast apply_diff(LitType t, const ast &inp, const ast &p, const ast &q){ - ast out; - if(!apply_diff_rec(t,inp,p,q,out)) - throw cannot_simplify(); - return out; - } - - bool merge_A_rewrites(const ast &A1, const ast &A2, ast &merged) { - if(arg(A1,1) == arg(A2,0)){ - merged = make(op(A1),arg(A1,0),arg(A2,1)); - return true; - } - ast diff1l, diff1r, diff2l, diff2r,diffBl,diffBr; - diff(LitA,arg(A1,0),arg(A1,1),diff1l,diff1r); - diff(LitA,arg(A2,0),arg(A2,1),diff2l,diff2r); - diff(LitB,arg(A1,1),arg(A2,0),diffBl,diffBr); - if(!term_common(diff2l) && !term_common(diffBr)){ - ast A1r = apply_diff(LitB,arg(A2,1),arg(A2,0),arg(A1,1)); - merged = make(op(A1),arg(A1,0),A1r); - return true; - } - if(!term_common(diff1r) && !term_common(diffBl)){ - ast A2l = apply_diff(LitB,arg(A1,0),arg(A1,1),arg(A2,0)); - merged = make(op(A1),A2l,arg(A2,1)); - return true; - } - return false; - } - - void collect_A_rewrites(const ast &t, std::vector &res){ - if(is_true(t)) - return; - if(sym(t) == concat){ - res.push_back(arg(t,0)); - collect_A_rewrites(arg(t,0),res); - return; - } - res.push_back(t); - } - - ast concat_A_rewrites(const std::vector &rew){ - if(rew.size() == 0) - return mk_true(); - ast res = rew[0]; - for(unsigned i = 1; i < rew.size(); i++) - res = make(concat,res,rew[i]); - return res; - } - - ast merge_concat_rewrites(const ast &A1, const ast &A2){ - std::vector rew; - collect_A_rewrites(A1,rew); - int first = rew.size(), last = first; // range that might need merging - collect_A_rewrites(A2,rew); - while(first > 0 && first < (int)rew.size() && first <= last){ - ast merged; - if(merge_A_rewrites(rew[first-1],rew[first],merged)){ - rew[first] = merged; - first--; - rew.erase(rew.begin()+first); - last--; - if(first >= last) last = first+1; - } - else - first++; - } - return concat_A_rewrites(rew); - } - - ast sep_cond(const ast &t, ast &cond){ - if(op(t) == Implies){ - cond = my_and(cond,arg(t,0)); - return arg(t,1); - } - return t; - } - - - /* operations on term positions */ - - /** Finds the difference between two positions. If p1 < p2 (p1 is a - position below p2), returns -1 and sets diff to p2-p1 (the psath - from position p2 to position p1). If p2 < p1 (p2 is a position - below p1), returns 1 and sets diff to p1-p2 (the psath from - position p1 to position p2). If equal, return 0 and set diff to - top_pos. Else (if p1 and p2 are independent) returns 2 and - leaves diff unchanged. */ - - int pos_diff(const ast &p1, const ast &p2, ast &diff){ - if(p1 == top_pos && p2 != top_pos){ - diff = p2; - return 1; - } - if(p2 == top_pos && p1 != top_pos){ - diff = p1; - return -1; - } - if(p1 == top_pos && p2 == top_pos){ - diff = p1; - return 0; - } - if(arg(p1,0) == arg(p2,0)) // same argument position, recur - return pos_diff(arg(p1,1),arg(p2,1),diff); - return 2; - } - - /* return the position of pos in the argth argument */ - ast pos_add(int arg, const ast &pos){ - return make(add_pos,make_int(rational(arg)),pos); - } - - ast add_pos_to_end(const ast &pos, int i){ - if(pos == top_pos) - return pos_add(i,pos); - return make(add_pos,arg(pos,0),add_pos_to_end(arg(pos,1),i)); - } - - /* return the argument number of position, if not top */ - int pos_arg(const ast &pos){ - rational r; - if(is_numeral(arg(pos,0),r)) - return r.get_unsigned(); - throw iz3_exception("bad position!"); - } - - /* substitute y into position pos in x */ - ast subst_in_pos(const ast &x, const ast &pos, const ast &y){ - if(pos == top_pos) - return y; - int p = pos_arg(pos); - int nargs = num_args(x); - if(p >= 0 && p < nargs){ - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = i == p ? subst_in_pos(arg(x,i),arg(pos,1),y) : arg(x,i); - return clone(x,args); - } - throw iz3_exception("bad term position!"); - } - - ast diff_chain(LitType t, const ast &pos, const ast &x, const ast &y, const ast &prefix){ - int nargs = num_args(x); - if(x == y) return prefix; - if(sym(x) == sym(y) && nargs == num_args(y)){ - ast res = prefix; - for(int i = 0; i < nargs; i++) - res = diff_chain(t,pos_add(i,pos),arg(x,i),arg(y,i),res); - return res; - } - return chain_cons(prefix,make_rewrite(t,pos,mk_true(),make_equiv_rel(x,y))); - } - - /* operations on rewrites */ - ast make_rewrite(LitType t, const ast &pos, const ast &cond, const ast &equality){ -#if 0 - if(pos == top_pos && op(equality) == Iff && !is_true(arg(equality,0))) - throw iz3_exception("bad rewrite"); -#endif - if(!is_equivrel(equality)) - throw iz3_exception("bad rewrite"); - return make(t == LitA ? rewrite_A : rewrite_B, pos, cond, equality); - } - - ast rewrite_pos(const ast &rew){ - return arg(rew,0); - } - - ast rewrite_cond(const ast &rew){ - return arg(rew,1); - } - - ast rewrite_equ(const ast &rew){ - return arg(rew,2); - } - - ast rewrite_lhs(const ast &rew){ - return arg(arg(rew,2),0); - } - - ast rewrite_rhs(const ast &rew){ - return arg(arg(rew,2),1); - } - - /* operations on rewrite chains */ - - ast chain_cons(const ast &chain, const ast &elem){ - return make(concat,chain,elem); - } - - ast chain_rest(const ast &chain){ - return arg(chain,0); - } - - ast chain_last(const ast &chain){ - return arg(chain,1); - } - - ast rewrite_update_rhs(const ast &rew, const ast &pos, const ast &new_rhs, const ast &new_cond){ - ast foo = subst_in_pos(rewrite_rhs(rew),pos,new_rhs); - ast equality = arg(rew,2); - return make(sym(rew),rewrite_pos(rew),my_and(rewrite_cond(rew),new_cond),make(op(equality),arg(equality,0),foo)); - } - - ast rewrite_update_lhs(const ast &rew, const ast &pos, const ast &new_lhs, const ast &new_cond){ - ast foo = subst_in_pos(rewrite_lhs(rew),pos,new_lhs); - ast equality = arg(rew,2); - return make(sym(rew),rewrite_pos(rew),my_and(rewrite_cond(rew),new_cond),make(op(equality),foo,arg(equality,1))); - } - - bool is_common_rewrite(const ast &rew){ - return term_common(arg(rew,2)); - } - - bool is_right_mover(const ast &rew){ - return term_common(rewrite_lhs(rew)) && !term_common(rewrite_rhs(rew)); - } - - bool is_left_mover(const ast &rew){ - return term_common(rewrite_rhs(rew)) && !term_common(rewrite_lhs(rew)); - } - - bool same_side(const ast &rew1, const ast &rew2){ - return sym(rew1) == sym(rew2); - } - - bool is_rewrite_side(LitType t, const ast &rew){ - if(t == LitA) - return sym(rew) == rewrite_A; - return sym(rew) == rewrite_B; - } - - LitType rewrite_side(const ast &rew){ - return (sym(rew) == rewrite_A) ? LitA : LitB; - } - - ast rewrite_to_formula(const ast &rew){ - return my_implies(arg(rew,1),arg(rew,2)); - } - - // make rewrite rew conditon on rewrite cond - ast rewrite_conditional(const ast &cond, const ast &rew){ - ast cf = rewrite_to_formula(cond); - return make(sym(rew),arg(rew,0),my_and(arg(rew,1),cf),arg(rew,2)); - } - - ast reverse_rewrite(const ast &rew){ - ast equ = arg(rew,2); - return make(sym(rew),arg(rew,0),arg(rew,1),make(op(equ),arg(equ,1),arg(equ,0))); - } - - ast rewrite_pos_add(int apos, const ast &rew){ - return make(sym(rew),pos_add(apos,arg(rew,0)),arg(rew,1),arg(rew,2)); - } - - ast rewrite_pos_set(const ast &pos, const ast &rew){ - return make(sym(rew),pos,arg(rew,1),arg(rew,2)); - } - - ast rewrite_up(const ast &rew){ - return make(sym(rew),arg(arg(rew,0),1),arg(rew,1),arg(rew,2)); - } - - /** Adds a rewrite to a chain of rewrites, keeping the chain in - normal form. An empty chain is represented by true.*/ - - ast add_rewrite_to_chain(const ast &chain, const ast &rewrite){ - if(is_true(chain)) - return chain_cons(chain,rewrite); - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(same_side(last,rewrite)){ - ast p1 = rewrite_pos(last); - ast p2 = rewrite_pos(rewrite); - ast diff; - switch(pos_diff(p1,p2,diff)){ - case 1: { - ast absorb = rewrite_update_rhs(last,diff,rewrite_rhs(rewrite),rewrite_cond(rewrite)); - return add_rewrite_to_chain(rest,absorb); - } - case 0: - case -1: { - ast absorb = rewrite_update_lhs(rewrite,diff,rewrite_lhs(last),rewrite_cond(last)); - return add_rewrite_to_chain(rest,absorb); - } - default: {// independent case - bool rm = is_right_mover(last); - bool lm = is_left_mover(rewrite); - if((lm && !rm) || (rm && !lm)) - return chain_swap(rest,last,rewrite); - } - } - } - else { - if(is_left_mover(rewrite)){ - if(is_common_rewrite(last)) - return add_rewrite_to_chain(chain_cons(rest,flip_rewrite(last)),rewrite); - if(!is_left_mover(last)) - return chain_swap(rest,last,rewrite); - } - if(is_right_mover(last)){ - if(is_common_rewrite(rewrite)) - return add_rewrite_to_chain(chain,flip_rewrite(rewrite)); - if(!is_right_mover(rewrite)) - return chain_swap(rest,last,rewrite); - } - } - return chain_cons(chain,rewrite); - } - - ast chain_swap(const ast &rest, const ast &last, const ast &rewrite){ - return chain_cons(add_rewrite_to_chain(rest,rewrite),last); - } - - ast flip_rewrite(const ast &rew){ - symb flip_sym = (sym(rew) == rewrite_A) ? rewrite_B : rewrite_A; - ast cf = rewrite_to_formula(rew); - return make(flip_sym,arg(rew,0),my_implies(arg(rew,1),cf),arg(rew,2)); - } - - /** concatenates two rewrite chains, keeping result in normal form. */ - - ast concat_rewrite_chain(const ast &chain1, const ast &chain2){ - if(is_true(chain2)) return chain1; - if(is_true(chain1)) return chain2; - ast foo = concat_rewrite_chain(chain1,chain_rest(chain2)); - return add_rewrite_to_chain(foo,chain_last(chain2)); - } - - /** reverse a chain of rewrites */ - - ast reverse_chain_rec(const ast &chain, const ast &prefix){ - if(is_true(chain)) - return prefix; - ast last = reverse_rewrite(chain_last(chain)); - ast rest = chain_rest(chain); - return reverse_chain_rec(rest,chain_cons(prefix,last)); - } - - ast reverse_chain(const ast &chain){ - return reverse_chain_rec(chain,mk_true()); - } - - bool is_equivrel_chain(const ast &chain){ - if(is_true(chain)) - return true; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_true(rest)) - return !is_true(rewrite_lhs(last)); - return is_equivrel_chain(rest); - } - - bool is_negation_chain(const ast &chain){ - if(is_true(chain)) - return false; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_true(rest)) - return op(rewrite_rhs(last)) == Not; - return is_negation_chain(rest); - } - - ast commute_negation_chain(const ast &chain){ - if(is_true(chain)) - return chain; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_true(rest)){ - ast old = rewrite_rhs(last); - if(!(op(old) == Not)) - throw iz3_exception("bad negative equality chain"); - ast equ = arg(old,0); - if(!is_equivrel(equ)) - throw iz3_exception("bad negative equality chain"); - last = rewrite_update_rhs(last,top_pos,make(Not,make(op(equ),arg(equ,1),arg(equ,0))),make(True)); - return chain_cons(rest,last); - } - ast pos = rewrite_pos(last); - if(pos == top_pos) - throw iz3_exception("bad negative equality chain"); - int idx = pos_arg(pos); - if(idx != 0) - throw iz3_exception("bad negative equality chain"); - pos = arg(pos,1); - if(pos == top_pos){ - ast lhs = rewrite_lhs(last); - ast rhs = rewrite_rhs(last); - if(op(lhs) != Equal || op(rhs) != Equal) - throw iz3_exception("bad negative equality chain"); - last = make_rewrite(rewrite_side(last),rewrite_pos(last),rewrite_cond(last), - make(Iff,make(Equal,arg(lhs,1),arg(lhs,0)),make(Equal,arg(rhs,1),arg(rhs,0)))); - } - else { - idx = pos_arg(pos); - if(idx == 0) - idx = 1; - else if(idx == 1) - idx = 0; - else - throw iz3_exception("bad negative equality chain"); - pos = pos_add(0,pos_add(idx,arg(pos,1))); - last = make_rewrite(rewrite_side(last),pos,rewrite_cond(last),rewrite_equ(last)); - } - return chain_cons(commute_negation_chain(rest),last); - } - - // split a rewrite chain into head and tail at last top-level rewrite - ast get_head_chain(const ast &chain, ast &tail, bool is_not = true){ - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast pos = rewrite_pos(last); - if(pos == top_pos || (is_not && arg(pos,1) == top_pos)){ - tail = mk_true(); - return chain; - } - if(is_true(rest)) - throw iz3_exception("bad rewrite chain"); - ast head = get_head_chain(rest,tail,is_not); - tail = chain_cons(tail,last); - return head; - } - - bool has_mixed_summands(const ast &e){ - if(op(e) == Plus){ - int nargs = num_args(e); - for(int i = 0; i < nargs; i++) - if(has_mixed_summands(arg(e,i))) - return true; - return false; - } - return get_term_type(e) == LitMixed; - } - - // split a rewrite chain into head and tail at last sum with no mixed sumands - ast get_right_movers(const ast &chain, const ast &rhs, ast &tail, ast &mid){ - if(is_true(chain) || !has_mixed_summands(rhs)){ - mid = rhs; - tail = mk_true(); - return chain; - } - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast mm = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); - ast res = get_right_movers(rest,mm,tail,mid); - tail = chain_cons(tail,last); - return res; - } - - // split a rewrite chain into head and tail at first sum with no mixed sumands - ast get_left_movers(const ast &chain, const ast &lhs, ast &tail, ast &mid){ - if(is_true(chain)){ - mid = lhs; - if(!has_mixed_summands(lhs)){ - tail = mk_true(); - return chain; - } - return ast(); - } - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast res = get_left_movers(rest,lhs,tail,mid); - if(res.null()){ - mid = subst_in_pos(mid,rewrite_pos(last),rewrite_rhs(last)); - if(get_term_type(mid) != LitMixed){ - tail = mk_true(); - return chain; - } - return ast(); - } - tail = chain_cons(tail,last); - return res; - } - - - struct cannot_split: public iz3_exception { - cannot_split(): iz3_exception("cannot_split") {} - }; - - /** Split a chain of rewrites two chains, operating on positions 0 and 1. - Fail if any rewrite in the chain operates on top position. */ - void split_chain_rec(const ast &chain, ast *res){ - if(is_true(chain)) - return; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - split_chain_rec(rest,res); - ast pos = rewrite_pos(last); - if(pos == top_pos){ - if(rewrite_lhs(last) == rewrite_rhs(last)) - return; // skip if it's a noop - throw cannot_split(); - } - int arg = pos_arg(pos); - if(arg<0 || arg > 1) - throw cannot_split(); - res[arg] = chain_cons(res[arg],rewrite_up(last)); - } - - void split_chain(const ast &chain, ast *res){ - res[0] = res[1] = mk_true(); - split_chain_rec(chain,res); - } - - ast extract_rewrites(const ast &chain, const ast &pos){ - if(is_true(chain)) - return chain; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast new_rest = extract_rewrites(rest,pos); - ast p1 = rewrite_pos(last); - ast diff; - switch(pos_diff(p1,pos,diff)){ - case -1: { - ast new_last = rewrite_pos_set(diff, last); - return chain_cons(new_rest,new_last); - } - case 1: - if(rewrite_lhs(last) != rewrite_rhs(last)) - throw iz3_exception("bad rewrite chain"); - break; - default:; - } - return new_rest; - } - - ast down_chain(const ast &chain){ - ast split[2]; - split_chain(chain,split); - return split[0]; - } - - ast chain_conditions(LitType t, const ast &chain){ - if(is_true(chain)) - return mk_true(); - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast cond = chain_conditions(t,rest); - if(is_rewrite_side(t,last)) - cond = my_and(cond,rewrite_cond(last)); - return cond; - } - - ast chain_formulas(LitType t, const ast &chain){ - if(is_true(chain)) - return mk_true(); - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast cond = chain_formulas(t,rest); - if(is_rewrite_side(t,last)) - cond = my_and(cond,rewrite_equ(last)); - return cond; - } - - - bool rewrites_from_to(const ast &chain, const ast &lhs, const ast &rhs){ - if(is_true(chain)) - return lhs == rhs; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); - return rewrites_from_to(rest,lhs,mid); - } - - struct bad_ineq_inference: public iz3_exception { - bad_ineq_inference(): iz3_exception("bad_ineq_inference") {} - }; - - ast chain_ineqs(opr comp_op, LitType t, const ast &chain, const ast &lhs, const ast &rhs){ - if(is_true(chain)){ - if (lhs != rhs) { - TRACE("duality", print_expr(tout, lhs); tout << " "; print_expr(tout, rhs); tout << "\n";); - throw bad_ineq_inference(); - } - return make(Leq,make_int(rational(0)),make_int(rational(0))); - } - TRACE("duality", print_expr(tout, chain); print_expr(tout << "\n", lhs); tout << " "; print_expr(tout, rhs); tout << "\n";); - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast mid = subst_in_pos(rhs,rewrite_pos(last),rewrite_lhs(last)); - ast cond = chain_ineqs(comp_op,t,rest,lhs,mid); - if(is_rewrite_side(t,last)){ - ast diff; - if(comp_op == Leq) diff = make(Sub,rhs,mid); - else diff = make(Sub,mid,rhs); - ast foo = make(Leq,make_int("0"),z3_simplify(diff)); - if(is_true(cond)) - cond = foo; - else { - linear_comb(cond,make_int(rational(1)),foo); - cond = simplify_ineq(cond); - } - } - return cond; - } - - ast ineq_to_lhs(const ast &ineq){ - ast s = make(Leq,make_int(rational(0)),make_int(rational(0))); - linear_comb(s,make_int(rational(1)),ineq); - return simplify_ineq(s); - } - - void eq_from_ineq(const ast &ineq, ast &lhs, ast &rhs){ - // ast s = ineq_to_lhs(ineq); - // ast srhs = arg(s,1); - ast srhs = arg(ineq,0); - if(op(srhs) == Plus && num_args(srhs) == 2 && arg(ineq,1) == make_int(rational(0))){ - lhs = arg(srhs,0); - rhs = arg(srhs,1); - // if(op(lhs) == Times) - // std::swap(lhs,rhs); - if(op(rhs) == Times){ - rhs = arg(rhs,1); - // if(op(ineq) == Leq) - // std::swap(lhs,rhs); - return; - } - } - if(op(ineq) == Leq || op(ineq) == Geq){ - lhs = srhs; - rhs = arg(ineq,1); - return; - } - throw iz3_exception("bad ineq"); - } - - ast chain_pos_add(int arg, const ast &chain){ - if(is_true(chain)) - return mk_true(); - ast last = rewrite_pos_add(arg,chain_last(chain)); - ast rest = chain_pos_add(arg,chain_rest(chain)); - return chain_cons(rest,last); - } - - ast apply_rewrite_chain(const ast &t, const ast &chain){ - if(is_true(chain)) - return t; - ast last = chain_last(chain); - ast rest = chain_rest(chain); - ast mid = apply_rewrite_chain(t,rest); - ast res = subst_in_pos(mid,rewrite_pos(last),rewrite_rhs(last)); - return res; - } - - ast drop_rewrites(LitType t, const ast &chain, ast &remainder){ - if(!is_true(chain)){ - ast last = chain_last(chain); - ast rest = chain_rest(chain); - if(is_rewrite_side(t,last)){ - ast res = drop_rewrites(t,rest,remainder); - remainder = chain_cons(remainder,last); - return res; - } - } - remainder = mk_true(); - return chain; - } - - // Normalization chains - - ast cons_normal(const ast &first, const ast &rest){ - return make(normal_chain,first,rest); - } - - ast normal_first(const ast &t){ - return arg(t,0); - } - - ast normal_rest(const ast &t){ - return arg(t,1); - } - - ast normal_lhs(const ast &t){ - return arg(arg(t,0),0); - } - - ast normal_rhs(const ast &t){ - return arg(arg(t,0),1); - } - - ast normal_proof(const ast &t){ - return arg(t,1); - } - - ast make_normal_step(const ast &lhs, const ast &rhs, const ast &proof){ - return make(normal_step,make_equiv(lhs,rhs),proof); - } - - ast make_normal(const ast &ineq, const ast &nrml){ - if(!is_ineq(ineq)) - throw iz3_exception("what?"); - return make(normal,ineq,nrml); - } - - ast fix_normal(const ast &lhs, const ast &rhs, const ast &proof){ - LitType lhst = get_term_type(lhs); - LitType rhst = get_term_type(rhs); - if(lhst == LitMixed && (rhst != LitMixed || ast_id(lhs) < ast_id(rhs))) - return make_normal_step(lhs,rhs,proof); - if(rhst == LitMixed && (lhst != LitMixed || ast_id(rhs) < ast_id(lhs))) - return make_normal_step(rhs,lhs,reverse_chain(proof)); - throw iz3_exception("help!"); - } - - ast chain_side_proves(LitType side, const ast &chain){ - LitType other_side = side == LitA ? LitB : LitA; - return my_and(chain_conditions(other_side,chain),my_implies(chain_conditions(side,chain),chain_formulas(side,chain))); - } - - // Merge two normalization chains - ast merge_normal_chains_rec(const ast &chain1, const ast &chain2, hash_map &trans, ast &Aproves, ast &Bproves){ - if(is_true(chain1)) - return chain2; - if(is_true(chain2)) - return chain1; - ast f1 = normal_first(chain1); - ast f2 = normal_first(chain2); - ast lhs1 = normal_lhs(f1); - ast lhs2 = normal_lhs(f2); - int id1 = ast_id(lhs1); - int id2 = ast_id(lhs2); - if(id1 < id2) - return cons_normal(f1,merge_normal_chains_rec(normal_rest(chain1),chain2,trans,Aproves,Bproves)); - if(id2 < id1) - return cons_normal(f2,merge_normal_chains_rec(chain1,normal_rest(chain2),trans,Aproves,Bproves)); - ast rhs1 = normal_rhs(f1); - ast rhs2 = normal_rhs(f2); - LitType t1 = get_term_type(rhs1); - LitType t2 = get_term_type(rhs2); - int tid1 = ast_id(rhs1); - int tid2 = ast_id(rhs2); - ast pf1 = normal_proof(f1); - ast pf2 = normal_proof(f2); - ast new_normal; - if(t1 == LitMixed && (t2 != LitMixed || tid2 > tid1)){ - ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); - new_normal = f2; - trans[rhs1] = make_normal_step(rhs1,rhs2,new_proof); - } - else if(t2 == LitMixed && (t1 != LitMixed || tid1 > tid2)) - return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); - else if(t1 == LitA && t2 == LitB){ - ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); - ast Bproof, Aproof = drop_rewrites(LitB,new_proof,Bproof); - ast mcA = chain_side_proves(LitB,Aproof); - Bproves = my_and(Bproves,mcA); - ast mcB = chain_side_proves(LitA,Bproof); - Aproves = my_and(Aproves,mcB); - ast rep = apply_rewrite_chain(rhs1,Aproof); - new_proof = concat_rewrite_chain(pf1,Aproof); - new_normal = make_normal_step(lhs1,rep,new_proof); - ast A_normal = make_normal_step(rhs1,rep,Aproof); - ast res = cons_normal(new_normal,merge_normal_chains_rec(normal_rest(chain1),normal_rest(chain2),trans,Aproves,Bproves)); - res = merge_normal_chains_rec(res,cons_normal(A_normal,make(True)),trans,Aproves,Bproves); - return res; - } - else if(t1 == LitB && t2 == LitA) - return merge_normal_chains_rec(chain2,chain1,trans,Aproves,Bproves); - else if(t1 == LitA) { - ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); - ast mc = chain_side_proves(LitB,new_proof); - Bproves = my_and(Bproves,mc); - new_normal = f1; // choice is arbitrary - } - else { /* t1 = t2 = LitB */ - ast new_proof = concat_rewrite_chain(reverse_chain(pf1),pf2); - ast mc = chain_side_proves(LitA,new_proof); - Aproves = my_and(Aproves,mc); - new_normal = f1; // choice is arbitrary - } - return cons_normal(new_normal,merge_normal_chains_rec(normal_rest(chain1),normal_rest(chain2),trans,Aproves,Bproves)); - } - - ast trans_normal_chain(const ast &chain, hash_map &trans){ - if(is_true(chain)) - return chain; - ast f = normal_first(chain); - ast r = normal_rest(chain); - r = trans_normal_chain(r,trans); - ast rhs = normal_rhs(f); - hash_map::iterator it = trans.find(rhs); - ast new_normal; - if(it != trans.end() && get_term_type(normal_lhs(f)) == LitMixed){ - const ast &f2 = it->second; - ast pf = concat_rewrite_chain(normal_proof(f),normal_proof(f2)); - new_normal = make_normal_step(normal_lhs(f),normal_rhs(f2),pf); - } - else - new_normal = f; - if(get_term_type(normal_lhs(f)) == LitMixed) - trans[normal_lhs(f)] = new_normal; - return cons_normal(new_normal,r); - } - - ast merge_normal_chains(const ast &chain1, const ast &chain2, ast &Aproves, ast &Bproves){ - hash_map trans; - ast res = merge_normal_chains_rec(chain1,chain2,trans,Aproves,Bproves); - res = trans_normal_chain(res,trans); - return res; - } - - bool destruct_cond_ineq(ast t, ast &Aproves, ast &Bproves, ast&ineq){ - if(op(t) == And){ - Aproves = arg(t,0); - t = arg(t,1); - } - else - Aproves = mk_true(); - if(op(t) == Implies){ - Bproves = arg(t,0); - t = arg(t,1); - } - else - Bproves = mk_true(); - if(is_normal_ineq(t)){ - ineq = t; - return true; - } - return false; - } - - ast cons_cond_ineq(const ast &Aproves, const ast &Bproves, const ast &ineq){ - return my_and(Aproves,my_implies(Bproves,ineq)); - } - - ast normalize(const ast &ct){ - ast Aproves,Bproves,t; - if(!destruct_cond_ineq(ct,Aproves,Bproves,t)) - return ct; - if(sym(t) != normal) - return ct; - ast chain = arg(t,1); - hash_map map; - for(ast c = chain; !is_true(c); c = normal_rest(c)){ - ast first = normal_first(c); - ast lhs = normal_lhs(first); - ast rhs = normal_rhs(first); - map[lhs] = rhs; - } - ast res = subst(map,arg(t,0)); - return cons_cond_ineq(Aproves,Bproves,res); - } - - /** Make an assumption node. The given clause is assumed in the given frame. */ - virtual node make_assumption(int frame, const std::vector &assumption){ - if(!weak){ - if(pv->in_range(frame,rng)){ - std::vector itp_clause; - for(unsigned i = 0; i < assumption.size(); i++) - if(get_term_type(assumption[i]) != LitA) - itp_clause.push_back(assumption[i]); - ast res = my_or(itp_clause); - return res; - } - else { - return mk_true(); - } - } - else { - if(pv->in_range(frame,rng)){ - return mk_false(); - } - else { - std::vector itp_clause; - for(unsigned i = 0; i < assumption.size(); i++) - if(get_term_type(assumption[i]) != LitB) - itp_clause.push_back(assumption[i]); - ast res = my_or(itp_clause); - return mk_not(res); - } - } - } - - ast make_local_rewrite(LitType t, const ast &p){ - ast rew = is_equivrel(p) ? p : make(Iff,mk_true(),p); -#if 0 - if(op(rew) == Iff && !is_true(arg(rew,0))) - return diff_chain(t,top_pos,arg(rew,0),arg(rew,1), mk_true()); -#endif - return chain_cons(mk_true(),make_rewrite(t, top_pos, mk_true(), rew)); - } - - ast triv_interp(const symb &rule, const std::vector &premises, int mask_in){ - std::vector ps; ps.resize(premises.size()); - std::vector conjs; - int mask = 0; - for(unsigned i = 0; i < ps.size(); i++){ - ast p = premises[i]; - LitType t = get_term_type(p); - switch(t){ - case LitA: - case LitB: - ps[i] = make_local_rewrite(t,p); - break; - default: - ps[i] = get_placeholder(p); // can only prove consequent! - if(mask_in & (1 << i)) - mask |= (1 << conjs.size()); - conjs.push_back(p); - } - } - ast ref = make(rule,ps); - ast res = make_contra_node(ref,conjs,mask); - return res; - } - - ast triv_interp(const symb &rule, const ast &p0, const ast &p1, const ast &p2, int mask){ - std::vector ps; ps.resize(3); - ps[0] = p0; - ps[1] = p1; - ps[2] = p2; - return triv_interp(rule,ps,mask); - } - - /** Make a modus-ponens node. This takes derivations of |- x - and |- x = y and produces |- y */ - - virtual node make_mp(const ast &p_eq_q, const ast &prem1, const ast &prem2){ - - /* Interpolate the axiom p, p=q -> q */ - ast p = arg(p_eq_q,0); - ast q = arg(p_eq_q,1); - ast itp; - if(get_term_type(p_eq_q) == LitMixed){ - int mask = 1 << 2; - if(op(p) == Not && is_equivrel(arg(p,0))) - mask |= 1; // we may need to run this rule backward if first premise is negative equality - itp = triv_interp(modpon,p,p_eq_q,mk_not(q),mask); - } - else { - if(get_term_type(p) == LitA){ - if(get_term_type(q) == LitA){ - if(op(q) == Or) - itp = make_assumption(rng.hi,args(q)); - else - itp = mk_false(); - } - else { - if(get_term_type(p_eq_q) == LitA) - itp = q; - else - throw proof_error(); - } - } - else { - if(get_term_type(q) == LitA){ - if(get_term_type(make(Equal,p,q)) == LitA) - itp = mk_not(p); - else - throw proof_error(); - } - else - itp = mk_true(); - } - } - - /* Resolve it with the premises */ - std::vector conc; conc.push_back(q); conc.push_back(mk_not(p_eq_q)); - itp = make_resolution(p,conc,itp,prem1); - conc.pop_back(); - itp = make_resolution(p_eq_q,conc,itp,prem2); - return itp; - } - - ast capture_localization(ast e){ - // #define CAPTURE_LOCALIZATION -#ifdef CAPTURE_LOCALIZATION - for(int i = localization_vars.size() - 1; i >= 0; i--){ - LocVar &lv = localization_vars[i]; - if(occurs_in(lv.var,e)){ - symb q = (pv->in_range(lv.frame,rng)) ? sexists : sforall; - e = make(q,make(Equal,lv.var,lv.term),e); // use Equal because it is polymorphic - } - } -#endif - return e; - } - - /** Make an axiom node. The conclusion must be an instance of an axiom. */ - virtual node make_axiom(const std::vector &conclusion, prover::range frng){ - int nargs = conclusion.size(); - std::vector largs(nargs); - std::vector eqs; - std::vector pfs; - - for(int i = 0; i < nargs; i++){ - ast argpf; - ast lit = conclusion[i]; - largs[i] = localize_term(lit,frng,argpf); - frng = pv->range_glb(frng,pv->ast_scope(largs[i])); - if(largs[i] != lit){ - eqs.push_back(make_equiv(largs[i],lit)); - pfs.push_back(argpf); - } - } - - int frame = pv->range_max(frng); - ast itp = make_assumption(frame,largs); - - for(unsigned i = 0; i < eqs.size(); i++) - itp = make_mp(eqs[i],itp,pfs[i]); - return capture_localization(itp); - } - - virtual node make_axiom(const std::vector &conclusion){ - return make_axiom(conclusion,pv->range_full()); - } - - /** Make a Contra node. This rule takes a derivation of the form - Gamma |- False and produces |- \/~Gamma. */ - - virtual node make_contra(node prem, const std::vector &conclusion){ - return prem; - } - - /** Make hypothesis. Creates a node of the form P |- P. */ - - virtual node make_hypothesis(const ast &P){ - if(is_not(P)) - return make_hypothesis(arg(P,0)); - switch(get_term_type(P)){ - case LitA: - return mk_false(); - case LitB: - return mk_true(); - default: // mixed hypothesis - switch(op(P)){ - case Geq: - case Leq: - case Gt: - case Lt: { - ast zleqz = make(Leq,make_int("0"),make_int("0")); - ast fark1 = make(sum,zleqz,make_int("1"),get_placeholder(P)); - ast fark2 = make(sum,fark1,make_int("1"),get_placeholder(mk_not(P))); - ast res = make(And,make(contra,fark2,mk_false()), - make(contra,get_placeholder(mk_not(P)),P), - make(contra,get_placeholder(P),mk_not(P))); - return res; - } - default: { - ast em = make(exmid,P,get_placeholder(P),get_placeholder(mk_not(P))); - ast res = make(And,make(contra,em,mk_false()), - make(contra,get_placeholder(mk_not(P)),P), - make(contra,get_placeholder(P),mk_not(P))); - return res; - } - } - } - } - - /** Make a Reflexivity node. This rule produces |- x = x */ - - virtual node make_reflexivity(ast con){ - if(get_term_type(con) == LitA) - return mk_false(); - if(get_term_type(con) == LitB) - return mk_true(); - ast itp = make(And,make(contra,no_proof,mk_false()), - make(contra,mk_true(),mk_not(con))); - return itp; - } - - /** Make a Symmetry node. This takes a derivation of |- x = y and - produces | y = x. Ditto for ~(x=y) */ - - virtual node make_symmetry(ast con, const ast &premcon, node prem){ -#if 0 - ast x = arg(con,0); - ast y = arg(con,1); - ast p = make(op(con),y,x); -#endif - if(get_term_type(con) != LitMixed) - return prem; // symmetry shmymmetry... - ast em = make(exmid,con,make(symm,get_placeholder(premcon)),get_placeholder(mk_not(con))); - ast itp = make(And,make(contra,em,mk_false()), - make(contra,make(symm,get_placeholder(mk_not(con))),premcon), - make(contra,make(symm,get_placeholder(premcon)),mk_not(con))); - - std::vector conc; conc.push_back(con); - itp = make_resolution(premcon,conc,itp,prem); - return itp; - } - - ast make_equiv_rel(const ast &x, const ast &y){ - if(is_bool_type(get_type(x))) - return make(Iff,x,y); - return make(Equal,x,y); - } - - /** Make a transitivity node. This takes derivations of |- x = y - and |- y = z produces | x = z */ - - virtual node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2){ - - /* Interpolate the axiom x=y,y=z,-> x=z */ - ast p = make_equiv_rel(x,y); - ast q = make_equiv_rel(y,z); - ast r = make_equiv_rel(x,z); - ast equiv = make(Iff,p,r); - ast itp; - - itp = make_congruence(q,equiv,prem2); - itp = make_mp(equiv,prem1,itp); - - return itp; - - } - - /** Make a congruence node. This takes derivations of |- x_i = y_i - and produces |- f(x_1,...,x_n) = f(y_1,...,y_n) */ - - virtual node make_congruence(const ast &p, const ast &con, const ast &prem1){ - ast x = arg(p,0), y = arg(p,1); - ast itp; - LitType con_t = get_term_type(con); - if(get_term_type(p) == LitA){ - if(con_t == LitA) - itp = mk_false(); - else if(con_t == LitB) - itp = p; - else - itp = make_mixed_congruence(x, y, p, con, prem1); - } - else { - if(con_t == LitA) - itp = mk_not(p); - else{ - if(con_t == LitB) - itp = mk_true(); - else - itp = make_mixed_congruence(x, y, p, con, prem1); - } - } - std::vector conc; conc.push_back(con); - itp = make_resolution(p,conc,itp,prem1); - return itp; - } - - int find_congruence_position(const ast &p, const ast &con){ - // find the argument position of x and y - const ast &x = arg(p,0); - const ast &y = arg(p,1); - int nargs = num_args(arg(con,0)); - for(int i = 0; i < nargs; i++) - if(x == arg(arg(con,0),i) && y == arg(arg(con,1),i)) - return i; - throw proof_error(); - } - - /** Make a congruence node. This takes derivations of |- x_i1 = y_i1, |- x_i2 = y_i2,... - and produces |- f(...x_i1...x_i2...) = f(...y_i1...y_i2...) */ - - node make_congruence(const std::vector &p, const ast &con, const std::vector &prems){ - if(p.size() == 0) - throw proof_error(); - if(p.size() == 1) - return make_congruence(p[0],con,prems[0]); - ast thing = con; - ast res = mk_true(); - for(unsigned i = 0; i < p.size(); i++){ - int pos = find_congruence_position(p[i],thing); - ast next = subst_in_arg_pos(pos,arg(p[i],1),arg(thing,0)); - ast goal = make(op(thing),arg(thing,0),next); - ast equa = make_congruence(p[i],goal,prems[i]); - if(i == 0) - res = equa; - else { - ast trace = make(op(con),arg(con,0),arg(thing,0)); - ast equiv = make(Iff,trace,make(op(trace),arg(trace,0),next)); - ast foo = make_congruence(goal,equiv,equa); - res = make_mp(equiv,res,foo); - } - thing = make(op(thing),next,arg(thing,1)); - } - return res; - } - - /* Interpolate a mixed congruence axiom. */ - - virtual ast make_mixed_congruence(const ast &x, const ast &y, const ast &p, const ast &con, const ast &prem1){ - ast foo = p; - std::vector conjs; - LitType t = get_term_type(foo); - switch(t){ - case LitA: - case LitB: - foo = make_local_rewrite(t,foo); - break; - case LitMixed: - conjs.push_back(foo); - foo = get_placeholder(foo); - } - // find the argument position of x and y - int pos = -1; - int nargs = num_args(arg(con,0)); - for(int i = 0; i < nargs; i++) - if(x == arg(arg(con,0),i) && y == arg(arg(con,1),i)) - pos = i; - if(pos == -1) - throw proof_error(); - ast bar = make(cong,foo,make_int(rational(pos)),get_placeholder(mk_not(con))); - conjs.push_back(mk_not(con)); - return make_contra_node(bar,conjs); - } - - ast subst_in_arg_pos(int pos, ast term, ast app){ - std::vector args; - get_args(app,args); - args[pos] = term; - return clone(app,args); - } - - /** Make a farkas proof node. */ - - virtual node make_farkas(ast con, const std::vector &prems, const std::vector &prem_cons, - const std::vector &coeffs){ - - /* Compute the interpolant for the clause */ - - ast zero = make_int("0"); - std::vector conjs; - ast thing = make(Leq,zero,zero); - for(unsigned i = 0; i < prem_cons.size(); i++){ - const ast &lit = prem_cons[i]; - if(get_term_type(lit) == LitA) - // Farkas rule seems to assume strict integer inequalities are rounded - linear_comb(thing,coeffs[i],lit,true /*round_off*/); - } - thing = simplify_ineq(thing); - for(unsigned i = 0; i < prem_cons.size(); i++){ - const ast &lit = prem_cons[i]; - if(get_term_type(lit) == LitMixed){ - thing = make(sum,thing,coeffs[i],get_placeholder(lit)); - conjs.push_back(lit); - } - } - thing = make_contra_node(thing,conjs); - - /* Resolve it with the premises */ - std::vector conc; conc.resize(prem_cons.size()); - for(unsigned i = 0; i < prem_cons.size(); i++) - conc[prem_cons.size()-i-1] = prem_cons[i]; - for(unsigned i = 0; i < prem_cons.size(); i++){ - thing = make_resolution(prem_cons[i],conc,thing,prems[i]); - conc.pop_back(); - } - return thing; - } - - /** Set P to P + cQ, where P and Q are linear inequalities. Assumes P is 0 <= y or 0 < y. */ - - void linear_comb(ast &P, const ast &c, const ast &Q, bool round_off = false){ - ast Qrhs; - bool qstrict = false; - if(is_not(Q)){ - ast nQ = arg(Q,0); - switch(op(nQ)){ - case Gt: - Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); - break; - case Lt: - Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); - break; - case Geq: - Qrhs = make(Sub,arg(nQ,1),arg(nQ,0)); - qstrict = true; - break; - case Leq: - Qrhs = make(Sub,arg(nQ,0),arg(nQ,1)); - qstrict = true; - break; - default: - throw proof_error(); - } - } - else { - switch(op(Q)){ - case Leq: - Qrhs = make(Sub,arg(Q,1),arg(Q,0)); - break; - case Geq: - Qrhs = make(Sub,arg(Q,0),arg(Q,1)); - break; - case Lt: - Qrhs = make(Sub,arg(Q,1),arg(Q,0)); - qstrict = true; - break; - case Gt: - Qrhs = make(Sub,arg(Q,0),arg(Q,1)); - qstrict = true; - break; - default: - throw proof_error(); - } - } -#if 0 - bool pstrict = op(P) == Lt, strict = pstrict || qstrict; - if(pstrict && qstrict && round_off) - Qrhs = make(Sub,Qrhs,make_int(rational(1))); -#else - if (round_off && get_type(Qrhs) != int_type()) - round_off = false; - bool pstrict = op(P) == Lt; - if(qstrict && round_off && (pstrict || !(c == make_int(rational(1))))){ - Qrhs = make(Sub,Qrhs,make_int(rational(1))); - qstrict = false; - } - Qrhs = make(Times,c,Qrhs); - bool strict = pstrict || qstrict; -#endif - if(strict) - P = make(Lt,arg(P,0),make(Plus,arg(P,1),Qrhs)); - else - P = make(Leq,arg(P,0),make(Plus,arg(P,1),Qrhs)); - } - - /* Make an axiom instance of the form |- x<=y, y<= x -> x =y */ - virtual node make_leq2eq(ast x, ast y, const ast &xleqy, const ast &yleqx){ - ast con = make(Equal,x,y); - ast itp; - switch(get_term_type(con)){ - case LitA: - itp = mk_false(); - break; - case LitB: - itp = mk_true(); - break; - default: { // mixed equality - if(get_term_type(x) == LitMixed || get_term_type(y) == LitMixed){ - if(y == make_int(rational(0)) && op(x) == Plus && num_args(x) == 2){ - // std::cerr << "WARNING: untested case in leq2eq\n"; - } - else { - // std::cerr << "WARNING: mixed term in leq2eq\n"; - std::vector lits; - lits.push_back(con); - lits.push_back(make(Not,xleqy)); - lits.push_back(make(Not,yleqx)); - return make_axiom(lits); - } - } - std::vector conjs; conjs.resize(3); - conjs[0] = mk_not(con); - conjs[1] = xleqy; - conjs[2] = yleqx; - itp = make_contra_node(make(leq2eq, - get_placeholder(mk_not(con)), - get_placeholder(xleqy), - get_placeholder(yleqx)), - conjs,1); - } - } - return itp; - } - - /* Make an axiom instance of the form |- x = y -> x <= y */ - virtual node make_eq2leq(ast x, ast y, const ast &xleqy){ - ast itp; - switch(get_term_type(xleqy)){ - case LitA: - itp = mk_false(); - break; - case LitB: - itp = mk_true(); - break; - default: { // mixed equality - std::vector conjs; conjs.resize(2); - conjs[0] = make(Equal,x,y); - conjs[1] = mk_not(xleqy); - itp = make(eq2leq,get_placeholder(conjs[0]),get_placeholder(conjs[1])); - itp = make_contra_node(itp,conjs,2); - } - } - return itp; - } - - - /* Make an inference of the form t <= c |- t/d <= floor(c/d) where t - is an affine term divisble by d and c is an integer constant */ - virtual node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem){ - ast itp = mk_false(); - switch(get_term_type(con)){ - case LitA: - itp = mk_false(); - break; - case LitB: - itp = mk_true(); - break; - default: { - std::vector conjs; conjs.resize(2); - conjs[0] = tleqc; - conjs[1] = mk_not(con); - itp = make(sum,get_placeholder(conjs[0]),d,get_placeholder(conjs[1])); - itp = make_contra_node(itp,conjs); - } - } - std::vector conc; conc.push_back(con); - itp = make_resolution(tleqc,conc,itp,prem); - return itp; - } - - - - // create a fresh variable for localization - ast fresh_localization_var(const ast &term, int frame){ - std::ostringstream s; - s << "%" << (localization_vars.size()); - ast var = make_var(s.str().c_str(),get_type(term)); - pv->sym_range(sym(var)) = pv->range_full(); // make this variable global - localization_vars.push_back(LocVar(var,term,frame)); - return var; - } - - struct LocVar { // localization vars - ast var; // a fresh variable - ast term; // term it represents - int frame; // frame in which it's defined - LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} - }; - - std::vector localization_vars; // localization vars in order of creation - - struct locmaps { - hash_map localization_map; // maps terms to their localization vars - hash_map localization_pf_map; // maps terms to proofs of their localizations - }; - - hash_map localization_maps_per_range; - - /* "localize" a term e to a given frame range, creating new symbols to - represent non-local subterms. This returns the localized version e_l, - as well as a proof thet e_l = l. - */ - - ast make_refl(const ast &e){ - if(get_term_type(e) == LitA) - return mk_false(); - return mk_true(); // TODO: is this right? - } - - - ast make_equiv(const ast &x, const ast &y){ - if(get_type(x) == bool_type()) - return make(Iff,x,y); - else - return make(Equal,x,y); - } - - bool range_is_global(const prover::range &r){ - if(pv->range_contained(r,rng)) - return false; - if(!pv->ranges_intersect(r,rng)) - return false; - return true; - } - - ast localize_term(ast e, const prover::range &rng, ast &pf){ - - // we need to memoize this function separately for A, B and global - prover::range map_range = rng; - if(range_is_global(map_range)) - map_range = pv->range_full(); - locmaps &maps = localization_maps_per_range[map_range]; - hash_map &localization_map = maps.localization_map; - hash_map &localization_pf_map = maps.localization_pf_map; - - ast orig_e = e; - pf = make_refl(e); // proof that e = e - - // prover::range erng = - pv->ast_scope(e); -#if 0 - if(!(erng.lo > erng.hi) && pv->ranges_intersect(pv->ast_scope(e),rng)){ - return e; // this term occurs in range, so it's O.K. - } -#endif - - hash_map::iterator it = localization_map.find(e); - - if(it != localization_map.end() && is_bool_type(get_type(e)) - && !pv->ranges_intersect(pv->ast_scope(it->second),rng)) - it = localization_map.end(); // prevent quantifiers over booleans - - if(it != localization_map.end()){ - pf = localization_pf_map[e]; - e = it->second; - } - - else { - // if it is non-local, we must first localize the arguments to - // the range of its function symbol - - int nargs = num_args(e); - if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ - prover::range frng = rng; - opr o = op(e); - if(o == Uninterpreted){ - symb f = sym(e); - prover::range srng = pv->sym_range(f); - if(pv->ranges_intersect(srng,rng)) // localize to desired range if possible - frng = pv->range_glb(srng,rng); - else - frng = srng; // this term will be localized - } - else if(o == Plus || o == Times){ // don't want bound variables inside arith ops - // std::cout << "WARNING: non-local arithmetic\n"; - // frng = erng; // this term will be localized - } - else if(o == Select){ // treat the array term like a function symbol - prover::range srng = pv->ast_scope(arg(e,0)); - if(!(srng.lo > srng.hi) && pv->ranges_intersect(srng,rng)) // localize to desired range if possible - frng = pv->range_glb(srng,rng); - else - frng = srng; // this term will be localized - } - std::vector largs(nargs); - std::vector eqs; - std::vector pfs; - for(int i = 0; i < nargs; i++){ - ast argpf; - largs[i] = localize_term(arg(e,i),frng,argpf); - frng = pv->range_glb(frng,pv->ast_scope(largs[i])); - if(largs[i] != arg(e,i)){ - eqs.push_back(make_equiv(largs[i],arg(e,i))); - pfs.push_back(argpf); - } - } - - e = clone(e,largs); - if(pfs.size()) - pf = make_congruence(eqs,make_equiv(e,orig_e),pfs); - // assert(is_local(e)); - } - - localization_pf_map[orig_e] = pf; - localization_map[orig_e] = e; - } - - if(pv->ranges_intersect(pv->ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - - if(is_array_type(get_type(e))) - std::cerr << "WARNING: array quantifier\n"; - - // choose a frame for the constraint that is close to range - int frame = pv->range_near(pv->ast_scope(e),rng); - - ast new_var = fresh_localization_var(e,frame); - localization_map[orig_e] = new_var; - std::vector foo; foo.push_back(make_equiv(new_var,e)); - ast bar = make_assumption(frame,foo); - pf = make_transitivity(new_var,e,orig_e,bar,pf); - localization_pf_map[orig_e] = pf; - - // HACK: try to bias this term in the future - if(!pv->range_is_full(rng)){ - prover::range rf = pv->range_full(); - locmaps &fmaps = localization_maps_per_range[rf]; - hash_map &flocalization_map = fmaps.localization_map; - hash_map &flocalization_pf_map = fmaps.localization_pf_map; - // if(flocalization_map.find(orig_e) == flocalization_map.end()) - { - flocalization_map[orig_e] = new_var; - flocalization_pf_map[orig_e] = pf; - } - } - - - return new_var; - } - - ast delete_quant(hash_map &memo, const ast &v, const ast &e){ - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - opr o = op(e); - switch(o){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(e); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - args[i] = delete_quant(memo, v, arg(e,i)); - res = make(o,args); - break; - } - case Uninterpreted: { - symb s = sym(e); - ast w = arg(arg(e,0),0); - if(s == sforall || s == sexists){ - res = delete_quant(memo,v,arg(e,1)); - if(w != v) - res = make(s,w,res); - break; - } - } - default: - res = e; - } - } - return res; - } - - ast insert_quants(hash_map &memo, const ast &e){ - std::pair foo(e,ast()); - std::pair::iterator,bool> bar = memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - opr o = op(e); - switch(o){ - case Or: - case And: - case Implies: { - unsigned nargs = num_args(e); - std::vector args; args.resize(nargs); - for(unsigned i = 0; i < nargs; i++) - args[i] = insert_quants(memo, arg(e,i)); - res = make(o,args); - break; - } - case Uninterpreted: { - symb s = sym(e); - if(s == sforall || s == sexists){ - opr q = (s == sforall) ? Forall : Exists; - ast v = arg(arg(e,0),0); - hash_map dmemo; - ast body = delete_quant(dmemo,v,arg(e,1)); - body = insert_quants(memo,body); - res = apply_quant(q,v,body); - break; - } - } - default: - res = e; - } - } - return res; - } - - ast add_quants(ast e){ -#ifdef CAPTURE_LOCALIZATION - if(!localization_vars.empty()){ - hash_map memo; - e = insert_quants(memo,e); - } -#else - for(int i = localization_vars.size() - 1; i >= 0; i--){ - LocVar &lv = localization_vars[i]; - opr quantifier = (pv->in_range(lv.frame,rng)) ? Exists : Forall; - e = apply_quant(quantifier,lv.var,e); - } -#endif - return e; - } - - node make_resolution(ast pivot, node premise1, node premise2) { - std::vector lits; - return make_resolution(pivot,lits,premise1,premise2); - } - - /* Return an interpolant from a proof of false */ - ast interpolate(const node &pf){ - // proof of false must be a formula, with quantified symbols -#ifndef BOGUS_QUANTS - return close_universally(add_quants(z3_simplify(pf))); -#else - return close_universally(z3_simplify(pf)); -#endif - } - - ast resolve_with_quantifier(const ast &pivot1, const ast &conj1, - const ast &pivot2, const ast &conj2){ - if(is_not(arg(pivot1,1))) - return resolve_with_quantifier(pivot2,conj2,pivot1,conj1); - ast eqpf; - ast P = arg(pivot1,1); - ast Ploc = localize_term(P, rng, eqpf); - ast pPloc = make_hypothesis(Ploc); - ast pP = make_mp(make(Iff,Ploc,P),pPloc,eqpf); - ast rP = make_resolution(P,conj1,pP); - ast nP = mk_not(P); - ast nPloc = mk_not(Ploc); - ast neqpf = make_congruence(make(Iff,Ploc,P),make(Iff,nPloc,nP),eqpf); - ast npPloc = make_hypothesis(nPloc); - ast npP = make_mp(make(Iff,nPloc,nP),npPloc,neqpf); - ast nrP = make_resolution(nP,conj2,npP); - ast res = make_resolution(Ploc,rP,nrP); - return capture_localization(res); - } - - ast get_contra_coeff(const ast &f){ - ast c = arg(f,0); - // if(!is_not(arg(f,1))) - // c = make(Uminus,c); - return c; - } - - ast my_or(const ast &a, const ast &b){ - return mk_or(a,b); - } - - ast my_and(const ast &a, const ast &b){ - return mk_and(a,b); - } - - ast my_implies(const ast &a, const ast &b){ - return mk_implies(a,b); - } - - ast my_or(const std::vector &a){ - return mk_or(a); - } - - ast my_and(const std::vector &a){ - return mk_and(a); - } - - ast get_lit_atom(const ast &l){ - if(op(l) == Not) - return arg(l,0); - return l; - } - - bool is_placeholder(const ast &e){ - if(op(e) == Uninterpreted){ - std::string name = string_of_symbol(sym(e)); - if(name.size() > 2 && name[0] == '@' && name[1] == 'p') - return true; - } - return false; - } - -public: - iz3proof_itp_impl(prover *p, const prover::range &r, bool w) - : iz3proof_itp(*p) - { - pv = p; - rng = r; - weak = false ; //w; - type boolintbooldom[3] = {bool_type(),int_type(),bool_type()}; - type booldom[1] = {bool_type()}; - type boolbooldom[2] = {bool_type(),bool_type()}; - type boolboolbooldom[3] = {bool_type(),bool_type(),bool_type()}; - type intbooldom[2] = {int_type(),bool_type()}; - contra = function("@contra",2,boolbooldom,bool_type()); - m().inc_ref(contra); - sum = function("@sum",3,boolintbooldom,bool_type()); - m().inc_ref(sum); - rotate_sum = function("@rotsum",2,boolbooldom,bool_type()); - m().inc_ref(rotate_sum); - leq2eq = function("@leq2eq",3,boolboolbooldom,bool_type()); - m().inc_ref(leq2eq); - eq2leq = function("@eq2leq",2,boolbooldom,bool_type()); - m().inc_ref(eq2leq); - cong = function("@cong",3,boolintbooldom,bool_type()); - m().inc_ref(cong); - exmid = function("@exmid",3,boolboolbooldom,bool_type()); - m().inc_ref(exmid); - symm = function("@symm",1,booldom,bool_type()); - m().inc_ref(symm); - epsilon = make_var("@eps",int_type()); - modpon = function("@mp",3,boolboolbooldom,bool_type()); - m().inc_ref(modpon); - no_proof = make_var("@nop",bool_type()); - concat = function("@concat",2,boolbooldom,bool_type()); - m().inc_ref(concat); - top_pos = make_var("@top_pos",bool_type()); - add_pos = function("@add_pos",2,intbooldom,bool_type()); - m().inc_ref(add_pos); - rewrite_A = function("@rewrite_A",3,boolboolbooldom,bool_type()); - m().inc_ref(rewrite_A); - rewrite_B = function("@rewrite_B",3,boolboolbooldom,bool_type()); - m().inc_ref(rewrite_B); - normal_step = function("@normal_step",2,boolbooldom,bool_type()); - m().inc_ref(normal_step); - normal_chain = function("@normal_chain",2,boolbooldom,bool_type()); - m().inc_ref(normal_chain); - normal = function("@normal",2,boolbooldom,bool_type()); - m().inc_ref(normal); - sforall = function("@sforall",2,boolbooldom,bool_type()); - m().inc_ref(sforall); - sexists = function("@sexists",2,boolbooldom,bool_type()); - m().inc_ref(sexists); - } - - ~iz3proof_itp_impl(){ - m().dec_ref(contra); - m().dec_ref(sum); - m().dec_ref(rotate_sum); - m().dec_ref(leq2eq); - m().dec_ref(eq2leq); - m().dec_ref(cong); - m().dec_ref(exmid); - m().dec_ref(symm); - m().dec_ref(modpon); - m().dec_ref(concat); - m().dec_ref(add_pos); - m().dec_ref(rewrite_A); - m().dec_ref(rewrite_B); - m().dec_ref(normal_step); - m().dec_ref(normal_chain); - m().dec_ref(normal); - m().dec_ref(sforall); - m().dec_ref(sexists); - } -}; - -iz3proof_itp *iz3proof_itp::create(prover *p, const prover::range &r, bool w){ - return new iz3proof_itp_impl(p,r,w); -} - diff --git a/src/interp/iz3proof_itp.h b/src/interp/iz3proof_itp.h deleted file mode 100644 index c9a36e9b1..000000000 --- a/src/interp/iz3proof_itp.h +++ /dev/null @@ -1,143 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3proof.h - - Abstract: - - This class defines a simple interpolating proof system. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifndef IZ3PROOF_ITP_H -#define IZ3PROOF_ITP_H - -#include - -#include "interp/iz3base.h" -#include "interp/iz3secondary.h" - -// #define CHECK_PROOFS - -/** This class defines a simple proof system. - - As opposed to iz3proof, this class directly computes interpolants, - so the proof representation is just the interpolant itself. - -*/ - -class iz3proof_itp : public iz3mgr { - public: - - /** Enumeration of proof rules. */ - enum rule {Resolution,Assumption,Hypothesis,Theory,Axiom,Contra,Lemma,Reflexivity,Symmetry,Transitivity,Congruence,EqContra}; - - /** Interface to prover. */ - typedef iz3base prover; - - /** Ast type. */ - typedef prover::ast ast; - - /** The type of proof nodes (just interpolants). */ - typedef ast node; - - /** Object thrown in case of a proof error. */ - struct proof_error: public iz3_exception { - proof_error(): iz3_exception("proof_error") {} - }; - - - /** Make a resolution node with given pivot literal and premises. - The conclusion of premise1 should contain the negation of the - pivot literal, while the conclusion of premise2 should containe the - pivot literal. - */ - virtual node make_resolution(ast pivot, const std::vector &conc, node premise1, node premise2) = 0; - - /** Make an assumption node. The given clause is assumed in the given frame. */ - virtual node make_assumption(int frame, const std::vector &assumption) = 0; - - /** Make a hypothesis node. If phi is the hypothesis, this is - effectively phi |- phi. */ - virtual node make_hypothesis(const ast &hypothesis) = 0; - - /** Make an axiom node. The conclusion must be an instance of an axiom. */ - virtual node make_axiom(const std::vector &conclusion) = 0; - - /** Make an axiom node. The conclusion must be an instance of an axiom. Localize axiom instance to range*/ - virtual node make_axiom(const std::vector &conclusion, prover::range) = 0; - - /** Make a Contra node. This rule takes a derivation of the form - Gamma |- False and produces |- \/~Gamma. */ - - virtual node make_contra(node prem, const std::vector &conclusion) = 0; - - /** Make a Reflexivity node. This rule produces |- x = x */ - - virtual node make_reflexivity(ast con) = 0; - - /** Make a Symmetry node. This takes a derivation of |- x = y and - produces | y = x */ - - virtual node make_symmetry(ast con, const ast &premcon, node prem) = 0; - - /** Make a transitivity node. This takes derivations of |- x = y - and |- y = z produces | x = z */ - - virtual node make_transitivity(const ast &x, const ast &y, const ast &z, node prem1, node prem2) = 0; - - /** Make a congruence node. This takes a derivation of |- x_i = y_i - and produces |- f(...x_i,...) = f(...,y_i,...) */ - - virtual node make_congruence(const ast &xi_eq_yi, const ast &con, const ast &prem1) = 0; - - /** Make a congruence node. This takes derivations of |- x_i1 = y_i1, |- x_i2 = y_i2,... - and produces |- f(...x_i1...x_i2...) = f(...y_i1...y_i2...) */ - - virtual node make_congruence(const std::vector &xi_eq_yi, const ast &con, const std::vector &prems) = 0; - - /** Make a modus-ponens node. This takes derivations of |- x - and |- x = y and produces |- y */ - - virtual node make_mp(const ast &x_eq_y, const ast &prem1, const ast &prem2) = 0; - - /** Make a farkas proof node. */ - - virtual node make_farkas(ast con, const std::vector &prems, const std::vector &prem_cons, const std::vector &coeffs) = 0; - - /* Make an axiom instance of the form |- x<=y, y<= x -> x =y */ - virtual node make_leq2eq(ast x, ast y, const ast &xleqy, const ast &yleqx) = 0; - - /* Make an axiom instance of the form |- x = y -> x <= y */ - virtual node make_eq2leq(ast x, ast y, const ast &xeqy) = 0; - - /* Make an inference of the form t <= c |- t/d <= floor(c/d) where t - is an affine term divisble by d and c is an integer constant */ - virtual node make_cut_rule(const ast &tleqc, const ast &d, const ast &con, const ast &prem) = 0; - - /* Return an interpolant from a proof of false */ - virtual ast interpolate(const node &pf) = 0; - - /** Create proof object to construct an interpolant. */ - static iz3proof_itp *create(prover *p, const prover::range &r, bool _weak); - - protected: - iz3proof_itp(iz3mgr &m) - : iz3mgr(m) - { - } - - public: - virtual ~iz3proof_itp(){ - } -}; - -#endif diff --git a/src/interp/iz3scopes.cpp b/src/interp/iz3scopes.cpp deleted file mode 100755 index e3a28abdd..000000000 --- a/src/interp/iz3scopes.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3scopes.cpp - - Abstract: - - Calculations with scopes, for both sequence and tree interpolation. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#include - -#include - -#include "interp/iz3scopes.h" - - -/** computes the least common ancestor of two nodes in the tree, or SHRT_MAX if none */ -int scopes::tree_lca(int n1, int n2){ - if(!tree_mode()) - return std::max(n1,n2); - if(n1 == SHRT_MIN) return n2; - if(n2 == SHRT_MIN) return n1; - if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; - while(n1 != n2){ - if(n1 == SHRT_MAX || n2 == SHRT_MAX) return SHRT_MAX; - assert(n1 >= 0 && n2 >= 0 && n1 < (int)parents.size() && n2 < (int)parents.size()); - if(n1 < n2) n1 = parents[n1]; - else n2 = parents[n2]; - } - return n1; -} - -/** computes the greatest common descendant two nodes in the tree, or SHRT_MIN if none */ -int scopes::tree_gcd(int n1, int n2){ - if(!tree_mode()) - return std::min(n1,n2); - int foo = tree_lca(n1,n2); - if(foo == n1) return n2; - if(foo == n2) return n1; - return SHRT_MIN; -} - -#ifndef FULL_TREE - -/** test whether a tree node is contained in a range */ -bool scopes::in_range(int n, const range &rng){ - return tree_lca(rng.lo,n) == n && tree_gcd(rng.hi,n) == n; -} - -/** test whether two ranges of tree nodes intersect */ -bool scopes::ranges_intersect(const range &rng1, const range &rng2){ - return tree_lca(rng1.lo,rng2.hi) == rng2.hi && tree_lca(rng1.hi,rng2.lo) == rng1.hi; -} - - -bool scopes::range_contained(const range &rng1, const range &rng2){ - return tree_lca(rng2.lo,rng1.lo) == rng1.lo - && tree_lca(rng1.hi,rng2.hi) == rng2.hi; -} - -scopes::range scopes::range_lub(const range &rng1, const range &rng2){ - range res; - res.lo = tree_gcd(rng1.lo,rng2.lo); - res.hi = tree_lca(rng1.hi,rng2.hi); - return res; -} - -scopes::range scopes::range_glb(const range &rng1, const range &rng2){ - range res; - res.lo = tree_lca(rng1.lo,rng2.lo); - res.hi = tree_gcd(rng1.hi,rng2.hi); - return res; -} - -#else - - -namespace std { - template <> - class hash { - public: - size_t operator()(const scopes::range_lo &p) const { - return p.lo + (size_t)p.next; - } - }; -} - -template <> inline -size_t stdext::hash_value(const scopes::range_lo& p) -{ - std::hash h; - return h(p); -} - -namespace std { - template <> - class less { - public: - bool operator()(const scopes::range_lo &x, const scopes::range_lo &y) const { - return x.lo < y.lo || x.lo == y.lo && (size_t)x.next < (size_t)y.next; - } - }; -} - - -struct range_op { - scopes::range_lo *x, *y; - int hi; - range_op(scopes::range_lo *_x, scopes::range_lo *_y, int _hi){ - x = _x; y = _y; hi = _hi; - } -}; - -namespace std { - template <> - class hash { - public: - size_t operator()(const range_op &p) const { - return (size_t) p.x + (size_t)p.y + p.hi; - } - }; -} - -template <> inline -size_t stdext::hash_value(const range_op& p) -{ - std::hash h; - return h(p); -} - -namespace std { - template <> - class less { - public: - bool operator()(const range_op &x, const range_op &y) const { - return (size_t)x.x < (size_t)y.x || x.x == y.x && - ((size_t)x.y < (size_t)y.y || x.y == y.y && x.hi < y.hi); - } - }; -} - -struct range_tables { - hash_map unique; - hash_map lub; - hash_map glb; -}; - - -scopes::range_lo *scopes::find_range_lo(int lo, range_lo *next){ - range_lo foo(lo,next); - std::pair baz(foo,(range_lo *)0); - std::pair::iterator,bool> bar = rt->unique.insert(baz); - if(bar.second) - bar.first->second = new range_lo(lo,next); - return bar.first->second; - //std::pair::iterator,bool> bar = rt->unique.insert(foo); - // const range_lo *baz = &*(bar.first); - // return (range_lo *)baz; // coerce const -} - -scopes::range_lo *scopes::range_lub_lo(range_lo *rng1, range_lo *rng2){ - if(!rng1) return rng2; - if(!rng2) return rng1; - if(rng1->lo > rng2->lo) - std::swap(rng1,rng2); - std::pair foo(range_op(rng1,rng2,0),(range_lo *)0); - std::pair::iterator,bool> bar = rt->lub.insert(foo); - range_lo *&res = bar.first->second; - if(!bar.second) return res; - if(!(rng1->next && rng1->next->lo <= rng2->lo)){ - for(int lo = rng1->lo; lo <= rng2->lo; lo = parents[lo]) - if(lo == rng2->lo) - {rng2 = rng2->next; break;} - } - range_lo *baz = range_lub_lo(rng1->next,rng2); - res = find_range_lo(rng1->lo,baz); - return res; -} - - -scopes::range_lo *scopes::range_glb_lo(range_lo *rng1, range_lo *rng2, int hi){ - if(!rng1) return rng1; - if(!rng2) return rng2; - if(rng1->lo > rng2->lo) - std::swap(rng1,rng2); - std::pair cand(range_op(rng1,rng2,hi),(range_lo *)0); - std::pair::iterator,bool> bar = rt->glb.insert(cand); - range_lo *&res = bar.first->second; - if(!bar.second) return res; - range_lo *foo; - if(!(rng1->next && rng1->next->lo <= rng2->lo)){ - int lim = hi; - if(rng1->next) lim = std::min(lim,rng1->next->lo); - int a = rng1->lo, b = rng2->lo; - while(a != b && b <= lim){ - a = parents[a]; - if(a > b)std::swap(a,b); - } - if(a == b && b <= lim){ - foo = range_glb_lo(rng1->next,rng2->next,hi); - foo = find_range_lo(b,foo); - } - else - foo = range_glb_lo(rng2,rng1->next,hi); - } - else foo = range_glb_lo(rng1->next,rng2,hi); - res = foo; - return res; -} - -/** computes the lub (smallest containing subtree) of two ranges */ -scopes::range scopes::range_lub(const range &rng1, const range &rng2){ - int hi = tree_lca(rng1.hi,rng2.hi); - if(hi == SHRT_MAX) return range_full(); - range_lo *lo = range_lub_lo(rng1.lo,rng2.lo); - return range(hi,lo); -} - -/** computes the glb (intersection) of two ranges */ -scopes::range scopes::range_glb(const range &rng1, const range &rng2){ - if(rng1.hi == SHRT_MAX) return rng2; - if(rng2.hi == SHRT_MAX) return rng1; - int hi = tree_gcd(rng1.hi,rng2.hi); - range_lo *lo = hi == SHRT_MIN ? 0 : range_glb_lo(rng1.lo,rng2.lo,hi); - if(!lo) hi = SHRT_MIN; - return range(hi,lo); -} - -/** is this range empty? */ -bool scopes::range_is_empty(const range &rng){ - return rng.hi == SHRT_MIN; -} - -/** return an empty range */ -scopes::range scopes::range_empty(){ - return range(SHRT_MIN,0); -} - -/** return a full range */ -scopes::range scopes::range_full(){ - return range(SHRT_MAX,0); -} - -/** return the maximal element of a range */ -int scopes::range_max(const range &rng){ - return rng.hi; -} - -/** return a minimal (not necessarily unique) element of a range */ -int scopes::range_min(const range &rng){ - if(rng.hi == SHRT_MAX) return SHRT_MIN; - return rng.lo ? rng.lo->lo : SHRT_MAX; -} - - -/** return range consisting of downward closure of a point */ -scopes::range scopes::range_downward(int _hi){ - std::vector descendants(parents.size()); - for(int i = descendants.size() - 1; i >= 0 ; i--) - descendants[i] = i == _hi || parents[i] < parents.size() && descendants[parents[i]]; - for(unsigned i = 0; i < descendants.size() - 1; i++) - if(parents[i] < parents.size()) - descendants[parents[i]] = false; - range_lo *foo = 0; - for(int i = descendants.size() - 1; i >= 0; --i) - if(descendants[i]) foo = find_range_lo(i,foo); - return range(_hi,foo); -} - -/** add an element to a range */ -void scopes::range_add(int i, range &n){ - range foo = range(i, find_range_lo(i,0)); - n = range_lub(foo,n); -} - -/** Choose an element of rng1 that is near to rng2 */ -int scopes::range_near(const range &rng1, const range &rng2){ - - int frame; - int thing = tree_lca(rng1.hi,rng2.hi); - if(thing != rng1.hi) return rng1.hi; - range line = range(rng1.hi,find_range_lo(rng2.hi,(range_lo *)0)); - line = range_glb(line,rng1); - return range_min(line); -} - - -/** test whether a tree node is contained in a range */ -bool scopes::in_range(int n, const range &rng){ - range r = range_empty(); - range_add(n,r); - r = range_glb(rng,r); - return !range_is_empty(r); -} - -/** test whether two ranges of tree nodes intersect */ -bool scopes::ranges_intersect(const range &rng1, const range &rng2){ - range r = range_glb(rng1,rng2); - return !range_is_empty(r); -} - - -bool scopes::range_contained(const range &rng1, const range &rng2){ - range r = range_glb(rng1,rng2); - return r.hi == rng1.hi && r.lo == rng1.lo; -} - - -#endif - - diff --git a/src/interp/iz3scopes.h b/src/interp/iz3scopes.h deleted file mode 100755 index ece30dc25..000000000 --- a/src/interp/iz3scopes.h +++ /dev/null @@ -1,222 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3scopes.h - - Abstract: - - Calculations with scopes, for both sequence and tree interpolation. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifndef IZ3SOPES_H -#define IZ3SOPES_H - -#include -#include -#include "interp/iz3hash.h" - -class scopes { - - public: - /** Construct from parents vector. */ - scopes(const std::vector &_parents){ - parents = _parents; - } - - scopes(){ - } - - void initialize(const std::vector &_parents){ - parents = _parents; - } - - /** The parents vector defining the tree structure */ - std::vector parents; - - // #define FULL_TREE -#ifndef FULL_TREE - struct range { - range(){ - lo = SHRT_MAX; - hi = SHRT_MIN; - } - short lo, hi; - }; - - /** computes the lub (smallest containing subtree) of two ranges */ - range range_lub(const range &rng1, const range &rng2); - - /** computes the glb (intersection) of two ranges */ - range range_glb(const range &rng1, const range &rng2); - - /** is this range empty? */ - bool range_is_empty(const range &rng){ - return rng.hi < rng.lo; - } - - /** is this range full? */ - bool range_is_full(const range &rng){ - return rng.lo == SHRT_MIN && rng.hi == SHRT_MAX; - } - - /** return an empty range */ - range range_empty(){ - range res; - res.lo = SHRT_MAX; - res.hi = SHRT_MIN; - return res; - } - - /** return an empty range */ - range range_full(){ - range res; - res.lo = SHRT_MIN; - res.hi = SHRT_MAX; - return res; - } - - /** return the maximal element of a range */ - int range_max(const range &rng){ - return rng.hi; - } - - /** return a minimal (not necessarily unique) element of a range */ - int range_min(const range &rng){ - return rng.lo; - } - - /** return range consisting of downward closure of a point */ - range range_downward(int _hi){ - range foo; - foo.lo = SHRT_MIN; - foo.hi = _hi; - return foo; - } - - void range_add(int i, range &n){ -#if 0 - if(i < n.lo) n.lo = i; - if(i > n.hi) n.hi = i; -#else - range rng; rng.lo = i; rng.hi = i; - n = range_lub(rng,n); -#endif - } - - /** Choose an element of rng1 that is near to rng2 */ - int range_near(const range &rng1, const range &rng2){ - int frame; - int thing = tree_lca(rng1.lo,rng2.hi); - if(thing == rng1.lo) frame = rng1.lo; - else frame = tree_gcd(thing,rng1.hi); - return frame; - } -#else - - struct range_lo { - int lo; - range_lo *next; - range_lo(int _lo, range_lo *_next){ - lo = _lo; - next = _next; - } - }; - - struct range { - int hi; - range_lo *lo; - range(int _hi, range_lo *_lo){ - hi = _hi; - lo = _lo; - } - range(){ - hi = SHRT_MIN; - lo = 0; - } - }; - - range_tables *rt; - - /** computes the lub (smallest containing subtree) of two ranges */ - range range_lub(const range &rng1, const range &rng2); - - /** computes the glb (intersection) of two ranges */ - range range_glb(const range &rng1, const range &rng2); - - /** is this range empty? */ - bool range_is_empty(const range &rng); - - /** return an empty range */ - range range_empty(); - - /** return a full range */ - range range_full(); - - /** return the maximal element of a range */ - int range_max(const range &rng); - - /** return a minimal (not necessarily unique) element of a range */ - int range_min(const range &rng); - - /** return range consisting of downward closure of a point */ - range range_downward(int _hi); - - /** add an element to a range */ - void range_add(int i, range &n); - - /** Choose an element of rng1 that is near to rng2 */ - int range_near(const range &rng1, const range &rng2); - - range_lo *find_range_lo(int lo, range_lo *next); - range_lo *range_lub_lo(range_lo *rng1, range_lo *rng2); - range_lo *range_glb_lo(range_lo *rng1, range_lo *rng2, int lim); - -#endif - - /** test whether a tree node is contained in a range */ - bool in_range(int n, const range &rng); - - /** test whether two ranges of tree nodes intersect */ - bool ranges_intersect(const range &rng1, const range &rng2); - - /** test whether range rng1 contained in range rng2 */ - bool range_contained(const range &rng1, const range &rng2); - - private: - int tree_lca(int n1, int n2); - int tree_gcd(int n1, int n2); - bool tree_mode(){return parents.size() != 0;} - - - -}; - -// let us hash on ranges - -#ifndef FULL_TREE -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const scopes::range &p) const { - return (size_t)p.lo + (size_t)p.hi; - } - }; -} - -inline bool operator==(const scopes::range &x, const scopes::range &y){ - return x.lo == y.lo && x.hi == y.hi; -} -#endif - -#endif diff --git a/src/interp/iz3secondary.h b/src/interp/iz3secondary.h deleted file mode 100755 index a5a949b54..000000000 --- a/src/interp/iz3secondary.h +++ /dev/null @@ -1,40 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3secondary - - Abstract: - - Interface for secondary provers. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifndef IZ3SECONDARY_H -#define IZ3SECONDARY_H - -/** Interface class for secondary provers. */ - -#include "interp/iz3base.h" -#include - -class iz3secondary : public iz3mgr { - public: - virtual int interpolate(const std::vector &frames, std::vector &interpolants) = 0; - virtual ~iz3secondary(){} - - protected: - iz3secondary(const iz3mgr &mgr) : iz3mgr(mgr) {} -}; - - - -#endif diff --git a/src/interp/iz3translate.cpp b/src/interp/iz3translate.cpp deleted file mode 100755 index ce2249a88..000000000 --- a/src/interp/iz3translate.cpp +++ /dev/null @@ -1,2194 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3translate.cpp - - Abstract: - - Translate a Z3 proof to in interpolated proof. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "interp/iz3translate.h" -#include "interp/iz3proof.h" -#include "interp/iz3profiling.h" -#include "interp/iz3interp.h" -#include "interp/iz3proof_itp.h" -#include "ast/ast_pp.h" - -#include -#include -#include -#include -#include -#include -#include - -//using std::vector; -using namespace stl_ext; - - - -/* This translator goes directly from Z3 proofs to interpolated - proofs without an intermediate representation. No secondary - prover is used. -*/ - -class iz3translation_full : public iz3translation { -public: - - - typedef iz3proof_itp Iproof; - - Iproof *iproof; - - /* Here we have lots of hash tables for memoizing various methods and - other such global data structures. - */ - - typedef hash_map AstToInt; - AstToInt locality; // memoizes locality of Z3 proof terms - - typedef std::pair EquivEntry; - typedef hash_map EquivTab; - EquivTab equivs; // maps non-local terms to equivalent local terms, with proof - - typedef hash_set AstHashSet; - AstHashSet equivs_visited; // proofs already checked for equivalences - - typedef std::pair, hash_map > AstToIpf; - AstToIpf translation; // Z3 proof nodes to Iproof nodes - - int frames; // number of frames - - typedef std::set AstSet; - typedef hash_map AstToAstSet; - AstToAstSet hyp_map; // map proof terms to hypothesis set - - struct LocVar { // localization vars - ast var; // a fresh variable - ast term; // term it represents - int frame; // frame in which it's defined - LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} - }; - - std::vector localization_vars; // localization vars in order of creation - typedef hash_map AstToAst; - AstToAst localization_map; // maps terms to their localization vars - - typedef hash_map AstToBool; - AstToBool occurs_in_memo; // memo of occurs_in function - - AstHashSet cont_eq_memo; // memo of cont_eq function - - AstToAst subst_memo; // memo of subst function - - symb commute; - -public: - - -#define from_ast(x) (x) - - // #define NEW_LOCALITY - -#ifdef NEW_LOCALITY - range rng; // the range of frames in the "A" part of the interpolant -#endif - - /* To handle skolemization, we have to scan the proof for skolem - symbols and assign each to a frame. THe assignment is heuristic. - */ - - int scan_skolems_rec(hash_map &memo, const ast &proof, int frame){ - std::pair foo(proof,INT_MAX); - std::pair bar = memo.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - pfrule dk = pr(proof); - if(dk == PR_ASSERTED){ - ast ass = conc(proof); - res = frame_of_assertion(ass); - } - else if(dk == PR_SKOLEMIZE){ - ast quanted = arg(conc(proof),0); - if(op(quanted) == Not) - quanted = arg(quanted,0); - // range r = ast_range(quanted); - // if(range_is_empty(r)) - range r = ast_scope(quanted); - if(range_is_empty(r)) - throw iz3_exception("can't skolemize"); - if(frame == INT_MAX || !in_range(frame,r)) - frame = range_max(r); // this is desperation -- may fail - if(frame >= frames) frame = frames - 1; - add_frame_range(frame,arg(conc(proof),1)); - r = ast_scope(arg(conc(proof),1)); - } - else if(dk==PR_MODUS_PONENS_OEQ){ - frame = scan_skolems_rec(memo,prem(proof,0),frame); - scan_skolems_rec(memo,prem(proof,1),frame); - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - int bar = scan_skolems_rec(memo,prem(proof,i),frame); - if(res == INT_MAX || res == bar) res = bar; - else if(bar != INT_MAX) res = -1; - } - } - return res; - } - - void scan_skolems(const ast &proof) { - hash_map memo; - scan_skolems_rec(memo,proof, INT_MAX); - } - - // determine locality of a proof term - // return frame of derivation if local, or -1 if not - // result INT_MAX means the proof term is a tautology - // memoized in hash_map "locality" - - int get_locality_rec(ast proof){ - std::pair foo(proof,INT_MAX); - std::pair bar = locality.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - pfrule dk = pr(proof); - if(dk == PR_ASSERTED){ - ast ass = conc(proof); - res = frame_of_assertion(ass); -#ifdef NEW_LOCALITY - if(in_range(res,rng)) - res = range_max(rng); - else - res = frames-1; -#endif - } - else if(dk == PR_QUANT_INST){ - std::vector lits; - ast con = conc(proof); - get_Z3_lits(con, lits); - iproof->make_axiom(lits); - } -#ifdef LOCALIZATION_KLUDGE - else if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST - && get_locality_rec(prem(proof,1)) == INT_MAX){ - std::vector lits; - ast con = conc(proof); - get_Z3_lits(con, lits); - iproof->make_axiom(lits); - } -#endif - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - int bar = get_locality_rec(arg); - if(res == INT_MAX || res == bar) res = bar; - else if(bar != INT_MAX) res = -1; - } - } - return res; - } - - - int get_locality(ast proof){ - // if(lia_z3_axioms_only) return -1; - int res = get_locality_rec(proof); - if(res != -1){ - ast con = conc(proof); - range rng = ast_scope(con); - - // hack: if a clause contains "true", it reduces to "true", - // which means we won't compute the range correctly. we handle - // this case by computing the ranges of the literals separately - - if(is_true(con)){ - std::vector lits; - get_Z3_lits(conc(proof),lits); - for(unsigned i = 0; i < lits.size(); i++) - rng = range_glb(rng,ast_scope(lits[i])); - } - - if(!range_is_empty(rng)){ - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - ast hyp = *it; - rng = range_glb(rng,ast_scope(hyp)); - } - } - - // if(!range_is_empty(rng)){ - // if (num_free_variables(con) > 0) - // rng = range_empty(); - // } - - if(res == INT_MAX){ - if(range_is_empty(rng)) - res = -1; - else res = range_max(rng); - } - else { - if(!in_range(res,rng)) - res = -1; - } - } - return res; - } - - - AstSet &get_hyps(ast proof){ - std::pair foo(proof,AstSet()); - std::pair bar = hyp_map.insert(foo); - AstSet &res = bar.first->second; - if(!bar.second) return res; - pfrule dk = pr(proof); - if(dk == PR_HYPOTHESIS){ - ast con = conc(proof); - res.insert(con); - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - AstSet &arg_hyps = get_hyps(arg); - res.insert(arg_hyps.begin(),arg_hyps.end()); - } - if(dk == PR_LEMMA){ - ast con = conc(proof); - res.erase(mk_not(con)); - if(is_or(con)){ - int clause_size = num_args(con); - for(int i = 0; i < clause_size; i++){ - ast neglit = mk_not(arg(con,i)); - res.erase(neglit); - } - } - } - } -#if 0 - AstSet::iterator it = res.begin(), en = res.end(); - if(it != en){ - AstSet::iterator old = it; - ++it; - for(; it != en; ++it, ++old) - if(!(*old < *it)) - std::cout << "foo!"; - } -#endif - return res; - } - - // Find all the judgements of the form p <-> q, where - // p is local and q is non-local, recording them in "equivs" - // the map equivs_visited is used to record the already visited proof terms - - void find_equivs(ast proof){ - if(equivs_visited.find(proof) != equivs_visited.end()) - return; - equivs_visited.insert(proof); - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++) // do all the sub_terms - find_equivs(prem(proof,i)); - ast con = conc(proof); // get the conclusion - if(is_iff(con)){ - ast iff = con; - for(int i = 0; i < 2; i++) - if(!is_local(arg(iff,i)) && is_local(arg(iff,1-i))){ - std::pair > foo(arg(iff,i),std::pair(arg(iff,1-i),proof)); - equivs.insert(foo); - } - } - } - - // get the lits of a Z3 clause - void get_Z3_lits(ast t, std::vector &lits){ - opr dk = op(t); - if(dk == False) - return; // false = empty clause - if(dk == Or){ - unsigned nargs = num_args(t); - lits.resize(nargs); - for(unsigned i = 0; i < nargs; i++) // do all the sub_terms - lits[i] = arg(t,i); - } - else { - lits.push_back(t); - } - } - - // resolve two clauses represented as vectors of lits. replace first clause - void resolve(ast pivot, std::vector &cls1, std::vector &cls2){ - ast neg_pivot = mk_not(pivot); - for(unsigned i = 0; i < cls1.size(); i++){ - if(cls1[i] == pivot){ - cls1[i] = cls1.back(); - cls1.pop_back(); - bool found_pivot2 = false; - for(unsigned j = 0; j < cls2.size(); j++){ - if(cls2[j] == neg_pivot) - found_pivot2 = true; - else - cls1.push_back(cls2[j]); - } - (void)found_pivot2; - assert(found_pivot2); - return; - } - } - assert(0 && "resolve failed"); - } - - // get lits resulting from unit resolution up to and including "position" - // TODO: this is quadratic -- fix it - void do_unit_resolution(ast proof, int position, std::vector &lits){ - ast orig_clause = conc(prem(proof,0)); - get_Z3_lits(orig_clause,lits); - for(int i = 1; i <= position; i++){ - std::vector unit(1); - unit[0] = conc(prem(proof,i)); - resolve(mk_not(unit[0]),lits,unit); - } - } - -#if 0 - // clear the localization variables - void clear_localization(){ - localization_vars.clear(); - localization_map.clear(); - } - - // create a fresh variable for localization - ast fresh_localization_var(ast term, int frame){ - std::ostringstream s; - s << "%" << (localization_vars.size()); - ast var = make_var(s.str().c_str(),get_type(term)); - sym_range(sym(var)) = range_full(); // make this variable global - localization_vars.push_back(LocVar(var,term,frame)); - return var; - } - - - // "localize" a term to a given frame range by - // creating new symbols to represent non-local subterms - - ast localize_term(ast e, const range &rng){ - if(ranges_intersect(ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - AstToAst::iterator it = localization_map.find(e); - if(it != localization_map.end()) - return it->second; - - // if is is non-local, we must first localize the arguments to - // the range of its function symbol - - int nargs = num_args(e); - if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ - range frng = rng; - if(op(e) == Uninterpreted){ - symb f = sym(e); - range srng = sym_range(f); - if(ranges_intersect(srng,rng)) // localize to desired range if possible - frng = range_glb(srng,rng); - } - std::vector largs(nargs); - for(int i = 0; i < nargs; i++){ - largs[i] = localize_term(arg(e,i),frng); - frng = range_glb(frng,ast_scope(largs[i])); - } - e = clone(e,largs); - assert(is_local(e)); - } - - - if(ranges_intersect(ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - - // choose a frame for the constraint that is close to range - int frame = range_near(ast_scope(e),rng); - - ast new_var = fresh_localization_var(e,frame); - localization_map[e] = new_var; - ast cnst = make(Equal,new_var,e); - // antes.push_back(std::pair(cnst,frame)); - return new_var; - } - - // some patterm matching functions - - // match logical or with nargs arguments - // assumes AIG form - bool match_or(ast e, ast *args, int nargs){ - if(op(e) != Or) return false; - int n = num_args(e); - if(n != nargs) return false; - for(int i = 0; i < nargs; i++) - args[i] = arg(e,i); - return true; - } - - // match operator f with exactly nargs arguments - bool match_op(ast e, opr f, ast *args, int nargs){ - if(op(e) != f) return false; - int n = num_args(e); - if(n != nargs) return false; - for(int i = 0; i < nargs; i++) - args[i] = arg(e,i); - return true; - } - - // see if the given formula can be interpreted as - // an axiom instance (e.g., an array axiom instance). - // if so, add it to "antes" in an appropriate frame. - // this may require "localization" - - void get_axiom_instance(ast e){ - - // "store" axiom - // (or (= w q) (= (select (store a1 w y) q) (select a1 q))) - // std::cout << "ax: "; show(e); - ast lits[2],eq_ops_l[2],eq_ops_r[2],sel_ops[2], sto_ops[3], sel_ops2[2] ; - if(match_or(e,lits,2)) - if(match_op(lits[0],Equal,eq_ops_l,2)) - if(match_op(lits[1],Equal,eq_ops_r,2)) - for(int i = 0; i < 2; i++){ // try the second equality both ways - if(match_op(eq_ops_r[0],Select,sel_ops,2)) - if(match_op(sel_ops[0],Store,sto_ops,3)) - if(match_op(eq_ops_r[1],Select,sel_ops2,2)) - for(int j = 0; j < 2; j++){ // try the first equality both ways - if(eq_ops_l[0] == sto_ops[1] - && eq_ops_l[1] == sel_ops[1] - && eq_ops_l[1] == sel_ops2[1] - && sto_ops[0] == sel_ops2[0]) - if(is_local(sel_ops[0])) // store term must be local - { - ast sto = sel_ops[0]; - ast addr = localize_term(eq_ops_l[1],ast_scope(sto)); - ast res = make(Or, - make(Equal,eq_ops_l[0],addr), - make(Equal, - make(Select,sto,addr), - make(Select,sel_ops2[0],addr))); - // int frame = range_min(ast_scope(res)); TODO - // antes.push_back(std::pair(res,frame)); - return; - } - std::swap(eq_ops_l[0],eq_ops_l[1]); - } - std::swap(eq_ops_r[0],eq_ops_r[1]); - } - } - - // a quantifier instantation looks like (~ forall x. P) \/ P[z/x] - // we need to find a time frame for P, then localize P[z/x] in this frame - - void get_quantifier_instance(ast e){ - ast disjs[2]; - if(match_or(e,disjs,2)){ - if(is_local(disjs[0])){ - ast res = localize_term(disjs[1], ast_scope(disjs[0])); - // int frame = range_min(ast_scope(res)); TODO - // antes.push_back(std::pair(res,frame)); - return; - } - } - } - - ast get_judgement(ast proof){ - ast con = from_ast(conc(proof)); - AstSet &hyps = get_hyps(proof); - std::vector hyps_vec; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - hyps_vec.push_back(*it); - if(hyps_vec.size() == 0) return con; - con = make(Or,mk_not(make(And,hyps_vec)),con); - return con; - } - - // does variable occur in expression? - int occurs_in1(ast var, ast e){ - std::pair foo(e,false); - std::pair bar = occurs_in_memo.insert(foo); - bool &res = bar.first->second; - if(bar.second){ - if(e == var) res = true; - int nargs = num_args(e); - for(int i = 0; i < nargs; i++) - res |= occurs_in1(var,arg(e,i)); - } - return res; - } - - int occurs_in(ast var, ast e){ - occurs_in_memo.clear(); - return occurs_in1(var,e); - } - - // find a controlling equality for a given variable v in a term - // a controlling equality is of the form v = t, which, being - // false would force the formula to have the specifid truth value - // returns t, or null if no such - - ast cont_eq(bool truth, ast v, ast e){ - if(is_not(e)) return cont_eq(!truth,v,arg(e,0)); - if(cont_eq_memo.find(e) != cont_eq_memo.end()) - return ast(); - cont_eq_memo.insert(e); - if(!truth && op(e) == Equal){ - if(arg(e,0) == v) return(arg(e,1)); - if(arg(e,1) == v) return(arg(e,0)); - } - if((!truth && op(e) == And) || (truth && op(e) == Or)){ - int nargs = num_args(e); - for(int i = 0; i < nargs; i++){ - ast res = cont_eq(truth, v, arg(e,i)); - if(!res.null()) return res; - } - } - return ast(); - } - - // substitute a term t for unbound occurrences of variable v in e - - ast subst(ast var, ast t, ast e){ - if(e == var) return t; - std::pair foo(e,ast()); - std::pair bar = subst_memo.insert(foo); - ast &res = bar.first->second; - if(bar.second){ - int nargs = num_args(e); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = subst(var,t,arg(e,i)); - opr f = op(e); - if(f == Equal && args[0] == args[1]) res = mk_true(); - else res = clone(e,args); - } - return res; - } - - // apply a quantifier to a formula, with some optimizations - // 1) bound variable does not occur -> no quantifier - // 2) bound variable must be equal to some term -> substitute - - ast apply_quant(opr quantifier, ast var, ast e){ - if(!occurs_in(var,e))return e; - cont_eq_memo.clear(); - ast cterm = cont_eq(quantifier == Forall, var, e); - if(!cterm.null()){ - subst_memo.clear(); - return subst(var,cterm,e); - } - std::vector bvs; bvs.push_back(var); - return make_quant(quantifier,bvs,e); - } - - // add quantifiers over the localization vars - // to an interpolant for frames lo-hi - - ast add_quants(ast e, int lo, int hi){ - for(int i = localization_vars.size() - 1; i >= 0; i--){ - LocVar &lv = localization_vars[i]; - opr quantifier = (lv.frame >= lo && lv.frame <= hi) ? Exists : Forall; - e = apply_quant(quantifier,lv.var,e); - } - return e; - } - - int get_lits_locality(std::vector &lits){ - range rng = range_full(); - for(std::vector::iterator it = lits.begin(), en = lits.end(); it != en; ++it){ - ast lit = *it; - rng = range_glb(rng,ast_scope(lit)); - } - if(range_is_empty(rng)) return -1; - int hi = range_max(rng); - if(hi >= frames) return frames - 1; - return hi; - } -#endif - - int num_lits(ast ast){ - opr dk = op(ast); - if(dk == False) - return 0; - if(dk == Or){ - unsigned nargs = num_args(ast); - int n = 0; - for(unsigned i = 0; i < nargs; i++) // do all the sub_terms - n += num_lits(arg(ast,i)); - return n; - } - else - return 1; - } - - void symbols_out_of_scope_rec(hash_set &memo, hash_set &symb_memo, int frame, const ast &t){ - if(memo.find(t) != memo.end()) - return; - memo.insert(t); - if(op(t) == Uninterpreted){ - symb s = sym(t); - range r = sym_range(s); - if(!in_range(frame,r) && symb_memo.find(s) == symb_memo.end()){ - std::cout << string_of_symbol(s) << "\n"; - symb_memo.insert(s); - } - } - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - symbols_out_of_scope_rec(memo,symb_memo,frame,arg(t,i)); - } - - void symbols_out_of_scope(int frame, const ast &t){ - hash_set memo; - hash_set symb_memo; - symbols_out_of_scope_rec(memo,symb_memo,frame,t); - } - - void conc_symbols_out_of_scope(int frame, const ast &t){ - symbols_out_of_scope(frame,conc(t)); - } - - std::vector lit_trace; - hash_set marked_proofs; - - bool proof_has_lit(const ast &proof, const ast &lit){ - AstSet &hyps = get_hyps(proof); - if(hyps.find(mk_not(lit)) != hyps.end()) - return true; - std::vector lits; - ast con = conc(proof); - get_Z3_lits(con, lits); - for(unsigned i = 0; i < lits.size(); i++) - if(lits[i] == lit) - return true; - return false; - } - - - void trace_lit_rec(const ast &lit, const ast &proof, AstHashSet &memo){ - if(memo.find(proof) == memo.end()){ - memo.insert(proof); - AstSet &hyps = get_hyps(proof); - std::vector lits; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - lits.push_back(mk_not(*it)); - ast con = conc(proof); - get_Z3_lits(con, lits); - for(unsigned i = 0; i < lits.size(); i++){ - if(lits[i] == lit){ - print_expr(std::cout,proof); - std::cout << "\n"; - marked_proofs.insert(proof); - pfrule dk = pr(proof); - if(dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA){ - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - trace_lit_rec(lit,arg,memo); - } - } - else - lit_trace.push_back(proof); - } - } - } - } - - ast traced_lit; - - int trace_lit(const ast &lit, const ast &proof){ - marked_proofs.clear(); - lit_trace.clear(); - traced_lit = lit; - AstHashSet memo; - trace_lit_rec(lit,proof,memo); - return lit_trace.size(); - } - - bool is_literal_or_lit_iff(const ast &lit){ - if(my_is_literal(lit)) return true; - if(op(lit) == Iff){ - return my_is_literal(arg(lit,0)) && my_is_literal(arg(lit,1)); - } - return false; - } - - bool my_is_literal(const ast &lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - int f = op(abslit); - return !(f == And || f == Or || f == Iff); - } - - hash_map asts_by_id; - - void print_lit(const ast &lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - if(!is_literal_or_lit_iff(lit)){ - if(is_not(lit)) std::cout << "~"; - int id = ast_id(abslit); - asts_by_id[id] = abslit; - std::cout << "[" << id << "]"; - } - else - print_expr(std::cout,lit); - } - - void expand(int id){ - if(asts_by_id.find(id) == asts_by_id.end()) - std::cout << "undefined\n"; - else { - ast lit = asts_by_id[id]; - std::string s = string_of_symbol(sym(lit)); - std::cout << "(" << s; - unsigned nargs = num_args(lit); - for(unsigned i = 0; i < nargs; i++){ - std::cout << " "; - print_lit(arg(lit,i)); - } - std::cout << ")\n";; - } - } - - void show_lit(const ast &lit){ - print_lit(lit); - std::cout << "\n"; - } - - void print_z3_lit(const ast &a){ - print_lit(from_ast(a)); - } - - void show_z3_lit(const ast &a){ - print_z3_lit(a); - std::cout << "\n"; - } - - - void show_con(const ast &proof, bool brief){ - if(!traced_lit.null() && proof_has_lit(proof,traced_lit)) - std::cout << "(*) "; - ast con = conc(proof); - AstSet &hyps = get_hyps(proof); - int count = 0; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - if(brief && ++count > 5){ - std::cout << "... "; - break; - } - print_lit(*it); - std::cout << " "; - } - std::cout << "|- "; - std::vector lits; - get_Z3_lits(con,lits); - for(unsigned i = 0; i < lits.size(); i++){ - print_lit(lits[i]); - std::cout << " "; - } - range r = ast_scope(con); - std::cout << " {" << r.lo << "," << r.hi << "}"; - std::cout << "\n"; - } - - void show_step(const ast &proof){ - std::cout << "\n"; - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - std::cout << "(" << i << ") "; - ast arg = prem(proof,i); - show_con(arg,true); - } - std::cout << "|------ "; - std::cout << string_of_symbol(sym(proof)) << "\n"; - show_con(proof,false); - } - - void show_marked( const ast &proof){ - std::cout << "\n"; - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - if(!traced_lit.null() && proof_has_lit(arg,traced_lit)){ - std::cout << "(" << i << ") "; - show_con(arg,true); - } - } - } - - std::vector pfhist; - int pfhist_pos; - - void pfgoto(const ast &proof){ - if(pfhist.size() == 0) - pfhist_pos = 0; - else pfhist_pos++; - pfhist.resize(pfhist_pos); - pfhist.push_back(proof); - show_step(proof); - } - - - void pfback(){ - if(pfhist_pos > 0){ - pfhist_pos--; - show_step(pfhist[pfhist_pos]); - } - } - - void pffwd(){ - if(pfhist_pos < ((int)pfhist.size()) - 1){ - pfhist_pos++; - show_step(pfhist[pfhist_pos]); - } - } - - void pfprem(int i){ - if(pfhist.size() > 0){ - ast proof = pfhist[pfhist_pos]; - unsigned nprems = num_prems(proof); - if(i >= 0 && i < (int)nprems) - pfgoto(prem(proof,i)); - } - } - - - - // translate a unit resolution sequence - Iproof::node translate_ur(ast proof){ - ast prem0 = prem(proof,0); - Iproof::node itp = translate_main(prem0,true); - std::vector clause; - ast conc0 = conc(prem0); - int nprems = num_prems(proof); - if(nprems == 2 && conc0 == mk_not(conc(prem(proof,1)))) - clause.push_back(conc0); - else - get_Z3_lits(conc0,clause); - for(int position = 1; position < nprems; position++){ - ast ante = prem(proof,position); - ast pnode = conc(ante); - ast pnode_abs = !is_not(pnode) ? pnode : mk_not(pnode); - Iproof::node neg = itp; - Iproof::node pos = translate_main(ante, false); - if(is_not(pnode)){ - pnode = mk_not(pnode); - std::swap(neg,pos); - } - std::vector unit(1); - unit[0] = conc(ante); - resolve(mk_not(conc(ante)),clause,unit); - itp = iproof->make_resolution(pnode,clause,neg,pos); - } - return itp; - } - - // get an inequality in the form 0 <= t where t is a linear term - ast rhs_normalize_inequality(const ast &ineq){ - ast zero = make_int("0"); - ast thing = make(Leq,zero,zero); - linear_comb(thing,make_int("1"),ineq); - thing = simplify_ineq(thing); - return thing; - } - - bool check_farkas(const std::vector &prems, const ast &con){ - ast zero = make_int("0"); - ast thing = make(Leq,zero,zero); - for(unsigned i = 0; i < prems.size(); i++) - linear_comb(thing,make_int(rational(1)),prems[i]); - linear_comb(thing,make_int(rational(-1)),con); - thing = simplify_ineq(thing); - return arg(thing,1) == make_int(rational(0)); - } - - // get an inequality in the form t <= c or t < c, there t is affine and c constant - ast normalize_inequality(const ast &ineq){ - ast zero = make_int("0"); - ast thing = make(Leq,zero,zero); - linear_comb(thing,make_int("1"),ineq); - thing = simplify_ineq(thing); - ast lhs = arg(thing,0); - ast rhs = arg(thing,1); - opr o = op(rhs); - if(o != Numeral){ - if(op(rhs) == Plus){ - int nargs = num_args(rhs); - ast const_term = zero; - int i = 0; - if(nargs > 0 && op(arg(rhs,0)) == Numeral){ - const_term = arg(rhs,0); - i++; - } - if(i < nargs){ - std::vector non_const; - for(; i < nargs; i++) - non_const.push_back(arg(rhs,i)); - lhs = make(Sub,lhs,make(Plus,non_const)); - } - rhs = const_term; - } - else { - lhs = make(Sub,lhs,make(Plus,rhs)); - rhs = zero; - } - lhs = z3_simplify(lhs); - rhs = z3_simplify(rhs); - thing = make(op(thing),lhs,rhs); - } - return thing; - } - - void get_linear_coefficients(const ast &t, std::vector &coeffs){ - if(op(t) == Plus){ - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - coeffs.push_back(get_coeff(arg(t,i))); - } - else - coeffs.push_back(get_coeff(t)); - } - - /* given an affine term t, get the GCD of the coefficients in t. */ - ast gcd_of_coefficients(const ast &t){ - std::vector coeffs; - get_linear_coefficients(t,coeffs); - if(coeffs.size() == 0) - return make_int("1"); // arbitrary - rational d = abs(coeffs[0]); - for(unsigned i = 1; i < coeffs.size(); i++){ - d = gcd(d,coeffs[i]); - } - return make_int(d); - } - - ast get_bounded_variable(const ast &ineq, bool &lb){ - ast nineq = normalize_inequality(ineq); - ast lhs = arg(nineq,0); - switch(op(lhs)){ - case Uninterpreted: - lb = false; - return lhs; - case Times: - if(arg(lhs,0) == make_int(rational(1))) - lb = false; - else if(arg(lhs,0) == make_int(rational(-1))) - lb = true; - else - throw unsupported(); - return arg(lhs,1); - default: - throw unsupported(); - } - } - - rational get_term_coefficient(const ast &t1, const ast &v){ - ast t = arg(normalize_inequality(t1),0); - if(op(t) == Plus){ - int nargs = num_args(t); - for(int i = 0; i < nargs; i++){ - if(get_linear_var(arg(t,i)) == v) - return get_coeff(arg(t,i)); - } - } - else - if(get_linear_var(t) == v) - return get_coeff(t); - return rational(0); - } - - - Iproof::node GCDtoDivRule(const ast &proof, bool pol, std::vector &coeffs, std::vector &prems, ast &cut_con){ - // gather the summands of the desired polarity - std::vector my_prems; - std::vector my_coeffs; - std::vector my_prem_cons; - for(unsigned i = pol ? 0 : 1; i < coeffs.size(); i+= 2){ - rational &c = coeffs[i]; - if(c.is_pos()){ - my_prems.push_back(prems[i]); - my_coeffs.push_back(make_int(c)); - my_prem_cons.push_back(conc(prem(proof,i))); - } - else if(c.is_neg()){ - int j = (i % 2 == 0) ? i + 1 : i - 1; - my_prems.push_back(prems[j]); - my_coeffs.push_back(make_int(-coeffs[j])); - my_prem_cons.push_back(conc(prem(proof,j))); - } - } - ast my_con = sum_inequalities(my_coeffs,my_prem_cons); - - // handle generalized GCD test. sadly, we dont' get the coefficients... - if(coeffs[0].is_zero()){ - bool lb; - int xtra_prem = 0; - ast bv = get_bounded_variable(conc(prem(proof,0)),lb); - rational bv_coeff = get_term_coefficient(my_con,bv); - if(bv_coeff.is_pos() != lb) - xtra_prem = 1; - if(bv_coeff.is_neg()) - bv_coeff = -bv_coeff; - - my_prems.push_back(prems[xtra_prem]); - my_coeffs.push_back(make_int(bv_coeff)); - my_prem_cons.push_back(conc(prem(proof,xtra_prem))); - my_con = sum_inequalities(my_coeffs,my_prem_cons); - } - - my_con = normalize_inequality(my_con); - Iproof::node hyp = iproof->make_hypothesis(mk_not(my_con)); - my_prems.push_back(hyp); - my_coeffs.push_back(make_int("1")); - my_prem_cons.push_back(mk_not(my_con)); - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_prem_cons,my_coeffs); - - ast t = arg(my_con,0); - ast c = arg(my_con,1); - ast d = gcd_of_coefficients(t); - t = z3_simplify(mk_idiv(t,d)); - c = z3_simplify(mk_idiv(c,d)); - cut_con = make(op(my_con),t,c); - return iproof->make_cut_rule(my_con,d,cut_con,res); - } - - - rational get_first_coefficient(const ast &t, ast &v){ - if(op(t) == Plus){ - unsigned best_id = UINT_MAX; - rational best_coeff(0); - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - if(op(arg(t,i)) != Numeral){ - ast lv = get_linear_var(arg(t,i)); - unsigned id = ast_id(lv); - if(id < best_id) { - v = lv; - best_id = id; - best_coeff = get_coeff(arg(t,i)); - } - } - return best_coeff; - } - else - if(op(t) != Numeral){ - v = get_linear_var(t); - return(get_coeff(t)); - } - return rational(0); - } - - ast divide_inequalities(const ast &x, const ast&y){ - ast xvar, yvar; - rational xcoeff = get_first_coefficient(arg(x,0),xvar); - rational ycoeff = get_first_coefficient(arg(y,0),yvar); - if(xcoeff == rational(0) || ycoeff == rational(0) || xvar != yvar) - throw unsupported(); // can be caused by non-linear arithmetic - rational ratio = xcoeff/ycoeff; - if(denominator(ratio) != rational(1)) - throw unsupported(); // can this ever happen? - return make_int(ratio); // better be integer! - } - - ast AssignBounds2Farkas(const ast &proof, const ast &con){ - std::vector farkas_coeffs; - get_assign_bounds_coeffs(proof,farkas_coeffs); - int nargs = num_args(con); - if(nargs != (int)(farkas_coeffs.size())) - throw unsupported(); // should never happen -#if 0 - if(farkas_coeffs[0] != make_int(rational(1))) - farkas_coeffs[0] = make_int(rational(1)); -#else - std::vector lits, lit_coeffs; - for(int i = 1; i < nargs; i++){ - lits.push_back(mk_not(arg(con,i))); - lit_coeffs.push_back(farkas_coeffs[i]); - } - ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); - ast conseq = normalize_inequality(arg(con,0)); - ast d = divide_inequalities(sum,conseq); -#if 0 - if(d != farkas_coeffs[0]) - std::cout << "wow!\n"; -#endif - farkas_coeffs[0] = d; -#endif - std::vector my_coeffs; - std::vector my_cons; - for(int i = 1; i < nargs; i++){ - my_cons.push_back(mk_not(arg(con,i))); - my_coeffs.push_back(farkas_coeffs[i]); - } - ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons,true /* round_off */)); - my_cons.push_back(mk_not(farkas_con)); - my_coeffs.push_back(make_int("1")); - std::vector my_hyps; - for(int i = 0; i < nargs; i++) - my_hyps.push_back(iproof->make_hypothesis(my_cons[i])); - ast res = iproof->make_farkas(mk_false(),my_hyps,my_cons,my_coeffs); - res = iproof->make_cut_rule(farkas_con,farkas_coeffs[0],arg(con,0),res); - return res; - } - - ast AssignBoundsRule2Farkas(const ast &proof, const ast &con, std::vector prems){ - std::vector farkas_coeffs; - get_assign_bounds_rule_coeffs(proof,farkas_coeffs); - int nargs = num_prems(proof)+1; - if(nargs != (int)(farkas_coeffs.size())) - throw iz3_exception("bad assign-bounds theory lemma"); -#if 0 - if(farkas_coeffs[0] != make_int(rational(1))) - farkas_coeffs[0] = make_int(rational(1)); -#else - std::vector lits, lit_coeffs; - for(int i = 1; i < nargs; i++){ - lits.push_back(conc(prem(proof,i-1))); - lit_coeffs.push_back(farkas_coeffs[i]); - } - ast sum = normalize_inequality(sum_inequalities(lit_coeffs,lits)); - ast conseq = normalize_inequality(con); - ast d = divide_inequalities(sum,conseq); -#if 0 - if(d != farkas_coeffs[0]) - std::cout << "wow!\n"; -#endif - farkas_coeffs[0] = d; -#endif - std::vector my_coeffs; - std::vector my_cons; - for(int i = 1; i < nargs; i++){ - my_cons.push_back(conc(prem(proof,i-1))); - my_coeffs.push_back(farkas_coeffs[i]); - } - ast farkas_con = normalize_inequality(sum_inequalities(my_coeffs,my_cons,true /* round_off */)); - std::vector my_hyps; - for(int i = 1; i < nargs; i++) - my_hyps.push_back(prems[i-1]); - my_cons.push_back(mk_not(farkas_con)); - my_coeffs.push_back(make_int("1")); - my_hyps.push_back(iproof->make_hypothesis(mk_not(farkas_con))); - ast res = iproof->make_farkas(mk_false(),my_hyps,my_cons,my_coeffs); - res = iproof->make_cut_rule(farkas_con,farkas_coeffs[0],conc(proof),res); - return res; - } - - ast GomoryCutRule2Farkas(const ast &proof, const ast &con, std::vector prems){ - std::vector my_prems = prems; - std::vector my_coeffs; - std::vector my_prem_cons; - get_gomory_cut_coeffs(proof,my_coeffs); - int nargs = num_prems(proof); - if(nargs != (int)(my_coeffs.size())) - throw "bad gomory-cut theory lemma"; - for(int i = 0; i < nargs; i++) - my_prem_cons.push_back(conc(prem(proof,i))); - ast my_con = normalize_inequality(sum_inequalities(my_coeffs,my_prem_cons)); - Iproof::node hyp = iproof->make_hypothesis(mk_not(my_con)); - my_prems.push_back(hyp); - my_coeffs.push_back(make_int("1")); - my_prem_cons.push_back(mk_not(my_con)); - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_prem_cons,my_coeffs); - ast t = arg(my_con,0); - ast c = arg(my_con,1); - ast d = gcd_of_coefficients(t); - /* - t = z3_simplify(mk_idiv(t,d)); - c = z3_simplify(mk_idiv(c,d)); - ast cut_con = make(op(my_con),t,c); - */ - ast cut_con = con; - return iproof->make_cut_rule(my_con,d,cut_con,res); - } - - Iproof::node RewriteClause(Iproof::node clause, const ast &rew){ - if(pr(rew) == PR_MONOTONICITY){ - int nequivs = num_prems(rew); - for(int i = 0; i < nequivs; i++){ - Iproof::node equiv_pf = translate_main(prem(rew,i),false); - ast equiv = conc(prem(rew,i)); - clause = iproof->make_mp(equiv,clause,equiv_pf); - } - return clause; - } - if(pr(rew) == PR_TRANSITIVITY){ - clause = RewriteClause(clause,prem(rew,0)); - clause = RewriteClause(clause,prem(rew,1)); - return clause; - } - if(pr(rew) == PR_REWRITE){ - return clause; // just hope the rewrite does nothing! - } - throw unsupported(); - } - - - // Following code is for elimination of "commutativity" axiom - - Iproof::node make_commuted_modus_ponens(const ast &proof, const std::vector &args){ - ast pf = arg(args[1],0); - ast comm_equiv = arg(args[1],1); // equivalence relation with possible commutations - ast P = conc(prem(proof,0)); - ast Q = conc(proof); - Iproof::node P_pf = args[0]; - ast P_comm = arg(comm_equiv,0); - ast Q_comm = arg(comm_equiv,1); - if(P != P_comm) - P_pf = iproof->make_symmetry(P_comm,P,P_pf); - Iproof::node res = iproof->make_mp(comm_equiv,P_pf,pf); - if(Q != Q_comm) - res = iproof->make_symmetry(Q,Q_comm,res); - return res; - } - - Iproof::node make_commuted_monotonicity(const ast &proof, const std::vector &args){ - ast pf = arg(args[0],0); - ast comm_equiv = arg(args[0],1); // equivalence relation with possible commutations - ast con = make(Iff,make(Not,arg(comm_equiv,0)),make(Not,arg(comm_equiv,1))); - std::vector eqs; eqs.push_back(comm_equiv); - std::vector pfs; pfs.push_back(pf); - ast res = iproof->make_congruence(eqs,con,pfs); - res = make(commute,res,con); - return res; - } - - Iproof::node make_commuted_symmetry(const ast &proof, const std::vector &args){ - ast pf = arg(args[0],0); - ast comm_equiv = arg(args[0],1); // equivalence relation with possible commutations - ast con = make(Iff,arg(comm_equiv,1),arg(comm_equiv,0)); - ast res = iproof->make_symmetry(con,comm_equiv,pf); - res = make(commute,res,con); - return res; - } - - void unpack_commuted(const ast &proof, const ast &cm, ast &pf, ast &comm_equiv){ - if(sym(cm) == commute){ - pf = arg(cm,0); - comm_equiv = arg(cm,1); - } - else { - pf = cm; - comm_equiv = conc(proof); - } - } - - Iproof::node make_commuted_transitivity(const ast &proof, const std::vector &args){ - ast pf[2], comm_equiv[2]; - for(int i = 0; i < 2; i++) - unpack_commuted(prem(proof,i),args[i],pf[i],comm_equiv[i]); - if(!(arg(comm_equiv[0],1) == arg(comm_equiv[1],0))){ - ast tw = twist(prem(proof,1)); - ast np = translate_main(tw,false); - unpack_commuted(tw,np,pf[1],comm_equiv[1]); - } - ast con = make(Iff,arg(comm_equiv[0],0),arg(comm_equiv[1],1)); - ast res = iproof->make_transitivity(arg(comm_equiv[0],0),arg(comm_equiv[0],1),arg(comm_equiv[1],1),pf[0],pf[1]); - res = make(commute,res,con); - return res; - } - - ast commute_equality(const ast &eq){ - return make(Equal,arg(eq,1),arg(eq,0)); - } - - ast commute_equality_iff(const ast &con){ - if(op(con) != Iff || op(arg(con,0)) != Equal) - throw unsupported(); - return make(Iff,commute_equality(arg(con,0)),commute_equality(arg(con,1))); - } - - // convert a proof of a=b <-> c=d into a proof of b=a <-> d=c - // TODO: memoize this? - ast twist(const ast &proof){ - pfrule dk = pr(proof); - ast con = commute_equality_iff(conc(proof)); - int n = num_prems(proof); - std::vector prs(n); - if(dk == PR_MONOTONICITY){ - for(int i = 0; i < n; i++) - prs[i] = prem(proof,i); - } - else - for(int i = 0; i < n; i++) - prs[i] = twist(prem(proof,i)); - switch(dk){ - case PR_MONOTONICITY: - case PR_SYMMETRY: - case PR_TRANSITIVITY: - case PR_COMMUTATIVITY: - prs.push_back(con); - return clone(proof,prs); - default: - throw unsupported(); - } - } - - struct TermLt { - iz3mgr &m; - bool operator()(const ast &x, const ast &y){ - unsigned xid = m.ast_id(x); - unsigned yid = m.ast_id(y); - return xid < yid; - } - TermLt(iz3mgr &_m) : m(_m) {} - }; - - void SortTerms(std::vector &terms){ - TermLt foo(*this); - std::sort(terms.begin(),terms.end(),foo); - } - - ast SortSum(const ast &t){ - if(!(op(t) == Plus)) - return t; - int nargs = num_args(t); - if(nargs < 2) return t; - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = arg(t,i); - SortTerms(args); - return make(Plus,args); - } - - void get_sum_as_vector(const ast &t, std::vector &coeffs, std::vector &vars){ - if(!(op(t) == Plus)){ - coeffs.push_back(get_coeff(t)); - vars.push_back(get_linear_var(t)); - } - else { - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - get_sum_as_vector(arg(t,i),coeffs,vars); - } - } - - ast replace_summands_with_fresh_vars(const ast &t, hash_map &map){ - if(op(t) == Plus){ - int nargs = num_args(t); - std::vector args(nargs); - for(int i = 0; i < nargs; i++) - args[i] = replace_summands_with_fresh_vars(arg(t,i),map); - return make(Plus,args); - } - if(op(t) == Times) - return make(Times,arg(t,0),replace_summands_with_fresh_vars(arg(t,1),map)); - if(map.find(t) == map.end()) - map[t] = mk_fresh_constant("@s",get_type(t)); - return map[t]; - } - - rational lcd(const std::vector &rats){ - rational res = rational(1); - for(unsigned i = 0; i < rats.size(); i++){ - res = lcm(res,denominator(rats[i])); - } - return res; - } - - Iproof::node reconstruct_farkas_with_dual(const std::vector &prems, const std::vector &pfs, const ast &con){ - int nprems = prems.size(); - std::vector npcons(nprems); - hash_map pain_map; // not needed - for(int i = 0; i < nprems; i++){ - npcons[i] = painfully_normalize_ineq(conc(prems[i]),pain_map); - if(op(npcons[i]) == Lt){ - ast constval = z3_simplify(make(Sub,arg(npcons[i],1),make_int(rational(1)))); - npcons[i] = make(Leq,arg(npcons[i],0),constval); - } - } - ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); - npcons.push_back(ncon); - - hash_map dual_map; - std::vector cvec, vars_seen; - m().enable_int_real_coercions(true); - ast rhs = make_real(rational(0)); - for(unsigned i = 0; i < npcons.size(); i++){ - ast c= mk_fresh_constant("@c",real_type()); - cvec.push_back(c); - ast lhs = arg(npcons[i],0); - std::vector coeffs; - std::vector vars; - get_sum_as_vector(lhs,coeffs,vars); - for(unsigned j = 0; j < coeffs.size(); j++){ - rational coeff = coeffs[j]; - ast var = vars[j]; - if(dual_map.find(var) == dual_map.end()){ - dual_map[var] = make_real(rational(0)); - vars_seen.push_back(var); - } - ast foo = make(Plus,dual_map[var],make(Times,make_real(coeff),c)); - dual_map[var] = foo; - } - rhs = make(Plus,rhs,make(Times,c,arg(npcons[i],1))); - } - std::vector cnstrs; - for(unsigned i = 0; i < vars_seen.size(); i++) - cnstrs.push_back(make(Equal,dual_map[vars_seen[i]],make_real(rational(0)))); - cnstrs.push_back(make(Leq,rhs,make_real(rational(0)))); - for(unsigned i = 0; i < cvec.size() - 1; i++) - cnstrs.push_back(make(Geq,cvec[i],make_real(rational(0)))); - cnstrs.push_back(make(Equal,cvec.back(),make_real(rational(1)))); - ast new_proof; - - // greedily reduce the core - for(unsigned i = 0; i < cvec.size() - 1; i++){ - std::vector dummy; - cnstrs.push_back(make(Equal,cvec[i],make_real(rational(0)))); - if(!is_sat(cnstrs,new_proof,dummy)) - cnstrs.pop_back(); - } - - std::vector vals = cvec; - if(!is_sat(cnstrs,new_proof,vals)) - throw iz3_exception("Proof error!"); - std::vector rat_farkas_coeffs; - for(unsigned i = 0; i < cvec.size(); i++){ - ast bar = vals[i]; - rational r; - if(is_numeral(bar,r)) - rat_farkas_coeffs.push_back(r); - else - throw iz3_exception("Proof error!"); - } - rational the_lcd = lcd(rat_farkas_coeffs); - std::vector farkas_coeffs; - std::vector my_prems; - std::vector my_pcons; - for(unsigned i = 0; i < prems.size(); i++){ - ast fc = make_int(rat_farkas_coeffs[i] * the_lcd); - if(!(fc == make_int(rational(0)))){ - farkas_coeffs.push_back(fc); - my_prems.push_back(pfs[i]); - my_pcons.push_back(conc(prems[i])); - } - } - farkas_coeffs.push_back(make_int(the_lcd)); - my_prems.push_back(iproof->make_hypothesis(mk_not(con))); - my_pcons.push_back(mk_not(con)); - - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); - return res; - } - - ast painfully_normalize_ineq(const ast &ineq, hash_map &map){ - ast res = normalize_inequality(ineq); - ast lhs = arg(res,0); - lhs = replace_summands_with_fresh_vars(lhs,map); - res = make(op(res),SortSum(lhs),arg(res,1)); - return res; - } - - Iproof::node painfully_reconstruct_farkas(const std::vector &prems, const std::vector &pfs, const ast &con){ - int nprems = prems.size(); - std::vector pcons(nprems),npcons(nprems); - hash_map pcon_to_pf, npcon_to_pcon, pain_map; - for(int i = 0; i < nprems; i++){ - pcons[i] = conc(prems[i]); - npcons[i] = painfully_normalize_ineq(pcons[i],pain_map); - pcon_to_pf[npcons[i]] = pfs[i]; - npcon_to_pcon[npcons[i]] = pcons[i]; - } - // ast leq = make(Leq,arg(con,0),arg(con,1)); - ast ncon = painfully_normalize_ineq(mk_not(con),pain_map); - pcons.push_back(mk_not(con)); - npcons.push_back(ncon); - // ast assumps = make(And,pcons); - ast new_proof; - std::vector dummy; - if(is_sat(npcons,new_proof,dummy)) - throw iz3_exception("Proof error!"); - pfrule dk = pr(new_proof); - int nnp = num_prems(new_proof); - std::vector my_prems; - std::vector farkas_coeffs, my_pcons; - - if(dk == PR_TH_LEMMA - && get_theory_lemma_theory(new_proof) == ArithTheory - && get_theory_lemma_kind(new_proof) == FarkasKind) - get_farkas_coeffs(new_proof,farkas_coeffs); - else if(dk == PR_UNIT_RESOLUTION && nnp == 2){ - for(int i = 0; i < nprems; i++) - farkas_coeffs.push_back(make_int(rational(1))); - } - else - return reconstruct_farkas_with_dual(prems,pfs,con); - - for(int i = 0; i < nnp; i++){ - ast p = conc(prem(new_proof,i)); - p = really_normalize_ineq(p); - if(pcon_to_pf.find(p) != pcon_to_pf.end()){ - my_prems.push_back(pcon_to_pf[p]); - my_pcons.push_back(npcon_to_pcon[p]); - } - else if(p == ncon){ - my_prems.push_back(iproof->make_hypothesis(mk_not(con))); - my_pcons.push_back(mk_not(con)); - } - else - return reconstruct_farkas_with_dual(prems,pfs,con); - } - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); - return res; - } - - - - ast really_normalize_ineq(const ast &ineq){ - ast res = normalize_inequality(ineq); - res = make(op(res),SortSum(arg(res,0)),arg(res,1)); - return res; - } - - Iproof::node reconstruct_farkas(const std::vector &prems, const std::vector &pfs, const ast &con){ - int nprems = prems.size(); - std::vector pcons(nprems),npcons(nprems); - hash_map pcon_to_pf, npcon_to_pcon; - for(int i = 0; i < nprems; i++){ - pcons[i] = conc(prems[i]); - npcons[i] = really_normalize_ineq(pcons[i]); - pcon_to_pf[npcons[i]] = pfs[i]; - npcon_to_pcon[npcons[i]] = pcons[i]; - } - // ast leq = make(Leq,arg(con,0),arg(con,1)); - ast ncon = really_normalize_ineq(mk_not(con)); - pcons.push_back(mk_not(con)); - npcons.push_back(ncon); - // ast assumps = make(And,pcons); - ast new_proof; - std::vector dummy; - if(is_sat(npcons,new_proof,dummy)) - throw iz3_exception("Proof error!"); - pfrule dk = pr(new_proof); - int nnp = num_prems(new_proof); - std::vector my_prems; - std::vector farkas_coeffs, my_pcons; - - if(dk == PR_TH_LEMMA - && get_theory_lemma_theory(new_proof) == ArithTheory - && get_theory_lemma_kind(new_proof) == FarkasKind) - get_farkas_coeffs(new_proof,farkas_coeffs); - else if(dk == PR_UNIT_RESOLUTION && nnp == 2){ - for(int i = 0; i < nprems; i++) - farkas_coeffs.push_back(make_int(rational(1))); - } - else - return painfully_reconstruct_farkas(prems,pfs,con); - - for(int i = 0; i < nnp; i++){ - ast p = conc(prem(new_proof,i)); - p = really_normalize_ineq(p); - if(pcon_to_pf.find(p) != pcon_to_pf.end()){ - my_prems.push_back(pcon_to_pf[p]); - my_pcons.push_back(npcon_to_pcon[p]); - } - else if(p == ncon){ - my_prems.push_back(iproof->make_hypothesis(mk_not(con))); - my_pcons.push_back(mk_not(con)); - } - else - return painfully_reconstruct_farkas(prems,pfs,con); - } - Iproof::node res = iproof->make_farkas(mk_false(),my_prems,my_pcons,farkas_coeffs); - return res; - } - - bool is_eq_propagate(const ast &proof){ - return pr(proof) == PR_TH_LEMMA && get_theory_lemma_theory(proof) == ArithTheory && get_theory_lemma_kind(proof) == EqPropagateKind; - } - - ast EqPropagate(const ast &con, const std::vector &prems, const std::vector &args){ - Iproof::node fps[2]; - ast ineq_con[2]; - for(int i = 0; i < 2; i++){ - opr o = i == 0 ? Leq : Geq; - ineq_con[i] = make(o, arg(con,0), arg(con,1)); - fps[i] = reconstruct_farkas(prems,args,ineq_con[i]); - } - ast res = iproof->make_leq2eq(arg(con,0), arg(con,1), ineq_con[0], ineq_con[1]); - std::vector dummy_clause; - for(int i = 0; i < 2; i++) - res = iproof->make_resolution(ineq_con[i],dummy_clause,res,fps[i]); - return res; - } - - ast ArithMysteryRule(const ast &con, const std::vector &prems, const std::vector &args){ - // Hope for the best! - Iproof::node guess = reconstruct_farkas(prems,args,con); - return guess; - } - - struct CannotCombineEqPropagate {}; - - void CombineEqPropagateRec(const ast &proof, std::vector &prems, std::vector &args, ast &eqprem){ - if(pr(proof) == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ - CombineEqPropagateRec(prem(proof,0), prems, args, eqprem); - ast dummy; - CombineEqPropagateRec(prem(proof,1), prems, args, dummy); - return; - } - if(is_eq_propagate(proof)){ - int nprems = num_prems(proof); - for(int i = 0; i < nprems; i++){ - prems.push_back(prem(proof,i)); - ast ppf = translate_main(prem(proof,i),false); - args.push_back(ppf); - } - return; - } - eqprem = proof; - } - - ast CombineEqPropagate(const ast &proof){ - std::vector prems, args; - ast eq1; - CombineEqPropagateRec(proof, prems, args, eq1); - ast eq2con = conc(proof); - if(!eq1.null()) - eq2con = make(Equal,arg(conc(eq1),1),arg(conc(proof),1)); - ast eq2 = EqPropagate(eq2con,prems,args); - if(!eq1.null()){ - Iproof::node foo = translate_main(eq1,false); - eq2 = iproof->make_transitivity(arg(conc(eq1),0), arg(conc(eq1),1), arg(conc(proof),1), foo, eq2); - } - return eq2; - } - - bool get_store_array(const ast &t, ast &res){ - if(op(t) == Store){ - res = t; - return true; - } - int nargs = num_args(t); - for(int i = 0; i < nargs; i++) - if(get_store_array(arg(t,i),res)) - return true; - return false; - } - - // translate a Z3 proof term into interpolating proof system - - Iproof::node translate_main(ast proof, bool expect_clause = true){ - AstToIpf &tr = translation; - hash_map &trc = expect_clause ? tr.first : tr.second; - std::pair foo(proof,Iproof::node()); - std::pair::iterator, bool> bar = trc.insert(foo); - Iproof::node &res = bar.first->second; - if(!bar.second) return res; - - // Try the locality rule first - - int frame = get_locality(proof); - if(frame != -1){ - ast e = from_ast(conc(proof)); - if(frame >= frames) frame = frames - 1; - std::vector foo; - if(expect_clause) - get_Z3_lits(conc(proof),foo); - else - foo.push_back(e); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - foo.push_back(mk_not(*it)); - res = iproof->make_assumption(frame,foo); - return res; - } - - // If the proof is not local, break it down by proof rule - - pfrule dk = pr(proof); - unsigned nprems = num_prems(proof); - if(dk == PR_UNIT_RESOLUTION){ - res = translate_ur(proof); - } - else if(dk == PR_LEMMA){ - ast contra = prem(proof,0); // this is a proof of false from some hyps - res = translate_main(contra); - if(!expect_clause){ - std::vector foo; // the negations of the hyps form a clause - foo.push_back(from_ast(conc(proof))); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - foo.push_back(mk_not(*it)); - res = iproof->make_contra(res,foo); - } - } - else { - std::vector lits; - ast con = conc(proof); - if(expect_clause) - get_Z3_lits(con, lits); - else - lits.push_back(from_ast(con)); - - // pattern match some idioms - if(dk == PR_MODUS_PONENS && pr(prem(proof,0)) == PR_QUANT_INST){ - if(get_locality_rec(prem(proof,1)) == INT_MAX) { - res = iproof->make_axiom(lits); - return res; - } - } - if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or && op(conc(prem(proof,0))) == Or){ - Iproof::node clause = translate_main(prem(proof,0),true); - res = RewriteClause(clause,prem(proof,1)); - return res; - } - -#if 0 - if(dk == PR_MODUS_PONENS && expect_clause && op(con) == Or) - std::cout << "foo!\n"; -#endif - - // no idea why this shows up - if(dk == PR_MODUS_PONENS_OEQ){ - if(conc(prem(proof,0)) == con){ - res = translate_main(prem(proof,0),expect_clause); - return res; - } - if(expect_clause && op(con) == Or){ // skolemization does this - Iproof::node clause = translate_main(prem(proof,0),true); - res = RewriteClause(clause,prem(proof,1)); - return res; - } - } - -#if 0 - if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,1)) == PR_COMMUTATIVITY){ - Iproof::node clause = translate_main(prem(proof,0),true); - res = make(commute,clause,conc(prem(proof,0))); // HACK -- we depend on Iproof::node being same as ast. - return res; - } - - if(1 && dk == PR_TRANSITIVITY && pr(prem(proof,0)) == PR_COMMUTATIVITY){ - Iproof::node clause = translate_main(prem(proof,1),true); - res = make(commute,clause,conc(prem(proof,1))); // HACK -- we depend on Iproof::node being same as ast. - return res; - } -#endif - - if(dk == PR_TRANSITIVITY && is_eq_propagate(prem(proof,1))){ - try { - res = CombineEqPropagate(proof); - return res; - } - catch(const CannotCombineEqPropagate &){ - } - } - - /* this is the symmetry rule for ~=, that is, takes x ~= y and yields y ~= x. - the proof idiom uses commutativity, monotonicity and mp, but we replace it here - with symmtrey and resolution, that is, we prove y = x |- x = y, then resolve - with the proof of ~(x=y) to get ~y=x. */ - if(dk == PR_MODUS_PONENS && pr(prem(proof,1)) == PR_MONOTONICITY && pr(prem(prem(proof,1),0)) == PR_COMMUTATIVITY && num_prems(prem(proof,1)) == 1){ - Iproof::node ante = translate_main(prem(proof,0),false); - ast eq0 = arg(conc(prem(prem(proof,1),0)),0); - ast eq1 = arg(conc(prem(prem(proof,1),0)),1); - Iproof::node eq1hy = iproof->make_hypothesis(eq1); - Iproof::node eq0pf = iproof->make_symmetry(eq0,eq1,eq1hy); - std::vector clause; // just a dummy - res = iproof->make_resolution(eq0,clause,ante,eq0pf); - return res; - } - - /* This idiom takes ~P and Q=P, yielding ~Q. It uses a "rewrite" - (Q=false) = ~Q. We eliminate the rewrite by using symmetry, - congruence and modus ponens. */ - - if(dk == PR_MODUS_PONENS && pr(prem(proof,1)) == PR_REWRITE && pr(prem(proof,0)) == PR_TRANSITIVITY && pr(prem(prem(proof,0),1)) == PR_IFF_FALSE){ - if(op(con) == Not && arg(con,0) == arg(conc(prem(proof,0)),0)){ - Iproof::node ante1 = translate_main(prem(prem(proof,0),0),false); - Iproof::node ante2 = translate_main(prem(prem(prem(proof,0),1),0),false); - ast ante1_con = conc(prem(prem(proof,0),0)); - ast eq0 = arg(ante1_con,0); - ast eq1 = arg(ante1_con,1); - ast symm_con = make(Iff,eq1,eq0); - Iproof::node ante1s = iproof->make_symmetry(symm_con,ante1_con,ante1); - ast cong_con = make(Iff,make(Not,eq1),make(Not,eq0)); - Iproof::node ante1sc = iproof->make_congruence(symm_con,cong_con,ante1s); - res = iproof->make_mp(cong_con,ante2,ante1sc); - return res; - } - } - - - // translate all the premises - std::vector args(nprems); - for(unsigned i = 0; i < nprems; i++) - args[i] = translate_main(prem(proof,i),false); - - for(unsigned i = 0; i < nprems; i++) - if(sym(args[i]) == commute - && !(dk == PR_TRANSITIVITY || dk == PR_MODUS_PONENS || dk == PR_SYMMETRY || (dk == PR_MONOTONICITY && op(arg(con,0)) == Not))) - throw unsupported(); - - switch(dk){ - case PR_TRANSITIVITY: { - if(sym(args[0]) == commute || sym(args[1]) == commute) - res = make_commuted_transitivity(proof,args); - else { - // assume the premises are x = y, y = z - ast x = arg(conc(prem(proof,0)),0); - ast y = arg(conc(prem(proof,0)),1); - ast z = arg(conc(prem(proof,1)),1); - res = iproof->make_transitivity(x,y,z,args[0],args[1]); - } - break; - } - case PR_TRANSITIVITY_STAR: { - // assume the premises are x = y, y = z, z = u, u = v, .. - - ast x = arg(conc(prem(proof,0)),0); - ast y = arg(conc(prem(proof,0)),1); - ast z = arg(conc(prem(proof,1)),1); - res = iproof->make_transitivity(x,y,z,args[0],args[1]); - - for (unsigned i = 2; i < nprems; ++i) { - y = z; - z = arg(conc(prem(proof,i)),1); - res = iproof->make_transitivity(x,y,z,res,args[i]); - } - break; - } - case PR_QUANT_INTRO: - case PR_MONOTONICITY: - { - std::vector eqs; eqs.resize(args.size()); - for(unsigned i = 0; i < args.size(); i++) - eqs[i] = conc(prem(proof,i)); - if(op(arg(con,0)) == Not && sym(args[0]) == commute) - res = make_commuted_monotonicity(proof,args); - else - res = iproof->make_congruence(eqs,con,args); - break; - } - case PR_REFLEXIVITY: { - res = iproof->make_reflexivity(con); - break; - } - case PR_SYMMETRY: { - if(sym(args[0]) == commute) - res = make_commuted_symmetry(proof,args); - else - res = iproof->make_symmetry(con,conc(prem(proof,0)),args[0]); - break; - } - case PR_MODUS_PONENS: { - if(sym(args[1]) == commute) - res = make_commuted_modus_ponens(proof,args); - else - res = iproof->make_mp(conc(prem(proof,1)),args[0],args[1]); - break; - } - case PR_TH_LEMMA: { - switch(get_theory_lemma_theory(proof)){ - case ArithTheory: - switch(get_theory_lemma_kind(proof)){ - case FarkasKind: { - std::vector farkas_coeffs, prem_cons; - get_farkas_coeffs(proof,farkas_coeffs); - if(nprems == 0) {// axiom, not rule - int nargs = num_args(con); - if(farkas_coeffs.size() != (unsigned)nargs){ - pfgoto(proof); - throw unsupported(); - } - for(int i = 0; i < nargs; i++){ - ast lit = mk_not(arg(con,i)); - prem_cons.push_back(lit); - args.push_back(iproof->make_hypothesis(lit)); - } - } - else { // rule version (proves false) - prem_cons.resize(nprems); - for(unsigned i = 0; i < nprems; i++) - prem_cons[i] = conc(prem(proof,i)); - } - res = iproof->make_farkas(con,args,prem_cons,farkas_coeffs); - break; - } - case Leq2EqKind: { - // conc should be (or x = y (not (leq x y)) (not(leq y z)) ) - ast xeqy = arg(conc(proof),0); - ast x = arg(xeqy,0); - ast y = arg(xeqy,1); - res = iproof->make_leq2eq(x,y,arg(arg(conc(proof),1),0),arg(arg(conc(proof),2),0)); - break; - } - case Eq2LeqKind: { - // conc should be (or (not (= x y)) (leq x y)) - ast xeqy = arg(arg(conc(proof),0),0); - ast xleqy = arg(conc(proof),1); - ast x = arg(xeqy,0); - ast y = arg(xeqy,1); - res = iproof->make_eq2leq(x,y,xleqy); - break; - } - case GCDTestKind: { - std::vector farkas_coeffs; - get_broken_gcd_test_coeffs(proof,farkas_coeffs); - if(farkas_coeffs.size() != nprems){ - pfgoto(proof); - throw unsupported(); - } - std::vector my_prems; my_prems.resize(2); - std::vector my_prem_cons; my_prem_cons.resize(2); - std::vector my_farkas_coeffs; my_farkas_coeffs.resize(2); - my_prems[0] = GCDtoDivRule(proof, true, farkas_coeffs, args, my_prem_cons[0]); - my_prems[1] = GCDtoDivRule(proof, false, farkas_coeffs, args, my_prem_cons[1]); - ast con = mk_false(); - my_farkas_coeffs[0] = my_farkas_coeffs[1] = make_int("1"); - res = iproof->make_farkas(con,my_prems,my_prem_cons,my_farkas_coeffs); - break; - } - case AssignBoundsKind: { - if(args.size() > 0) - res = AssignBoundsRule2Farkas(proof, conc(proof), args); - else - res = AssignBounds2Farkas(proof,conc(proof)); - break; - } - case GomoryCutKind: { - if(args.size() > 0) - res = GomoryCutRule2Farkas(proof, conc(proof), args); - else - throw unsupported(); - break; - } - case EqPropagateKind: { - std::vector prems(nprems); - for(unsigned i = 0; i < nprems; i++) - prems[i] = prem(proof,i); - res = EqPropagate(con,prems,args); - break; - } - case ArithMysteryKind: { - // Z3 hasn't told us what kind of lemma this is -- maybe we can guess - std::vector prems(nprems); - for(unsigned i = 0; i < nprems; i++) - prems[i] = prem(proof,i); - res = ArithMysteryRule(con,prems,args); - break; - } - default: - throw unsupported(); - } - break; - case ArrayTheory: {// nothing fancy for this - ast store_array; - if(get_store_array(con,store_array)) - res = iproof->make_axiom(lits,ast_scope(store_array)); - else - res = iproof->make_axiom(lits); // for array extensionality axiom - break; - } - default: - throw unsupported(); - } - break; - } - case PR_HYPOTHESIS: { - res = iproof->make_hypothesis(conc(proof)); - break; - } - case PR_QUANT_INST: { - res = iproof->make_axiom(lits); - break; - } - case PR_DEF_AXIOM: { // this should only happen for formulas resulting from quantifier instantiation - res = iproof->make_axiom(lits); - break; - } - case PR_IFF_TRUE: { // turns p into p <-> true, noop for us - res = args[0]; - break; - } - case PR_IFF_FALSE: { // turns ~p into p <-> false, noop for us - if(is_local(con)) - res = args[0]; - else - throw unsupported(); - break; - } - case PR_COMMUTATIVITY: { - ast comm_equiv = make(op(con),arg(con,0),arg(con,0)); - ast pf = iproof->make_reflexivity(comm_equiv); - res = make(commute,pf,comm_equiv); - break; - } - case PR_NOT_OR_ELIM: - case PR_AND_ELIM: { - std::vector rule_ax, res_conc; - ast piv = conc(prem(proof,0)); - rule_ax.push_back(make(Not,piv)); - rule_ax.push_back(con); - ast pf = iproof->make_axiom(rule_ax); - res_conc.push_back(con); - res = iproof->make_resolution(piv,res_conc,pf,args[0]); - break; - } - default: - IF_VERBOSE(0, verbose_stream() << "Unsupported proof rule: " << expr_ref((expr*)proof.raw(), *proof.mgr()) << "\n";); - // pfgoto(proof); - // SASSERT(0 && "translate_main: unsupported proof rule"); - throw unsupported(); - } - } - - return res; - } - - void clear_translation(){ - translation.first.clear(); - translation.second.clear(); - } - - // We actually compute the interpolant here and then produce a proof consisting of just a lemma - - iz3proof::node translate(ast proof, iz3proof &dst){ - std::vector itps; - scan_skolems(proof); - for(int i = 0; i < frames -1; i++){ -#ifdef NEW_LOCALITY - rng = range_downward(i); - locality.clear(); -#endif - iproof = iz3proof_itp::create(this,range_downward(i),weak_mode()); - try { - Iproof::node ipf = translate_main(proof); - ast itp = iproof->interpolate(ipf); - itps.push_back(itp); - delete iproof; - clear_translation(); - } - catch (const iz3proof_itp::proof_error &) { - delete iproof; - clear_translation(); - throw iz3proof::proof_error(); - } - catch (const unsupported &exc) { - delete iproof; - clear_translation(); - throw exc; - } - } - // Very simple proof -- lemma of the empty clause with computed interpolation - iz3proof::node Ipf = dst.make_lemma(std::vector(),itps); // builds result in dst - return Ipf; - } - - iz3translation_full(iz3mgr &mgr, - iz3secondary *_secondary, - const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory) - : iz3translation(mgr, cnsts, parents, theory) - { - frames = cnsts.size(); - traced_lit = ast(); - type boolbooldom[2] = {bool_type(),bool_type()}; - commute = function("@commute",2,boolbooldom,bool_type()); - m().inc_ref(commute); - } - - ~iz3translation_full(){ - m().dec_ref(commute); - } -}; - - - - -#ifdef IZ3_TRANSLATE_FULL - -iz3translation *iz3translation::create(iz3mgr &mgr, - iz3secondary *secondary, - const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory){ - return new iz3translation_full(mgr,secondary,cnsts,parents,theory); -} - - -#if 1 - -// This is just to make sure certain methods are compiled, so we can call then from the debugger. - -void iz3translation_full_trace_lit(iz3translation_full *p, iz3mgr::ast lit, iz3mgr::ast proof){ - p->trace_lit(lit, proof); -} - -void iz3translation_full_show_step(iz3translation_full *p, iz3mgr::ast proof){ - p->show_step(proof); -} - -void iz3translation_full_show_marked(iz3translation_full *p, iz3mgr::ast proof){ - p->show_marked(proof); -} - -void iz3translation_full_show_lit(iz3translation_full *p, iz3mgr::ast lit){ - p->show_lit(lit); -} - -void iz3translation_full_show_z3_lit(iz3translation_full *p, iz3mgr::ast a){ - p->show_z3_lit(a); -} - -void iz3translation_full_pfgoto(iz3translation_full *p, iz3mgr::ast proof){ - p->pfgoto(proof); -} - - -void iz3translation_full_pfback(iz3translation_full *p ){ - p->pfback(); -} - -void iz3translation_full_pffwd(iz3translation_full *p ){ - p->pffwd(); -} - -void iz3translation_full_pfprem(iz3translation_full *p, int i){ - p->pfprem(i); -} - -void iz3translation_full_expand(iz3translation_full *p, int i){ - p->expand(i); -} - -void iz3translation_full_symbols_out_of_scope(iz3translation_full *p, int i, const iz3mgr::ast &t){ - p->symbols_out_of_scope(i,t); -} - -void iz3translation_full_conc_symbols_out_of_scope(iz3translation_full *p, int i, const iz3mgr::ast &t){ - p->conc_symbols_out_of_scope(i,t); -} - -struct stdio_fixer { - stdio_fixer(){ - std::cout.rdbuf()->pubsetbuf(0,0); - } - -} my_stdio_fixer; - -#endif - -#endif - - diff --git a/src/interp/iz3translate.h b/src/interp/iz3translate.h deleted file mode 100755 index 8ecafbd3a..000000000 --- a/src/interp/iz3translate.h +++ /dev/null @@ -1,62 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3translate.h - - Abstract: - - Interface for proof translations from Z3 proofs to interpolatable - proofs. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifndef IZ3TRANSLATION_H -#define IZ3TRANSLATION_H - -#include "interp/iz3proof.h" -#include "interp/iz3secondary.h" - -// This is a interface class for translation from Z3 proof terms to -// an interpolatable proof - -class iz3translation : public iz3base { - public: - virtual iz3proof::node translate(ast, iz3proof &) = 0; - virtual ast quantify(ast e, const range &rng){return e;} - virtual ~iz3translation(){} - - /** This is thrown when the proof cannot be translated. */ - struct unsupported: public iz3_exception { - unsupported(): iz3_exception("unsupported") { } - }; - - static iz3translation *create(iz3mgr &mgr, - iz3secondary *secondary, - const std::vector > &frames, - const std::vector &parents, - const std::vector &theory); - - protected: - iz3translation(iz3mgr &mgr, - const std::vector > &_cnsts, - const std::vector &_parents, - const std::vector &_theory) - : iz3base(mgr,_cnsts,_parents,_theory) {} -}; - -// To use a secondary prover, define IZ3_TRANSLATE_DIRECT instead of this -#define IZ3_TRANSLATE_FULL - -#endif - - - diff --git a/src/interp/iz3translate_direct.cpp b/src/interp/iz3translate_direct.cpp deleted file mode 100755 index b88e488b4..000000000 --- a/src/interp/iz3translate_direct.cpp +++ /dev/null @@ -1,1717 +0,0 @@ -/*++ - Copyright (c) 2011 Microsoft Corporation - - Module Name: - - iz3translate_direct.cpp - - Abstract: - - Translate a Z3 proof into the interpolating proof calculus. - Translation is direct, without transformations on the target proof - representaiton. - - Author: - - Ken McMillan (kenmcmil) - - Revision History: - - --*/ - - -#ifdef _WINDOWS -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#pragma warning(disable:4390) -#endif - -#include "interp/iz3translate.h" -#include "interp/iz3proof.h" -#include "interp/iz3profiling.h" -#include "interp/iz3interp.h" - -#include -#include -#include -#include -#include -#include -#include - -//using std::vector; -using namespace stl_ext; - -/* This can introduce an address dependency if the range type of hash_map has - a destructor. Since the code in this file is not used and only here for - historical comparisons, we allow this non-determinism. -*/ -namespace stl_ext { - template - class hash { - public: - size_t operator()(const T *p) const { - return (size_t) p; - } - }; -} - -static int lemma_count = 0; -#if 0 -static int nll_lemma_count = 0; -#endif -#define SHOW_LEMMA_COUNT -1 - -// One half of a resolution. We need this to distinguish -// between resolving as a clause and as a unit clause. -// if pivot == conclusion(proof) it is unit. - -struct Z3_resolvent { - iz3base::ast proof; - bool is_unit; - iz3base::ast pivot; - Z3_resolvent(const iz3base::ast &_proof, bool _is_unit, const iz3base::ast &_pivot){ - proof = _proof; - is_unit = _is_unit; - pivot = _pivot; - } -}; - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const Z3_resolvent &p) const { - return (p.proof.hash() + p.pivot.hash()); - } - }; -} - - -bool operator==(const Z3_resolvent &x, const Z3_resolvent &y) { - return x.proof == y.proof && x.pivot == y.pivot; -} - - - -typedef std::vector ResolventAppSet; - -struct non_local_lits { - ResolventAppSet proofs; // the proof nodes being raised - non_local_lits(ResolventAppSet &_proofs){ - proofs.swap(_proofs); - } -}; - -namespace hash_space { - template <> - class hash { - public: - size_t operator()(const non_local_lits &p) const { - size_t h = 0; - for(ResolventAppSet::const_iterator it = p.proofs.begin(), en = p.proofs.end(); it != en; ++it) - h += (size_t)*it; - return h; - } - }; -} - - -bool operator==(const non_local_lits &x, const non_local_lits &y) { - ResolventAppSet::const_iterator itx = x.proofs.begin(); - ResolventAppSet::const_iterator ity = y.proofs.begin(); - while(true){ - if(ity == y.proofs.end()) return itx == x.proofs.end(); - if(itx == x.proofs.end()) return ity == y.proofs.end(); - if(*itx != *ity) return false; - ++itx; ++ity; - } -} - - -/* This translator goes directly from Z3 proofs to interpolatable - proofs without an intermediate representation as an iz3proof. */ - -class iz3translation_direct : public iz3translation { -public: - - typedef ast Zproof; // type of non-interpolating proofs - typedef iz3proof Iproof; // type of interpolating proofs - - /* Here we have lots of hash tables for memoizing various methods and - other such global data structures. - */ - - typedef hash_map AstToInt; - AstToInt locality; // memoizes locality of Z3 proof terms - - typedef std::pair EquivEntry; - typedef hash_map EquivTab; - EquivTab equivs; // maps non-local terms to equivalent local terms, with proof - - typedef hash_set AstHashSet; - AstHashSet equivs_visited; // proofs already checked for equivalences - - - typedef std::pair, hash_map > AstToIpf; - AstToIpf translation; // Zproof nodes to Iproof nodes - - AstHashSet antes_added; // Z3 proof terms whose antecedents have been added to the list - std::vector > antes; // list of antecedent/frame pairs - std::vector local_antes; // list of local antecedents - - Iproof *iproof; // the interpolating proof we are constructing - - int frames; // number of frames - - typedef std::set AstSet; - typedef hash_map AstToAstSet; - AstToAstSet hyp_map; // map proof terms to hypothesis set - - struct LocVar { // localization vars - ast var; // a fresh variable - ast term; // term it represents - int frame; // frame in which it's defined - LocVar(ast v, ast t, int f){var=v;term=t;frame=f;} - }; - - std::vector localization_vars; // localization vars in order of creation - typedef hash_map AstToAst; - AstToAst localization_map; // maps terms to their localization vars - - typedef hash_map AstToBool; - - - - iz3secondary *secondary; // the secondary prover - - // Unique table for sets of non-local resolutions - hash_map non_local_lits_unique; - - // Unique table for resolvents - hash_map Z3_resolvent_unique; - - // Translation memo for case of non-local resolutions - hash_map non_local_translation; - -public: - - -#define from_ast(x) (x) - - // determine locality of a proof term - // return frame of derivation if local, or -1 if not - // result INT_MAX means the proof term is a tautology - // memoized in hash_map "locality" - - int get_locality_rec(ast proof){ - std::pair foo(proof,INT_MAX); - std::pair bar = locality.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - if(pr(proof) == PR_ASSERTED){ - ast ass = conc(proof); - res = frame_of_assertion(ass); - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - int bar = get_locality_rec(arg); - if(res == INT_MAX || res == bar) res = bar; - else if(bar != INT_MAX) res = -1; - } - } - return res; - } - - - int get_locality(ast proof){ - // if(lia_z3_axioms_only) return -1; - int res = get_locality_rec(proof); - if(res != -1){ - ast con = conc(proof); - range rng = ast_scope(con); - - // hack: if a clause contains "true", it reduces to "true", - // which means we won't compute the range correctly. we handle - // this case by computing the ranges of the literals separately - - if(is_true(con)){ - std::vector lits; - get_Z3_lits(conc(proof),lits); - for(unsigned i = 0; i < lits.size(); i++) - rng = range_glb(rng,ast_scope(lits[i])); - } - - if(!range_is_empty(rng)){ - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - ast hyp = *it; - rng = range_glb(rng,ast_scope(hyp)); - } - } - - if(res == INT_MAX){ - if(range_is_empty(rng)) - res = -1; - else res = range_max(rng); - } - else { - if(!in_range(res,rng)) - res = -1; - } - } - return res; - } - - AstSet &get_hyps(ast proof){ - std::pair foo(proof,AstSet()); - std::pair bar = hyp_map.insert(foo); - AstSet &res = bar.first->second; - if(!bar.second) return res; - pfrule dk = pr(proof); - if(dk == PR_HYPOTHESIS){ - ast con = conc(proof); - res.insert(con); - } - else { - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - AstSet &arg_hyps = get_hyps(arg); - res.insert(arg_hyps.begin(),arg_hyps.end()); - } - if(dk == PR_LEMMA){ - ast con = conc(proof); - res.erase(mk_not(con)); - if(is_or(con)){ - int clause_size = num_args(con); - for(int i = 0; i < clause_size; i++){ - ast neglit = mk_not(arg(con,i)); - res.erase(neglit); - } - } - } - } -#if 0 - AstSet::iterator it = res.begin(), en = res.end(); - if(it != en){ - AstSet::iterator old = it; - ++it; - for(; it != en; ++it, ++old) - if(!(*old < *it)) - std::cout << "foo!"; - } -#endif - return res; - } - - - // Find all the judgements of the form p <-> q, where - // p is local and q is non-local, recording them in "equivs" - // the map equivs_visited is used to record the already visited proof terms - - void find_equivs(ast proof){ - if(equivs_visited.find(proof) != equivs_visited.end()) - return; - equivs_visited.insert(proof); - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++) // do all the sub_terms - find_equivs(prem(proof,i)); - ast con = conc(proof); // get the conclusion - if(is_iff(con)){ - ast iff = con; - for(int i = 0; i < 2; i++) - if(!is_local(arg(iff,i)) && is_local(arg(iff,1-i))){ - std::pair > foo(arg(iff,i),std::pair(arg(iff,1-i),proof)); - equivs.insert(foo); - } - } - } - - // get the lits of a Z3 clause as secondary prover terms - void get_Z3_lits(ast t, std::vector &lits){ - opr dk = op(t); - if(dk == False) - return; // false = empty clause - if(dk == Or){ - unsigned nargs = num_args(t); - lits.resize(nargs); - for(unsigned i = 0; i < nargs; i++) // do all the sub_terms - lits[i] = arg(t,i); - } - else { - lits.push_back(t); - } - } - - // resolve two clauses represented as vectors of lits. replace first clause - void resolve(ast pivot, std::vector &cls1, std::vector &cls2){ - ast neg_pivot = mk_not(pivot); - for(unsigned i = 0; i < cls1.size(); i++){ - if(cls1[i] == pivot){ - cls1[i] = cls1.back(); - cls1.pop_back(); - bool found_pivot2 = false; - for(unsigned j = 0; j < cls2.size(); j++){ - if(cls2[j] == neg_pivot) - found_pivot2 = true; - else - cls1.push_back(cls2[j]); - } - (void)found_pivot2; - assert(found_pivot2); - return; - } - } - assert(0 && "resolve failed"); - } - - // get lits resulting from unit resolution up to and including "position" - // TODO: this is quadratic -- fix it - void do_unit_resolution(ast proof, int position, std::vector &lits){ - ast orig_clause = conc(prem(proof,0)); - get_Z3_lits(orig_clause,lits); - for(int i = 1; i <= position; i++){ - std::vector unit(1); - unit[0] = conc(prem(proof,i)); - resolve(mk_not(unit[0]),lits,unit); - } - } - - - // clear the localization variables - void clear_localization(){ - localization_vars.clear(); - localization_map.clear(); - } - - // create a fresh variable for localization - ast fresh_localization_var(ast term, int frame){ - std::ostringstream s; - s << "%" << (localization_vars.size()); - ast var = make_var(s.str().c_str(),get_type(term)); - sym_range(sym(var)) = range_full(); // make this variable global - localization_vars.push_back(LocVar(var,term,frame)); - return var; - } - - - // "localize" a term to a given frame range by - // creating new symbols to represent non-local subterms - - ast localize_term(ast e, const range &rng){ - if(ranges_intersect(ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - AstToAst::iterator it = localization_map.find(e); - if(it != localization_map.end()) - return it->second; - - // if is is non-local, we must first localize the arguments to - // the range of its function symbol - - int nargs = num_args(e); - if(nargs > 0 /* && (!is_local(e) || flo <= hi || fhi >= lo) */){ - range frng = rng; - if(op(e) == Uninterpreted){ - symb f = sym(e); - range srng = sym_range(f); - if(ranges_intersect(srng,rng)) // localize to desired range if possible - frng = range_glb(srng,rng); - } - std::vector largs(nargs); - for(int i = 0; i < nargs; i++){ - largs[i] = localize_term(arg(e,i),frng); - frng = range_glb(frng,ast_scope(largs[i])); - } - e = clone(e,largs); - assert(is_local(e)); - } - - - if(ranges_intersect(ast_scope(e),rng)) - return e; // this term occurs in range, so it's O.K. - - // choose a frame for the constraint that is close to range - int frame = range_near(ast_scope(e),rng); - - ast new_var = fresh_localization_var(e,frame); - localization_map[e] = new_var; - ast cnst = make(Equal,new_var,e); - antes.push_back(std::pair(cnst,frame)); - return new_var; - } - - // some patterm matching functions - - // match logical or with nargs arguments - // assumes AIG form - bool match_or(ast e, ast *args, int nargs){ - if(op(e) != Or) return false; - int n = num_args(e); - if(n != nargs) return false; - for(int i = 0; i < nargs; i++) - args[i] = arg(e,i); - return true; - } - - // match operator f with exactly nargs arguments - bool match_op(ast e, opr f, ast *args, int nargs){ - if(op(e) != f) return false; - int n = num_args(e); - if(n != nargs) return false; - for(int i = 0; i < nargs; i++) - args[i] = arg(e,i); - return true; - } - - // see if the given formula can be interpreted as - // an axiom instance (e.g., an array axiom instance). - // if so, add it to "antes" in an appropriate frame. - // this may require "localization" - - void get_axiom_instance(ast e){ - - // "store" axiom - // (or (= w q) (= (select (store a1 w y) q) (select a1 q))) - // std::cout << "ax: "; show(e); - ast lits[2],eq_ops_l[2],eq_ops_r[2],sel_ops[2], sto_ops[3], sel_ops2[2] ; - if(match_or(e,lits,2)) - if(match_op(lits[0],Equal,eq_ops_l,2)) - if(match_op(lits[1],Equal,eq_ops_r,2)) - for(int i = 0; i < 2; i++){ // try the second equality both ways - if(match_op(eq_ops_r[0],Select,sel_ops,2)) - if(match_op(sel_ops[0],Store,sto_ops,3)) - if(match_op(eq_ops_r[1],Select,sel_ops2,2)) - for(int j = 0; j < 2; j++){ // try the first equality both ways - if(eq_ops_l[0] == sto_ops[1] - && eq_ops_l[1] == sel_ops[1] - && eq_ops_l[1] == sel_ops2[1] - && sto_ops[0] == sel_ops2[0]) - if(is_local(sel_ops[0])) // store term must be local - { - ast sto = sel_ops[0]; - ast addr = localize_term(eq_ops_l[1],ast_scope(sto)); - ast res = make(Or, - make(Equal,eq_ops_l[0],addr), - make(Equal, - make(Select,sto,addr), - make(Select,sel_ops2[0],addr))); - int frame = range_min(ast_scope(res)); - antes.push_back(std::pair(res,frame)); - return; - } - std::swap(eq_ops_l[0],eq_ops_l[1]); - } - std::swap(eq_ops_r[0],eq_ops_r[1]); - } - } - - // a quantifier instantation looks like (~ forall x. P) \/ P[z/x] - // we need to find a time frame for P, then localize P[z/x] in this frame - - void get_quantifier_instance(ast e){ - ast disjs[2]; - if(match_or(e,disjs,2)){ - if(is_local(disjs[0])){ - ast res = localize_term(disjs[1], ast_scope(disjs[0])); - int frame = range_min(ast_scope(res)); - antes.push_back(std::pair(res,frame)); - return; - } - } - } - - ast get_judgement(ast proof){ - ast con = from_ast(conc(proof)); - AstSet &hyps = get_hyps(proof); - std::vector hyps_vec; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - hyps_vec.push_back(*it); - if(hyps_vec.size() == 0) return con; - con = make(Or,mk_not(make(And,hyps_vec)),con); - return con; - } - - // add the premises of a proof term to the "antes" list - - void add_antes(ast proof){ - if(antes_added.find(proof) != antes_added.end()) return; - antes_added.insert(proof); - int frame = get_locality(proof); - if(frame != -1) - if(1){ - ast e = get_judgement(proof); - if(frame >= frames) frame = frames-1; // can happen if there are no symbols - antes.push_back(std::pair(e,frame)); - return; - } - pfrule dk = pr(proof); - if(dk == PR_ASSERTED){ - ast ass = conc(proof); - frame = frame_of_assertion(ass); - if(frame >= frames) frame = frames-1; // can happen if a theory fact - antes.push_back(std::pair(ass,frame)); - return; - } - if(dk == PR_TH_LEMMA && num_prems(proof) == 0){ - get_axiom_instance(conc(proof)); - } - if(dk == PR_QUANT_INST && num_prems(proof) == 0){ - get_quantifier_instance(conc(proof)); - } - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - add_antes(arg); - } - } - - - // add quantifiers over the localization vars - // to an interpolant for frames lo-hi - - ast add_quants(ast e, int lo, int hi){ - for(int i = localization_vars.size() - 1; i >= 0; i--){ - LocVar &lv = localization_vars[i]; - opr quantifier = (lv.frame >= lo && lv.frame <= hi) ? Exists : Forall; - e = apply_quant(quantifier,lv.var,e); - } - return e; - } - - int get_lits_locality(std::vector &lits){ - range rng = range_full(); - for(std::vector::iterator it = lits.begin(), en = lits.end(); it != en; ++it){ - ast lit = *it; - rng = range_glb(rng,ast_scope(lit)); - } - if(range_is_empty(rng)) return -1; - int hi = range_max(rng); - if(hi >= frames) return frames - 1; - return hi; - } - - - struct invalid_lemma: public iz3_exception { - invalid_lemma(): iz3_exception("invalid_lemma") {} - }; - - - - - // prove a lemma (clause) using current antes list - // return proof of the lemma - // use the secondary prover - - int prove_lemma(std::vector &lits){ - - - // first try localization - if(antes.size() == 0){ - int local_frame = get_lits_locality(lits); - if(local_frame != -1) - return iproof->make_assumption(local_frame,lits); // no proof needed for purely local fact - } - - // group the assumptions by frame - std::vector preds(frames); - for(unsigned i = 0; i < preds.size(); i++) - preds[i] = mk_true(); - for(unsigned i = 0; i < antes.size(); i++){ - int frame = antes[i].second; - preds[frame] = mk_and(preds[frame],antes[i].first); // conjoin it to frame - } - - for(unsigned i = 0; i < lits.size(); i++){ - int frame; - if(!weak_mode()){ - frame = range_max(ast_scope(lits[i])); - if(frame >= frames) frame = frames-1; // could happen if contains no symbols - } - else { - frame = range_min(ast_scope(lits[i])); - if(frame < 0){ - frame = range_max(ast_scope(lits[i])); // could happen if contains no symbols - if(frame >= frames) frame = frames-1; - } - } - preds[frame] = mk_and(preds[frame],mk_not(lits[i])); - } - - - std::vector itps; // holds interpolants - - -#if 1 - ++lemma_count; - // std::cout << "lemma: " << lemma_count << std::endl; - if(lemma_count == SHOW_LEMMA_COUNT){ - for(unsigned i = 0; i < lits.size(); i++) - show_lit(lits[i]); - std::cerr << "lemma written to file lemma.smt:\n"; - iz3base foo(*this,preds,std::vector(),std::vector()); - foo.print("lemma.smt"); - throw invalid_lemma(); - } -#endif - -#if 0 - std::cout << "\nLemma:\n"; - for(unsigned i = 0; i < lits.size(); i++) - show_lit(lits[i]); -#endif - - // interpolate using secondary prover - profiling::timer_start("secondary prover"); - int sat = secondary->interpolate(preds,itps); - profiling::timer_stop("secondary prover"); - - std::cout << "lemma done" << std::endl; - - // if sat, lemma isn't valid, something is wrong - if(sat){ -#if 1 - std::cerr << "invalid lemma written to file invalid_lemma.smt:\n"; - iz3base foo(*this,preds,std::vector(),std::vector()); - foo.print("invalid_lemma.smt"); -#endif - throw iz3_incompleteness(); - } - assert(sat == 0); // if sat, lemma doesn't hold! - - // quantifiy the localization vars - for(unsigned i = 0; i < itps.size(); i++) - itps[i] = add_quants(itps[i],0,i); - - // Make a lemma, storing interpolants - Iproof::node res = iproof->make_lemma(lits,itps); - -#if 0 - std::cout << "Lemma interps\n"; - for(unsigned i = 0; i < itps.size(); i++) - show(itps[i]); -#endif - - // Reset state for the next lemma - antes.clear(); - antes_added.clear(); - clear_localization(); // use a fresh localization for each lemma - - return res; - } - - // sanity check: make sure that any non-local lit is really resolved - // with something in the non_local_lits set - - void check_non_local(ast lit, non_local_lits *nll){ - if(nll) - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - ast con = (*it)->pivot; - if(con == mk_not(lit)) return; - } - assert(0 && "bug in non-local resolution handling"); - } - - - void get_local_conclusion_lits(ast proof, bool expect_clause, AstSet &lits){ - std::vector reslits; - if(expect_clause) - get_Z3_lits(conc(proof),reslits); - else reslits.push_back(conc(proof)); - for(unsigned i = 0; i < reslits.size(); i++) - if(is_local(reslits[i])) - lits.insert(reslits[i]); - AstSet &pfhyps = get_hyps(proof); - for(AstSet::iterator hit = pfhyps.begin(), hen = pfhyps.end(); hit != hen; ++hit) - if(is_local(*hit)) - lits.insert(mk_not(*hit)); - } - - - void collect_resolvent_lits(Z3_resolvent *res, const AstSet &hyps, std::vector &lits){ - if(!res->is_unit){ - std::vector reslits; - get_Z3_lits(conc(res->proof),reslits); - for(unsigned i = 0; i < reslits.size(); i++) - if(reslits[i] != res->pivot) - lits.push_back(reslits[i]); - } - AstSet &pfhyps = get_hyps(res->proof); - for(AstSet::iterator hit = pfhyps.begin(), hen = pfhyps.end(); hit != hen; ++hit) - if(hyps.find(*hit) == hyps.end()) - lits.push_back(mk_not(*hit)); - } - - void filter_resolvent_lits(non_local_lits *nll, std::vector &lits){ - std::vector orig_lits; orig_lits.swap(lits); - std::set pivs; - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - pivs.insert(res->pivot); - pivs.insert(mk_not(res->pivot)); - } - for(unsigned i = 0; i < orig_lits.size(); i++) - if(pivs.find(orig_lits[i]) == pivs.end()) - lits.push_back(orig_lits[i]); - } - - void collect_all_resolvent_lits(non_local_lits *nll, std::vector &lits){ - if(nll){ - std::vector orig_lits; orig_lits.swap(lits); - std::set pivs; - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - pivs.insert(res->pivot); - pivs.insert(mk_not(res->pivot)); - } - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - { - std::vector reslits; - if(!res->is_unit) get_Z3_lits(conc(res->proof),reslits); - else reslits.push_back(conc(res->proof)); - for(unsigned i = 0; i < reslits.size(); i++) -#if 0 - if(reslits[i] != res->pivot && pivs.find(reslits[i]) == pivs.end()) -#endif - if(is_local(reslits[i])) - lits.push_back(reslits[i]); - } - } - for(unsigned i = 0; i < orig_lits.size(); i++) - if(pivs.find(orig_lits[i]) == pivs.end()) - lits.push_back(orig_lits[i]); - } - } - - void collect_proof_clause(ast proof, bool expect_clause, std::vector &lits){ - if(expect_clause) - get_Z3_lits(conc(proof),lits); - else - lits.push_back(from_ast(conc(proof))); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator hit = hyps.begin(), hen = hyps.end(); hit != hen; ++hit) - lits.push_back(mk_not(*hit)); - } - - - // turn a bunch of literals into a lemma, replacing - // non-local lits with their local equivalents - // adds the accumulated antecedents (antes) as - // proof obligations of the lemma - - Iproof::node fix_lemma(std::vector &con_lits, AstSet &hyps, non_local_lits *nll){ - std::vector lits(con_lits); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - lits.push_back(mk_not(*it)); - if(nll){ - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - collect_resolvent_lits(res,hyps,lits); - add_antes(res->proof); - } - filter_resolvent_lits(nll,lits); - } - for(unsigned int i = 0; i < lits.size(); i++){ - EquivTab::iterator it = equivs.find(lits[i]); - if(it != equivs.end()){ - lits[i] = it->second.first; // replace with local equivalent - add_antes(it->second.second); // collect the premises that prove this - } - else { - if(!is_local(lits[i])){ - check_non_local(lits[i],nll); - lits[i] = mk_false(); - } - } - } - // TODO: should check here that derivation is local? - Iproof::node res = prove_lemma(lits); - return res; - } - - int num_lits(ast ast){ - opr dk = op(ast); - if(dk == False) - return 0; - if(dk == Or){ - unsigned nargs = num_args(ast); - int n = 0; - for(unsigned i = 0; i < nargs; i++) // do all the sub_terms - n += num_lits(arg(ast,i)); - return n; - } - else - return 1; - } - - struct non_lit_local_ante: public iz3_exception { - non_lit_local_ante(): iz3_exception("non_lit_local_ante") {} - }; - - bool local_antes_simple; - - bool add_local_antes(ast proof, AstSet &hyps, bool expect_clause = false){ - if(antes_added.find(proof) != antes_added.end()) return true; - antes_added.insert(proof); - ast con = from_ast(conc(proof)); - pfrule dk = pr(proof); - if(is_local(con) || equivs.find(con) != equivs.end()){ - if(!expect_clause || num_lits(conc(proof)) == 1){ - AstSet &this_hyps = get_hyps(proof); - if(std::includes(hyps.begin(),hyps.end(),this_hyps.begin(),this_hyps.end())){ - // if(hyps.find(con) == hyps.end()) -#if 0 - if(/* lemma_count == SHOW_LEMMA_COUNT - 1 && */ !is_literal_or_lit_iff(conc(proof))){ - std::cout << "\nnon-lit local ante\n"; - show_step(proof); - show(conc(proof)); - throw non_lit_local_ante(); - } -#endif - local_antes.push_back(proof); - return true; - } - else - ; //std::cout << "bar!\n"; - } - } - if(dk == PR_ASSERTED - //|| dk == PR_HYPOTHESIS - //|| dk == PR_TH_LEMMA - || dk == PR_QUANT_INST - //|| dk == PR_UNIT_RESOLUTION - //|| dk == PR_LEMMA - ) - return false; - if(dk == PR_HYPOTHESIS && hyps.find(con) != hyps.end()) - ; //std::cout << "blif!\n"; - if(dk == PR_HYPOTHESIS - || dk == PR_LEMMA) - ; //std::cout << "foo!\n"; - if(dk == PR_TH_LEMMA && num_prems(proof) == 0){ - // Check if this is an axiom instance - get_axiom_instance(conc(proof)); - } - - // #define SIMPLE_PROOFS -#ifdef SIMPLE_PROOFS - if(!(dk == PR_TRANSITIVITY - || dk == PR_MONOTONICITY)) - local_antes_simple = false; -#endif - - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - try { - if(!add_local_antes(arg, hyps, dk == PR_UNIT_RESOLUTION && i == 0)) - return false; - } - catch (non_lit_local_ante) { - std::cout << "\n"; - show_step(proof); - show(conc(proof)); - throw non_lit_local_ante(); - } - } - return true; - } - - std::vector lit_trace; - hash_set marked_proofs; - - bool proof_has_lit(const ast &proof, const ast &lit){ - AstSet &hyps = get_hyps(proof); - if(hyps.find(mk_not(lit)) != hyps.end()) - return true; - std::vector lits; - ast con = conc(proof); - get_Z3_lits(con, lits); - for(unsigned i = 0; i < lits.size(); i++) - if(lits[i] == lit) - return true; - return false; - } - - - void trace_lit_rec(const ast &lit, const ast &proof, AstHashSet &memo){ - if(memo.find(proof) == memo.end()){ - memo.insert(proof); - AstSet &hyps = get_hyps(proof); - std::vector lits; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - lits.push_back(mk_not(*it)); - ast con = conc(proof); - get_Z3_lits(con, lits); - for(unsigned i = 0; i < lits.size(); i++){ - if(lits[i] == lit){ - print_expr(std::cout,proof); - std::cout << "\n"; - marked_proofs.insert(proof); - pfrule dk = pr(proof); - if(dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA){ - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - trace_lit_rec(lit,arg,memo); - } - } - else - lit_trace.push_back(proof); - } - } - } - } - - ast traced_lit; - - int trace_lit(const ast &lit, const ast &proof){ - marked_proofs.clear(); - lit_trace.clear(); - traced_lit = lit; - AstHashSet memo; - trace_lit_rec(lit,proof,memo); - return lit_trace.size(); - } - - bool is_literal_or_lit_iff(const ast &lit){ - if(my_is_literal(lit)) return true; - if(op(lit) == Iff){ - return my_is_literal(arg(lit,0)) && my_is_literal(arg(lit,1)); - } - return false; - } - - bool my_is_literal(const ast &lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - int f = op(abslit); - return !(f == And || f == Or || f == Iff); - } - - void print_lit(const ast &lit){ - ast abslit = is_not(lit) ? arg(lit,0) : lit; - if(!is_literal_or_lit_iff(lit)){ - if(is_not(lit)) std::cout << "~"; - std::cout << "["; - print_expr(std::cout,abslit); - std::cout << "]"; - } - else - print_expr(std::cout,lit); - } - - void show_lit(const ast &lit){ - print_lit(lit); - std::cout << "\n"; - } - - void print_z3_lit(const ast &a){ - print_lit(from_ast(a)); - } - - void show_z3_lit(const ast &a){ - print_z3_lit(a); - std::cout << "\n"; - } - - - void show_con(const ast &proof, bool brief){ - if(!traced_lit.null() && proof_has_lit(proof,traced_lit)) - std::cout << "(*) "; - ast con = conc(proof); - AstSet &hyps = get_hyps(proof); - int count = 0; - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - if(brief && ++count > 5){ - std::cout << "... "; - break; - } - print_lit(*it); - std::cout << " "; - } - std::cout << "|- "; - std::vector lits; - get_Z3_lits(con,lits); - for(unsigned i = 0; i < lits.size(); i++){ - print_lit(lits[i]); - std::cout << " "; - } - std::cout << "\n"; - } - - void show_step(const ast &proof){ - std::cout << "\n"; - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - std::cout << "(" << i << ") "; - ast arg = prem(proof,i); - show_con(arg,true); - } - std::cout << "|------ "; - std::cout << string_of_symbol(sym(proof)) << "\n"; - show_con(proof,false); - } - - void show_marked( const ast &proof){ - std::cout << "\n"; - unsigned nprems = num_prems(proof); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - if(!traced_lit.null() && proof_has_lit(arg,traced_lit)){ - std::cout << "(" << i << ") "; - show_con(arg,true); - } - } - } - - std::vector pfhist; - int pfhist_pos; - - void pfgoto(const ast &proof){ - if(pfhist.size() == 0) - pfhist_pos = 0; - else pfhist_pos++; - pfhist.resize(pfhist_pos); - pfhist.push_back(proof); - show_step(proof); - } - - void show_nll(non_local_lits *nll){ - if(!nll)return; - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - show_step(res->proof); - std::cout << "Pivot: "; - show(res->pivot); - std::cout << std::endl; - } - } - - void pfback(){ - if(pfhist_pos > 0){ - pfhist_pos--; - show_step(pfhist[pfhist_pos]); - } - } - - void pffwd(){ - if(pfhist_pos < ((int)pfhist.size()) - 1){ - pfhist_pos++; - show_step(pfhist[pfhist_pos]); - } - } - - void pfprem(int i){ - if(pfhist.size() > 0){ - ast proof = pfhist[pfhist_pos]; - unsigned nprems = num_prems(proof); - if(i >= 0 && i < (int)nprems) - pfgoto(prem(proof,i)); - } - } - - int extract_th_lemma_common(std::vector &lits, non_local_lits *nll, bool lemma_nll = true){ - std::vector la = local_antes; - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - // std::vector lits; - AstSet hyps; // no hyps - for(unsigned i = 0; i < la.size(); i++) - lits.push_back(mk_not(from_ast(conc(la[i])))); - // lits.push_back(from_ast(conc(proof))); - Iproof::node res =fix_lemma(lits,hyps, lemma_nll ? nll : 0); - for(unsigned i = 0; i < la.size(); i++){ - Iproof::node q = translate_main(la[i],nll,false); - ast pnode = from_ast(conc(la[i])); - assert(is_local(pnode) || equivs.find(pnode) != equivs.end()); - Iproof::node neg = res; - Iproof::node pos = q; - if(is_not(pnode)){ - pnode = mk_not(pnode); - std::swap(neg,pos); - } - try { - res = iproof->make_resolution(pnode,neg,pos); - } - catch (const iz3proof::proof_error){ - std::cout << "\nresolution error in theory lemma\n"; - std::cout << "lits:\n"; - for(unsigned j = 0; j < lits.size(); j++) - show_lit(lits[j]); - std::cout << "\nstep:\n"; - show_step(la[i]); - throw invalid_lemma(); - } - } - return res; - } - - Iproof::node extract_simple_proof(const ast &proof, hash_set &leaves){ - if(leaves.find(proof) != leaves.end()) - return iproof->make_hypothesis(conc(proof)); - ast con = from_ast(conc(proof)); - pfrule dk = pr(proof); - unsigned nprems = num_prems(proof); - std::vector args(nprems); - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - args[i] = extract_simple_proof(arg,leaves); - } - - switch(dk){ - case PR_TRANSITIVITY: - return iproof->make_transitivity(con,args[0],args[1]); - case PR_MONOTONICITY: - return iproof->make_congruence(con,args); - case PR_REFLEXIVITY: - return iproof->make_reflexivity(con); - case PR_SYMMETRY: - return iproof->make_symmetry(con,args[0]); - } - assert(0 && "extract_simple_proof: unknown op"); - return 0; - } - - int extract_th_lemma_simple(const ast &proof, std::vector &lits){ - std::vector la = local_antes; - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - - hash_set leaves; - for(unsigned i = 0; i < la.size(); i++) - leaves.insert(la[i]); - - Iproof::node ipf = extract_simple_proof(proof,leaves); - ast con = from_ast(conc(proof)); - Iproof::node hyp = iproof->make_hypothesis(mk_not(con)); - ipf = iproof->make_eqcontra(ipf,hyp); - - // std::vector lits; - AstSet hyps; // no hyps - for(unsigned i = 0; i < la.size(); i++) - lits.push_back(mk_not(from_ast(conc(la[i])))); - // lits.push_back(from_ast(conc(proof))); - - Iproof::node res = iproof->make_contra(ipf,lits); - - for(unsigned i = 0; i < la.size(); i++){ - Iproof::node q = translate_main(la[i],0,false); - ast pnode = from_ast(conc(la[i])); - assert(is_local(pnode) || equivs.find(pnode) != equivs.end()); - Iproof::node neg = res; - Iproof::node pos = q; - if(is_not(pnode)){ - pnode = mk_not(pnode); - std::swap(neg,pos); - } - try { - res = iproof->make_resolution(pnode,neg,pos); - } - catch (const iz3proof::proof_error){ - std::cout << "\nresolution error in theory lemma\n"; - std::cout << "lits:\n"; - for(unsigned j = 0; j < lits.size(); j++) - show_lit(lits[j]); - std::cout << "\nstep:\n"; - show_step(la[i]); - throw invalid_lemma(); - } - } - return res; - } - - // #define NEW_EXTRACT_TH_LEMMA - - void get_local_hyps(const ast &proof, std::set &res){ - std::set hyps = get_hyps(proof); - for(std::set::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it){ - ast hyp = *it; - if(is_local(hyp)) - res.insert(hyp); - } - } - - int extract_th_lemma(ast proof, std::vector &lits, non_local_lits *nll){ - pfrule dk = pr(proof); - unsigned nprems = num_prems(proof); -#ifdef NEW_EXTRACT_TH_LEMMA - if(nprems == 0 && !nll) -#else - if(nprems == 0) -#endif - return 0; - if(nprems == 0 && dk == PR_TH_LEMMA) - // Check if this is an axiom instance - get_axiom_instance(conc(proof)); - - local_antes_simple = true; - for(unsigned i = 0; i < nprems; i++){ - ast arg = prem(proof,i); - if(!add_local_antes(arg,get_hyps(proof))){ - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - antes.clear(); - return 0; - } - } -#ifdef NEW_EXTRACT_TH_LEMMA - bool lemma_nll = nprems > 1; - if(nll && !lemma_nll){ - lemma_nll = false; - // std::cout << "lemma count = " << nll_lemma_count << "\n"; - for(ResolventAppSet::iterator it = nll->proofs.begin(), en = nll->proofs.end(); it != en; ++it){ - Z3_resolvent *res = *it; - ast arg = res->proof; - std::set loc_hyps; get_local_hyps(arg,loc_hyps); - if(!add_local_antes(arg,loc_hyps)){ - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - antes.clear(); - return 0; - } - } - collect_all_resolvent_lits(nll,lits); - } - int my_count = nll_lemma_count++; - int res; - try { - res = extract_th_lemma_common(lits,nll,lemma_nll); - } -#if 1 - catch (const invalid_lemma &) { - std::cout << "\n\nlemma: " << my_count; - std::cout << "\n\nproof node: \n"; - show_step(proof); - std::cout << "\n\nnon-local: \n"; - show_nll(nll); - pfgoto(nll->proofs[0]->proof); - show(conc(pfhist.back())); - pfprem(1); - show(conc(pfhist.back())); - pfprem(0); - show(conc(pfhist.back())); - pfprem(0); - show(conc(pfhist.back())); - pfprem(0); - show(conc(pfhist.back())); - std::cout << "\n\nliterals: \n"; - for(int i = 0; i < lits.size(); i++) - show_lit(lits[i]); - throw invalid_lemma(); - } -#endif - - return res; -#else -#ifdef SIMPLE_PROOFS - if(local_antes_simple && !nll) - return extract_th_lemma_simple(proof, lits); -#endif - return extract_th_lemma_common(lits,nll); -#endif - } - - int extract_th_lemma_ur(ast proof, int position, std::vector &lits, non_local_lits *nll){ - for(int i = 0; i <= position; i++){ - ast arg = prem(proof,i); - if(!add_local_antes(arg,get_hyps(proof),i==0)){ - local_antes.clear(); // clear antecedents for next lemma - antes_added.clear(); - antes.clear(); - return 0; - } - } - return extract_th_lemma_common(lits,nll); - } - - // see if any of the pushed resolvents are resolutions - // push the current proof into the latest such - int push_into_resolvent(ast proof, std::vector &lits, non_local_lits *nll, bool expect_clause){ - if(!nll) return 0; - if(num_args(proof) > 1) return 0; - ResolventAppSet resos = nll->proofs; - int pos = resos.size()-1; - for( ResolventAppSet::reverse_iterator it = resos.rbegin(), en = resos.rend(); it != en; ++it, --pos){ - Z3_resolvent *reso = *it; - ast ante = reso->proof; - ast pivot = reso->pivot; - bool is_unit = reso->is_unit; - pfrule dk = pr(ante); - bool pushable = dk == PR_UNIT_RESOLUTION || dk == PR_LEMMA; - if(!pushable && num_args(ante) > 1){ -#if 0 - if (!is_local(conc(ante))) - std::cout << "non-local "; - std::cout << "pushable!\n"; -#endif - pushable = true; - } - if(pushable){ - // remove the resolvent from list and add new clause as resolvent - resos.erase((++it).base()); - for(; pos < (int)resos.size(); pos++){ - Z3_resolvent *r = resos[pos]; - resos[pos] = find_resolvent(r->proof,r->is_unit,mk_not(pivot)); - } - resos.push_back(find_resolvent(proof,!expect_clause,mk_not(pivot))); - non_local_lits *new_nll = find_nll(resos); - try { - int res = translate_main(ante,new_nll,!is_unit); - return res; - } - catch (const invalid_lemma &) { - std::cout << "\n\npushing: \n"; - std::cout << "nproof node: \n"; - show_step(proof); - std::cout << "\n\nold non-local: \n"; - show_nll(nll); - std::cout << "\n\nnew non-local: \n"; - show_nll(new_nll); - throw invalid_lemma(); - } - } - } - return 0; // no pushed resolvents are resolution steps - } - - non_local_lits *find_nll(ResolventAppSet &proofs){ - if(proofs.empty()) - return (non_local_lits *)0; - std::pair foo(non_local_lits(proofs),(non_local_lits *)0); - std::pair::iterator,bool> bar = - non_local_lits_unique.insert(foo); - non_local_lits *&res = bar.first->second; - if(bar.second) - res = new non_local_lits(bar.first->first); - return res; - } - - Z3_resolvent *find_resolvent(ast proof, bool unit, ast pivot){ - std::pair foo(Z3_resolvent(proof,unit,pivot),(Z3_resolvent *)0); - std::pair::iterator,bool> bar = - Z3_resolvent_unique.insert(foo); - Z3_resolvent *&res = bar.first->second; - if(bar.second) - res = new Z3_resolvent(bar.first->first); - return res; - } - - // translate a unit resolution at position pos of given app - int translate_ur(ast proof, int position, non_local_lits *nll){ - ast ante = prem(proof,position); - if(position <= 0) - return translate_main(ante, nll); - ast pnode = conc(ante); - ast pnode_abs = !is_not(pnode) ? pnode : mk_not(pnode); - if(is_local(pnode) || equivs.find(pnode) != equivs.end()){ - Iproof::node neg = translate_ur(proof,position-1,nll); - Iproof::node pos = translate_main(ante, nll, false); - if(is_not(pnode)){ - pnode = mk_not(pnode); - std::swap(neg,pos); - } - try { - return iproof->make_resolution(pnode,neg,pos); - } - catch (const iz3proof::proof_error){ - std::cout << "resolution error in unit_resolution, position" << position << "\n"; - show_step(proof); - throw invalid_lemma(); - } - } - else { - // non-local pivot we have no local equivalent for - if(true){ - // try pushing the non-local resolution up - pfrule dk = pr(ante); - non_local_lits *old_nll = nll; - if(dk == PR_HYPOTHESIS) - ; //std::cout << "non-local hyp!\n"; // resolving with a hyp is a no-op - else { - ResolventAppSet new_proofs; - if(nll) new_proofs = nll->proofs; - Z3_resolvent *reso = find_resolvent(ante,true,pnode); - new_proofs.push_back(reso); - nll = find_nll(new_proofs); - } - try { - return translate_ur(proof,position-1,nll); - } - catch (const invalid_lemma &) { - if(old_nll != nll){ - std::cout << "\n\nadded_nll: \n"; - std::cout << "nproof node: \n"; - show_step(proof); - std::cout << "\n\new non-local step: \n"; - show_step(nll->proofs.back()->proof); - } - throw invalid_lemma(); - } - - } - else { - // just make a lemma - std::vector lits; - do_unit_resolution(proof,position,lits); - int res; - if(!(res = extract_th_lemma_ur(proof,position,lits,nll))){ - for(int i = 0; i <= position; i++){ - z3pf p = prem(proof,i); - add_antes(p); - } - res = fix_lemma(lits,get_hyps(proof),nll); - } - return res; - } - } - } - - non_local_lits *update_nll(ast proof, bool expect_clause, non_local_lits *nll){ - std::vector lits; - collect_proof_clause(proof,expect_clause,lits); - AstSet litset; - litset.insert(lits.begin(),lits.end()); - ResolventAppSet to_keep; - for(int i = nll->proofs.size()-1; i >= 0; --i){ - ast traced_lit = (nll->proofs[i])->pivot; - ast traced_lit_neg = mk_not(traced_lit); - if(litset.find(traced_lit) != litset.end() || litset.find(traced_lit_neg) != litset.end()){ - to_keep.push_back(nll->proofs[i]); - std::vector reslits; - AstSet dummy; - collect_resolvent_lits(nll->proofs[i],dummy,reslits); - litset.insert(reslits.begin(),reslits.end()); - } - } - if(to_keep.size() == nll->proofs.size()) return nll; - ResolventAppSet new_proofs; - for(int i = to_keep.size() - 1; i >= 0; --i) - new_proofs.push_back(to_keep[i]); - return find_nll(new_proofs); - } - - // translate a Z3 proof term into a secondary prover proof term - - Iproof::node translate_main(ast proof, non_local_lits *nll, bool expect_clause = true){ - non_local_lits *old_nll = nll; - if(nll) nll = update_nll(proof,expect_clause,nll); - AstToIpf &tr = nll ? non_local_translation[nll] : translation; - hash_map &trc = expect_clause ? tr.first : tr.second; - std::pair foo(proof,INT_MAX); - std::pair bar = trc.insert(foo); - int &res = bar.first->second; - if(!bar.second) return res; - - - try { - int frame = get_locality(proof); - if(frame != -1){ - ast e = from_ast(conc(proof)); - if(frame >= frames) frame = frames - 1; - std::vector foo; - if(expect_clause) - get_Z3_lits(conc(proof),foo); - else - foo.push_back(e); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - foo.push_back(mk_not(*it)); - res = iproof->make_assumption(frame,foo); - return res; - } - - pfrule dk = pr(proof); - unsigned nprems = num_prems(proof); - if(dk == PR_UNIT_RESOLUTION){ - res = translate_ur(proof, nprems - 1, nll); - } - else if(dk == PR_LEMMA){ - ast contra = prem(proof,0); // this is a proof of false from some hyps - res = translate_main(contra, nll); - if(!expect_clause){ - std::vector foo; // the negations of the hyps form a clause - foo.push_back(from_ast(conc(proof))); - AstSet &hyps = get_hyps(proof); - for(AstSet::iterator it = hyps.begin(), en = hyps.end(); it != en; ++it) - foo.push_back(mk_not(*it)); - res = iproof->make_contra(res,foo); - } - } - else { - std::vector lits; - ast con = conc(proof); - if(expect_clause) - get_Z3_lits(con, lits); - else - lits.push_back(from_ast(con)); -#ifdef NEW_EXTRACT_TH_LEMMA - if(!(res = push_into_resolvent(proof,lits,nll,expect_clause))){ - if(!(res = extract_th_lemma(proof,lits,nll))){ -#else - if(!(res = extract_th_lemma(proof,lits,nll))){ - if(!(res = push_into_resolvent(proof,lits,nll,expect_clause))){ -#endif - // std::cout << "extract theory lemma failed\n"; - add_antes(proof); - res = fix_lemma(lits,get_hyps(proof),nll); - } - } - } -#ifdef CHECK_PROOFS - - if(0){ - AstSet zpf_con_lits, ipf_con_lits; - get_local_conclusion_lits(proof, expect_clause, zpf_con_lits); - if(nll){ - for(unsigned i = 0; i < nll->proofs.size(); i++) - get_local_conclusion_lits(nll->proofs[i]->proof,!nll->proofs[i]->is_unit,zpf_con_lits); - } - std::vector ipf_con; - iproof->get_conclusion(res,ipf_con); - for(unsigned i = 0; i < ipf_con.size(); i++) - ipf_con_lits.insert(ipf_con[i]); - if(!(ipf_con_lits == zpf_con_lits)){ - std::cout << "proof error:\n"; - std::cout << "expected lits:\n"; - for(AstSet::iterator hit = zpf_con_lits.begin(), hen = zpf_con_lits.end(); hit != hen; ++hit) - show_lit(*hit); - std::cout << "got lits:\n"; - for(AstSet::iterator hit = ipf_con_lits.begin(), hen = ipf_con_lits.end(); hit != hen; ++hit) - show_lit(*hit); - std::cout << "\nproof step:"; - show_step(proof); - std::cout << "\n"; - throw invalid_lemma(); - } - } -#endif - - return res; - } - - catch (const invalid_lemma &) { - if(old_nll != nll){ - std::cout << "\n\nupdated nll: \n"; - std::cout << "nproof node: \n"; - show_step(proof); - std::cout << "\n\new non-local: \n"; - show_nll(nll); - } - throw invalid_lemma(); - } - - } - - // Proof translation is in two stages: - // 1) Translate ast proof term to Zproof - // 2) Translate Zproof to Iproof - - Iproof::node translate(ast proof, Iproof &dst){ - iproof = &dst; - Iproof::node Ipf = translate_main(proof,0); // builds result in dst - return Ipf; - } - - iz3translation_direct(iz3mgr &mgr, - iz3secondary *_secondary, - const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory) - : iz3translation(mgr, cnsts, parents, theory) - { - secondary = _secondary; - frames = cnsts.size(); - traced_lit = ast(); - } - - ~iz3translation_direct(){ - for(hash_map::iterator - it = non_local_lits_unique.begin(), - en = non_local_lits_unique.end(); - it != en; - ++it) - delete it->second; - - for(hash_map::iterator - it = Z3_resolvent_unique.begin(), - en = Z3_resolvent_unique.end(); - it != en; - ++it) - delete it->second; - } - }; - - - - -#ifdef IZ3_TRANSLATE_DIRECT - - iz3translation *iz3translation::create(iz3mgr &mgr, - iz3secondary *secondary, - const std::vector > &cnsts, - const std::vector &parents, - const std::vector &theory){ - return new iz3translation_direct(mgr,secondary,cnsts,parents,theory); - } - - -#if 1 - - void iz3translation_direct_trace_lit(iz3translation_direct *p, iz3mgr::ast lit, iz3mgr::ast proof){ - p->trace_lit(lit, proof); - } - - void iz3translation_direct_show_step(iz3translation_direct *p, iz3mgr::ast proof){ - p->show_step(proof); - } - - void iz3translation_direct_show_marked(iz3translation_direct *p, iz3mgr::ast proof){ - p->show_marked(proof); - } - - void iz3translation_direct_show_lit(iz3translation_direct *p, iz3mgr::ast lit){ - p->show_lit(lit); - } - - void iz3translation_direct_show_z3_lit(iz3translation_direct *p, iz3mgr::ast a){ - p->show_z3_lit(a); - } - - void iz3translation_direct_pfgoto(iz3translation_direct *p, iz3mgr::ast proof){ - p->pfgoto(proof); - } - - void iz3translation_direct_show_nll(iz3translation_direct *p, non_local_lits *nll){ - p->show_nll(nll); - } - - void iz3translation_direct_pfback(iz3translation_direct *p ){ - p->pfback(); - } - - void iz3translation_direct_pffwd(iz3translation_direct *p ){ - p->pffwd(); - } - - void iz3translation_direct_pfprem(iz3translation_direct *p, int i){ - p->pfprem(i); - } - - - struct stdio_fixer { - stdio_fixer(){ - std::cout.rdbuf()->pubsetbuf(0,0); - } - - } my_stdio_fixer; - -#endif - -#endif - - diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index 41fc19907..de72d6b2d 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -43,7 +43,7 @@ public: unsigned m_src; unsigned m_dst; public: - move(M& m, unsigned s, unsigned d, T* t = 0): m(m), m_t(t), m_src(s), m_dst(d) { + move(M& m, unsigned s, unsigned d, T* t = nullptr): m(m), m_t(t), m_src(s), m_dst(d) { if (t) m.inc_ref(t); } ~move() { @@ -69,7 +69,7 @@ public: unsigned src() const { return m_src; } T* t() const { return m_t; } - bool is_epsilon() const { return m_t == 0; } + bool is_epsilon() const { return m_t == nullptr; } }; typedef vector moves; private: @@ -407,7 +407,7 @@ public: mvs1.push_back(move(m, mv1.src(), dst1, t)); } for (move const& mv1 : mvs1) { - remove(mv1.src(), dst, 0); + remove(mv1.src(), dst, nullptr); add(mv1); } remove(dst, dst1, t); @@ -431,7 +431,7 @@ public: else { continue; } - remove(src, dst, 0); + remove(src, dst, nullptr); --j; } } diff --git a/src/math/automata/boolean_algebra.h b/src/math/automata/boolean_algebra.h index d54ff5d1a..ea2b66995 100644 --- a/src/math/automata/boolean_algebra.h +++ b/src/math/automata/boolean_algebra.h @@ -39,7 +39,7 @@ public: template class boolean_algebra : public positive_boolean_algebra { public: - virtual ~boolean_algebra() {} + ~boolean_algebra() override {} virtual T mk_not(T x) = 0; }; diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index be38a60bc..17a53fc33 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -399,7 +399,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_produ continue; } else if (is_sat == l_undef) { - return 0; + return nullptr; } unsigned_pair tgt_pair(mvsA[i].dst(), mvsB[j].dst()); unsigned tgt; diff --git a/src/math/euclid/euclidean_solver.cpp b/src/math/euclid/euclidean_solver.cpp index 1be239c36..70b424375 100644 --- a/src/math/euclid/euclidean_solver.cpp +++ b/src/math/euclid/euclidean_solver.cpp @@ -506,8 +506,8 @@ struct euclidean_solver::imp { } imp(numeral_manager * m): - m_manager(m == 0 ? alloc(numeral_manager) : m), - m_owns_m(m == 0), + m_manager(m == nullptr ? alloc(numeral_manager) : m), + m_owns_m(m == nullptr), m_decompose_buffer(*m_manager), m_as_buffer(*m_manager), m_bs_buffer(*m_manager), @@ -550,7 +550,7 @@ struct euclidean_solver::imp { return; equation * eq; if (j == null_justification) { - eq = mk_eq(num, as, xs, c, 0, 0, 0); + eq = mk_eq(num, as, xs, c, 0, nullptr, nullptr); } else { mpq one(1); @@ -694,7 +694,7 @@ struct euclidean_solver::imp { mpz new_c; decompose(m_next_pos_a, m_next_a, eq.m_c, new_c, eq.m_c); // create auxiliary equation - equation * new_eq = mk_eq(m_tmp_xs.size(), buffer.c_ptr(), m_tmp_xs.c_ptr(), new_c, 0, 0, 0, false); + equation * new_eq = mk_eq(m_tmp_xs.size(), buffer.c_ptr(), m_tmp_xs.c_ptr(), new_c, 0, nullptr, nullptr, false); // new_eq doesn't need to normalized, since it has unit coefficients TRACE("euclidean_solver", tout << "decomposition: new parameter x" << p << " aux eq:\n"; display(tout, *new_eq); tout << "\n"; diff --git a/src/math/grobner/grobner.cpp b/src/math/grobner/grobner.cpp index 3295c4eae..017f1cf77 100644 --- a/src/math/grobner/grobner.cpp +++ b/src/math/grobner/grobner.cpp @@ -29,7 +29,7 @@ grobner::grobner(ast_manager & m, v_dependency_manager & d): m_var_lt(m_var2weight), m_monomial_lt(m_var_lt), m_changed_leading_term(false), - m_unsat(0) { + m_unsat(nullptr) { } grobner::~grobner() { @@ -116,7 +116,7 @@ void grobner::reset() { m_to_process.reset(); m_equations_to_unfreeze.reset(); m_equations_to_delete.reset(); - m_unsat = 0; + m_unsat = nullptr; } void grobner::display_var(std::ostream & out, expr * var) const { @@ -410,7 +410,7 @@ void grobner::assert_monomial_tautology(expr * m) { m1->m_vars.push_back(m); eq->m_monomials.push_back(m1); normalize_coeff(eq->m_monomials); - init_equation(eq, static_cast(0)); \ + init_equation(eq, static_cast(nullptr)); \ m_to_process.insert(eq); } @@ -625,7 +625,7 @@ grobner::equation * grobner::copy_equation(equation const * eq) { grobner::equation * grobner::simplify(equation const * source, equation * target) { TRACE("grobner", tout << "simplifying: "; display_equation(tout, *target); tout << "using: "; display_equation(tout, *source);); if (source->get_num_monomials() == 0) - return 0; + return nullptr; m_stats.m_simplify++; bool result = false; bool simplified; @@ -676,7 +676,7 @@ grobner::equation * grobner::simplify(equation const * source, equation * target } while (simplified && !m_manager.canceled()); TRACE("grobner", tout << "result: "; display_equation(tout, *target);); - return result ? target : 0; + return result ? target : nullptr; } /** @@ -702,13 +702,13 @@ grobner::equation * grobner::simplify_using_processed(equation * eq) { eq = new_eq; } if (m_manager.canceled()) { - return 0; + return nullptr; } } } while (simplified); TRACE("grobner", tout << "simplification result: "; display_equation(tout, *eq);); - return result ? eq : 0; + return result ? eq : nullptr; } /** @@ -732,7 +732,7 @@ bool grobner::is_better_choice(equation * eq1, equation * eq2) { \brief Pick next unprocessed equation */ grobner::equation * grobner::pick_next() { - equation * r = 0; + equation * r = nullptr; ptr_buffer to_delete; equation_set::iterator it = m_to_process.begin(); equation_set::iterator end = m_to_process.end(); @@ -767,7 +767,7 @@ bool grobner::simplify_processed(equation * eq) { m_changed_leading_term = false; // if the leading term is simplified, then the equation has to be moved to m_to_process equation * new_curr = simplify(eq, curr); - if (new_curr != 0) { + if (new_curr != nullptr) { if (new_curr != curr) { m_equations_to_unfreeze.push_back(curr); to_remove.push_back(curr); @@ -817,7 +817,7 @@ void grobner::simplify_to_process(equation * eq) { for (; it != end; ++it) { equation * curr = *it; equation * new_curr = simplify(eq, curr); - if (new_curr != 0 && new_curr != curr) { + if (new_curr != nullptr && new_curr != curr) { m_equations_to_unfreeze.push_back(curr); to_insert.push_back(new_curr); to_remove.push_back(curr); @@ -947,7 +947,7 @@ bool grobner::compute_basis_step() { } #endif equation * new_eq = simplify_using_processed(eq); - if (new_eq != 0 && eq != new_eq) { + if (new_eq != nullptr && eq != new_eq) { // equation was updated using non destructive updates m_equations_to_unfreeze.push_back(eq); eq = new_eq; diff --git a/src/math/grobner/grobner.h b/src/math/grobner/grobner.h index f024a33e4..9233866d5 100644 --- a/src/math/grobner/grobner.h +++ b/src/math/grobner/grobner.h @@ -220,25 +220,25 @@ public: \brief Assert the given equality. This method assumes eq is simplified. */ - void assert_eq(expr * eq, v_dependency * ex = 0); + void assert_eq(expr * eq, v_dependency * ex = nullptr); /** \brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0. This method assumes the monomials were simplified. */ - void assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex = 0); + void assert_eq_0(unsigned num_monomials, expr * const * monomials, v_dependency * ex = nullptr); /** \brief Assert the equality monomials[0] + ... + monomials[num_monomials - 1] = 0. This method assumes the monomials were simplified. */ - void assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex = 0); + void assert_eq_0(unsigned num_monomials, monomial * const * monomials, v_dependency * ex = nullptr); /** \brief Assert the equality coeffs[0] * monomials[0] + ... + coeffs[num_monomials-1] * monomials[num_monomials - 1] = 0. This method assumes the monomials were simplified. */ - void assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex = 0); + void assert_eq_0(unsigned num_monomials, rational const * coeffs, expr * const * monomials, v_dependency * ex = nullptr); /** \brief Assert the monomial tautology (quote (x_1 * ... * x_n)) - x_1 * ... * x_n = 0 @@ -263,7 +263,7 @@ public: /** \brief Return true if an inconsistency was detected. */ - bool inconsistent() const { return m_unsat != 0; } + bool inconsistent() const { return m_unsat != nullptr; } /** \brief Simplify the given expression using the equalities asserted diff --git a/src/math/hilbert/heap_trie.h b/src/math/hilbert/heap_trie.h index ace5381b8..3c6ecd1cb 100644 --- a/src/math/hilbert/heap_trie.h +++ b/src/math/hilbert/heap_trie.h @@ -81,14 +81,14 @@ class heap_trie { Value m_value; public: leaf(): node(leaf_t) {} - virtual ~leaf() {} + ~leaf() override {} Value const& get_value() const { return m_value; } void set_value(Value const& v) { m_value = v; } - virtual void display(std::ostream& out, unsigned indent) const { + void display(std::ostream& out, unsigned indent) const override { out << " value: " << m_value; } - virtual unsigned num_nodes() const { return 1; } - virtual unsigned num_leaves() const { return this->ref_count()>0?1:0; } + unsigned num_nodes() const override { return 1; } + unsigned num_leaves() const override { return this->ref_count()>0?1:0; } }; typedef buffer, true, 2> children_t; @@ -99,7 +99,7 @@ class heap_trie { public: trie(): node(trie_t) {} - virtual ~trie() { + ~trie() override { } node* find_or_insert(Key k, node* n) { @@ -137,7 +137,7 @@ class heap_trie { children_t const& nodes() const { return m_nodes; } children_t & nodes() { return m_nodes; } - virtual void display(std::ostream& out, unsigned indent) const { + void display(std::ostream& out, unsigned indent) const override { for (unsigned j = 0; j < m_nodes.size(); ++j) { if (j != 0 || indent > 0) { out << "\n"; @@ -151,7 +151,7 @@ class heap_trie { } } - virtual unsigned num_nodes() const { + unsigned num_nodes() const override { unsigned sz = 1; for (unsigned j = 0; j < m_nodes.size(); ++j) { sz += m_nodes[j].second->num_nodes(); @@ -159,7 +159,7 @@ class heap_trie { return sz; } - virtual unsigned num_leaves() const { + unsigned num_leaves() const override { unsigned sz = 0; for (unsigned j = 0; j < m_nodes.size(); ++j) { sz += m_nodes[j].second->num_leaves(); @@ -195,9 +195,9 @@ public: m_le(le), m_num_keys(0), m_do_reshuffle(4), - m_root(0), - m_spare_leaf(0), - m_spare_trie(0) + m_root(nullptr), + m_spare_leaf(nullptr), + m_spare_trie(nullptr) {} ~heap_trie() { @@ -283,7 +283,7 @@ public: ++m_stats.m_num_removes; // assumption: key is in table. node* n = m_root; - node* m = 0; + node* m = nullptr; for (unsigned i = 0; i < num_keys(); ++i) { n->dec_ref(); VERIFY (to_trie(n)->find(get_key(keys, i), m)); diff --git a/src/math/hilbert/hilbert_basis.cpp b/src/math/hilbert/hilbert_basis.cpp index 9baa0beac..d6d4366db 100644 --- a/src/math/hilbert/hilbert_basis.cpp +++ b/src/math/hilbert/hilbert_basis.cpp @@ -174,8 +174,8 @@ class hilbert_basis::value_index2 { struct checker : public ht::check_value { hilbert_basis* hb; offset_t m_value; - checker(): hb(0) {} - virtual bool operator()(unsigned const& v) { + checker(): hb(nullptr) {} + bool operator()(unsigned const& v) override { if (m_value.m_offset != v) { // && hb->is_subsumed(m_value, offset_t(v))) { return true; } @@ -278,7 +278,7 @@ public: m_zero.insert(idx, vs); } else { - value_index* map = 0; + value_index* map = nullptr; if (!m_neg.find(vs.weight(), map)) { map = alloc(value_index, hb); map->reset(m_num_ineqs); diff --git a/src/math/hilbert/hilbert_basis.h b/src/math/hilbert/hilbert_basis.h index a0350492a..54208d268 100644 --- a/src/math/hilbert/hilbert_basis.h +++ b/src/math/hilbert/hilbert_basis.h @@ -20,7 +20,7 @@ Revision History: Hilbert basis can be templatized based on traits that define numeral: - as rational, mpz, checked_int64 + as rational, mpz, checked_int64 (checked or unchecked). --*/ diff --git a/src/math/polynomial/algebraic_numbers.cpp b/src/math/polynomial/algebraic_numbers.cpp index 9484c3c83..5811811fc 100644 --- a/src/math/polynomial/algebraic_numbers.cpp +++ b/src/math/polynomial/algebraic_numbers.cpp @@ -48,7 +48,7 @@ namespace algebraic_numbers { unsigned m_sign_lower:1; unsigned m_not_rational:1; // if true we know for sure it is not a rational unsigned m_i:29; // number is the i-th root of p, 0 if it is not known which root of p the number is. - algebraic_cell():m_p_sz(0), m_p(0), m_minimal(false), m_not_rational(false), m_i(0) {} + algebraic_cell():m_p_sz(0), m_p(nullptr), m_minimal(false), m_not_rational(false), m_i(0) {} bool is_minimal() const { return m_minimal != 0; } }; @@ -186,7 +186,7 @@ namespace algebraic_numbers { for (unsigned i = 0; i < c->m_p_sz; i++) qm().del(c->m_p[i]); m_allocator.deallocate(sizeof(mpz)*c->m_p_sz, c->m_p); - c->m_p = 0; + c->m_p = nullptr; c->m_p_sz = 0; } @@ -201,13 +201,13 @@ namespace algebraic_numbers { } void del(numeral & a) { - if (a.m_cell == 0) + if (a.m_cell == nullptr) return; if (a.is_basic()) del(a.to_basic()); else del(a.to_algebraic()); - a.m_cell = 0; + a.m_cell = nullptr; } void reset(numeral & a) { @@ -215,7 +215,7 @@ namespace algebraic_numbers { } bool is_zero(numeral const & a) { - return a.m_cell == 0; + return a.m_cell == nullptr; } bool is_pos(numeral const & a) { @@ -361,7 +361,7 @@ namespace algebraic_numbers { basic_cell * mk_basic_cell(mpq & n) { if (qm().is_zero(n)) - return 0; + return nullptr; void * mem = static_cast(m_allocator.allocate(sizeof(basic_cell))); basic_cell * c = new (mem) basic_cell(); qm().swap(c->m_value, n); @@ -1037,7 +1037,7 @@ namespace algebraic_numbers { unsigned target_i = UINT_MAX; // index of sequence that is isolating int target_lV = 0, target_uV = 0; for (unsigned i = 0; i < num_fs; i++) { - if (seqs[i] == 0) + if (seqs[i] == nullptr) continue; // sequence was discarded because it does not contain the root. TRACE("anum_mk_binary", tout << "sequence " << i << "\n"; upm().display(tout, *(seqs[i])); tout << "\n";); int lV = upm().sign_variations_at(*(seqs[i]), r_i.lower()); @@ -1050,7 +1050,7 @@ namespace algebraic_numbers { ); if (V <= 0) { // discard sequence, since factor does not contain the root - seqs.set(i, 0); + seqs.set(i, nullptr); } else if (V == 1) { target_i = i; @@ -1115,7 +1115,7 @@ namespace algebraic_numbers { unsigned target_i = UINT_MAX; // index of sequence that is isolating int target_lV = 0, target_uV = 0; for (unsigned i = 0; i < num_fs; i++) { - if (seqs[i] == 0) + if (seqs[i] == nullptr) continue; // sequence was discarded because it does not contain the root. int lV = upm().sign_variations_at(*(seqs[i]), r_i.lower()); int uV = upm().sign_variations_at(*(seqs[i]), r_i.upper()); @@ -1126,7 +1126,7 @@ namespace algebraic_numbers { ); if (V <= 0) { // discard sequence, since factor does not contain the root - seqs.set(i, 0); + seqs.set(i, nullptr); } else if (V == 1) { target_i = i; @@ -1932,9 +1932,9 @@ namespace algebraic_numbers { imp & m_imp; polynomial::var2anum const & m_x2v; opt_var2basic(imp & i, polynomial::var2anum const & x2v):m_imp(i), m_x2v(x2v) {} - virtual unsynch_mpq_manager & m() const { return m_imp.qm(); } - virtual bool contains(polynomial::var x) const { return m_x2v.contains(x); } - virtual mpq const & operator()(polynomial::var x) const { + unsynch_mpq_manager & m() const override { return m_imp.qm(); } + bool contains(polynomial::var x) const override { return m_x2v.contains(x); } + mpq const & operator()(polynomial::var x) const override { anum const & v = m_x2v(x); if (!v.is_basic()) throw failed(); @@ -1949,9 +1949,9 @@ namespace algebraic_numbers { imp & m_imp; polynomial::var2anum const & m_x2v; var2basic(imp & i, polynomial::var2anum const & x2v):m_imp(i), m_x2v(x2v) {} - virtual unsynch_mpq_manager & m() const { return m_imp.qm(); } - virtual bool contains(polynomial::var x) const { return m_x2v.contains(x) && m_x2v(x).is_basic(); } - virtual mpq const & operator()(polynomial::var x) const { + unsynch_mpq_manager & m() const override { return m_imp.qm(); } + bool contains(polynomial::var x) const override { return m_x2v.contains(x) && m_x2v(x).is_basic(); } + mpq const & operator()(polynomial::var x) const override { anum const & v = m_x2v(x); SASSERT(v.is_basic()); TRACE("var2basic", tout << "getting value of x" << x << " -> " << m().to_string(m_imp.basic_value(v)) << "\n";); @@ -1966,9 +1966,9 @@ namespace algebraic_numbers { imp & m_imp; polynomial::var2anum const & m_x2v; var2interval(imp & i, polynomial::var2anum const & x2v):m_imp(i), m_x2v(x2v) {} - virtual mpbqi_manager & m() const { return m_imp.bqim(); } - virtual bool contains(polynomial::var x) const { return m_x2v.contains(x) && !m_x2v(x).is_basic(); } - virtual mpbqi const & operator()(polynomial::var x) const { + mpbqi_manager & m() const override { return m_imp.bqim(); } + bool contains(polynomial::var x) const override { return m_x2v.contains(x) && !m_x2v(x).is_basic(); } + mpbqi const & operator()(polynomial::var x) const override { anum const & v = m_x2v(x); SASSERT(!v.is_basic()); return v.to_algebraic()->m_interval; @@ -2220,9 +2220,9 @@ namespace algebraic_numbers { m_x(x), m_v(v) { } - virtual manager & m() const { return m_am; } - virtual bool contains(polynomial::var x) const { return x == m_x || m_x2v.contains(x); } - virtual anum const & operator()(polynomial::var x) const { + manager & m() const override { return m_am; } + bool contains(polynomial::var x) const override { return x == m_x || m_x2v.contains(x); } + anum const & operator()(polynomial::var x) const override { if (x == m_x) return m_v; else @@ -2553,9 +2553,9 @@ namespace algebraic_numbers { m_x2v(x2v), m_v(v) { } - virtual manager & m() const { return m_am; } - virtual bool contains(polynomial::var x) const { return true; } - virtual anum const & operator()(polynomial::var x) const { + manager & m() const override { return m_am; } + bool contains(polynomial::var x) const override { return true; } + anum const & operator()(polynomial::var x) const override { if (m_x2v.contains(x)) return m_x2v(x); else @@ -2772,7 +2772,7 @@ namespace algebraic_numbers { manager::manager(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p, small_object_allocator * a) { m_own_allocator = false; m_allocator = a; - if (m_allocator == 0) { + if (m_allocator == nullptr) { m_own_allocator = true; m_allocator = alloc(small_object_allocator, "algebraic"); } diff --git a/src/math/polynomial/algebraic_numbers.h b/src/math/polynomial/algebraic_numbers.h index 3a8ee766b..fa9731b62 100644 --- a/src/math/polynomial/algebraic_numbers.h +++ b/src/math/polynomial/algebraic_numbers.h @@ -58,7 +58,7 @@ namespace algebraic_numbers { typedef _scoped_numeral scoped_numeral; typedef _scoped_numeral_vector scoped_numeral_vector; - manager(reslimit& rl, unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); + manager(reslimit& rl, unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); ~manager(); static void get_param_descrs(param_descrs & r); @@ -372,7 +372,7 @@ namespace algebraic_numbers { basic_cell * to_basic() const { SASSERT(is_basic()); return UNTAG(basic_cell*, m_cell); } algebraic_cell * to_algebraic() const { SASSERT(!is_basic()); return UNTAG(algebraic_cell*, m_cell); } public: - anum():m_cell(0) {} + anum():m_cell(nullptr) {} }; }; diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index d6d392148..35a95ce82 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -150,7 +150,6 @@ namespace polynomial { return r; } - /** \brief Monomials (power products) */ @@ -192,7 +191,7 @@ namespace polynomial { }; static unsigned get_obj_size(unsigned sz) { return sizeof(monomial) + sz * sizeof(power); } - + monomial(unsigned id, unsigned sz, power const * pws, unsigned h): m_ref_count(0), m_id(id), @@ -257,9 +256,7 @@ namespace polynomial { if (m_size < SMALL_MONOMIAL) { // use linear search for small monomials // search backwards since we usually ask for the degree of "big" variables - unsigned i = last; - while (i > 0) { - --i; + for (unsigned i = last; i-- > 0; ) { if (get_var(i) == x) return i; } @@ -513,7 +510,7 @@ namespace polynomial { monomial * allocate(unsigned capacity) { void * mem = memory::allocate(monomial::get_obj_size(capacity)); - return new (mem) monomial(UINT_MAX, 0, 0, 0); + return new (mem) monomial(UINT_MAX, 0, nullptr, 0); } void deallocate(monomial * ptr, unsigned capacity) { @@ -779,10 +776,10 @@ namespace polynomial { tmp_monomial m_tmp3; svector m_powers_tmp; public: - monomial_manager(small_object_allocator * a = 0) { + monomial_manager(small_object_allocator * a = nullptr) { m_ref_count = 0; m_next_var = 0; - if (a == 0) { + if (a == nullptr) { m_allocator = alloc(small_object_allocator, "polynomial"); m_own_allocator = true; } @@ -790,7 +787,7 @@ namespace polynomial { m_allocator = a; m_own_allocator = false; } - m_unit = mk_monomial(0, static_cast(0)); + m_unit = mk_monomial(0, static_cast(nullptr)); inc_ref(m_unit); } @@ -798,9 +795,8 @@ namespace polynomial { dec_ref(m_unit); CTRACE("polynomial", !m_monomials.empty(), tout << "monomials leaked\n"; - monomial_table::iterator it = m_monomials.begin(); monomial_table::iterator end = m_monomials.end(); - for (; it != end; ++it) { - (*it)->display(tout); tout << "\n"; + for (auto * m : m_monomials) { + m->display(tout); tout << "\n"; }); SASSERT(m_monomials.empty()); if (m_own_allocator) @@ -1189,7 +1185,7 @@ namespace polynomial { sqrt_tmp.reserve(sz); for (unsigned i = 0; i < sz; i++) { if (m->degree(i) % 2 == 1) - return 0; + return nullptr; sqrt_tmp.set_power(i, power(m->get_var(i), m->degree(i) / 2)); } sqrt_tmp.set_size(sz); @@ -1425,7 +1421,7 @@ namespace polynomial { } } - // Return the maximal variable y occuring in [m_ms + start, m_ms + end) that is smaller than x + // Return the maximal variable y occurring in [m_ms + start, m_ms + end) that is smaller than x var max_smaller_than(unsigned start, unsigned end, var x) { var max = null_var; for (unsigned i = start; i < end; i++) { @@ -1460,7 +1456,7 @@ namespace polynomial { } /** - \brief Make sure that the first monomial contains the maximal variable x occuring in the polynomial, + \brief Make sure that the first monomial contains the maximal variable x occurring in the polynomial, and x occurs with maximal degree. */ void make_first_maximal() { @@ -1510,6 +1506,8 @@ namespace polynomial { unsigned id() const { return m_id; } unsigned size() const { return m_size; } monomial * m(unsigned idx) const { SASSERT(idx < size()); return m_ms[idx]; } + monomial *const* begin() const { return m_ms; } + monomial *const* end() const { return m_ms + size(); } numeral const & a(unsigned idx) const { SASSERT(idx < size()); return m_as[idx]; } numeral & a(unsigned idx) { SASSERT(idx < size()); return m_as[idx]; } numeral const * as() const { return m_as; } @@ -1773,11 +1771,9 @@ namespace polynomial { } bool manager::is_linear(polynomial const * p) { - unsigned sz = p->size(); - for (unsigned i = 0; i < sz; i++) { - if (!is_linear(p->m(0))) + for (monomial* m : *p) + if (!is_linear(m)) return false; - } return true; } @@ -1931,7 +1927,7 @@ namespace polynomial { } public: - som_buffer():m_owner(0) {} + som_buffer():m_owner(nullptr) {} void reset() { if (empty()) @@ -2177,7 +2173,7 @@ namespace polynomial { public: som_buffer_vector() { - m_owner = 0; + m_owner = nullptr; } ~som_buffer_vector() { @@ -2189,7 +2185,7 @@ namespace polynomial { void set_owner(imp * owner) { SASSERT(m_owner == owner || m_owner == 0); - if (m_owner == 0) { + if (m_owner == nullptr) { m_owner = owner; unsigned sz = m_buffers.size(); for (unsigned i = 0; i < sz; i++) { @@ -2226,7 +2222,7 @@ namespace polynomial { numeral_vector m_tmp_as; monomial_vector m_tmp_ms; public: - cheap_som_buffer():m_owner(0) {} + cheap_som_buffer():m_owner(nullptr) {} void set_owner(imp * o) { m_owner = o; } bool empty() const { return m_tmp_ms.empty(); } @@ -2310,12 +2306,12 @@ namespace polynomial { cheap_som_buffer m_cheap_som_buffer2; void init() { - m_del_eh = 0; + m_del_eh = nullptr; m_som_buffer.set_owner(this); m_som_buffer2.set_owner(this); m_cheap_som_buffer.set_owner(this); m_cheap_som_buffer2.set_owner(this); - m_zero = mk_polynomial_core(0, 0, 0); + m_zero = mk_polynomial_core(0, nullptr, nullptr); m().set(m_zero_numeral, 0); inc_ref(m_zero); numeral one(1); @@ -2330,7 +2326,7 @@ namespace polynomial { m_wrapper(w), m_manager(m), m_upm(lim, m) { - if (mm == 0) + if (mm == nullptr) mm = alloc(monomial_manager); m_monomial_manager = mm; m_monomial_manager->inc_ref(); @@ -2396,6 +2392,7 @@ namespace polynomial { return mm().is_valid(x); } + void add_del_eh(del_eh * eh) { eh->m_next = m_del_eh; m_del_eh = eh; @@ -2422,13 +2419,13 @@ namespace polynomial { void del(polynomial * p) { TRACE("polynomial", tout << "deleting: "; p->display(tout, m_manager); tout << "\n";); - if (m_del_eh != 0) { + if (m_del_eh != nullptr) { del_eh * curr = m_del_eh; do { (*curr)(p); curr = curr->m_next; } - while (curr != 0); + while (curr != nullptr); } unsigned sz = p->size(); unsigned obj_sz = polynomial::get_obj_size(sz); @@ -2927,7 +2924,7 @@ namespace polynomial { imp * m_imp; ptr_vector m_data; public: - newton_interpolator_vector():m_imp(0) {} + newton_interpolator_vector():m_imp(nullptr) {} ~newton_interpolator_vector() { flush(); @@ -2986,7 +2983,7 @@ namespace polynomial { ms.push_back(p->m(i)); } std::sort(ms.begin(), ms.end(), lex_lt2(x)); - monomial * prev = 0; + monomial * prev = nullptr; for (unsigned i = 0; i < sz; i++) { monomial * orig_m = ms[i]; monomial * m; @@ -3212,7 +3209,7 @@ namespace polynomial { typedef sbuffer var_buffer; /** - Store in pws the variables occuring in p and their (minimal or maximal) degrees. + Store in pws the variables occurring in p and their (minimal or maximal) degrees. */ unsigned_vector m_var_degrees_tmp; template @@ -3473,7 +3470,7 @@ namespace polynomial { pp = mk_one(); return; } - // Apply filter and collect powers of x occuring in p + // Apply filter and collect powers of x occurring in p // The quick filter is the following: // If p contains a monomial x^k and no monomial of the form m*x^k m != 1, then // c = m_unit_poly @@ -3482,7 +3479,7 @@ namespace polynomial { // - found monomial x^k then iccp_powers[k]++; // - found monomial m*x^k then iccp_powers[k]+=2; // If after traversing p, there is a k s.t. iccp_powers[k] == 1, we know c == 1 - // We store iccp_powers the powers of x occuring in p. + // We store iccp_powers the powers of x occurring in p. sbuffer iccp_filter; sbuffer iccp_powers; iccp_filter.resize(d+1, 0); @@ -3834,7 +3831,7 @@ namespace polynomial { r = mk_const(d_a); return; } - if (C_star.get() == 0) { + if (C_star.get() == nullptr) { C_star = q; m().set(bound, p); } @@ -4156,7 +4153,7 @@ namespace polynomial { counter = 0; min_deg_q = deg_q; // start from scratch - if (sk == 0) { + if (sk == nullptr) { interpolator.reset(); interpolator.add(val, q); } @@ -4168,7 +4165,7 @@ namespace polynomial { } else if (deg_q == min_deg_q) { TRACE("mgcd_detail", tout << "adding sample point...\n";); - if (sk == 0) { + if (sk == nullptr) { interpolator.add(val, q); } else { @@ -4181,7 +4178,7 @@ namespace polynomial { continue; } bool found_candidate = false; - if (sk == 0) { + if (sk == nullptr) { SASSERT(interpolator.num_sample_points() > 0); interpolator.mk(x, H); TRACE("mgcd_detail", tout << "idx: " << idx << "\ncandidate H: " << H << "\n";); @@ -4217,14 +4214,14 @@ namespace polynomial { r = mul(ci_g, r); done = true; } - else if (sk != 0) { + else if (sk != nullptr) { throw sparse_mgcd_failed(); } } if (done) { TRACE("mgcd", tout << "idx: " << idx << "\nresult: " << r << "\n";); - if (sk == 0 && m_use_sparse_gcd) { + if (sk == nullptr && m_use_sparse_gcd) { // save skeleton skeleton * new_sk = alloc(skeleton, *this, H, x); m_mgcd_skeletons.set(idx, new_sk); @@ -4258,7 +4255,7 @@ namespace polynomial { m_mgcd_skeletons.reset(); for (unsigned i = 0; i < num_vars; i++) { vars.push_back(var_min_degrees[i].get_var()); - m_mgcd_skeletons.push_back(0); + m_mgcd_skeletons.push_back(nullptr); } scoped_numeral c_u(m()), c_v(m()); @@ -4314,7 +4311,7 @@ namespace polynomial { r = mk_const(d_a); return; } - if (C_star.get() == 0) { + if (C_star.get() == nullptr) { C_star = q; m().set(bound, p); } @@ -4906,7 +4903,7 @@ namespace polynomial { */ template void pseudo_division_core(polynomial const * p, polynomial const * q, var x, unsigned & d, polynomial_ref & Q, polynomial_ref & R, - var2degree const * x2d = 0) { + var2degree const * x2d = nullptr) { SASSERT(is_valid(x)); SASSERT(!ModD || x2d != 0); TRACE("polynomial", tout << "pseudo_division\np: "; p->display(tout, m_manager); @@ -5141,7 +5138,7 @@ namespace polynomial { } monomial const * m_r = R.m(max_R); numeral const & a_r = R.a(max_R); - monomial * m_r_q = 0; + monomial * m_r_q = nullptr; VERIFY(div(m_r, m_q, m_r_q)); TRACE("polynomial", tout << "m_r: "; m_r->display(tout); tout << "\nm_q: "; m_q->display(tout); tout << "\n"; if (m_r_q) { tout << "m_r_q: "; m_r_q->display(tout); tout << "\n"; }); @@ -5178,7 +5175,7 @@ namespace polynomial { } monomial const * m_r = R.m(max_R); numeral const & a_r = R.a(max_R); - monomial * m_r_q = 0; + monomial * m_r_q = nullptr; bool q_div_r = div(m_r, m_q, m_r_q); m_r_q_ref = m_r_q; TRACE("polynomial", tout << "m_r: "; m_r->display(tout); tout << "\nm_q: "; m_q->display(tout); tout << "\n"; @@ -5482,7 +5479,7 @@ namespace polynomial { } p_prime = derivative(p, x); resultant(p, p_prime, x, r); - bool sign = (static_cast(m) * static_cast(m-1))%4 != 0; + bool sign = (static_cast(m) * static_cast(m-1))%4 != 0; TRACE("resultant", tout << "discriminant sign: " << sign << "\n";); scoped_numeral lc(m_manager); if (const_coeff(p, x, m, lc)) { @@ -5642,7 +5639,7 @@ namespace polynomial { pw(h1, d1, hs1); hs1 = mul(g1, hs1); G3 = exact_div(Gh3, hs1); - hs1 = 0; + hs1 = nullptr; } // prepare for next iteration @@ -6086,7 +6083,7 @@ namespace polynomial { polynomial_vector::iterator end2 = m_polynomials.end(); for (; it2 != end2; ++it2) { polynomial * p = *it2; - if (p == 0) + if (p == nullptr) continue; p->make_first_maximal(); SASSERT(p->size() <= 1 || !p->lex_sorted()); @@ -6101,6 +6098,33 @@ namespace polynomial { }); } + lbool sign(monomial* m, numeral const& c, svector const& sign_of_vars) { + unsigned sz = size(m); + lbool sign1 = m_manager.is_pos(c) ? l_true : l_false; + for (unsigned i = 0; i < sz; ++i) { + var v = get_var(m, i); + unsigned d = degree(m, i); + lbool sign2 = sign_of_vars.get(v, l_undef); + if (sign2 == l_undef) + return l_undef; + else if (1 == (d % 2) && sign2 == l_false) { + sign1 = sign1 == l_true ? l_false : l_true; + } + } + return sign1; + } + + lbool sign(polynomial const * p, svector const& sign_of_vars) { + unsigned sz = size(p); + if (sz == 0) return l_undef; + lbool sign1 = sign(p->m(0), p->a(0), sign_of_vars); + for (unsigned i = 1; sign1 != l_undef && i < sz; ++i) { + if (sign(p->m(i), p->a(i), sign_of_vars) != sign1) + return l_undef; + } + return sign1; + } + bool is_pos(polynomial const * p) { bool found_unit = false; unsigned sz = p->size(); @@ -6326,9 +6350,9 @@ namespace polynomial { m_var_pos(buffer, xs_sz, xs), m_vs(vs) { } - virtual unsynch_mpq_manager & m() const { UNREACHABLE(); static unsynch_mpq_manager m; return m; } - virtual bool contains(var x) const { return m_var_pos(x) != UINT_MAX; } - virtual mpq const & operator()(var x) const { return m_vs[m_var_pos(x)]; } + unsynch_mpq_manager & m() const override { UNREACHABLE(); static unsynch_mpq_manager m; return m; } + bool contains(var x) const override { return m_var_pos(x) != UINT_MAX; } + mpq const & operator()(var x) const override { return m_vs[m_var_pos(x)]; } }; polynomial * substitute(polynomial const * p, unsigned xs_sz, var const * xs, mpq const * vs) { @@ -6372,6 +6396,31 @@ namespace polynomial { R.add(new_a, mk_monomial(new_m)); } return R.mk(); + } + + void substitute(polynomial const* r, var x, polynomial const* p, polynomial const* q, polynomial_ref& result) { + unsigned md = degree(r, x); + if (md == 0) { + result = const_cast(r); + return; + } + result = nullptr; + polynomial_ref p1(pm()), q1(pm()); + polynomial_ref_buffer ps(pm()); + unsigned sz = r->size(); + for (unsigned i = 0; i < sz; i++) { + monomial * m0 = r->m(i); + unsigned dm = m0->degree_of(x); + SASSERT(md >= dm); + monomial_ref m1(div_x(m0, x), pm()); + pw(p, dm, p1); + pw(q, md - dm, q1); + p1 = mul(r->a(i), m1, p1 * q1); + if (result) + result = add(result, p1); + else + result = p1; + } } @@ -6478,9 +6527,9 @@ namespace polynomial { numeral const & m_val; public: single_var2value(numeral_manager & m, var x, numeral const & val):m_manager(m), m_x(x), m_val(val) {} - virtual numeral_manager & m() const { return m_manager; } - virtual bool contains(var x) const { return m_x == x; } - virtual numeral const & operator()(var x) const { SASSERT(m_x == x); return m_val; } + numeral_manager & m() const override { return m_manager; } + bool contains(var x) const override { return m_x == x; } + numeral const & operator()(var x) const override { SASSERT(m_x == x); return m_val; } }; void univ_eval(polynomial const * p, var x, numeral const & val, numeral & r) { @@ -6914,10 +6963,22 @@ namespace polynomial { return m_imp->m().set_zp(p); } - void manager::set_zp(uint64 p) { + void manager::set_zp(uint64_t p) { return m_imp->m().set_zp(p); } + bool manager::is_var(polynomial const* p, var& v) { + return p->size() == 1 && is_var(p->m(0), v) && m_imp->m().is_one(p->a(0)); + } + + bool manager::is_var(monomial const* m, var& v) { + return m->size() == 1 && m->degree(0) == 1 && (v = m->get_var(0), true); + } + + bool manager::is_var_num(polynomial const* p, var& v, scoped_numeral& n) { + return p->size() == 2 && m_imp->m().is_one(p->a(0)) && is_var(p->m(0), v) && is_unit(p->m(1)) && (n = p->a(1), true); + } + small_object_allocator & manager::allocator() const { return m_imp->mm().allocator(); } @@ -7271,6 +7332,10 @@ namespace polynomial { void manager::psc_chain(polynomial const * p, polynomial const * q, var x, polynomial_ref_vector & S) { m_imp->psc_chain(p, q, x, S); } + + lbool manager::sign(polynomial const * p, svector const& sign_of_vars) { + return m_imp->sign(p, sign_of_vars); + } bool manager::is_pos(polynomial const * p) { return m_imp->is_pos(p); @@ -7307,6 +7372,10 @@ namespace polynomial { polynomial * manager::substitute(polynomial const * p, unsigned xs_sz, var const * xs, numeral const * vs) { return m_imp->substitute(p, xs_sz, xs, vs); } + + void manager::substitute(polynomial const* r, var x, polynomial const* p, polynomial const* q, polynomial_ref& result) { + m_imp->substitute(r, x, p, q, result); + } void manager::factor(polynomial const * p, factors & r, factor_params const & params) { m_imp->factor(p, r, params); diff --git a/src/math/polynomial/polynomial.h b/src/math/polynomial/polynomial.h index 43b3c1138..374a51084 100644 --- a/src/math/polynomial/polynomial.h +++ b/src/math/polynomial/polynomial.h @@ -29,6 +29,7 @@ Notes: #include "util/params.h" #include "util/mpbqi.h" #include "util/rlimit.h" +#include "util/lbool.h" class small_object_allocator; @@ -98,7 +99,7 @@ namespace polynomial { }; struct display_var_proc { - virtual void operator()(std::ostream & out, var x) const { out << "x" << x; } + virtual std::ostream& operator()(std::ostream & out, var x) const { return out << "x" << x; } }; class polynomial; @@ -191,7 +192,7 @@ namespace polynomial { private: imp * m_imp; public: - manager(reslimit& lim, numeral_manager & m, monomial_manager * mm = 0); + manager(reslimit& lim, numeral_manager & m, monomial_manager * mm = nullptr); manager(reslimit& lim, numeral_manager & m, small_object_allocator * a); ~manager(); @@ -217,7 +218,7 @@ namespace polynomial { \brief Set manager as Z_p[X1, ..., Xn] */ void set_zp(numeral const & p); - void set_zp(uint64 p); + void set_zp(uint64_t p); /** \brief Abstract event handler. @@ -226,13 +227,13 @@ namespace polynomial { friend class manager; del_eh * m_next; public: - del_eh():m_next(0) {} + del_eh():m_next(nullptr) {} virtual void operator()(polynomial * p) = 0; }; /** \brief Install a "delete polynomial" event handler. - The even hanlder is not owned by the polynomial manager. + The event handler is not owned by the polynomial manager. If eh = 0, then it uninstall the event handler. */ void add_del_eh(del_eh * eh); @@ -306,12 +307,27 @@ namespace polynomial { \brief Return true if m is linear (i.e., it is of the form 1 or x). */ static bool is_linear(monomial const * m); - + /** \brief Return true if all monomials in p are linear. */ static bool is_linear(polynomial const * p); + /** + \brief Return true if the monomial is a variable. + */ + static bool is_var(monomial const* p, var& v); + + /** + \brief Return true if the polynomial is a variable. + */ + bool is_var(polynomial const* p, var& v); + + /** + \brief Return true if the polynomial is of the form x + k + */ + bool is_var_num(polynomial const* p, var& v, scoped_numeral& n); + /** \brief Return the degree of variable x in p. */ @@ -410,7 +426,7 @@ namespace polynomial { polynomial * flip_sign_if_lm_neg(polynomial const * p); /** - \breif Return the gcd g of p and q. + \brief Return the gcd g of p and q. */ void gcd(polynomial const * p, polynomial const * q, polynomial_ref & g); @@ -837,7 +853,7 @@ namespace polynomial { void resultant(polynomial const * p, polynomial const * q, var x, polynomial_ref & r); /** - \brief Stroe in r the discriminant of p with respect to variable x. + \brief Store in r the discriminant of p with respect to variable x. discriminant(p, x, r) == resultant(p, derivative(p, x), x, r) */ void discriminant(polynomial const * p, var x, polynomial_ref & r); @@ -860,7 +876,13 @@ namespace polynomial { \brief Return true if p is a square, and store its square root in r. */ bool sqrt(polynomial const * p, polynomial_ref & r); - + + + /** + \brief obtain the sign of the polynomial given sign of variables. + */ + lbool sign(polynomial const* p, svector const& sign_of_vars); + /** \brief Return true if p is always positive for any assignment of its variables. @@ -936,6 +958,13 @@ namespace polynomial { return substitute(p, 1, &x, &v); } + /** + \brief Apply substitution [x -> p/q] in r. + That is, given r \in Z[x, y_1, .., y_m] return + polynomial q^k * r(p/q, y_1, .., y_m), where k is the maximal degree of x in r. + */ + void substitute(polynomial const* r, var x, polynomial const* p, polynomial const* q, polynomial_ref& result); + /** \brief Factorize the given polynomial p and store its factors in r. */ @@ -1014,7 +1043,7 @@ namespace polynomial { scoped_numeral m_p; public: scoped_set_zp(manager & _m, numeral const & p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } - scoped_set_zp(manager & _m, uint64 p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } + scoped_set_zp(manager & _m, uint64_t p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } ~scoped_set_zp() { if (m_modular) m.set_zp(m_p); else m.set_z(); } }; }; diff --git a/src/math/polynomial/polynomial_cache.cpp b/src/math/polynomial/polynomial_cache.cpp index 9a021ce36..a617e9412 100644 --- a/src/math/polynomial/polynomial_cache.cpp +++ b/src/math/polynomial/polynomial_cache.cpp @@ -47,7 +47,7 @@ namespace polynomial { m_x(x), m_hash(h), m_result_sz(0), - m_result(0) { + m_result(nullptr) { } struct hash_proc { unsigned operator()(psc_chain_entry const * entry) const { return entry->m_hash; } }; @@ -69,7 +69,7 @@ namespace polynomial { m_p(p), m_hash(h), m_result_sz(0), - m_result(0) { + m_result(nullptr) { } struct hash_proc { unsigned operator()(factor_entry const * entry) const { return entry->m_hash; } }; diff --git a/src/math/polynomial/polynomial_var2value.h b/src/math/polynomial/polynomial_var2value.h index c4aa16dfa..523703709 100644 --- a/src/math/polynomial/polynomial_var2value.h +++ b/src/math/polynomial/polynomial_var2value.h @@ -32,9 +32,9 @@ namespace polynomial { public: simple_var2value(ValManager & m):m_vs(m) {} void push_back(var x, typename ValManager::numeral const & v) { m_xs.push_back(x); m_vs.push_back(v); } - virtual ValManager & m() const { return m_vs.m(); } - virtual bool contains(var x) const { return std::find(m_xs.begin(), m_xs.end(), x) != m_xs.end(); } - virtual typename ValManager::numeral const & operator()(var x) const { + ValManager & m() const override { return m_vs.m(); } + bool contains(var x) const override { return std::find(m_xs.begin(), m_xs.end(), x) != m_xs.end(); } + typename ValManager::numeral const & operator()(var x) const override { for (unsigned i = 0; i < m_xs.size(); i++) if (m_xs[i] == x) return m_vs[i]; diff --git a/src/math/polynomial/rpolynomial.cpp b/src/math/polynomial/rpolynomial.cpp index 5e7a829f6..90db9c832 100644 --- a/src/math/polynomial/rpolynomial.cpp +++ b/src/math/polynomial/rpolynomial.cpp @@ -63,8 +63,8 @@ namespace rpolynomial { m_wrapper(w), m_manager(m), m_allocator(a), - m_own_allocator(a == 0) { - if (a == 0) + m_own_allocator(a == nullptr) { + if (a == nullptr) m_allocator = alloc(small_object_allocator, "rpolynomial"); } @@ -107,7 +107,7 @@ namespace rpolynomial { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = p->arg(i); - if (pn == 0) + if (pn == nullptr) continue; if (is_num(pn)) { del_numeral(to_num_ptr(pn)); @@ -141,11 +141,11 @@ namespace rpolynomial { static bool is_const(polynomial const * p) { SASSERT(p == 0 || (p->max_var() == null_var) == (p->size() == 1 && p->arg(0) != 0 && is_num(p->arg(0)))); - return p == 0 || p->max_var() == null_var; + return p == nullptr || p->max_var() == null_var; } bool is_zero(polynomial const * p) { - return p == 0; + return p == nullptr; } static bool is_univariate(polynomial const * p) { @@ -154,7 +154,7 @@ namespace rpolynomial { unsigned sz = p->size(); for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = p->arg(i); - if (pn == 0) + if (pn == nullptr) continue; if (is_poly(pn)) return false; @@ -169,7 +169,7 @@ namespace rpolynomial { SASSERT(sz > 0); SASSERT(p->arg(sz - 1) != 0); for (unsigned i = 0; i < sz - 1; i++) { - if (p->arg(i) != 0) + if (p->arg(i) != nullptr) return false; } SASSERT(is_poly(p->arg(sz - 1))); @@ -179,13 +179,13 @@ namespace rpolynomial { unsigned degree(polynomial const * p) { SASSERT(p != 0); SASSERT(p->size() > 0); - return p == 0 ? 0 : p->size() - 1; + return p == nullptr ? 0 : p->size() - 1; } bool eq(polynomial const * p1, polynomial const * p2) { if (p1 == p2) return true; - if (p1 == 0 || p2 == 0) + if (p1 == nullptr || p2 == nullptr) return false; if (p1->size() != p2->size()) return false; @@ -195,9 +195,9 @@ namespace rpolynomial { for (unsigned i = 0; i < sz; i++) { poly_or_num * pn1 = p1->arg(i); poly_or_num * pn2 = p2->arg(i); - if (pn1 == 0 && pn2 == 0) + if (pn1 == nullptr && pn2 == nullptr) continue; - if (pn1 == 0 || pn2 == 0) + if (pn1 == nullptr || pn2 == nullptr) return false; if (is_num(pn1) && is_num(pn2)) { if (!m_manager.eq(to_num(pn1), to_num(pn2))) @@ -217,7 +217,7 @@ namespace rpolynomial { void inc_ref_args(unsigned sz, poly_or_num * const * args) { for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = args[i]; - if (pn == 0 || is_num(pn)) + if (pn == nullptr || is_num(pn)) continue; inc_ref(to_poly(pn)); } @@ -226,7 +226,7 @@ namespace rpolynomial { void dec_ref_args(unsigned sz, poly_or_num * const * args) { for (unsigned i = 0; i < sz; i++) { poly_or_num * pn = args[i]; - if (pn == 0 || is_num(pn)) + if (pn == nullptr || is_num(pn)) continue; dec_ref(to_poly(pn)); } @@ -234,7 +234,7 @@ namespace rpolynomial { unsigned trim(unsigned sz, poly_or_num * const * args) { while (sz > 0) { - if (args[sz - 1] != 0) + if (args[sz - 1] != nullptr) return sz; sz--; } @@ -281,8 +281,8 @@ namespace rpolynomial { polynomial * mk_poly(unsigned sz, poly_or_num * const * args, var max_var) { poly_or_num * _p = mk_poly_core(sz, args, max_var); - if (_p == 0) - return 0; + if (_p == nullptr) + return nullptr; else if (is_num(_p)) return allocate_poly(1, &_p, null_var); else @@ -291,7 +291,7 @@ namespace rpolynomial { polynomial * mk_const(numeral const & n) { if (m_manager.is_zero(n)) - return 0; + return nullptr; numeral * a = mk_numeral(); m_manager.set(*a, n); poly_or_num * _a = to_poly_or_num(a); @@ -322,8 +322,8 @@ namespace rpolynomial { } poly_or_num * unpack(polynomial const * p) { - if (p == 0) { - return 0; + if (p == nullptr) { + return nullptr; } else if (is_const(p)) { SASSERT(p->size() == 1); @@ -336,8 +336,8 @@ namespace rpolynomial { } polynomial * pack(poly_or_num * p) { - if (p == 0) - return 0; + if (p == nullptr) + return nullptr; else if (is_num(p)) return mk_poly(1, &p, null_var); else @@ -345,8 +345,8 @@ namespace rpolynomial { } poly_or_num * mul_core(numeral const & c, poly_or_num * p) { - if (m_manager.is_zero(c) || p == 0) { - return 0; + if (m_manager.is_zero(c) || p == nullptr) { + return nullptr; } else if (is_num(p)) { numeral * r = mk_numeral(); @@ -379,7 +379,7 @@ namespace rpolynomial { if (m_manager.is_zero(c)) { return p; } - else if (p == 0) { + else if (p == nullptr) { numeral * r = mk_numeral(); m_manager.set(*r, c); return to_poly_or_num(r); @@ -388,7 +388,7 @@ namespace rpolynomial { numeral a; m_manager.add(c, to_num(p), a); if (m_manager.is_zero(a)) - return 0; + return nullptr; numeral * new_arg = mk_numeral(); m_manager.swap(*new_arg, a); return to_poly_or_num(new_arg); @@ -662,7 +662,7 @@ namespace rpolynomial { while (i > 0) { --i; poly_or_num * pn = p->arg(i); - if (pn == 0) + if (pn == nullptr) continue; if (first) first = false; @@ -730,7 +730,7 @@ namespace rpolynomial { } bool manager::is_zero(polynomial const * p) { - return p == 0; + return p == nullptr; } #if 0 diff --git a/src/math/polynomial/rpolynomial.h b/src/math/polynomial/rpolynomial.h index f2ba78894..ba51f785e 100644 --- a/src/math/polynomial/rpolynomial.h +++ b/src/math/polynomial/rpolynomial.h @@ -51,7 +51,7 @@ namespace rpolynomial { private: imp * m_imp; public: - manager(numeral_manager & m, small_object_allocator * a = 0); + manager(numeral_manager & m, small_object_allocator * a = nullptr); ~manager(); numeral_manager & m() const; diff --git a/src/math/polynomial/upolynomial.cpp b/src/math/polynomial/upolynomial.cpp index fe890b040..cc2442981 100644 --- a/src/math/polynomial/upolynomial.cpp +++ b/src/math/polynomial/upolynomial.cpp @@ -190,7 +190,7 @@ namespace upolynomial { // Copy elements from p to buffer. void core_manager::set(unsigned sz, numeral const * p, numeral_vector & buffer) { - if (p != 0 && buffer.c_ptr() == p) { + if (p != nullptr && buffer.c_ptr() == p) { SASSERT(buffer.size() == sz); return; } @@ -781,17 +781,15 @@ namespace upolynomial { set(q.size(), q.c_ptr(), C); m().set(bound, p); } + else if (q.size() < C.size() || m().m().is_even(p) || m().m().is_even(bound)) { + // discard accumulated image, it was affected by unlucky primes + TRACE("mgcd", tout << "discarding image\n";); + set(q.size(), q.c_ptr(), C); + m().set(bound, p); + } else { - if (q.size() < C.size()) { - // discard accumulated image, it was affected by unlucky primes - TRACE("mgcd", tout << "discarding image\n";); - set(q.size(), q.c_ptr(), C); - m().set(bound, p); - } - else { - CRA_combine_images(q, p, C, bound); - TRACE("mgcd", tout << "new combined:\n"; display_star(tout, C); tout << "\n";); - } + CRA_combine_images(q, p, C, bound); + TRACE("mgcd", tout << "new combined:\n"; display_star(tout, C); tout << "\n";); } numeral_vector & candidate = q; get_primitive(C, candidate); diff --git a/src/math/polynomial/upolynomial.h b/src/math/polynomial/upolynomial.h index f8efae465..439b4be9f 100644 --- a/src/math/polynomial/upolynomial.h +++ b/src/math/polynomial/upolynomial.h @@ -117,7 +117,7 @@ namespace upolynomial { numeral_vector m_sqf_tmp2; numeral_vector m_pw_tmp; - static bool is_alias(numeral const * p, numeral_vector & buffer) { return buffer.c_ptr() != 0 && buffer.c_ptr() == p; } + static bool is_alias(numeral const * p, numeral_vector & buffer) { return buffer.c_ptr() != nullptr && buffer.c_ptr() == p; } void neg_core(unsigned sz1, numeral const * p1, numeral_vector & buffer); void add_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); void sub_core(unsigned sz1, numeral const * p1, unsigned sz2, numeral const * p2, numeral_vector & buffer); @@ -153,7 +153,7 @@ namespace upolynomial { \brief Set manager as Z_p[X] */ void set_zp(numeral const & p) { m().set_zp(p); } - void set_zp(uint64 p) { m().set_zp(p); } + void set_zp(uint64_t p) { m().set_zp(p); } void checkpoint(); @@ -486,7 +486,7 @@ namespace upolynomial { core_manager::scoped_numeral m_p; public: scoped_set_zp(core_manager & _m, numeral const & p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } - scoped_set_zp(core_manager & _m, uint64 p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } + scoped_set_zp(core_manager & _m, uint64_t p):m(_m), m_modular(m.modular()), m_p(m.m()) { m_p = m.p(); m.set_zp(p); } ~scoped_set_zp() { if (m_modular) m.set_zp(m_p); else m.set_z(); } }; diff --git a/src/math/polynomial/upolynomial_factorization.cpp b/src/math/polynomial/upolynomial_factorization.cpp index 11a37648a..5d9d3f1f1 100644 --- a/src/math/polynomial/upolynomial_factorization.cpp +++ b/src/math/polynomial/upolynomial_factorization.cpp @@ -38,9 +38,9 @@ unsigned get_p_from_manager(zp_numeral_manager const & zp_nm) { if (!nm.is_uint64(p)) { throw upolynomial_exception("The prime number attempted in factorization is too big!"); } - uint64 p_uint64 = nm.get_uint64(p); + uint64_t p_uint64 = nm.get_uint64(p); unsigned p_uint = static_cast(p_uint64); - if (((uint64)p_uint) != p_uint64) { + if (((uint64_t)p_uint) != p_uint64) { throw upolynomial_exception("The prime number attempted in factorization is too big!"); } return p_uint; @@ -152,7 +152,7 @@ public: } /** - \brief 'Disagonalizes' the matrix using only column operations. The reusling matrix will have -1 at pivot + \brief 'Diagonalizes' the matrix using only column operations. The resulting matrix will have -1 at pivot elements. Returns the rank of the null space. */ unsigned diagonalize() { @@ -170,7 +170,7 @@ public: m_column_pivot[j] = i; m_row_pivot[i] = j; - // found a pivot, to make it -1 we compute the multuplier -p^-1 + // found a pivot, to make it -1 we compute the multiplier -p^-1 m_zpm.set(multiplier, get(i, j)); m_zpm.inv(multiplier); m_zpm.neg(multiplier); @@ -201,7 +201,7 @@ public: } /** - If rank of the matrix is n - r, we are interested in linearly indeprendent vectors v_1, ..., v_r (the basis of + If rank of the matrix is n - r, we are interested in linearly independent vectors v_1, ..., v_r (the basis of the null space), such that v_k A = 0. This method will give one at a time. The method returns true if vector has been computed properly. The first vector [1, 0, ..., 0] is ignored (m_null_row starts from 1). */ @@ -417,7 +417,7 @@ bool zp_factor_square_free_berlekamp(zp_manager & upm, numeral_vector const & f, // construct the berlekamp Q matrix to get the null space berlekamp_matrix Q_I(upm, f); - // copy the inital polynomial to factors + // copy the initial polynomial to factors unsigned first_factor = factors.distinct_factors(); factors.push_back(f, 1); @@ -473,7 +473,7 @@ bool zp_factor_square_free_berlekamp(zp_manager & upm, numeral_vector const & f, // get the gcd upm.gcd(v_k.size(), v_k.c_ptr(), current_factor.size(), current_factor.c_ptr(), gcd); - // if the gcd is 1, or the the gcd is f, we just ignroe it + // if the gcd is 1, or the gcd is f, we just ignore it if (gcd.size() != 1 && gcd.size() != current_factor.size()) { // get the divisor also (no need to normalize the div, both are monic) @@ -568,13 +568,13 @@ bool check_hansel_lift(z_manager & upm, numeral_vector const & C, } /** - Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and and a = p^{a_k}, b = p^{b_k}, + Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and a = p^{a_k}, b = p^{b_k}, r = (a, b), with the following assumptions: - (1) UA + VB = 1 (mod a) + (1) UA + VB = 1 (mod a) (2) C = A*B (mod b) - (3) (l(A), r) = 1 (importand in order to divide by A, i.e. to invert l(A)) - (4) deg(A) + deg(B) = deg(C) + (3) (l(A), r) = 1 (important in order to divide by A, i.e. to invert l(A)) + (4) deg(A) + deg(B) = deg(C) The output of is two polynomials A1, B1 such that A1 = A (mod b), B1 = B (mod b), l(A1) = l(A), deg(A1) = deg(A), deg(B1) = deg(B) and C = A1 B1 (mod b*r). Such A1, B1 are unique if @@ -625,7 +625,7 @@ void hensel_lift(z_manager & upm, numeral const & a, numeral const & b, numeral // having (1) AU + BV = 1 (mod r) and (5) AT + BS = f (mod r), we know that // A*(fU) + B*(fV) = f (mod r), i.e. T = fU, S = fV is a solution // but we also know that we need an S with deg(S) <= deg(A) so we can do the following - // we know that l(A) is invertible so we can find the exact remainder of fV with A, i.e. find the qotient + // we know that l(A) is invertible so we can find the exact remainder of fV with A, i.e. find the quotient // t in the division and set // A*(fU + tB) + B*(fV - tA) = f // T = fU + tB, S = fU - tA @@ -1075,7 +1075,7 @@ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, while (trials < params.m_p_trials) { upm.checkpoint(); // construct prime to check - uint64 next_prime = prime_it.next(); + uint64_t next_prime = prime_it.next(); if (next_prime > params.m_max_p) { fs.push_back(f_pp, k); return false; @@ -1093,7 +1093,7 @@ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, continue; } - // if it's not square free, we also try somehting else + // if it's not square free, we also try something else scoped_numeral_vector f_pp_zp(nm); to_zp_manager(zp_upm, f_pp, f_pp_zp); @@ -1170,7 +1170,7 @@ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, zp_numeral_manager & zpe_nm = zpe_upm.m(); zp_factors zpe_fs(zpe_upm); - // this might give something bigger than p^e, but the lifting proocedure will update the zpe_nm + // this might give something bigger than p^e, but the lifting procedure will update the zpe_nm // zp factors are monic, so will be the zpe factors, i.e. f_pp = zpe_fs * lc(f_pp) (mod p^e) hensel_lift(upm, f_pp, zp_fs, e, zpe_fs); @@ -1182,7 +1182,7 @@ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, scoped_numeral f_pp_lc(nm); zpe_nm.set(f_pp_lc, f_pp.back()); - // we always keep in f_pp the the actual primitive part f_pp*lc(f_pp) + // we always keep in f_pp the actual primitive part f_pp*lc(f_pp) upm.mul(f_pp, f_pp_lc); // now we go through the combinations of factors to check construct the factorization @@ -1287,7 +1287,7 @@ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, fs.push_back(f_pp, k); } else { - // if a constant it must be 1 (it was primitve) + // if a constant it must be 1 (it was primitive) SASSERT(f_pp.size() == 1 && nm.is_one(f_pp.back())); } diff --git a/src/math/polynomial/upolynomial_factorization.h b/src/math/polynomial/upolynomial_factorization.h index 6fe0f2be6..c33f1844d 100644 --- a/src/math/polynomial/upolynomial_factorization.h +++ b/src/math/polynomial/upolynomial_factorization.h @@ -34,12 +34,12 @@ namespace upolynomial { typedef manager::scoped_numeral scoped_numeral; /** - \breif Factor f into f = f_1^k_1 * ... * p_n^k_n, such that p_i are square-free and coprime. + \brief Factor f into f = f_1^k_1 * ... * p_n^k_n, such that p_i are square-free and coprime. */ void zp_square_free_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & sq_free_factors); /** - \brief Factor the monic square-free polynomial f from Z_p[x]. Returns true if factorization was sucesseful, or false + \brief Factor the monic square-free polynomial f from Z_p[x]. Returns true if factorization was successful, or false if f is an irreducible square-free polynomial in Z_p[x]. */ bool zp_factor_square_free(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors); @@ -55,17 +55,17 @@ namespace upolynomial { bool zp_factor_square_free_berlekamp(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors, bool randomized = true); /** - \brief Factor the polynomial f from Z_p[x]. Returns true if factorization was sucesseful, or false if f is + \brief Factor the polynomial f from Z_p[x]. Returns true if factorization was successful, or false if f is an irreducible polynomial in Z_p[x] */ bool zp_factor(zp_manager & zp_upm, numeral_vector const & f, zp_factors & factors); /** - \brief Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and and a = p^{a_k}, b = p^{b_k}, + \brief Performs a Hensel lift of A and B in Z_a to Z_b, where p is prime and a = p^{a_k}, b = p^{b_k}, r = (a, b), with the following assumptions: * UA + VB = 1 (mod a) * C = AB (mod b) - * (l(A), r) = 1 (importand in order to divide by A, i.e. to invert l(A)) + * (l(A), r) = 1 (important in order to divide by A, i.e. to invert l(A)) the output of is two polynomials A1, B1 (replacing A and B) such that A1 = A (mod b), B1 = B (mod b), l(A1) = l(A), deg(A1) = deg(A), deg(B1) = deg(B) and C = A1 B1 (mod b*r). Such A1, B1 are unique if r is prime. See [3] p. 138. @@ -82,7 +82,7 @@ namespace upolynomial { void hensel_lift(z_manager & upm, numeral_vector const & f, zp_factors const & factors_p, unsigned e, zp_factors & factors_pe); /** - \brief Factor the square-free polynomial f from Z[x]. Returns true if factorization was sucesseful, or false if + \brief Factor the square-free polynomial f from Z[x]. Returns true if factorization was successful, or false if f is an irreducible polynomial in Z[x]. The vector of factors is cleared. */ bool factor_square_free(z_manager & upm, numeral_vector const & f, factors & fs, factor_params const & ps = factor_params()); diff --git a/src/math/polynomial/upolynomial_factorization_int.h b/src/math/polynomial/upolynomial_factorization_int.h index ea2b51d8f..e422c15a6 100644 --- a/src/math/polynomial/upolynomial_factorization_int.h +++ b/src/math/polynomial/upolynomial_factorization_int.h @@ -322,7 +322,7 @@ namespace upolynomial { /** \brief Filter the ones not in the degree set. */ - bool filter_current() const { + bool filter_current() const override { // select only the ones that have degrees in the degree set if (!m_degree_set.in_set(current_degree())) { diff --git a/src/math/realclosure/mpz_matrix.cpp b/src/math/realclosure/mpz_matrix.cpp index 62a328b8d..3bbd387c6 100644 --- a/src/math/realclosure/mpz_matrix.cpp +++ b/src/math/realclosure/mpz_matrix.cpp @@ -49,7 +49,7 @@ void mpz_matrix_manager::mk(unsigned m, unsigned n, mpz_matrix & A) { } void mpz_matrix_manager::del(mpz_matrix & A) { - if (A.a_ij != 0) { + if (A.a_ij != nullptr) { for (unsigned i = 0; i < A.m; i++) for (unsigned j = 0; j < A.n; j++) nm().del(A(i,j)); @@ -57,7 +57,7 @@ void mpz_matrix_manager::del(mpz_matrix & A) { m_allocator.deallocate(sz, A.a_ij); A.m = 0; A.n = 0; - A.a_ij = 0; + A.a_ij = nullptr; } } @@ -208,7 +208,7 @@ bool mpz_matrix_manager::eliminate(mpz_matrix & A, mpz * b, unsigned k1, unsigne // a_ik <- 0 nm().set(A(i, k2), 0); // normalize row i - if (!normalize_row(A.row(i), A.n, b ? &(b[i]) : 0, int_solver)) + if (!normalize_row(A.row(i), A.n, b ? &(b[i]) : nullptr, int_solver)) return false; } SASSERT(nm().is_zero(A(i, k2))); @@ -386,7 +386,7 @@ unsigned mpz_matrix_manager::linear_independent_rows(mpz_matrix const & _A, unsi r_sz++; if (r_sz >= A.n()) break; - eliminate(A, 0, k1, k2, false); + eliminate(A, nullptr, k1, k2, false); k2++; } std::sort(r, r + r_sz); diff --git a/src/math/realclosure/mpz_matrix.h b/src/math/realclosure/mpz_matrix.h index f5d7358da..92716ec0d 100644 --- a/src/math/realclosure/mpz_matrix.h +++ b/src/math/realclosure/mpz_matrix.h @@ -45,7 +45,7 @@ class mpz_matrix { unsigned n; mpz * a_ij; public: - mpz_matrix():m(0), n(0), a_ij(0) {} + mpz_matrix():m(0), n(0), a_ij(nullptr) {} mpz const & operator()(unsigned i, unsigned j) const { SASSERT(i < m); SASSERT(j < n); diff --git a/src/math/realclosure/realclosure.cpp b/src/math/realclosure/realclosure.cpp index 8f2d933e2..d41937c29 100644 --- a/src/math/realclosure/realclosure.cpp +++ b/src/math/realclosure/realclosure.cpp @@ -162,7 +162,7 @@ namespace realclosure { // To cope with this issue, we cache the value m_interval in m_old_interval whenever the width of m_interval is below // a give threshold. Then, after finishing OP, we restore the old_interval. mpbqi * m_old_interval; - value(bool rat):m_ref_count(0), m_rational(rat), m_old_interval(0) {} + value(bool rat):m_ref_count(0), m_rational(rat), m_old_interval(nullptr) {} bool is_rational() const { return m_rational; } mpbqi const & interval() const { return m_interval; } mpbqi & interval() { return m_interval; } @@ -220,7 +220,7 @@ namespace realclosure { mpbqi m_interval; mpbqi * m_old_interval; - extension(kind k, unsigned idx):m_ref_count(0), m_kind(k), m_idx(idx), m_old_interval(0) {} + extension(kind k, unsigned idx):m_ref_count(0), m_kind(k), m_idx(idx), m_old_interval(nullptr) {} unsigned idx() const { return m_idx; } kind knd() const { return static_cast(m_kind); } @@ -256,7 +256,7 @@ namespace realclosure { unsigned m_mark:1; // auxiliary mark used during deletion int m_sign; // Sign of the polynomial associated with m_q_idx sign_condition * m_prev; // Antecedent - sign_condition():m_q_idx(0), m_mark(false), m_sign(0), m_prev(0) {} + sign_condition():m_q_idx(0), m_mark(false), m_sign(0), m_prev(nullptr) {} sign_condition(unsigned qidx, int sign, sign_condition * prev):m_q_idx(qidx), m_mark(false), m_sign(sign), m_prev(prev) {} sign_condition * prev() const { return m_prev; } @@ -287,13 +287,13 @@ namespace realclosure { unsigned m_sc_idx; //!< != UINT_MAX if m_sign_det != 0, in this case m_sc_idx < m_sign_det->m_sign_conditions.size() bool m_depends_on_infinitesimals; //!< True if the polynomial p depends on infinitesimal extensions. - algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(0), m_sc_idx(0), m_depends_on_infinitesimals(false) {} + algebraic(unsigned idx):extension(ALGEBRAIC, idx), m_sign_det(nullptr), m_sc_idx(0), m_depends_on_infinitesimals(false) {} polynomial const & p() const { return m_p; } bool depends_on_infinitesimals() const { return m_depends_on_infinitesimals; } sign_det * sdt() const { return m_sign_det; } unsigned sc_idx() const { return m_sc_idx; } - unsigned num_roots_inside_interval() const { return m_sign_det == 0 ? 1 : m_sign_det->num_roots(); } + unsigned num_roots_inside_interval() const { return m_sign_det == nullptr ? 1 : m_sign_det->num_roots(); } mpbqi & iso_interval() { return m_iso_interval; } }; @@ -344,13 +344,13 @@ namespace realclosure { // --------------------------------- struct mk_pi_interval : public mk_interval { - virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) { + void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) override { im.pi(k, r); } }; struct mk_e_interval : public mk_interval { - virtual void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) { + void operator()(unsigned k, mpqi_manager & im, mpqi_manager::interval & r) override { im.e(k, r); } }; @@ -497,8 +497,8 @@ namespace realclosure { imp(reslimit& lim, unsynch_mpq_manager & qm, params_ref const & p, small_object_allocator * a): m_limit(lim), - m_allocator(a == 0 ? alloc(small_object_allocator, "realclosure") : a), - m_own_allocator(a == 0), + m_allocator(a == nullptr ? alloc(small_object_allocator, "realclosure") : a), + m_own_allocator(a == nullptr), m_qm(qm), m_mm(m_qm, *m_allocator), m_bqm(m_qm), @@ -509,8 +509,8 @@ namespace realclosure { mpq one(1); m_one = mk_rational(one); inc_ref(m_one); - m_pi = 0; - m_e = 0; + m_pi = nullptr; + m_e = nullptr; m_exec_depth = 0; @@ -657,7 +657,7 @@ namespace realclosure { */ template void save_interval(T * v, ptr_vector & to_restore) { - if (v->m_old_interval != 0) + if (v->m_old_interval != nullptr) return; // interval was already saved. to_restore.push_back(v); inc_ref(v); @@ -698,7 +698,7 @@ namespace realclosure { set_interval(v->m_interval, *(v->m_old_interval)); bqim().del(*(v->m_old_interval)); allocator().deallocate(sizeof(mpbqi), v->m_old_interval); - v->m_old_interval = 0; + v->m_old_interval = nullptr; dec_ref(v); } to_restore.reset(); @@ -811,12 +811,12 @@ namespace realclosure { } void inc_ref_sign_det(sign_det * sd) { - if (sd != 0) + if (sd != nullptr) sd->m_ref_count++; } void dec_ref_sign_det(sign_det * sd) { - if (sd != 0) { + if (sd != nullptr) { sd->m_ref_count--; if (sd->m_ref_count == 0) { del_sign_det(sd); @@ -887,7 +887,7 @@ namespace realclosure { void del(numeral & a) { dec_ref(a.m_value); - a.m_value = 0; + a.m_value = nullptr; } void del(numeral_vector & v) { @@ -910,7 +910,7 @@ namespace realclosure { \brief Return true if v is zero. */ static bool is_zero(value * v) { - return v == 0; + return v == nullptr; } /** @@ -1260,14 +1260,14 @@ namespace realclosure { } rational_function_value * mk_rational_function_value_core(algebraic * ext, unsigned num_sz, value * const * num) { - return mk_rational_function_value_core(ext, num_sz, num, 0, 0); + return mk_rational_function_value_core(ext, num_sz, num, 0, nullptr); } /** \brief Create a value using the given extension. */ rational_function_value * mk_rational_function_value(extension * ext) { - value * num[2] = { 0, one() }; + value * num[2] = { nullptr, one() }; value * den[1] = { one() }; rational_function_value * v = mk_rational_function_value_core(ext, 2, num, 1, den); set_interval(v->interval(), ext->interval()); @@ -1838,7 +1838,7 @@ namespace realclosure { interval contains only one root of p. */ void add_root(unsigned p_sz, value * const * p, mpbqi const & interval, mpbqi const & iso_interval, numeral_vector & roots) { - add_root(p_sz, p, interval, iso_interval, 0, UINT_MAX, roots); + add_root(p_sz, p, interval, iso_interval, nullptr, UINT_MAX, roots); } /** @@ -1965,7 +1965,7 @@ namespace realclosure { // Sequence of sign conditions associated with the columns of M_s // These are sign conditions on the polynomials in qs. scoped_sign_conditions scs(*this); - scs.push_back(0); + scs.push_back(nullptr); // Starting configuration // @@ -2322,7 +2322,7 @@ namespace realclosure { if (num_neg_roots > 0) { if (num_neg_roots == 1) { - add_root(n, p, neg_interval, minf_zero, 0, UINT_MAX, roots); + add_root(n, p, neg_interval, minf_zero, nullptr, UINT_MAX, roots); } else { if (has_neg_lower) { @@ -2336,7 +2336,7 @@ namespace realclosure { if (num_pos_roots > 0) { if (num_pos_roots == 1) { - add_root(n, p, pos_interval, zero_inf, 0, UINT_MAX, roots); + add_root(n, p, pos_interval, zero_inf, nullptr, UINT_MAX, roots); } else { if (has_pos_upper) { @@ -2668,7 +2668,7 @@ namespace realclosure { \brief Remove 0s */ void adjust_size(value_ref_buffer & r) { - while (!r.empty() && r.back() == 0) { + while (!r.empty() && r.back() == nullptr) { r.pop_back(); } } @@ -2753,7 +2753,7 @@ namespace realclosure { void mul(value * a, unsigned sz, value * const * p, value_ref_buffer & r) { SASSERT(p != r.c_ptr()); r.reset(); - if (a == 0) + if (a == nullptr) return; value_ref a_i(*this); for (unsigned i = 0; i < sz; i++) { @@ -2778,7 +2778,7 @@ namespace realclosure { value_ref tmp(*this); for (unsigned i = 0; i < sz1; i++) { checkpoint(); - if (p1[i] == 0) + if (p1[i] == nullptr) continue; for (unsigned j = 0; j < sz2; j++) { // r[i+j] <- r[i+j] + p1[i]*p2[j] @@ -3060,7 +3060,7 @@ namespace realclosure { bool struct_eq(value * a, value * b) const { if (a == b) return true; - else if (a == 0 || b == 0) + else if (a == nullptr || b == nullptr) return false; else if (is_nz_rational(a) && is_nz_rational(b)) return qm().eq(to_mpq(a), to_mpq(b)); @@ -3112,7 +3112,7 @@ namespace realclosure { - a is a rational_function_value of the form p_a(x)/1 where the coefficients of p_a also have clean denominators. */ bool has_clean_denominators(value * a) const { - if (a == 0) + if (a == nullptr) return true; else if (is_nz_rational(a)) return qm().is_int(to_mpq(a)); @@ -3150,7 +3150,7 @@ namespace realclosure { INC_DEPTH(); TRACE("rcf_clean", tout << "clean_denominators_core [" << m_exec_depth << "]\na: "; display(tout, a, false); tout << "\n";); p.reset(); q.reset(); - if (a == 0) { + if (a == nullptr) { p = a; q = one(); } @@ -3205,8 +3205,8 @@ namespace realclosure { dens.push_back(a_d); } else { - nums.push_back(0); - dens.push_back(0); + nums.push_back(nullptr); + dens.push_back(nullptr); } } if (all_one) { @@ -3258,7 +3258,7 @@ namespace realclosure { value_ref m(*this); for (unsigned i = 0; i < p_sz; i++) { if (!nums[i]) { - norm_p.push_back(0); + norm_p.push_back(nullptr); } else { SASSERT(dens[i]); @@ -3348,7 +3348,7 @@ namespace realclosure { If g != 0, then it will compute the gcd of g and the coefficients in a. */ bool gcd_int_coeffs(value * a, mpz & g) { - if (a == 0) { + if (a == nullptr) { return false; } else if (is_nz_rational(a)) { @@ -3410,7 +3410,7 @@ namespace realclosure { for (unsigned i = 0; i < p.size(); i++) { if (p[i]) { a = p[i]; - p.set(i, 0); + p.set(i, nullptr); exact_div_z(a, g); p.set(i, a); } @@ -3448,7 +3448,7 @@ namespace realclosure { new_ais.push_back(ai); } else { - new_ais.push_back(0); + new_ais.push_back(nullptr); } } rational_function_value * r = mk_rational_function_value_core(rf->ext(), new_ais.size(), new_ais.c_ptr(), 1, &m_one); @@ -3759,7 +3759,7 @@ namespace realclosure { while (i > 0) { checkpoint(); --i; - if (p[i] != 0) + if (p[i] != nullptr) bqim().add(r, interval(p[i]), r); if (i > 0) bqim().mul(r, bi, r); @@ -3772,7 +3772,7 @@ namespace realclosure { */ bool has_refineable_approx_coeffs(unsigned n, value * const * p) { for (unsigned i = 0; i < n; i++) { - if (p[i] != 0) { + if (p[i] != nullptr) { mpbqi & a_i = interval(p[i]); if (a_i.lower_is_inf() || a_i.upper_is_inf()) return false; @@ -3786,7 +3786,7 @@ namespace realclosure { */ void mk_polynomial_value(unsigned n, value * const * p, value * b, value_ref & r) { SASSERT(n > 0); - if (n == 1 || b == 0) { + if (n == 1 || b == nullptr) { r = p[0]; } else { @@ -3798,7 +3798,7 @@ namespace realclosure { unsigned i = n - 1; while (i > 0) { --i; - if (p[i] != 0) + if (p[i] != nullptr) add(r, p[i], r); // r <- r + a_i if (i > 0) mul(r, b, r); // r <- r * b @@ -3856,7 +3856,7 @@ namespace realclosure { int find_biggest_interval_magnitude(unsigned n, value * const * p) { int r = INT_MIN; for (unsigned i = 0; i < n; i++) { - if (p[i] != 0) { + if (p[i] != nullptr) { mpbqi & a_i = interval(p[i]); SASSERT(!a_i.lower_is_inf() && !a_i.upper_is_inf()); int m = magnitude(a_i); @@ -4089,7 +4089,7 @@ namespace realclosure { */ bool refine_coeffs_interval(unsigned n, value * const * p, unsigned prec) { for (unsigned i = 0; i < n; i++) { - if (p[i] != 0 && !refine_interval(p[i], prec)) + if (p[i] != nullptr && !refine_interval(p[i], prec)) return false; } return true; @@ -4243,7 +4243,7 @@ namespace realclosure { bool refine_algebraic_interval(algebraic * a, unsigned prec) { save_interval_if_too_small(a, prec); - if (a->sdt() != 0) { + if (a->sdt() != nullptr) { // We don't bisect the interval, since it contains more than one root. // To bisect this kind of interval we would have to use Tarski queries. return false; @@ -4941,7 +4941,7 @@ namespace realclosure { } else { // The new value is 0 - r = 0; + r = nullptr; } } } @@ -4979,7 +4979,7 @@ namespace realclosure { // num <- a + b * ad add(an.size(), an.c_ptr(), b_ad.size(), b_ad.c_ptr(), num); if (num.empty()) - r = 0; + r = nullptr; else { value_ref_buffer new_num(*this); value_ref_buffer new_den(*this); @@ -5003,7 +5003,7 @@ namespace realclosure { value_ref_buffer new_num(*this); add(an.size(), an.c_ptr(), bn.size(), bn.c_ptr(), new_num); if (new_num.empty()) - r = 0; + r = nullptr; else { // We don't need to invoke normalize_algebraic even if x (== a->ext()) is algebraic. // Reason: by construction the polynomials a->num() and b->num() are "normalized". @@ -5035,7 +5035,7 @@ namespace realclosure { value_ref_buffer num(*this); add(an_bd.size(), an_bd.c_ptr(), bn_ad.size(), bn_ad.c_ptr(), num); if (num.empty()) { - r = 0; + r = nullptr; } else { value_ref_buffer den(*this); @@ -5050,17 +5050,17 @@ namespace realclosure { } void add(value * a, value * b, value_ref & r) { - if (a == 0) { + if (a == nullptr) { r = b; } - else if (b == 0) { + else if (b == nullptr) { r = a; } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().add(to_mpq(a), to_mpq(b), v); if (qm().is_zero(v)) - r = 0; + r = nullptr; else r = mk_rational_and_swap(v); } @@ -5079,17 +5079,17 @@ namespace realclosure { } void sub(value * a, value * b, value_ref & r) { - if (a == 0) { + if (a == nullptr) { neg(b, r); } - else if (b == 0) { + else if (b == nullptr) { r = a; } else if (is_nz_rational(a) && is_nz_rational(b)) { scoped_mpq v(qm()); qm().sub(to_mpq(a), to_mpq(b), v); if (qm().is_zero(v)) - r = 0; + r = nullptr; else r = mk_rational_and_swap(v); } @@ -5118,8 +5118,8 @@ namespace realclosure { } void neg(value * a, value_ref & r) { - if (a == 0) { - r = 0; + if (a == nullptr) { + r = nullptr; } else if (is_nz_rational(a)) { scoped_mpq v(qm()); @@ -5155,7 +5155,7 @@ namespace realclosure { } else { // The new value is 0 - r = 0; + r = nullptr; } } } @@ -5252,8 +5252,8 @@ namespace realclosure { } void mul(value * a, value * b, value_ref & r) { - if (a == 0 || b == 0) { - r = 0; + if (a == nullptr || b == nullptr) { + r = nullptr; } else if (is_rational_one(a)) { r = b; @@ -5287,10 +5287,10 @@ namespace realclosure { } void div(value * a, value * b, value_ref & r) { - if (a == 0) { - r = 0; + if (a == nullptr) { + r = nullptr; } - else if (b == 0) { + else if (b == nullptr) { throw exception("division by zero"); } else if (is_rational_one(b)) { @@ -5461,7 +5461,7 @@ namespace realclosure { // r == 1/inv(new_a) inv(new_a, r); } - else if (alpha->sdt() == 0) { + else if (alpha->sdt() == nullptr) { // Another easy case: we just have to replace // alpha->p() with new_p. // The m_iso_interval for p() is also an isolating interval for new_p, @@ -5552,7 +5552,7 @@ namespace realclosure { } void inv(value * a, value_ref & r) { - if (a == 0) { + if (a == nullptr) { throw exception("division by zero"); } if (is_nz_rational(a)) { @@ -5644,7 +5644,7 @@ namespace realclosure { neg(a.m_value, neg_a); p.push_back(neg_a); for (unsigned i = 0; i < k - 1; i++) - p.push_back(0); + p.push_back(nullptr); p.push_back(one()); numeral_vector roots; @@ -5687,9 +5687,9 @@ namespace realclosure { // --------------------------------- int compare(value * a, value * b) { - if (a == 0) + if (a == nullptr) return -sign(b); - else if (b == 0) + else if (b == nullptr) return sign(a); else if (is_nz_rational(a) && is_nz_rational(b)) { if (qm().eq(to_mpq(a), to_mpq(b))) @@ -5744,7 +5744,7 @@ namespace realclosure { } void mark(value * v) { - if (v == 0 || is_nz_rational(v)) + if (v == nullptr || is_nz_rational(v)) return; rational_function_value * rf = to_rational_function(v); mark(rf->ext()); @@ -5779,7 +5779,7 @@ namespace realclosure { bool first = true; while (i > 0) { --i; - if (p[i] == 0) + if (p[i] == nullptr) continue; if (first) first = false; @@ -5893,7 +5893,7 @@ namespace realclosure { out << ", "; display_interval(out, a->iso_interval(), pp); out << ", "; - if (a->sdt() != 0) + if (a->sdt() != nullptr) display_sign_conditions(out, a->sdt()->sc(a->sc_idx()), a->sdt()->qs(), compact, pp); else out << "{}"; @@ -5931,7 +5931,7 @@ namespace realclosure { } void display(std::ostream & out, value * v, bool compact, bool pp=false) const { - if (v == 0) + if (v == nullptr) out << "0"; else if (is_nz_rational(v)) qm().display(out, to_mpq(v)); diff --git a/src/math/realclosure/realclosure.h b/src/math/realclosure/realclosure.h index c18f626a3..cfba0e99e 100644 --- a/src/math/realclosure/realclosure.h +++ b/src/math/realclosure/realclosure.h @@ -48,7 +48,7 @@ namespace realclosure { friend class save_interval_ctx; imp * m_imp; public: - manager(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); + manager(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); ~manager(); typedef num numeral; typedef svector numeral_vector; @@ -271,7 +271,7 @@ namespace realclosure { friend struct manager::imp; value * m_value; public: - num():m_value(0) {} + num():m_value(nullptr) {} // Low level functions for implementing the C API void * c_ptr() { return m_value; } diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index f12918bfe..09f6d5c2d 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -35,11 +35,114 @@ std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { namespace opt { + /** + * Convert a row ax + coeffs + coeff = value into a definition for x + * x = (value - coeffs - coeff)/a + * as backdrop we have existing assignments to x and other variables that + * satisfy the equality with value, and such that value satisfies + * the row constraint ( = , <= , < , mod) + */ + model_based_opt::def::def(row const& r, unsigned x) { + for (var const & v : r.m_vars) { + if (v.m_id != x) { + m_vars.push_back(v); + } + else { + m_div = -v.m_coeff; + } + } + m_coeff = r.m_coeff; + if (r.m_type == opt::t_lt) m_coeff += m_div; + normalize(); + SASSERT(m_div.is_pos()); + } + + model_based_opt::def model_based_opt::def::operator+(def const& other) const { + def result; + vector const& vs1 = m_vars; + vector const& vs2 = other.m_vars; + vector & vs = result.m_vars; + rational c1(1), c2(1); + if (m_div != other.m_div) { + c1 = other.m_div; + c2 = m_div; + } + unsigned i = 0, j = 0; + while (i < vs1.size() || j < vs2.size()) { + unsigned v1 = UINT_MAX, v2 = UINT_MAX; + if (i < vs1.size()) v1 = vs1[i].m_id; + if (j < vs2.size()) v2 = vs2[j].m_id; + if (v1 == v2) { + vs.push_back(vs1[i]); + vs.back().m_coeff *= c1; + vs.back().m_coeff += c2 * vs2[j].m_coeff; + ++i; ++j; + if (vs.back().m_coeff.is_zero()) { + vs.pop_back(); + } + } + else if (v1 < v2) { + vs.push_back(vs1[i]); + vs.back().m_coeff *= c1; + } + else { + vs.push_back(vs2[j]); + vs.back().m_coeff *= c2; + } + } + result.m_div = c1*m_div; + result.m_coeff = (m_coeff*c1) + (other.m_coeff*c2); + result.normalize(); + return result; + } + + model_based_opt::def model_based_opt::def::operator/(rational const& r) const { + def result(*this); + result.m_div *= r; + result.normalize(); + return result; + } + + model_based_opt::def model_based_opt::def::operator*(rational const& n) const { + def result(*this); + for (var& v : result.m_vars) { + v.m_coeff *= n; + } + result.m_coeff *= n; + result.normalize(); + return result; + } + + model_based_opt::def model_based_opt::def::operator+(rational const& n) const { + def result(*this); + result.m_coeff += n * result.m_div; + result.normalize(); + return result; + } + + void model_based_opt::def::normalize() { + if (m_div.is_one()) return; + rational g(m_div); + g = gcd(g, m_coeff); + for (var const& v : m_vars) { + g = gcd(g, abs(v.m_coeff)); + if (g.is_one()) break; + } + if (m_div.is_neg()) { + g.neg(); + } + if (!g.is_one()) { + for (var& v : m_vars) { + v.m_coeff /= g; + } + m_coeff /= g; + m_div /= g; + } + } model_based_opt::model_based_opt() { m_rows.push_back(row()); } - bool model_based_opt::invariant() { for (unsigned i = 0; i < m_rows.size(); ++i) { @@ -61,7 +164,7 @@ namespace opt { PASSERT(index == 0 || m_var2row_ids[vars[i].m_id].contains(index)); } - PASSERT(r.m_value == get_row_value(r)); + PASSERT(r.m_value == eval(r)); PASSERT(r.m_type != t_eq || r.m_value.is_zero()); // values satisfy constraints PASSERT(index == 0 || r.m_type != t_lt || r.m_value.is_neg()); @@ -105,14 +208,14 @@ namespace opt { if (find_bound(x, bound_row_index, bound_coeff, coeff.is_pos())) { SASSERT(!bound_coeff.is_zero()); TRACE("opt", display(tout << "update: " << v << " ", objective()); - for (unsigned i = 0; i < m_above.size(); ++i) { - display(tout << "resolve: ", m_rows[m_above[i]]); + for (unsigned above : m_above) { + display(tout << "resolve: ", m_rows[above]); }); - for (unsigned i = 0; i < m_above.size(); ++i) { - resolve(bound_row_index, bound_coeff, m_above[i], x); + for (unsigned above : m_above) { + resolve(bound_row_index, bound_coeff, above, x); } - for (unsigned i = 0; i < m_below.size(); ++i) { - resolve(bound_row_index, bound_coeff, m_below[i], x); + for (unsigned below : m_below) { + resolve(bound_row_index, bound_coeff, below, x); } // coeff*x + objective <= ub // a2*x + t2 <= 0 @@ -151,8 +254,7 @@ namespace opt { rational old_val = m_var2value[x]; m_var2value[x] = val; unsigned_vector const& row_ids = m_var2row_ids[x]; - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id = row_ids[i]; + for (unsigned row_id : row_ids) { rational coeff = get_coefficient(row_id, x); if (coeff.is_zero()) { continue; @@ -166,8 +268,7 @@ namespace opt { void model_based_opt::update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail) { - for (unsigned i = bound_trail.size(); i > 0; ) { - --i; + for (unsigned i = bound_trail.size(); i-- > 0; ) { unsigned x = bound_vars[i]; row& r = m_rows[bound_trail[i]]; rational val = r.m_coeff; @@ -175,8 +276,7 @@ namespace opt { rational new_x_val; rational x_coeff, eps(0); vector const& vars = r.m_vars; - for (unsigned j = 0; j < vars.size(); ++j) { - var const& v = vars[j]; + for (var const& v : vars) { if (x == v.m_id) { x_coeff = v.m_coeff; } @@ -218,19 +318,17 @@ namespace opt { << " eps: " << eps << " ", r); ); m_var2value[x] = new_x_val; - r.m_value = get_row_value(r); + r.m_value = eval(r); SASSERT(invariant(bound_trail[i], r)); } // update and check bounds for all other affected rows. - for (unsigned i = bound_trail.size(); i > 0; ) { - --i; + for (unsigned i = bound_trail.size(); i-- > 0; ) { unsigned x = bound_vars[i]; unsigned_vector const& row_ids = m_var2row_ids[x]; - for (unsigned j = 0; j < row_ids.size(); ++j) { - unsigned row_id = row_ids[j]; + for (unsigned row_id : row_ids) { row & r = m_rows[row_id]; - r.m_value = get_row_value(r); + r.m_value = eval(r); SASSERT(invariant(row_id, r)); } } @@ -245,8 +343,7 @@ namespace opt { uint_set visited; m_above.reset(); m_below.reset(); - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id = row_ids[i]; + for (unsigned row_id : row_ids) { SASSERT(row_id != m_objective_id); if (visited.contains(row_id)) { continue; @@ -289,27 +386,43 @@ namespace opt { m_rows[row_id].m_alive = false; m_retired_rows.push_back(row_id); } + + rational model_based_opt::eval(unsigned x) const { + return m_var2value[x]; + } + + rational model_based_opt::eval(def const& d) const { + vector const& vars = d.m_vars; + rational val = d.m_coeff; + for (var const& v : vars) { + val += v.m_coeff * eval(v.m_id); + } + val /= d.m_div; + return val; + } - rational model_based_opt::get_row_value(row const& r) const { + rational model_based_opt::eval(row const& r) const { vector const& vars = r.m_vars; rational val = r.m_coeff; - for (unsigned i = 0; i < vars.size(); ++i) { - var const& v = vars[i]; - val += v.m_coeff * m_var2value[v.m_id]; + for (var const& v : vars) { + val += v.m_coeff * eval(v.m_id); } return val; } rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) const { - row const& r = m_rows[row_id]; - if (r.m_vars.empty()) { + return m_rows[row_id].get_coefficient(var_id); + } + + rational model_based_opt::row::get_coefficient(unsigned var_id) const { + if (m_vars.empty()) { return rational::zero(); } - unsigned lo = 0, hi = r.m_vars.size(); + unsigned lo = 0, hi = m_vars.size(); while (lo < hi) { unsigned mid = lo + (hi - lo)/2; SASSERT(mid < hi); - unsigned id = r.m_vars[mid].m_id; + unsigned id = m_vars[mid].m_id; if (id == var_id) { lo = mid; break; @@ -321,12 +434,12 @@ namespace opt { hi = mid; } } - if (lo == r.m_vars.size()) { + if (lo == m_vars.size()) { return rational::zero(); } - unsigned id = r.m_vars[lo].m_id; + unsigned id = m_vars[lo].m_id; if (id == var_id) { - return r.m_vars[lo].m_coeff; + return m_vars[lo].m_coeff; } else { return rational::zero(); @@ -368,7 +481,7 @@ namespace opt { tout << a1 << " " << a2 << ": "; display(tout, m_rows[row_dst]); display(tout, m_rows[row_src]);); - if (a1.is_pos() != a2.is_pos()) { + if (a1.is_pos() != a2.is_pos() || m_rows[row_src].m_type == opt::t_eq) { mul_add(x, a1, row_src, a2, row_dst); } else { @@ -384,6 +497,17 @@ namespace opt { } } + void model_based_opt::solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { + SASSERT(a1 == get_coefficient(row_src, x)); + SASSERT(a1.is_pos()); + SASSERT(row_src != row_dst); + if (!m_rows[row_dst].m_alive) return; + rational a2 = get_coefficient(row_dst, x); + mul(row_dst, a1); + mul_add(false, row_dst, -a2, row_src); + SASSERT(get_coefficient(row_dst, x).is_zero()); + } + // resolution for integer rows. void model_based_opt::mul_add( unsigned x, rational const& src_c, unsigned row_src, rational const& dst_c, unsigned row_dst) { @@ -400,18 +524,28 @@ namespace opt { rational slack = (abs_src_c - rational::one()) * (abs_dst_c - rational::one()); rational dst_val = dst.m_value - x_val*dst_c; rational src_val = src.m_value - x_val*src_c; - bool use_case1 = - (src_c * dst_val + dst_c * src_val + slack).is_nonpos() - || abs_src_c.is_one() - || abs_dst_c.is_one(); + rational distance = src_c * dst_val + dst_c * src_val + slack; + bool use_case1 = distance.is_nonpos() || abs_src_c.is_one() || abs_dst_c.is_one(); + +#if 0 + if (distance.is_nonpos() && !abs_src_c.is_one() && !abs_dst_c.is_one()) { + unsigned r = copy_row(row_src); + mul_add(false, r, rational::one(), row_dst); + del_var(r, x); + add(r, slack); + TRACE("qe", tout << m_rows[r];); + SASSERT(!m_rows[r].m_value.is_pos()); + } +#endif if (use_case1) { - // dst <- abs_src_c*dst + abs_dst_c*src - slack + TRACE("opt", tout << "slack: " << slack << " " << src_c << " " << dst_val << " " << dst_c << " " << src_val << "\n";); + // dst <- abs_src_c*dst + abs_dst_c*src + slack mul(row_dst, abs_src_c); - sub(row_dst, slack); - mul_add(false, row_dst, abs_dst_c, row_src); + add(row_dst, slack); + mul_add(false, row_dst, abs_dst_c, row_src); return; - } + } // // create finite disjunction for |b|. @@ -432,6 +566,7 @@ namespace opt { // exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0 // + TRACE("qe", tout << "finite disjunction " << distance << " " << src_c << " " << dst_c << "\n";); vector coeffs; if (abs_dst_c <= abs_src_c) { rational z = mod(dst_val, abs_dst_c); @@ -457,8 +592,8 @@ namespace opt { } void model_based_opt::mk_coeffs_without(vector& dst, vector const src, unsigned x) { - for (unsigned i = 0; i < src.size(); ++i) { - if (src[i].m_id != x) dst.push_back(src[i]); + for (var const & v : src) { + if (v.m_id != x) dst.push_back(v); } } @@ -469,8 +604,8 @@ namespace opt { void model_based_opt::mul(unsigned dst, rational const& c) { if (c.is_one()) return; row& r = m_rows[dst]; - for (unsigned i = 0; i < r.m_vars.size(); ++i) { - r.m_vars[i].m_coeff *= c; + for (auto & v : r.m_vars) { + v.m_coeff *= c; } r.m_coeff *= c; r.m_value *= c; @@ -488,6 +623,21 @@ namespace opt { r.m_value -= c; } + void model_based_opt::del_var(unsigned dst, unsigned x) { + row& r = m_rows[dst]; + unsigned j = 0; + for (var & v : r.m_vars) { + if (v.m_id == x) { + r.m_value -= eval(x)*r.m_coeff; + } + else { + r.m_vars[j++] = v; + } + } + r.m_vars.shrink(j); + } + + void model_based_opt::normalize(unsigned row_id) { row& r = m_rows[row_id]; if (r.m_vars.empty()) return; @@ -529,7 +679,7 @@ namespace opt { row& r1 = m_rows[row_id1]; row const& r2 = m_rows[row_id2]; unsigned i = 0, j = 0; - for(; i < r1.m_vars.size() || j < r2.m_vars.size(); ) { + while (i < r1.m_vars.size() || j < r2.m_vars.size()) { if (j == r2.m_vars.size()) { m_new_vars.append(r1.m_vars.size() - i, r1.m_vars.c_ptr() + i); break; @@ -583,40 +733,59 @@ namespace opt { } void model_based_opt::display(std::ostream& out) const { - for (unsigned i = 0; i < m_rows.size(); ++i) { - display(out, m_rows[i]); + for (auto const& r : m_rows) { + display(out, r); } for (unsigned i = 0; i < m_var2row_ids.size(); ++i) { unsigned_vector const& rows = m_var2row_ids[i]; out << i << ": "; - for (unsigned j = 0; j < rows.size(); ++j) { - out << rows[j] << " "; + for (auto const& r : rows) { + out << r << " "; } out << "\n"; } } - void model_based_opt::display(std::ostream& out, row const& r) const { - vector const& vars = r.m_vars; - out << (r.m_alive?"+":"-") << " "; - for (unsigned i = 0; i < vars.size(); ++i) { - if (i > 0 && vars[i].m_coeff.is_pos()) { + void model_based_opt::display(std::ostream& out, vector const& vars, rational const& coeff) { + unsigned i = 0; + for (var const& v : vars) { + if (i > 0 && v.m_coeff.is_pos()) { out << "+ "; } - out << vars[i].m_coeff << "* v" << vars[i].m_id << " "; + ++i; + if (v.m_coeff.is_one()) { + out << "v" << v.m_id << " "; + } + else { + out << v.m_coeff << "*v" << v.m_id << " "; + } } - if (r.m_coeff.is_pos()) { - out << " + " << r.m_coeff << " "; + if (coeff.is_pos()) { + out << " + " << coeff << " "; } - else if (r.m_coeff.is_neg()) { - out << r.m_coeff << " "; - } + else if (coeff.is_neg()) { + out << coeff << " "; + } + } + + std::ostream& model_based_opt::display(std::ostream& out, row const& r) { + out << (r.m_alive?"+":"-") << " "; + display(out, r.m_vars, r.m_coeff); if (r.m_type == opt::t_mod) { out << r.m_type << " " << r.m_mod << " = 0; value: " << r.m_value << "\n"; } else { out << r.m_type << " 0; value: " << r.m_value << "\n"; } + return out; + } + + std::ostream& model_based_opt::display(std::ostream& out, def const& r) { + display(out, r.m_vars, r.m_coeff); + if (!r.m_div.is_one()) { + out << " / " << r.m_div; + } + return out; } unsigned model_based_opt::add_var(rational const& value, bool is_int) { @@ -638,10 +807,10 @@ namespace opt { r.m_vars.append(coeffs.size(), coeffs.c_ptr()); bool is_int_row = true; std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); - for (unsigned i = 0; i < coeffs.size(); ++i) { - val += m_var2value[coeffs[i].m_id] * coeffs[i].m_coeff; - SASSERT(!is_int(coeffs[i].m_id) || coeffs[i].m_coeff.is_int()); - is_int_row &= is_int(coeffs[i].m_id); + for (auto const& c : coeffs) { + val += m_var2value[c.m_id] * c.m_coeff; + SASSERT(!is_int(c.m_id) || c.m_coeff.is_int()); + is_int_row &= is_int(c.m_id); } r.m_alive = true; r.m_coeff = c; @@ -674,8 +843,8 @@ namespace opt { unsigned dst = new_row(); row const& r = m_rows[src]; set_row(dst, r.m_vars, r.m_coeff, r.m_mod, r.m_type); - for (unsigned i = 0; i < r.m_vars.size(); ++i) { - m_var2row_ids[r.m_vars[i].m_id].push_back(dst); + for (auto const& v : r.m_vars) { + m_var2row_ids[v.m_id].push_back(dst); } SASSERT(invariant(dst, m_rows[dst])); return dst; @@ -692,8 +861,8 @@ namespace opt { void model_based_opt::add_constraint(vector const& coeffs, rational const& c, rational const& m, ineq_type rel) { unsigned row_id = new_row(); set_row(row_id, coeffs, c, m, rel); - for (unsigned i = 0; i < coeffs.size(); ++i) { - m_var2row_ids[coeffs[i].m_id].push_back(row_id); + for (var const& coeff : coeffs) { + m_var2row_ids[coeff.m_id].push_back(row_id); } SASSERT(invariant(row_id, m_rows[row_id])); } @@ -703,9 +872,9 @@ namespace opt { } void model_based_opt::get_live_rows(vector& rows) { - for (unsigned i = 0; i < m_rows.size(); ++i) { - if (m_rows[i].m_alive) { - rows.push_back(m_rows[i]); + for (row const& r : m_rows) { + if (r.m_alive) { + rows.push_back(r); } } } @@ -728,7 +897,7 @@ namespace opt { // t0 <= s for each s (M inequalities). // If N >= M the construction is symmetric. // - void model_based_opt::project(unsigned x) { + model_based_opt::def model_based_opt::project(unsigned x, bool compute_def) { unsigned_vector& lub_rows = m_lub; unsigned_vector& glb_rows = m_glb; unsigned_vector& mod_rows = m_mod; @@ -742,9 +911,9 @@ namespace opt { glb_rows.reset(); mod_rows.reset(); bool lub_is_unit = false, glb_is_unit = false; + unsigned eq_row = UINT_MAX; // select the lub and glb. - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id = row_ids[i]; + for (unsigned row_id : row_ids) { if (visited.contains(row_id)) { continue; } @@ -758,8 +927,8 @@ namespace opt { continue; } if (r.m_type == t_eq) { - solve_for(row_id, x); - return; + eq_row = row_id; + continue; } if (r.m_type == t_mod) { mod_rows.push_back(row_id); @@ -792,24 +961,56 @@ namespace opt { } if (!mod_rows.empty()) { - solve_mod(x, mod_rows); - return; + return solve_mod(x, mod_rows, compute_def); } - + + if (eq_row != UINT_MAX) { + return solve_for(eq_row, x, compute_def); + } + + def result; unsigned lub_size = lub_rows.size(); unsigned glb_size = glb_rows.size(); unsigned row_index = (lub_size <= glb_size) ? lub_index : glb_index; - glb_rows.append(lub_rows); // There are only upper or only lower bounds. if (row_index == UINT_MAX) { - for (unsigned i = 0; i < glb_rows.size(); ++i) { - unsigned row_id = glb_rows[i]; - SASSERT(m_rows[row_id].m_alive); - SASSERT(!get_coefficient(row_id, x).is_zero()); - retire_row(row_id); + if (compute_def) { + if (lub_index != UINT_MAX) { + result = solve_for(lub_index, x, true); + } + else if (glb_index != UINT_MAX) { + result = solve_for(glb_index, x, true); + } + else { + result = def(); + m_var2value[x] = rational::zero(); + } + SASSERT(eval(result) == eval(x)); } - return; + else { + for (unsigned row_id : lub_rows) retire_row(row_id); + for (unsigned row_id : glb_rows) retire_row(row_id); + } + return result; + } + + SASSERT(lub_index != UINT_MAX); + SASSERT(glb_index != UINT_MAX); + if (compute_def) { +#if 0 + def d1(m_rows[lub_index], x); + def d2(m_rows[glb_index], x); + result = (d1 + d2)/2; +#else + if (lub_size <= glb_size) { + result = def(m_rows[lub_index], x); + } + else { + result = def(m_rows[glb_index], x); + } + m_var2value[x] = eval(result); +#endif } // The number of matching lower and upper bounds is small. @@ -818,10 +1019,9 @@ namespace opt { (!is_int(x) || lub_is_unit || glb_is_unit)) { for (unsigned i = 0; i < lub_size; ++i) { unsigned row_id1 = lub_rows[i]; - bool last = i + 1 == lub_rows.size(); + bool last = i + 1 == lub_size; rational coeff = get_coefficient(row_id1, x); - for (unsigned j = 0; j < glb_size; ++j) { - unsigned row_id2 = glb_rows[j]; + for (unsigned row_id2 : glb_rows) { if (last) { resolve(row_id1, coeff, row_id2, x); } @@ -831,21 +1031,25 @@ namespace opt { } } } - for (unsigned i = 0; i < lub_size; ++i) { - retire_row(lub_rows[i]); - } - return; + for (unsigned row_id : lub_rows) retire_row(row_id); + + return result; } // General case. rational coeff = get_coefficient(row_index, x); - for (unsigned i = 0; i < glb_rows.size(); ++i) { - unsigned row_id = glb_rows[i]; + for (unsigned row_id : lub_rows) { + if (row_id != row_index) { + resolve(row_index, coeff, row_id, x); + } + } + for (unsigned row_id : glb_rows) { if (row_id != row_index) { resolve(row_index, coeff, row_id, x); } } retire_row(row_index); + return result; } // @@ -863,11 +1067,11 @@ namespace opt { // x := D*x' + u // - void model_based_opt::solve_mod(unsigned x, unsigned_vector const& mod_rows) { + model_based_opt::def model_based_opt::solve_mod(unsigned x, unsigned_vector const& mod_rows, bool compute_def) { SASSERT(!mod_rows.empty()); rational D(1); - for (unsigned i = 0; i < mod_rows.size(); ++i) { - D = lcm(D, m_rows[mod_rows[i]].m_mod); + for (unsigned idx : mod_rows) { + D = lcm(D, m_rows[idx].m_mod); } if (D.is_zero()) { throw default_exception("modulo 0 is not defined"); @@ -876,9 +1080,9 @@ namespace opt { rational val_x = m_var2value[x]; rational u = mod(val_x, D); SASSERT(u.is_nonneg() && u < D); - for (unsigned i = 0; i < mod_rows.size(); ++i) { - replace_var(mod_rows[i], x, u); - SASSERT(invariant(mod_rows[i], m_rows[mod_rows[i]])); + for (unsigned idx : mod_rows) { + replace_var(idx, x, u); + SASSERT(invariant(idx, m_rows[idx])); } // // update inequalities such that u is added to t and @@ -894,15 +1098,20 @@ namespace opt { unsigned y = add_var(new_val, true); unsigned_vector const& row_ids = m_var2row_ids[x]; uint_set visited; - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id = row_ids[i]; + for (unsigned row_id : row_ids) { if (!visited.contains(row_id)) { // x |-> D*y + u replace_var(row_id, x, D, y, u); visited.insert(row_id); } } - project(y); + def result = project(y, compute_def); + if (compute_def) { + result = (result * D) + u; + m_var2value[x] = eval(result); + } + SASSERT(!compute_def || eval(result) == eval(x)); + return result; } // update row with: x |-> C @@ -949,39 +1158,67 @@ namespace opt { // 3x + t = 0 & 7 | (c*x + s) & ax <= u // 3 | -t & 21 | (-ct + 3s) & a-t <= 3u - void model_based_opt::solve_for(unsigned row_id1, unsigned x) { + model_based_opt::def model_based_opt::solve_for(unsigned row_id1, unsigned x, bool compute_def) { + TRACE("opt", tout << "v" << x << "\n" << m_rows[row_id1] << "\n";); rational a = get_coefficient(row_id1, x), b; + ineq_type ty = m_rows[row_id1].m_type; SASSERT(!a.is_zero()); - SASSERT(m_rows[row_id1].m_type == t_eq); SASSERT(m_rows[row_id1].m_alive); - if (m_var2is_int[x] && !abs(a).is_one()) { + if (a.is_neg()) { + a.neg(); + m_rows[row_id1].neg(); + } + SASSERT(a.is_pos()); + if (ty == t_lt) { + SASSERT(compute_def); + m_rows[row_id1].m_coeff += a; + } + if (m_var2is_int[x] && !a.is_one()) { row& r1 = m_rows[row_id1]; vector coeffs; mk_coeffs_without(coeffs, r1.m_vars, x); rational c = r1.m_coeff; - add_divides(coeffs, c, abs(a)); + add_divides(coeffs, c, a); } unsigned_vector const& row_ids = m_var2row_ids[x]; uint_set visited; visited.insert(row_id1); - for (unsigned i = 0; i < row_ids.size(); ++i) { - unsigned row_id2 = row_ids[i]; + for (unsigned row_id2 : row_ids) { if (!visited.contains(row_id2)) { - visited.insert(row_id2); + visited.insert(row_id2); b = get_coefficient(row_id2, x); if (!b.is_zero()) { - resolve(row_id1, a, row_id2, x); + row& dst = m_rows[row_id2]; + switch (dst.m_type) { + case t_eq: + case t_lt: + case t_le: + solve(row_id1, a, row_id2, x); + break; + case t_mod: + // mod reduction already done. + UNREACHABLE(); + break; + } } } } + def result; + if (compute_def) { + result = def(m_rows[row_id1], x); + m_var2value[x] = eval(result); + } retire_row(row_id1); + return result; } - - void model_based_opt::project(unsigned num_vars, unsigned const* vars) { + + vector model_based_opt::project(unsigned num_vars, unsigned const* vars, bool compute_def) { + vector result; for (unsigned i = 0; i < num_vars; ++i) { - project(vars[i]); + result.push_back(project(vars[i], compute_def)); TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n");); } + return result; } } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index 54360d0ac..e52d0cfe0 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -58,6 +58,25 @@ namespace opt { rational m_value; // value of m_vars + m_coeff under interpretation of m_var2value. bool m_alive; // rows can be marked dead if they have been processed. void reset() { m_vars.reset(); m_coeff.reset(); m_value.reset(); } + + void neg() { for (var & v : m_vars) v.m_coeff.neg(); m_coeff.neg(); m_value.neg(); } + rational get_coefficient(unsigned x) const; + }; + + // A definition is a linear term of the form (vars + coeff) / div + struct def { + def(): m_div(1) {} + def(row const& r, unsigned x); + def(def const& other): m_vars(other.m_vars), m_coeff(other.m_coeff), m_div(other.m_div) {} + vector m_vars; + rational m_coeff; + rational m_div; + def operator+(def const& other) const; + def operator/(unsigned n) const { return *this / rational(n); } + def operator/(rational const& n) const; + def operator*(rational const& n) const; + def operator+(rational const& n) const; + void normalize(); }; private: @@ -81,10 +100,16 @@ namespace opt { rational get_coefficient(unsigned row_id, unsigned var_id) const; - rational get_row_value(row const& r) const; + rational eval(row const& r) const; + + rational eval(unsigned x) const; + + rational eval(def const& d) const; void resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x); + void solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x); + void mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2); void mul_add(unsigned x, rational const& a1, unsigned row_src, rational const& a2, unsigned row_dst); @@ -95,6 +120,8 @@ namespace opt { void sub(unsigned dst, rational const& c); + void del_var(unsigned dst, unsigned x); + void set_row(unsigned row_id, vector const& coeffs, rational const& c, rational const& m, ineq_type rel); void add_constraint(vector const& coeffs, rational const& c, rational const& m, ineq_type r); @@ -117,12 +144,12 @@ namespace opt { void update_value(unsigned x, rational const& val); - void project(unsigned var); + def project(unsigned var, bool compute_def); - void solve_for(unsigned row_id, unsigned x); - - void solve_mod(unsigned x, unsigned_vector const& mod_rows); + def solve_for(unsigned row_id, unsigned x, bool compute_def); + def solve_mod(unsigned x, unsigned_vector const& mod_rows, bool compute_def); + bool is_int(unsigned x) const { return m_var2is_int[x]; } void retire_row(unsigned row_id); @@ -159,7 +186,7 @@ namespace opt { // // Project set of variables from inequalities. // - void project(unsigned num_vars, unsigned const* vars); + vector project(unsigned num_vars, unsigned const* vars, bool compute_def); // // Extract current rows (after projection). @@ -167,13 +194,17 @@ namespace opt { void get_live_rows(vector& rows); void display(std::ostream& out) const; - void display(std::ostream& out, row const& r) const; + static std::ostream& display(std::ostream& out, row const& r); + static std::ostream& display(std::ostream& out, def const& r); + static void display(std::ostream& out, vector const& vars, rational const& coeff); }; } std::ostream& operator<<(std::ostream& out, opt::ineq_type ie); +inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::def const& d) { return opt::model_based_opt::display(out, d); } +inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::row const& r) { return opt::model_based_opt::display(out, r); } inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::var const v) { return out << "v" << v.m_id; } diff --git a/src/math/simplex/simplex_def.h b/src/math/simplex/simplex_def.h index 762e8ceb2..89af0b3ad 100644 --- a/src/math/simplex/simplex_def.h +++ b/src/math/simplex/simplex_def.h @@ -634,7 +634,7 @@ namespace simplex { // // max { c*x | A*x = 0 and l <= x <= u } // - // start with feasible assigment + // start with feasible assignment // A*x0 = 0 and l <= x0 <= u // // Identify pivot: i, j: such that x_i is base, @@ -745,7 +745,7 @@ namespace simplex { numeral const& base_coeff = vs.m_base_coeff; SASSERT(!m.is_zero(coeff)); bool base_to_lower = (m.is_pos(coeff) != m.is_pos(base_coeff)) == to_lower; - eps_numeral const* bound = 0; + eps_numeral const* bound = nullptr; if (!base_to_lower && vs.m_upper_valid) { bound = &vs.m_upper; } diff --git a/src/math/simplex/sparse_matrix_def.h b/src/math/simplex/sparse_matrix_def.h index be18a8702..0b48e0049 100644 --- a/src/math/simplex/sparse_matrix_def.h +++ b/src/math/simplex/sparse_matrix_def.h @@ -233,7 +233,7 @@ namespace simplex { return it; } } - return 0; + return nullptr; } template diff --git a/src/math/subpaving/subpaving.cpp b/src/math/subpaving/subpaving.cpp index 96fc5b6d7..c43b74f0d 100644 --- a/src/math/subpaving/subpaving.cpp +++ b/src/math/subpaving/subpaving.cpp @@ -38,22 +38,22 @@ namespace subpaving { CTX m_ctx; public: context_wrapper(reslimit& lim, typename CTX::numeral_manager & m, params_ref const & p, small_object_allocator * a):m_ctx(lim, m, p, a) {} - virtual ~context_wrapper() {} - virtual unsigned num_vars() const { return m_ctx.num_vars(); } - virtual var mk_var(bool is_int) { return m_ctx.mk_var(is_int); } - virtual bool is_int(var x) const { return m_ctx.is_int(x); } - virtual var mk_monomial(unsigned sz, power const * pws) { return m_ctx.mk_monomial(sz, pws); } - virtual void inc_ref(ineq * a) { m_ctx.inc_ref(reinterpret_cast(a)); } - virtual void dec_ref(ineq * a) { m_ctx.dec_ref(reinterpret_cast(a)); } - virtual void add_clause(unsigned sz, ineq * const * atoms) { m_ctx.add_clause(sz, reinterpret_cast(atoms)); } - virtual void display_constraints(std::ostream & out, bool use_star) const { m_ctx.display_constraints(out, use_star); } - virtual void set_display_proc(display_var_proc * p) { m_ctx.set_display_proc(p); } - virtual void reset_statistics() { m_ctx.reset_statistics(); } - virtual void collect_statistics(statistics & st) const { m_ctx.collect_statistics(st); } - virtual void collect_param_descrs(param_descrs & r) { m_ctx.collect_param_descrs(r); } - virtual void updt_params(params_ref const & p) { m_ctx.updt_params(p); } - virtual void operator()() { m_ctx(); } - virtual void display_bounds(std::ostream & out) const { m_ctx.display_bounds(out); } + ~context_wrapper() override {} + unsigned num_vars() const override { return m_ctx.num_vars(); } + var mk_var(bool is_int) override { return m_ctx.mk_var(is_int); } + bool is_int(var x) const override { return m_ctx.is_int(x); } + var mk_monomial(unsigned sz, power const * pws) override { return m_ctx.mk_monomial(sz, pws); } + void inc_ref(ineq * a) override { m_ctx.inc_ref(reinterpret_cast(a)); } + void dec_ref(ineq * a) override { m_ctx.dec_ref(reinterpret_cast(a)); } + void add_clause(unsigned sz, ineq * const * atoms) override { m_ctx.add_clause(sz, reinterpret_cast(atoms)); } + void display_constraints(std::ostream & out, bool use_star) const override { m_ctx.display_constraints(out, use_star); } + void set_display_proc(display_var_proc * p) override { m_ctx.set_display_proc(p); } + void reset_statistics() override { m_ctx.reset_statistics(); } + void collect_statistics(statistics & st) const override { m_ctx.collect_statistics(st); } + void collect_param_descrs(param_descrs & r) override { m_ctx.collect_param_descrs(r); } + void updt_params(params_ref const & p) override { m_ctx.updt_params(p); } + void operator()() override { m_ctx(); } + void display_bounds(std::ostream & out) const override { m_ctx.display_bounds(out); } }; class context_mpq_wrapper : public context_wrapper { @@ -66,11 +66,11 @@ namespace subpaving { m_as(m) {} - virtual ~context_mpq_wrapper() {} + ~context_mpq_wrapper() override {} - virtual unsynch_mpq_manager & qm() const { return m_ctx.nm(); } + unsynch_mpq_manager & qm() const override { return m_ctx.nm(); } - virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) { + var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) override { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { m_ctx.nm().set(m_as[i], as[i]); @@ -78,7 +78,7 @@ namespace subpaving { m_ctx.nm().set(m_c, c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } - virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) { + ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) override { return reinterpret_cast(m_ctx.mk_ineq(x, k, lower, open)); } }; @@ -108,11 +108,11 @@ namespace subpaving { m_q2(m_qm) { } - virtual ~context_mpf_wrapper() {} + ~context_mpf_wrapper() override {} - virtual unsynch_mpq_manager & qm() const { return m_qm; } + unsynch_mpq_manager & qm() const override { return m_qm; } - virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) { + var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) override { try { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { @@ -121,11 +121,11 @@ namespace subpaving { int2mpf(c, m_c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } - catch (f2n::exception) { + catch (const f2n::exception &) { throw subpaving::exception(); } } - virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) { + ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) override { try { f2n & m = m_ctx.nm(); if (lower) @@ -135,7 +135,7 @@ namespace subpaving { m.set(m_c, k); return reinterpret_cast(m_ctx.mk_ineq(x, m_c, lower, open)); } - catch (f2n::exception) { + catch (const f2n::exception &) { throw subpaving::exception(); } } @@ -150,12 +150,12 @@ namespace subpaving { void int2hwf(mpz const & a, hwf & o) { if (!m_qm.is_int64(a)) throw subpaving::exception(); - int64 val = m_qm.get_int64(a); + int64_t val = m_qm.get_int64(a); double dval = static_cast(val); m_ctx.nm().set(o, dval); double _dval = m_ctx.nm().m().to_double(o); // TODO check the following test - if (static_cast(_dval) != val) + if (static_cast(_dval) != val) throw subpaving::exception(); } @@ -165,11 +165,11 @@ namespace subpaving { m_qm(qm) { } - virtual ~context_hwf_wrapper() {} + ~context_hwf_wrapper() override {} - virtual unsynch_mpq_manager & qm() const { return m_qm; } + unsynch_mpq_manager & qm() const override { return m_qm; } - virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) { + var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) override { try { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { @@ -178,11 +178,11 @@ namespace subpaving { int2hwf(c, m_c); return m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } - catch (f2n::exception) { + catch (const f2n::exception &) { throw subpaving::exception(); } } - virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) { + ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) override { try { f2n & m = m_ctx.nm(); if (lower) @@ -192,7 +192,7 @@ namespace subpaving { m.set(m_c, k); return reinterpret_cast(m_ctx.mk_ineq(x, m_c, lower, open)); } - catch (f2n::exception) { + catch (const f2n::exception &) { throw subpaving::exception(); } } @@ -223,11 +223,11 @@ namespace subpaving { m_z2(m_qm) { } - virtual ~context_fpoint_wrapper() {} + ~context_fpoint_wrapper() override {} - virtual unsynch_mpq_manager & qm() const { return m_qm; } + unsynch_mpq_manager & qm() const override { return m_qm; } - virtual var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) { + var mk_sum(mpz const & c, unsigned sz, mpz const * as, var const * xs) override { try { m_as.reserve(sz); for (unsigned i = 0; i < sz; i++) { @@ -236,12 +236,12 @@ namespace subpaving { int2fpoint(c, m_c); return this->m_ctx.mk_sum(m_c, sz, m_as.c_ptr(), xs); } - catch (typename context_fpoint::numeral_manager::exception) { + catch (const typename context_fpoint::numeral_manager::exception &) { throw subpaving::exception(); } } - virtual ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) { + ineq * mk_ineq(var x, mpq const & k, bool lower, bool open) override { try { typename context_fpoint::numeral_manager & m = this->m_ctx.nm(); if (lower) @@ -251,7 +251,7 @@ namespace subpaving { m.set(m_c, m_qm, k); return reinterpret_cast(this->m_ctx.mk_ineq(x, m_c, lower, open)); } - catch (typename context_fpoint::numeral_manager::exception) { + catch (const typename context_fpoint::numeral_manager::exception &) { throw subpaving::exception(); } } diff --git a/src/math/subpaving/subpaving.h b/src/math/subpaving/subpaving.h index 2c2c9978f..541f7ea7a 100644 --- a/src/math/subpaving/subpaving.h +++ b/src/math/subpaving/subpaving.h @@ -111,11 +111,11 @@ public: virtual void display_bounds(std::ostream & out) const = 0; }; - context * mk_mpq_context(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); -context * mk_mpf_context(reslimit& lim, f2n & m, params_ref const & p = params_ref(), small_object_allocator * a = 0); -context * mk_hwf_context(reslimit& lim, f2n & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = 0); -context * mk_mpff_context(reslimit& lim, mpff_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = 0); -context * mk_mpfx_context(reslimit& lim, mpfx_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = 0); + context * mk_mpq_context(reslimit& lim, unsynch_mpq_manager & m, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); +context * mk_mpf_context(reslimit& lim, f2n & m, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); +context * mk_hwf_context(reslimit& lim, f2n & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); +context * mk_mpff_context(reslimit& lim, mpff_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); +context * mk_mpfx_context(reslimit& lim, mpfx_manager & m, unsynch_mpq_manager & qm, params_ref const & p = params_ref(), small_object_allocator * a = nullptr); }; diff --git a/src/math/subpaving/subpaving_t.h b/src/math/subpaving/subpaving_t.h index a4bc02ff6..02c538828 100644 --- a/src/math/subpaving/subpaving_t.h +++ b/src/math/subpaving/subpaving_t.h @@ -73,17 +73,17 @@ public: // TODO: add SIN, COS, TAN, ... }; protected: - kind m_kind; - uint64 m_timestamp; + kind m_kind; + uint64_t m_timestamp; public: constraint(kind k):m_kind(k), m_timestamp(0) {} kind get_kind() const { return m_kind; } // Return the timestamp of the last propagation visit - uint64 timestamp() const { return m_timestamp; } + uint64_t timestamp() const { return m_timestamp; } // Reset propagation visit time - void set_visited(uint64 ts) { m_timestamp = ts; } + void set_visited(uint64_t ts) { m_timestamp = ts; } }; /** @@ -149,17 +149,17 @@ public: unsigned m_lower:1; unsigned m_open:1; unsigned m_mark:1; - uint64 m_timestamp; + uint64_t m_timestamp; bound * m_prev; justification m_jst; - void set_timestamp(uint64 ts) { m_timestamp = ts; } + void set_timestamp(uint64_t ts) { m_timestamp = ts; } public: var x() const { return static_cast(m_x); } numeral const & value() const { return m_val; } numeral & value() { return m_val; } bool is_lower() const { return m_lower; } bool is_open() const { return m_open; } - uint64 timestamp() const { return m_timestamp; } + uint64_t timestamp() const { return m_timestamp; } bound * prev() const { return m_prev; } justification jst() const { return m_jst; } void display(std::ostream & out, numeral_manager & nm, display_var_proc const & proc = display_var_proc()); @@ -210,7 +210,7 @@ public: bool inconsistent() const { return m_conflict != null_var; } void set_conflict(var x) { SASSERT(!inconsistent()); m_conflict = x; } bound * trail_stack() const { return m_trail; } - bound * parent_trail_stack() const { return m_parent == 0 ? 0 : m_parent->m_trail; } + bound * parent_trail_stack() const { return m_parent == nullptr ? nullptr : m_parent->m_trail; } bound * lower(var x) const { return bm().get(m_lowers, x); } bound * upper(var x) const { return bm().get(m_uppers, x); } node * parent() const { return m_parent; } @@ -221,7 +221,7 @@ public: /** \brief Return true if x is unbounded in this node */ - bool is_unbounded(var x) const { return lower(x) == 0 && upper(x) == 0; } + bool is_unbounded(var x) const { return lower(x) == nullptr && upper(x) == nullptr; } void push(bound * b); void set_first_child(node * n) { m_first_child = n; } @@ -275,32 +275,32 @@ public: numeral const & lower(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->lower(a.m_x); - return b == 0 ? a.m_l_val /* don't care */ : b->value(); + return b == nullptr ? a.m_l_val /* don't care */ : b->value(); } return a.m_l_val; } numeral const & upper(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->upper(a.m_x); - return b == 0 ? a.m_u_val /* don't care */ : b->value(); + return b == nullptr ? a.m_u_val /* don't care */ : b->value(); } return a.m_u_val; } numeral & lower(interval & a) { SASSERT(!a.m_constant); return a.m_l_val; } numeral & upper(interval & a) { SASSERT(!a.m_constant); return a.m_u_val; } - bool lower_is_inf(interval const & a) const { return a.m_constant ? a.m_node->lower(a.m_x) == 0 : a.m_l_inf; } - bool upper_is_inf(interval const & a) const { return a.m_constant ? a.m_node->upper(a.m_x) == 0 : a.m_u_inf; } + bool lower_is_inf(interval const & a) const { return a.m_constant ? a.m_node->lower(a.m_x) == nullptr : a.m_l_inf; } + bool upper_is_inf(interval const & a) const { return a.m_constant ? a.m_node->upper(a.m_x) == nullptr : a.m_u_inf; } bool lower_is_open(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->lower(a.m_x); - return b == 0 || b->is_open(); + return b == nullptr || b->is_open(); } return a.m_l_open; } bool upper_is_open(interval const & a) const { if (a.m_constant) { bound * b = a.m_node->upper(a.m_x); - return b == 0 || b->is_open(); + return b == nullptr || b->is_open(); } return a.m_u_open; } @@ -367,7 +367,7 @@ public: private: void * m_data; public: - watched():m_data(0) {} + watched():m_data(nullptr) {} explicit watched(var x) { m_data = BOXTAGINT(void*, x, DEFINITION); } explicit watched(clause * c) { m_data = TAG(void*, c, CLAUSE); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } @@ -486,7 +486,7 @@ private: id_gen m_node_id_gen; - uint64 m_timestamp; + uint64_t m_timestamp; node * m_root; // m_leaf_head is the head of a doubly linked list of leaf nodes to be processed. node * m_leaf_head; @@ -563,7 +563,7 @@ private: void add_clause_core(unsigned sz, ineq * const * atoms, bool lemma, bool watched); void del_clause(clause * cls); - node * mk_node(node * parent = 0); + node * mk_node(node * parent = nullptr); void del_node(node * n); void del_nodes(); diff --git a/src/math/subpaving/subpaving_t_def.h b/src/math/subpaving/subpaving_t_def.h index 3574787d8..cf93fbfad 100644 --- a/src/math/subpaving/subpaving_t_def.h +++ b/src/math/subpaving/subpaving_t_def.h @@ -36,7 +36,7 @@ public: context_t::node_selector(ctx) { } - virtual node * operator()(node * front, node * back) { + node * operator()(node * front, node * back) override { return back; } }; @@ -80,7 +80,7 @@ public: } // Return the next variable to branch. - virtual var operator()(typename context_t::node * n) { + var operator()(typename context_t::node * n) override { typename context_t::numeral_manager & nm = this->ctx()->nm(); SASSERT(this->ctx()->num_vars() > 0); var x = this->ctx()->splitting_var(n); @@ -93,7 +93,7 @@ public: if (!m_only_non_def || !this->ctx()->is_definition(x)) { bound * lower = n->lower(x); bound * upper = n->upper(x); - if (lower == 0 || upper == 0 || !nm.eq(lower->value(), upper->value())) { + if (lower == nullptr || upper == nullptr || !nm.eq(lower->value(), upper->value())) { return x; } } @@ -197,7 +197,7 @@ public: SASSERT(m_delta < INT_MAX); } - virtual void operator()(node * n, var x) { + void operator()(node * n, var x) override { SASSERT(!n->inconsistent()); numeral_manager & nm = this->ctx()->nm(); node * left = this->mk_node(n); @@ -205,11 +205,11 @@ public: bound * lower = n->lower(x); bound * upper = n->upper(x); _scoped_numeral mid(nm); - if (lower == 0 && upper == 0) { + if (lower == nullptr && upper == nullptr) { nm.set(mid, 0); // mid == 0 } - else if (lower == 0) { + else if (lower == nullptr) { _scoped_numeral delta(nm); SASSERT(upper != 0); nm.set(delta, static_cast(m_delta)); @@ -218,7 +218,7 @@ public: nm.sub(mid, delta, mid); // mid == upper - delta } - else if (upper == 0) { + else if (upper == nullptr) { _scoped_numeral delta(nm); SASSERT(lower != 0); nm.set(delta, static_cast(m_delta)); @@ -294,17 +294,17 @@ context_t::node::node(context_t & s, unsigned id): m_depth = 0; unsigned num_vars = s.num_vars(); m_conflict = null_var; - m_trail = 0; - m_parent = 0; - m_first_child = 0; - m_next_sibling = 0; - m_prev = 0; - m_next = 0; + m_trail = nullptr; + m_parent = nullptr; + m_first_child = nullptr; + m_next_sibling = nullptr; + m_prev = nullptr; + m_next = nullptr; bm().mk(m_lowers); bm().mk(m_uppers); for (unsigned i = 0; i < num_vars; i++) { - bm().push_back(m_lowers, 0); - bm().push_back(m_uppers, 0); + bm().push_back(m_lowers, nullptr); + bm().push_back(m_uppers, nullptr); } } @@ -318,10 +318,10 @@ context_t::node::node(node * parent, unsigned id): m_conflict = parent->m_conflict; m_trail = parent->m_trail; m_parent = parent; - m_first_child = 0; + m_first_child = nullptr; m_next_sibling = parent->m_first_child; - m_prev = 0; - m_next = 0; + m_prev = nullptr; + m_next = nullptr; parent->m_first_child = this; } @@ -350,7 +350,7 @@ var context_t::splitting_var(node * n) const { if (n == m_root) return null_var; bound * b = n->trail_stack(); - while (b != 0) { + while (b != nullptr) { if (b->jst().is_axiom()) return b->x(); b = b->prev(); @@ -416,16 +416,16 @@ template context_t::context_t(reslimit& lim, C const & c, params_ref const & p, small_object_allocator * a): m_limit(lim), m_c(c), - m_own_allocator(a == 0), - m_allocator(a == 0 ? alloc(small_object_allocator, "subpaving") : a), + m_own_allocator(a == nullptr), + m_allocator(a == nullptr ? alloc(small_object_allocator, "subpaving") : a), m_bm(*this, *m_allocator), m_im(lim, interval_config(m_c.m())), m_num_buffer(nm()) { m_arith_failed = false; m_timestamp = 0; - m_root = 0; - m_leaf_head = 0; - m_leaf_tail = 0; + m_root = nullptr; + m_leaf_head = nullptr; + m_leaf_tail = nullptr; m_conflict = null_var; m_qhead = 0; m_display_proc = &m_default_display_proc; @@ -638,14 +638,14 @@ void context_t::display_bounds(std::ostream & out, node * n) const { for (unsigned x = 0; x < num; x++) { bound * l = n->lower(x); bound * u = n->upper(x); - if (l != 0) { + if (l != nullptr) { display(out, l); out << " "; } - if (u != 0) { + if (u != nullptr) { display(out, u); } - if (l != 0 || u != 0) + if (l != nullptr || u != nullptr) out << "\n"; } } @@ -878,7 +878,7 @@ template typename context_t::node * context_t::mk_node(node * parent) { void * mem = allocator().allocate(sizeof(node)); node * r; - if (parent == 0) + if (parent == nullptr) r = new (mem) node(*this, m_node_id_gen.mk()); else r = new (mem) node(parent, m_node_id_gen.mk()); @@ -910,7 +910,7 @@ void context_t::del_node(node * n) { node * p = n->parent(); bound * b = n->trail_stack(); bound * b_old; - if (p != 0) { + if (p != nullptr) { node * c = p->first_child(); if (c == n) { // n is the first child @@ -928,7 +928,7 @@ void context_t::del_node(node * n) { b_old = p->trail_stack(); } else { - b_old = 0; + b_old = nullptr; } while (b != b_old) { bound * old = b; @@ -944,18 +944,18 @@ void context_t::del_node(node * n) { template void context_t::del_nodes() { ptr_buffer todo; - if (m_root == 0) + if (m_root == nullptr) return; todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); node * c = n->first_child(); - if (c == 0) { + if (c == nullptr) { del_node(n); todo.pop_back(); } else { - while (c != 0) { + while (c != nullptr) { todo.push_back(c); c = c->next_sibling(); } @@ -969,7 +969,7 @@ void context_t::push_front(node * n) { SASSERT(n->next() == 0); SASSERT(n->prev() == 0); n->set_next(m_leaf_head); - if (m_leaf_head != 0) { + if (m_leaf_head != nullptr) { SASSERT(m_leaf_head->prev() == 0); m_leaf_head->set_prev(n); } @@ -986,7 +986,7 @@ void context_t::push_back(node * n) { SASSERT(n->next() == 0); SASSERT(n->prev() == 0); n->set_prev(m_leaf_tail); - if (m_leaf_tail != 0) { + if (m_leaf_tail != nullptr) { SASSERT(m_leaf_tail->next() == 0); m_leaf_tail->set_next(n); } @@ -1001,14 +1001,14 @@ template void context_t::reset_leaf_dlist() { // Remove all nodes from the lead doubly linked list node * n = m_leaf_head; - while (n != 0) { + while (n != nullptr) { node * next = n->next(); - n->set_next(0); - n->set_prev(0); + n->set_next(nullptr); + n->set_prev(nullptr); n = next; } - m_leaf_head = 0; - m_leaf_tail = 0; + m_leaf_head = nullptr; + m_leaf_tail = nullptr; } template @@ -1016,18 +1016,18 @@ void context_t::rebuild_leaf_dlist(node * n) { reset_leaf_dlist(); // Reinsert all leaves in the leaf dlist. ptr_buffer todo; - if (m_root != 0) + if (m_root != nullptr) todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); node * c = n->first_child(); - if (c == 0) { + if (c == nullptr) { if (!n->inconsistent()) push_front(n); } else { - while (c != 0) { + while (c != nullptr) { SASSERT(c->parent() == n); todo.push_back(c); c = c->next_sibling(); @@ -1043,19 +1043,19 @@ void context_t::remove_from_leaf_dlist(node * n) { SASSERT(prev == 0 || prev != next); SASSERT(next == 0 || prev != next); SASSERT(prev != n); SASSERT(next != n); - if (prev != 0) { + if (prev != nullptr) { SASSERT(m_leaf_head != n); prev->set_next(next); - n->set_prev(0); + n->set_prev(nullptr); } else if (m_leaf_head == n) { m_leaf_head = next; } - if (next != 0) { + if (next != nullptr) { SASSERT(m_leaf_tail != n); next->set_prev(prev); - n->set_next(0); + n->set_next(nullptr); } else if (m_leaf_tail == n) { m_leaf_tail = prev; @@ -1067,18 +1067,18 @@ template void context_t::collect_leaves(ptr_vector & leaves) const { // Copy all leaves to the given vector. ptr_buffer todo; - if (m_root != 0) + if (m_root != nullptr) todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); node * c = n->first_child(); - if (c == 0) { + if (c == nullptr) { if (!n->inconsistent()) leaves.push_back(n); } else { - while (c != 0) { + while (c != nullptr) { SASSERT(c->parent() == n); todo.push_back(c); c = c->next_sibling(); @@ -1115,7 +1115,7 @@ void context_t::del_definitions() { unsigned sz = num_vars(); for (unsigned i = 0; i < sz; i++) { definition * d = m_defs[i]; - if (d == 0) + if (d == nullptr) continue; switch (d->get_kind()) { case constraint::MONOMIAL: @@ -1219,24 +1219,24 @@ bool context_t::relevant_new_bound(var x, numeral const & k, bool lower, bool return true; } // If m_epsilon is zero, then bound is relevant only if it improves existing bound. - if (m_zero_epsilon && curr_lower != 0 && (nm().lt(k, curr_lower->value()) || ((curr_lower->is_open() || !open) && nm().eq(k, curr_lower->value())))) { + if (m_zero_epsilon && curr_lower != nullptr && (nm().lt(k, curr_lower->value()) || ((curr_lower->is_open() || !open) && nm().eq(k, curr_lower->value())))) { // new lower bound does not improve existing bound TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound.\n";); return false; } - if (curr_upper == 0 && nm().lt(m_max_bound, k)) { + if (curr_upper == nullptr && nm().lt(m_max_bound, k)) { // new lower bound exceeds the :max-bound threshold TRACE("subpaving_relevant_bound", tout << "irrelevant because exceeds :max-bound threshold.\n";); return false; } - if (!m_zero_epsilon && curr_lower != 0) { + if (!m_zero_epsilon && curr_lower != nullptr) { // check if: // new-lower > lower + m_epsilon * max(min(upper - lower, |lower|), 1) numeral & min = m_tmp1; numeral & abs_lower = m_tmp2; nm().set(abs_lower, curr_lower->value()); nm().abs(abs_lower); - if (curr_upper != 0) { + if (curr_upper != nullptr) { nm().sub(curr_upper->value(), curr_lower->value(), min); if (nm().lt(abs_lower, min)) nm().set(min, abs_lower); @@ -1269,24 +1269,24 @@ bool context_t::relevant_new_bound(var x, numeral const & k, bool lower, bool return true; } // If m_epsilon is zero, then bound is relevant only if it improves existing bound. - if (m_zero_epsilon && curr_upper != 0 && (nm().lt(curr_upper->value(), k) || ((curr_upper->is_open() || !open) && nm().eq(k, curr_upper->value())))) { + if (m_zero_epsilon && curr_upper != nullptr && (nm().lt(curr_upper->value(), k) || ((curr_upper->is_open() || !open) && nm().eq(k, curr_upper->value())))) { // new upper bound does not improve existing bound TRACE("subpaving_relevant_bound", tout << "irrelevant because does not improve existing bound.\n";); return false; } - if (curr_lower == 0 && nm().lt(k, m_minus_max_bound)) { + if (curr_lower == nullptr && nm().lt(k, m_minus_max_bound)) { // new upper bound exceeds the -:max-bound threshold TRACE("subpaving_relevant_bound", tout << "irrelevant because exceeds -:max-bound threshold.\n";); return false; } - if (!m_zero_epsilon && curr_upper != 0) { + if (!m_zero_epsilon && curr_upper != nullptr) { // check if: // new-upper < upper - m_epsilon * max(min(upper - lower, |upper|), 1) numeral & min = m_tmp1; numeral & abs_upper = m_tmp2; nm().set(abs_upper, curr_upper->value()); nm().abs(abs_upper); - if (curr_lower != 0) { + if (curr_lower != nullptr) { nm().sub(curr_upper->value(), curr_lower->value(), min); if (nm().lt(abs_upper, min)) nm().set(min, abs_upper); @@ -1310,7 +1310,7 @@ bool context_t::relevant_new_bound(var x, numeral const & k, bool lower, bool TRACE("subpaving_relevant_bound", tout << "new bound is relevant\n";); return true; } - catch (typename C::exception) { + catch (const typename C::exception &) { // arithmetic module failed. set_arith_failed(); return false; @@ -1323,14 +1323,14 @@ bool context_t::is_zero(var x, node * n) const { // Return true if lower(x) == upper(x) == 0 at n bound * l = n->lower(x); bound * u = n->upper(x); - return l != 0 && u != 0 && nm().is_zero(l->value()) && nm().is_zero(u->value()) && !l->is_open() && !u->is_open(); + return l != nullptr && u != nullptr && nm().is_zero(l->value()) && nm().is_zero(u->value()) && !l->is_open() && !u->is_open(); } template bool context_t::is_upper_zero(var x, node * n) const { // Return true if upper(x) is zero at node n bound * u = n->upper(x); - return u != 0 && nm().is_zero(u->value()) && !u->is_open(); + return u != nullptr && nm().is_zero(u->value()) && !u->is_open(); } template @@ -1338,7 +1338,7 @@ bool context_t::conflicting_bounds(var x, node * n) const { // Return true if upper(x) < lower(x) at node n bound * l = n->lower(x); bound * u = n->upper(x); - return l != 0 && u != 0 && (nm().lt(u->value(), l->value()) || ((l->is_open() || u->is_open()) && nm().eq(u->value(), l->value()))); + return l != nullptr && u != nullptr && (nm().lt(u->value(), l->value()) || ((l->is_open() || u->is_open()) && nm().eq(u->value(), l->value()))); } /** @@ -1351,20 +1351,20 @@ lbool context_t::value(ineq * t, node * n) { var x = t->x(); bound * u = n->upper(x); bound * l = n->lower(x); - if (u == 0 && l == 0) + if (u == nullptr && l == nullptr) return l_undef; else if (t->is_lower()) { - if (u != 0 && (nm().lt(u->value(), t->value()) || ((u->is_open() || t->is_open()) && nm().eq(u->value(), t->value())))) + if (u != nullptr && (nm().lt(u->value(), t->value()) || ((u->is_open() || t->is_open()) && nm().eq(u->value(), t->value())))) return l_false; - else if (l != 0 && (nm().gt(l->value(), t->value()) || ((l->is_open() || !t->is_open()) && nm().eq(l->value(), t->value())))) + else if (l != nullptr && (nm().gt(l->value(), t->value()) || ((l->is_open() || !t->is_open()) && nm().eq(l->value(), t->value())))) return l_true; else return l_undef; } else { - if (l != 0 && (nm().gt(l->value(), t->value()) || ((l->is_open() || t->is_open()) && nm().eq(l->value(), t->value())))) + if (l != nullptr && (nm().gt(l->value(), t->value()) || ((l->is_open() || t->is_open()) && nm().eq(l->value(), t->value())))) return l_false; - else if (u != 0 && (nm().lt(u->value(), t->value()) || ((u->is_open() || !t->is_open()) && nm().eq(u->value(), t->value())))) + else if (u != nullptr && (nm().lt(u->value(), t->value()) || ((u->is_open() || !t->is_open()) && nm().eq(u->value(), t->value())))) return l_true; else return l_undef; @@ -1722,7 +1722,7 @@ void context_t::propagate(node * n, bound * b) { } } } - catch (typename C::exception) { + catch (const typename C::exception &) { // arithmetic module failed, ignore constraint set_arith_failed(); } @@ -1804,17 +1804,17 @@ void context_t::init() { template void context_t::operator()() { - if (m_root == 0) + if (m_root == nullptr) init(); TRACE("subpaving_stats", statistics st; collect_statistics(st); tout << "statistics:\n"; st.display_smt2(tout);); TRACE("subpaving_main", display_params(tout);); - while (m_leaf_head != 0) { + while (m_leaf_head != nullptr) { checkpoint(); SASSERT(m_queue.empty()); if (m_num_nodes > m_max_nodes) break; node * n = (*m_node_selector)(m_leaf_head, m_leaf_tail); - if (n == 0) + if (n == nullptr) break; TRACE("subpaving_main", tout << "selected node: #" << n->id() << ", depth: " << n->depth() << "\n";); remove_from_leaf_dlist(n); @@ -1892,7 +1892,7 @@ void context_t::collect_statistics(statistics & st) const { template bool context_t::is_bound_of(bound * b, node * n) const { bound * c = n->trail_stack(); - while (c != 0) { + while (c != nullptr) { if (c == b) return true; if (c->timestamp() <= b->timestamp()) @@ -1905,7 +1905,7 @@ bool context_t::is_bound_of(bound * b, node * n) const { template bool context_t::check_leaf_dlist() const { node * n = m_leaf_head; - while (n != 0) { + while (n != nullptr) { node * next = n->next(); SASSERT(next != 0 || m_leaf_tail == n); SASSERT(next == 0 || next->prev() == n); @@ -1917,13 +1917,13 @@ bool context_t::check_leaf_dlist() const { template bool context_t::check_tree() const { ptr_buffer todo; - if (m_root != 0) + if (m_root != nullptr) todo.push_back(m_root); while (!todo.empty()) { node * n = todo.back(); todo.pop_back(); node * c = n->first_child(); - while (c != 0) { + while (c != nullptr) { SASSERT(c->parent() == n); todo.push_back(c); c = c->next_sibling(); diff --git a/src/math/subpaving/tactic/expr2subpaving.cpp b/src/math/subpaving/tactic/expr2subpaving.cpp index b00c0007f..1eb4ad108 100644 --- a/src/math/subpaving/tactic/expr2subpaving.cpp +++ b/src/math/subpaving/tactic/expr2subpaving.cpp @@ -30,7 +30,7 @@ struct expr2subpaving::imp { struct frame { app * m_curr; unsigned m_idx; - frame():m_curr(0), m_idx(0) {} + frame():m_curr(nullptr), m_idx(0) {} frame(app * t):m_curr(t), m_idx(0) {} }; @@ -62,7 +62,7 @@ struct expr2subpaving::imp { m_cached_numerators(m_qm), m_cached_denominators(m_qm) { - if (e2v == 0) { + if (e2v == nullptr) { m_expr2var = alloc(expr2var, m); m_expr2var_owner = true; } @@ -107,7 +107,7 @@ struct expr2subpaving::imp { x = s().mk_var(is_int); m_expr2var->insert(t, x); if (x >= m_var2expr.size()) - m_var2expr.resize(x+1, 0); + m_var2expr.resize(x+1, nullptr); m_var2expr.set(x, t); } return x; diff --git a/src/math/subpaving/tactic/expr2subpaving.h b/src/math/subpaving/tactic/expr2subpaving.h index d95699759..32caf49a9 100644 --- a/src/math/subpaving/tactic/expr2subpaving.h +++ b/src/math/subpaving/tactic/expr2subpaving.h @@ -29,7 +29,7 @@ class expr2subpaving { struct imp; imp * m_imp; public: - expr2subpaving(ast_manager & m, subpaving::context & s, expr2var * e2v = 0); + expr2subpaving(ast_manager & m, subpaving::context & s, expr2var * e2v = nullptr); ~expr2subpaving(); ast_manager & m() const; diff --git a/src/math/subpaving/tactic/subpaving_tactic.cpp b/src/math/subpaving/tactic/subpaving_tactic.cpp index 6a7bc11e8..935fd5e19 100644 --- a/src/math/subpaving/tactic/subpaving_tactic.cpp +++ b/src/math/subpaving/tactic/subpaving_tactic.cpp @@ -40,9 +40,9 @@ class subpaving_tactic : public tactic { ast_manager & m() const { return m_inv.get_manager(); } - virtual void operator()(std::ostream & out, subpaving::var x) const { - expr * t = m_inv.get(x, 0); - if (t != 0) + void operator()(std::ostream & out, subpaving::var x) const override { + expr * t = m_inv.get(x, nullptr); + if (t != nullptr) out << mk_ismt2_pp(t, m()); else out << "k!" << x; @@ -160,7 +160,7 @@ class subpaving_tactic : public tactic { } void process_clause(expr * c) { - expr * const * args = 0; + expr * const * args = nullptr; unsigned sz; if (m().is_or(c)) { args = to_app(c)->get_args(); @@ -216,44 +216,38 @@ public: m_params(p) { } - virtual ~subpaving_tactic() { + ~subpaving_tactic() override { dealloc(m_imp); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(subpaving_tactic, m, m_params); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { m_imp->collect_param_descrs(r); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.copy(m_stats); } - virtual void reset_statistics() { + void reset_statistics() override { m_stats.reset(); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { m_imp->process(*in); m_imp->collect_statistics(m_stats); result.reset(); result.push_back(in.get()); - mc = 0; - pc = 0; - core = 0; } catch (z3_exception & ex) { // convert all Z3 exceptions into tactic exceptions @@ -261,7 +255,7 @@ public: } } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m(); dealloc(m_imp); m_imp = alloc(imp, m, m_params); diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index e458cc4b0..2eb687dae 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -71,9 +71,9 @@ void func_entry::deallocate(ast_manager & m, unsigned arity) { func_interp::func_interp(ast_manager & m, unsigned arity): m_manager(m), m_arity(arity), - m_else(0), + m_else(nullptr), m_args_are_values(true), - m_interp(0) { + m_interp(nullptr) { } func_interp::~func_interp() { @@ -102,7 +102,7 @@ func_interp * func_interp::copy() const { void func_interp::reset_interp_cache() { m_manager.dec_ref(m_interp); - m_interp = 0; + m_interp = nullptr; } bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { @@ -112,7 +112,8 @@ bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { return false; } - if ((m_arity == 0) || + if (!is_ground(t) || + (m_arity == 0) || (m_arity == 1 && !m().is_eq(c, a0, a1)) || (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity))) return false; @@ -183,13 +184,13 @@ func_entry * func_interp::get_entry(expr * const * args) const { if (curr->eq_args(m(), m_arity, args)) return curr; } - return 0; + return nullptr; } void func_interp::insert_entry(expr * const * args, expr * r) { reset_interp_cache(); func_entry * entry = get_entry(args); - if (entry != 0) { + if (entry != nullptr) { entry->set_result(m_manager, r); return; } @@ -219,7 +220,7 @@ void func_interp::insert_new_entry(expr * const * args, expr * r) { } bool func_interp::eval_else(expr * const * args, expr_ref & result) const { - if (m_else == 0) + if (m_else == nullptr) return false; var_subst s(m_manager, false); SASSERT(!s.std_order()); // (VAR 0) <- args[0], (VAR 1) <- args[1], ... @@ -232,9 +233,9 @@ bool func_interp::eval_else(expr * const * args, expr_ref & result) const { */ expr * func_interp::get_max_occ_result() const { if (m_entries.empty()) - return 0; + return nullptr; obj_map num_occs; - expr * r_max = 0; + expr * r_max = nullptr; unsigned max = 0; ptr_vector::const_iterator it = m_entries.begin(); ptr_vector::const_iterator end = m_entries.end(); @@ -257,7 +258,7 @@ expr * func_interp::get_max_occ_result() const { \brief Remove entries e such that e.get_result() == m_else. */ void func_interp::compress() { - if (m_else == 0 || m_entries.empty()) + if (m_else == nullptr || m_entries.empty()) return; // nothing to be done if (!is_ground(m_else)) return; // forall entries e in m_entries e.get_result() is ground @@ -284,8 +285,8 @@ void func_interp::compress() { } expr * func_interp::get_interp_core() const { - if (m_else == 0) - return 0; + if (m_else == nullptr) + return nullptr; expr * r = m_else; ptr_buffer vars; ptr_vector::const_iterator it = m_entries.begin(); @@ -313,10 +314,10 @@ expr * func_interp::get_interp_core() const { } expr * func_interp::get_interp() const { - if (m_interp != 0) + if (m_interp != nullptr) return m_interp; expr * r = get_interp_core(); - if (r != 0) { + if (r != nullptr) { const_cast(this)->m_interp = r; m_manager.inc_ref(m_interp); } diff --git a/src/model/func_interp.h b/src/model/func_interp.h index 47eb0e82c..e2b98d6ea 100644 --- a/src/model/func_interp.h +++ b/src/model/func_interp.h @@ -86,7 +86,7 @@ public: unsigned get_arity() const { return m_arity; } - bool is_partial() const { return m_else == 0; } + bool is_partial() const { return m_else == nullptr; } // A function interpretation is said to be simple if m_else is ground. bool is_simple() const { return is_partial() || is_ground(m_else); } bool is_constant() const; diff --git a/src/model/model.cpp b/src/model/model.cpp index 7ac1300de..6ddf9765c 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -26,43 +26,34 @@ Revision History: #include "model/model_evaluator.h" model::model(ast_manager & m): - model_core(m) { + model_core(m), + m_mev(*this) { } model::~model() { - sort2universe::iterator it3 = m_usort2universe.begin(); - sort2universe::iterator end3 = m_usort2universe.end(); - for (; it3 != end3; ++it3) { - m_manager.dec_ref(it3->m_key); - m_manager.dec_array_ref(it3->m_value->size(), it3->m_value->c_ptr()); - dealloc(it3->m_value); + for (auto & kv : m_usort2universe) { + m_manager.dec_ref(kv.m_key); + m_manager.dec_array_ref(kv.m_value->size(), kv.m_value->c_ptr()); + dealloc(kv.m_value); } } void model::copy_const_interps(model const & source) { - decl2expr::iterator it1 = source.m_interp.begin(); - decl2expr::iterator end1 = source.m_interp.end(); - for (; it1 != end1; ++it1) { - register_decl(it1->m_key, it1->m_value); + for (auto const& kv : source.m_interp) { + register_decl(kv.m_key, kv.m_value); } } void model::copy_func_interps(model const & source) { - decl2finterp::iterator it2 = source.m_finterp.begin(); - decl2finterp::iterator end2 = source.m_finterp.end(); - for (; it2 != end2; ++it2) { - register_decl(it2->m_key, it2->m_value->copy()); - } + for (auto const& kv : source.m_finterp) + register_decl(kv.m_key, kv.m_value->copy()); } void model::copy_usort_interps(model const & source) { - sort2universe::iterator it3 = source.m_usort2universe.begin(); - sort2universe::iterator end3 = source.m_usort2universe.end(); - for (; it3 != end3; ++it3) { - register_usort(it3->m_key, it3->m_value->size(), it3->m_value->c_ptr()); - } + for (auto const& kv : source.m_usort2universe) + register_usort(kv.m_key, kv.m_value->size(), kv.m_value->c_ptr()); } model * model::copy() const { @@ -75,12 +66,10 @@ model * model::copy() const { return m; } -// Remark: eval is for backward compatibility. We should use model_evaluator. -bool model::eval(expr * e, expr_ref & result, bool model_completion) { - model_evaluator ev(*this); - ev.set_model_completion(model_completion); +bool model::eval_expr(expr * e, expr_ref & result, bool model_completion) { + scoped_model_completion _smc(*this, model_completion); try { - ev(e, result); + result = (*this)(e); return true; } catch (model_evaluator_exception & ex) { @@ -93,13 +82,13 @@ bool model::eval(expr * e, expr_ref & result, bool model_completion) { struct model::value_proc : public some_value_proc { model & m_model; value_proc(model & m):m_model(m) {} - virtual expr * operator()(sort * s) { - ptr_vector * u = 0; + expr * operator()(sort * s) override { + ptr_vector * u = nullptr; if (m_model.m_usort2universe.find(s, u)) { if (u->size() > 0) return u->get(0); } - return 0; + return nullptr; } }; @@ -109,16 +98,16 @@ expr * model::get_some_value(sort * s) { } ptr_vector const & model::get_universe(sort * s) const { - ptr_vector * u = 0; + ptr_vector * u = nullptr; m_usort2universe.find(s, u); SASSERT(u != 0); return *u; } bool model::has_uninterpreted_sort(sort * s) const { - ptr_vector * u = 0; + ptr_vector * u = nullptr; m_usort2universe.find(s, u); - return u != 0; + return u != nullptr; } unsigned model::get_num_uninterpreted_sorts() const { @@ -153,28 +142,21 @@ model * model::translate(ast_translation & translator) const { model * res = alloc(model, translator.to()); // Translate const interps - decl2expr::iterator it1 = m_interp.begin(); - decl2expr::iterator end1 = m_interp.end(); - for (; it1 != end1; ++it1) { - res->register_decl(translator(it1->m_key), translator(it1->m_value)); - } + for (auto const& kv : m_interp) + res->register_decl(translator(kv.m_key), translator(kv.m_value)); // Translate func interps - decl2finterp::iterator it2 = m_finterp.begin(); - decl2finterp::iterator end2 = m_finterp.end(); - for (; it2 != end2; ++it2) { - func_interp * fi = it2->m_value; - res->register_decl(translator(it2->m_key), fi->translate(translator)); + for (auto const& kv : m_finterp) { + func_interp * fi = kv.m_value; + res->register_decl(translator(kv.m_key), fi->translate(translator)); } // Translate usort interps - sort2universe::iterator it3 = m_usort2universe.begin(); - sort2universe::iterator end3 = m_usort2universe.end(); - for (; it3 != end3; ++it3) { + for (auto const& kv : m_usort2universe) { ptr_vector new_universe; - for (unsigned i=0; im_value->size(); i++) - new_universe.push_back(translator(it3->m_value->get(i))); - res->register_usort(translator(it3->m_key), + for (unsigned i=0; i < kv.m_value->size(); i++) + new_universe.push_back(translator(kv.m_value->get(i))); + res->register_usort(translator(kv.m_key), new_universe.size(), new_universe.c_ptr()); } @@ -182,3 +164,30 @@ model * model::translate(ast_translation & translator) const { return res; } +expr_ref model::operator()(expr* t) { + return m_mev(t); +} + +expr_ref_vector model::operator()(expr_ref_vector const& ts) { + expr_ref_vector rs(m()); + for (expr* t : ts) rs.push_back((*this)(t)); + return rs; +} + +bool model::is_true(expr* t) { + return m().is_true((*this)(t)); +} + +bool model::is_false(expr* t) { + return m().is_false((*this)(t)); +} + +bool model::is_true(expr_ref_vector const& ts) { + for (expr* t : ts) if (!is_true(t)) return false; + return true; +} + +void model::reset_eval_cache() { + m_mev.reset(); +} + diff --git a/src/model/model.h b/src/model/model.h index f6de8ce0e..cfc44a8fc 100644 --- a/src/model/model.h +++ b/src/model/model.h @@ -20,35 +20,39 @@ Revision History: #define MODEL_H_ #include "model/model_core.h" +#include "model/model_evaluator.h" #include "util/ref.h" #include "ast/ast_translation.h" +class model; +typedef ref model_ref; + class model : public model_core { protected: typedef obj_map*> sort2universe; - + ptr_vector m_usorts; sort2universe m_usort2universe; + model_evaluator m_mev; struct value_proc; public: model(ast_manager & m); - virtual ~model(); + ~model() override; void copy_func_interps(model const & source); void copy_const_interps(model const & source); void copy_usort_interps(model const & source); model * copy() const; - - bool eval(func_decl * f, expr_ref & r) const { return model_core::eval(f, r); } - bool eval(expr * e, expr_ref & result, bool model_completion = false); - - virtual expr * get_some_value(sort * s); - virtual ptr_vector const & get_universe(sort * s) const; - virtual unsigned get_num_uninterpreted_sorts() const; - virtual sort * get_uninterpreted_sort(unsigned idx) const; - bool has_uninterpreted_sort(sort * s) const; + + bool eval_expr(expr * e, expr_ref & result, bool model_completion = false); + + expr * get_some_value(sort * s) override; + ptr_vector const & get_universe(sort * s) const override; + unsigned get_num_uninterpreted_sorts() const override; + sort * get_uninterpreted_sort(unsigned idx) const override; + bool has_uninterpreted_sort(sort * s) const; // // Primitives for building models @@ -58,9 +62,39 @@ public: // Model translation // model * translate(ast_translation & translator) const; + + void set_model_completion(bool f) { m_mev.set_model_completion(f); } + void updt_params(params_ref const & p) { m_mev.updt_params(p); } + + /** + * evaluation using the model evaluator. Caches results. + */ + expr_ref operator()(expr* t); + expr_ref_vector operator()(expr_ref_vector const& ts); + bool is_true(expr* t); + bool is_false(expr* t); + bool is_true(expr_ref_vector const& ts); + void reset_eval_cache(); + + class scoped_model_completion { + bool m_old_completion; + model& m_model; + public: + scoped_model_completion(model& m, bool c): + m_old_completion(m.m_mev.get_model_completion()), m_model(m) { + m.set_model_completion(c); + } + scoped_model_completion(model_ref& m, bool c): + m_old_completion(m->m_mev.get_model_completion()), m_model(*m.get()) { + m->set_model_completion(c); + } + ~scoped_model_completion() { + m_model.set_model_completion(m_old_completion); + } + }; }; -typedef ref model_ref; +std::ostream& operator<<(std::ostream& out, model_core const& m); + #endif /* MODEL_H_ */ - diff --git a/src/model/model_core.cpp b/src/model/model_core.cpp index 833c254c3..d41854c22 100644 --- a/src/model/model_core.cpp +++ b/src/model/model_core.cpp @@ -37,7 +37,7 @@ bool model_core::eval(func_decl* f, expr_ref & r) const { } else { func_interp * fi = get_func_interp(f); - if (fi != 0) { + if (fi != nullptr) { r = fi->get_interp(); return r != 0; } @@ -47,8 +47,8 @@ bool model_core::eval(func_decl* f, expr_ref & r) const { void model_core::register_decl(func_decl * d, expr * v) { SASSERT(d->get_arity() == 0); - decl2expr::obj_map_entry * entry = m_interp.insert_if_not_there2(d, 0); - if (entry->get_data().m_value == 0) { + decl2expr::obj_map_entry * entry = m_interp.insert_if_not_there2(d, nullptr); + if (entry->get_data().m_value == nullptr) { // new entry m_decls.push_back(d); m_const_decls.push_back(d); @@ -67,8 +67,8 @@ void model_core::register_decl(func_decl * d, expr * v) { void model_core::register_decl(func_decl * d, func_interp * fi) { SASSERT(d->get_arity() > 0); SASSERT(&fi->m() == &m_manager); - decl2finterp::obj_map_entry * entry = m_finterp.insert_if_not_there2(d, 0); - if (entry->get_data().m_value == 0) { + decl2finterp::obj_map_entry * entry = m_finterp.insert_if_not_there2(d, nullptr); + if (entry->get_data().m_value == nullptr) { // new entry m_decls.push_back(d); m_func_decls.push_back(d); diff --git a/src/model/model_core.h b/src/model/model_core.h index ec128c796..3f1e92bad 100644 --- a/src/model/model_core.h +++ b/src/model/model_core.h @@ -40,12 +40,13 @@ public: virtual ~model_core(); ast_manager & get_manager() const { return m_manager; } + ast_manager& m() const { return m_manager; } unsigned get_num_decls() const { return m_decls.size(); } func_decl * get_decl(unsigned i) const { return m_decls[i]; } bool has_interpretation(func_decl * d) const { return m_interp.contains(d) || m_finterp.contains(d); } - expr * get_const_interp(func_decl * d) const { expr * v; return m_interp.find(d, v) ? v : 0; } - func_interp * get_func_interp(func_decl * d) const { func_interp * fi; return m_finterp.find(d, fi) ? fi : 0; } + expr * get_const_interp(func_decl * d) const { expr * v; return m_interp.find(d, v) ? v : nullptr; } + func_interp * get_func_interp(func_decl * d) const { func_interp * fi; return m_finterp.find(d, fi) ? fi : nullptr; } bool eval(func_decl * f, expr_ref & r) const; diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 32ed1e236..f7c7fa74d 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -53,6 +53,7 @@ struct evaluator_cfg : public default_rewriter_cfg { bool m_model_completion; bool m_cache; bool m_array_equalities; + bool m_array_as_stores; evaluator_cfg(ast_manager & m, model_core & md, params_ref const & p): m(m), @@ -84,14 +85,15 @@ struct evaluator_cfg : public default_rewriter_cfg { model_evaluator_params p(_p); m_max_memory = megabytes_to_bytes(p.max_memory()); m_max_steps = p.max_steps(); - m_model_completion = p.completion(); m_cache = p.cache(); + m_model_completion = p.completion(); m_array_equalities = p.array_equalities(); + m_array_as_stores = p.array_as_stores(); } bool evaluate(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { func_interp * fi = m_model.get_func_interp(f); - return (fi != 0) && eval_fi(fi, num, args, result); + return (fi != nullptr) && eval_fi(fi, num, args, result); } // Try to use the entries to quickly evaluate the fi @@ -110,7 +112,7 @@ struct evaluator_cfg : public default_rewriter_cfg { return false; // let get_macro handle it func_entry * entry = fi->get_entry(args); - if (entry != 0) { + if (entry != nullptr) { result = entry->get_result(); return true; } @@ -119,12 +121,12 @@ struct evaluator_cfg : public default_rewriter_cfg { } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; family_id fid = f->get_family_id(); bool is_uninterp = fid != null_family_id && m.get_plugin(fid)->is_considered_uninterpreted(f); if (num == 0 && (fid == null_family_id || is_uninterp)) { expr * val = m_model.get_const_interp(f); - if (val != 0) { + if (val != nullptr) { result = val; return BR_DONE; } @@ -187,29 +189,30 @@ struct evaluator_cfg : public default_rewriter_cfg { TRACE("model_evaluator", tout << "reduce_app " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m) << "\n"; tout << "---->\n" << mk_ismt2_pp(result, m) << "\n";); - return BR_DONE; + return BR_REWRITE1; } if (st == BR_FAILED && !m.is_builtin_family_id(fid)) st = evaluate_partial_theory_func(f, num, args, result, result_pr); if (st == BR_DONE && is_app(result)) { app* a = to_app(result); if (evaluate(a->get_decl(), a->get_num_args(), a->get_args(), result)) { - return BR_DONE; + return BR_REWRITE1; } } CTRACE("model_evaluator", st != BR_FAILED, tout << result << "\n";); return st; } - void expand_value(expr_ref& val) { + void expand_stores(expr_ref& val) { vector stores; expr_ref else_case(m); bool _unused; - if (m_ar.is_array(val) && extract_array_func_interp(val, stores, else_case, _unused)) { + if (m_array_as_stores && + m_ar.is_array(val) && + extract_array_func_interp(val, stores, else_case, _unused)) { sort* srt = m.get_sort(val); val = m_ar.mk_const_array(srt, else_case); - for (unsigned i = stores.size(); i > 0; ) { - --i; + for (unsigned i = stores.size(); i-- > 0; ) { expr_ref_vector args(m); args.push_back(val); args.append(stores[i].size(), stores[i].c_ptr()); @@ -224,7 +227,7 @@ struct evaluator_cfg : public default_rewriter_cfg { func_interp * fi = m_model.get_func_interp(f); - if (fi != 0) { + if (fi != nullptr) { TRACE_MACRO; if (fi->is_partial()) { if (m_model_completion) { @@ -262,8 +265,8 @@ struct evaluator_cfg : public default_rewriter_cfg { expr_ref & result, proof_ref & result_pr) { SASSERT(f != 0); SASSERT(!m.is_builtin_family_id(f->get_family_id())); - result = 0; - result_pr = 0; + result = nullptr; + result_pr = nullptr; func_interp * fi = m_model.get_func_interp(f); if (fi) { @@ -288,8 +291,8 @@ struct evaluator_cfg : public default_rewriter_cfg { bool cache_results() const { return m_cache; } - br_status mk_array_eq(expr* a, expr* b, expr_ref& result) { + TRACE("model_evaluator", tout << "mk_array_eq " << m_array_equalities << "\n";); if (a == b) { result = m.mk_true(); return BR_DONE; @@ -315,6 +318,7 @@ struct evaluator_cfg : public default_rewriter_cfg { conj.push_back(m.mk_eq(else1, else2)); } if (args_are_unique1 && args_are_unique2 && !stores1.empty()) { + TRACE("model_evalator", tout << "argss are unique";); return mk_array_eq_core(stores1, else1, stores2, else2, conj, result); } @@ -329,7 +333,10 @@ struct evaluator_cfg : public default_rewriter_cfg { expr_ref s2(m_ar.mk_select(args2.size(), args2.c_ptr()), m); conj.push_back(m.mk_eq(s1, s2)); } - result = m.mk_and(conj.size(), conj.c_ptr()); + result = mk_and(conj); + TRACE("model_evaluator", tout << mk_pp(a, m) << " == " << mk_pp(b, m) << " -> " << conj << "\n"; + for (auto& s : stores1) tout << "store: " << s << "\n"; + ); return BR_REWRITE_FULL; } return BR_FAILED; @@ -382,7 +389,7 @@ struct evaluator_cfg : public default_rewriter_cfg { continue; } table2.insert(stores2[i].c_ptr()); - expr * const* args = 0; + expr * const* args = nullptr; expr* val = stores2[i][arity]; if (table1.find(stores2[i].c_ptr(), args)) { switch (compare(args[arity], val)) { @@ -399,12 +406,11 @@ struct evaluator_cfg : public default_rewriter_cfg { } } } - args_table::iterator it = table1.begin(), end = table1.end(); - for (; it != end; ++it) { - switch (compare((*it)[arity], else2)) { + for (auto const& t : table1) { + switch (compare((t)[arity], else2)) { case l_true: break; case l_false: result = m.mk_false(); return BR_DONE; - default: conj.push_back(m.mk_eq((*it)[arity], else2)); break; + default: conj.push_back(m.mk_eq((t)[arity], else2)); break; } } result = mk_and(conj); @@ -455,6 +461,7 @@ struct evaluator_cfg : public default_rewriter_cfg { func_decl* f = m_ar.get_as_array_func_decl(to_app(a)); func_interp* g = m_model.get_func_interp(f); + if (!g) return false; unsigned sz = g->num_entries(); unsigned arity = f->get_arity(); unsigned base_sz = stores.size(); @@ -497,9 +504,6 @@ struct evaluator_cfg : public default_rewriter_cfg { TRACE("model_evaluator", tout << "else case: " << mk_pp(else_case, m) << "\n";); return true; } - - - }; template class rewriter_tpl; @@ -513,10 +517,7 @@ struct model_evaluator::imp : public rewriter_tpl { m_cfg(md.get_manager(), md, p) { set_cancel_check(false); } - - void expand_value (expr_ref &val) { - m_cfg.expand_value (val); - } + void expand_stores(expr_ref &val) {m_cfg.expand_stores(val);} }; model_evaluator::model_evaluator(model_core & md, params_ref const & p) { @@ -543,6 +544,10 @@ void model_evaluator::set_model_completion(bool f) { m_imp->cfg().m_model_completion = f; } +bool model_evaluator::get_model_completion() const { + return m_imp->cfg().m_model_completion; +} + void model_evaluator::set_expand_array_equalities(bool f) { m_imp->cfg().m_array_equalities = f; } @@ -562,10 +567,16 @@ void model_evaluator::reset(params_ref const & p) { updt_params(p); } +void model_evaluator::reset(model_core &model, params_ref const& p) { + dealloc(m_imp); + m_imp = alloc(imp, model, p); +} + + void model_evaluator::operator()(expr * t, expr_ref & result) { TRACE("model_evaluator", tout << mk_ismt2_pp(t, m()) << "\n";); m_imp->operator()(t, result); - m_imp->expand_value(result); + m_imp->expand_stores(result); } expr_ref model_evaluator::operator()(expr * t) { @@ -574,3 +585,44 @@ expr_ref model_evaluator::operator()(expr * t) { this->operator()(t, result); return result; } + +expr_ref_vector model_evaluator::operator()(expr_ref_vector const& ts) { + expr_ref_vector rs(m()); + for (expr* t : ts) rs.push_back((*this)(t)); + return rs; +} + + +bool model_evaluator::is_true(expr* t) { + expr_ref tmp(m()); + return eval(t, tmp, true) && m().is_true(tmp); +} + +bool model_evaluator::is_false(expr* t) { + expr_ref tmp(m()); + return eval(t, tmp, true) && m().is_false(tmp); +} + +bool model_evaluator::is_true(expr_ref_vector const& ts) { + for (expr* t : ts) if (!is_true(t)) return false; + return true; +} + +bool model_evaluator::eval(expr* t, expr_ref& r, bool model_completion) { + set_model_completion(model_completion); + try { + r = (*this)(t); + return true; + } + catch (model_evaluator_exception &ex) { + (void)ex; + TRACE("model_evaluator", tout << ex.msg () << "\n";); + return false; + } +} + +bool model_evaluator::eval(expr_ref_vector const& ts, expr_ref& r, bool model_completion) { + expr_ref tmp(m()); + tmp = mk_and(ts); + return eval(tmp, r, model_completion); +} diff --git a/src/model/model_evaluator.h b/src/model/model_evaluator.h index bd2b2d664..8666e3519 100644 --- a/src/model/model_evaluator.h +++ b/src/model/model_evaluator.h @@ -37,17 +37,32 @@ public: ast_manager & m () const; void set_model_completion(bool f); + bool get_model_completion() const; void set_expand_array_equalities(bool f); void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); void operator()(expr * t, expr_ref & r); - expr_ref operator()(expr* t); + expr_ref_vector operator()(expr_ref_vector const& ts); + + // exception safe + bool eval(expr* t, expr_ref& r, bool model_completion = true); + bool eval(expr_ref_vector const& ts, expr_ref& r, bool model_completion = true); + + bool is_true(expr * t); + bool is_false(expr * t); + bool is_true(expr_ref_vector const& ts); + + /** + * best effort evaluator of extensional array equality. + */ + expr_ref eval_array_eq(app* e, expr* arg1, expr* arg2); void cleanup(params_ref const & p = params_ref()); void reset(params_ref const & p = params_ref()); + void reset(model_core& model, params_ref const & p = params_ref()); unsigned get_num_steps() const; }; diff --git a/src/model/model_evaluator_params.pyg b/src/model/model_evaluator_params.pyg index b6417f7fc..509b3e7c7 100644 --- a/src/model/model_evaluator_params.pyg +++ b/src/model/model_evaluator_params.pyg @@ -4,6 +4,7 @@ def_module_params('model_evaluator', max_steps_param(), ('completion', BOOL, False, 'assigns an interptetation to symbols that do not have one in the current model, when evaluating expressions in the current model'), ('cache', BOOL, True, 'cache intermediate results in the model evaluator'), - ('array_equalities', BOOL, True, 'evaluate array equalities') + ('array_equalities', BOOL, True, 'evaluate array equalities'), + ('array_as_stores', BOOL, True, 'return array as a set of stores'), )) diff --git a/src/model/model_implicant.cpp b/src/model/model_implicant.cpp index 6abdacfba..375834477 100644 --- a/src/model/model_implicant.cpp +++ b/src/model/model_implicant.cpp @@ -90,7 +90,7 @@ void model_implicant::reset() { m_visited.reset(); m_numbers.reset(); m_refs.reset(); - m_model = 0; + m_model = nullptr; } expr_ref_vector model_implicant::minimize_model(ptr_vector const & formulas, model_ref& mdl) { @@ -172,7 +172,6 @@ void model_implicant::process_formula(app* e, ptr_vector& todo, ptr_vector case OP_FALSE: break; case OP_EQ: - case OP_IFF: if (args[0] == args[1]) { SASSERT(v); // no-op @@ -536,9 +535,8 @@ bool model_implicant::extract_array_func_interp(expr* a, vector */ void model_implicant::eval_array_eq(app* e, expr* arg1, expr* arg2) { TRACE("pdr", tout << "array equality: " << mk_pp(e, m) << "\n";); - expr_ref v1(m), v2(m); - m_model->eval(arg1, v1); - m_model->eval(arg2, v2); + expr_ref v1 = (*m_model)(arg1); + expr_ref v2 = (*m_model)(arg2); if (v1 == v2) { set_true(e); return; @@ -588,8 +586,8 @@ void model_implicant::eval_array_eq(app* e, expr* arg1, expr* arg2) { args2.append(store[i].size()-1, store[i].c_ptr()); s1 = m_array.mk_select(args1.size(), args1.c_ptr()); s2 = m_array.mk_select(args2.size(), args2.c_ptr()); - m_model->eval(s1, w1); - m_model->eval(s2, w2); + w1 = (*m_model)(s1); + w2 = (*m_model)(s2); if (w1 == w2) { continue; } @@ -622,9 +620,9 @@ void model_implicant::eval_eq(app* e, expr* arg1, expr* arg2) { eval_array_eq(e, arg1, arg2); } else if (is_x(arg1) || is_x(arg2)) { - expr_ref eq(m), vl(m); + expr_ref eq(m); eq = m.mk_eq(arg1, arg2); - m_model->eval(eq, vl); + expr_ref vl = (*m_model)(eq); if (m.is_true(vl)) { set_bool(e, true); } @@ -666,8 +664,8 @@ void model_implicant::eval_eq(app* e, expr* arg1, expr* arg2) { } void model_implicant::eval_basic(app* e) { - expr* arg1 = 0, *arg2 = 0; - expr *argCond = 0, *argThen = 0, *argElse = 0, *arg = 0; + expr* arg1 = nullptr, *arg2 = nullptr; + expr *argCond = nullptr, *argThen = nullptr, *argElse = nullptr, *arg = nullptr; bool has_x = false; unsigned arity = e->get_num_args(); switch(e->get_decl_kind()) { @@ -742,10 +740,6 @@ void model_implicant::eval_basic(app* e) { set_x(e); } break; - case OP_IFF: - VERIFY(m.is_iff(e, arg1, arg2)); - eval_eq(e, arg1, arg2); - break; case OP_ITE: VERIFY(m.is_ite(e, argCond, argThen, argElse)); if (is_true(argCond)) { @@ -842,8 +836,7 @@ bool model_implicant::check_model(ptr_vector const& formulas) { eval_basic(curr); } else { - expr_ref vl(m); - m_model->eval(curr, vl); + expr_ref vl = (*m_model)(curr); assign_value(curr, vl); } @@ -889,7 +882,7 @@ expr_ref model_implicant::eval(model_ref& model, func_decl* d) { expr_ref model_implicant::eval(model_ref& model, expr* e) { expr_ref result(m); m_model = model; - VERIFY(m_model->eval(e, result, true)); + result = (*m_model)(e); if (m_array.is_array(e)) { vector stores; expr_ref_vector args(m); diff --git a/src/model/model_pp.cpp b/src/model/model_pp.cpp index 2f9b114bb..08d63803b 100644 --- a/src/model/model_pp.cpp +++ b/src/model/model_pp.cpp @@ -31,10 +31,8 @@ static void display_uninterp_sorts(std::ostream & out, model_core const & md) { sort * s = md.get_uninterpreted_sort(i); out << "(define-sort " << mk_pp(s, m); ptr_vector const & univ = md.get_universe(s); - ptr_vector::const_iterator it = univ.begin(); - ptr_vector::const_iterator end = univ.end(); - for (; it != end; ++it) { - out << " " << mk_ismt2_pp(*it, m); + for (expr* e : univ) { + out << " " << mk_ismt2_pp(e, m); } out << ")\n"; } diff --git a/src/model/model_smt2_pp.cpp b/src/model/model_smt2_pp.cpp index 152cbc8d3..bc1900ba7 100644 --- a/src/model/model_smt2_pp.cpp +++ b/src/model/model_smt2_pp.cpp @@ -302,3 +302,8 @@ void model_smt2_pp(std::ostream & out, ast_manager & m, model_core const & md, u pp_consts(out, *(ctx.get()), md, indent); pp_funs(out, *(ctx.get()), md, indent); } + +std::ostream& operator<<(std::ostream& out, model_core const& m) { + model_smt2_pp(out, m.m(), m, 0); + return out; +} diff --git a/src/muz/base/CMakeLists.txt b/src/muz/base/CMakeLists.txt index 6b0334664..8c21e1557 100644 --- a/src/muz/base/CMakeLists.txt +++ b/src/muz/base/CMakeLists.txt @@ -18,5 +18,5 @@ z3_add_component(muz smt smt2parser PYG_FILES - fixedpoint_params.pyg + fp_params.pyg ) diff --git a/src/muz/base/dl_boogie_proof.h b/src/muz/base/dl_boogie_proof.h index f335159b6..ed2f5a801 100644 --- a/src/muz/base/dl_boogie_proof.h +++ b/src/muz/base/dl_boogie_proof.h @@ -82,7 +82,7 @@ namespace datalog { void get_labels(proof* p, labels&); public: - boogie_proof(ast_manager& m): m(m), m_proof(m), m_model(0) {} + boogie_proof(ast_manager& m): m(m), m_proof(m), m_model(nullptr) {} void set_proof(proof* p); diff --git a/src/muz/base/dl_context.cpp b/src/muz/base/dl_context.cpp index 39e044ec3..220b2516d 100644 --- a/src/muz/base/dl_context.cpp +++ b/src/muz/base/dl_context.cpp @@ -27,7 +27,7 @@ Revision History: #include "ast/ast_smt2_pp.h" #include "ast/datatype_decl_plugin.h" #include "ast/scoped_proof.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "ast/ast_pp_util.h" @@ -45,7 +45,7 @@ namespace datalog { protected: sort_ref m_sort; bool m_limited_size; - uint64 m_size; + uint64_t m_size; sort_domain(sort_kind k, context & ctx, sort * s) : m_kind(k), m_sort(s, ctx.get_manager()) { @@ -90,10 +90,10 @@ namespace datalog { return idx; } - virtual unsigned get_constant_count() const { + unsigned get_constant_count() const override { return m_el_names.size(); } - virtual void print_element(finite_element el_num, std::ostream & out) { + void print_element(finite_element el_num, std::ostream & out) override { if (el_num>=m_el_names.size()) { out << el_num; return; @@ -103,15 +103,15 @@ namespace datalog { }; class context::uint64_sort_domain : public sort_domain { - typedef map > el2num; - typedef svector num2el; + typedef map > el2num; + typedef svector num2el; el2num m_el_numbers; num2el m_el_names; public: uint64_sort_domain(context & ctx, sort * s) : sort_domain(SK_UINT64, ctx, s) {} - finite_element get_number(uint64 el) { + finite_element get_number(uint64_t el) { //we number symbols starting from zero, so table->size() is equal to the //index of the symbol to be added next @@ -132,10 +132,10 @@ namespace datalog { } return idx; } - virtual unsigned get_constant_count() const { + unsigned get_constant_count() const override { return m_el_names.size(); } - virtual void print_element(finite_element el_num, std::ostream & out) { + void print_element(finite_element el_num, std::ostream & out) override { if (el_num >= m_el_names.size()) { out << "get_name() << ":" << el_num << '>'; return; @@ -152,16 +152,16 @@ namespace datalog { class context::restore_rules : public trail { rule_set* m_old_rules; - void reset() { - dealloc(m_old_rules); - m_old_rules = 0; + void reset() { + dealloc(m_old_rules); + m_old_rules = nullptr; } public: restore_rules(rule_set& r): m_old_rules(alloc(rule_set, r)) {} - virtual ~restore_rules() {} - - virtual void undo(context& ctx) { + ~restore_rules() override {} + + void undo(context& ctx) override { ctx.replace_rules(*m_old_rules); reset(); } @@ -173,8 +173,8 @@ namespace datalog { unsigned m_old_size; public: restore_vec_size_trail(Vec& v): m_vector(v), m_old_size(v.size()) {} - virtual ~restore_vec_size_trail() {} - virtual void undo(Ctx& ctx) { m_vector.shrink(m_old_size); } + ~restore_vec_size_trail() override {} + void undo(Ctx& ctx) override { m_vector.shrink(m_old_size); } }; void context::push() { @@ -188,10 +188,8 @@ namespace datalog { if (m_trail.get_num_scopes() == 0) { throw default_exception("there are no backtracking points to pop to"); } - if (m_engine.get() && get_engine() != DUALITY_ENGINE) { - throw default_exception("pop operation is only supported by duality engine"); - } - m_trail.pop_scope(1); + throw default_exception("pop operation is not supported"); + m_trail.pop_scope(1); } // ----------------------------------- @@ -205,7 +203,7 @@ namespace datalog { m_register_engine(re), m_fparams(fp), m_params_ref(pa), - m_params(alloc(fixedpoint_params, m_params_ref)), + m_params(alloc(fp_params, m_params_ref)), m_decl_util(m), m_rewriter(m), m_var_subst(m), @@ -221,9 +219,9 @@ namespace datalog { m_rule_fmls_head(0), m_rule_fmls(m), m_background(m), - m_mc(0), - m_rel(0), - m_engine(0), + m_mc(nullptr), + m_rel(nullptr), + m_engine(nullptr), m_closed(false), m_saturation_was_run(false), m_enable_bind_variables(true), @@ -237,7 +235,7 @@ namespace datalog { context::~context() { reset(); - dealloc(m_params); + dealloc(m_params); } void context::reset() { @@ -251,8 +249,8 @@ namespace datalog { m_preds.reset(); m_preds_by_name.reset(); reset_dealloc_values(m_sorts); - m_engine = 0; - m_rel = 0; + m_engine = nullptr; + m_rel = nullptr; } bool context::is_fact(app * head) const { @@ -293,14 +291,14 @@ namespace datalog { bool context::similarity_compressor() const { return m_params->datalog_similarity_compressor(); } unsigned context::similarity_compressor_threshold() const { return m_params->datalog_similarity_compressor_threshold(); } unsigned context::soft_timeout() const { return m_fparams.m_timeout; } - unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); } + unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); } bool context::generate_explanations() const { return m_params->datalog_generate_explanations(); } bool context::explanations_on_relation_level() const { return m_params->datalog_explanations_on_relation_level(); } bool context::magic_sets_for_queries() const { return m_params->datalog_magic_sets_for_queries(); } symbol context::tab_selection() const { return m_params->tab_selection(); } bool context::xform_coi() const { return m_params->xform_coi(); } bool context::xform_slice() const { return m_params->xform_slice(); } - bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); } + bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); } bool context::karr() const { return m_params->xform_karr(); } bool context::scale() const { return m_params->xform_scale(); } bool context::magic() const { return m_params->xform_magic(); } @@ -368,14 +366,14 @@ namespace datalog { return dom.get_number(sym); } - context::finite_element context::get_constant_number(relation_sort srt, uint64 el) { + context::finite_element context::get_constant_number(relation_sort srt, uint64_t el) { sort_domain & dom0 = get_sort_domain(srt); SASSERT(dom0.get_kind()==SK_UINT64); uint64_sort_domain & dom = static_cast(dom0); return dom.get_number(el); } - void context::print_constant_name(relation_sort srt, uint64 num, std::ostream & out) + void context::print_constant_name(relation_sort srt, uint64_t num, std::ostream & out) { if (has_sort_domain(srt)) { SASSERT(num<=UINT_MAX); @@ -386,7 +384,7 @@ namespace datalog { } } - bool context::try_get_sort_constant_count(relation_sort srt, uint64 & constant_count) { + bool context::try_get_sort_constant_count(relation_sort srt, uint64_t & constant_count) { if (!has_sort_domain(srt)) { return false; } @@ -394,24 +392,24 @@ namespace datalog { return true; } - uint64 context::get_sort_size_estimate(relation_sort srt) { + uint64_t context::get_sort_size_estimate(relation_sort srt) { if (get_decl_util().is_rule_sort(srt)) { return 1; } - uint64 res; + uint64_t res; if (!try_get_sort_constant_count(srt, res)) { - sort_size sz = srt->get_num_elements(); + const sort_size & sz = srt->get_num_elements(); if (sz.is_finite()) { res = sz.size(); } else { - res = std::numeric_limits::max(); + res = std::numeric_limits::max(); } } return res; } - void context::set_argument_names(const func_decl * pred, svector var_names) + void context::set_argument_names(const func_decl * pred, const svector & var_names) { SASSERT(!m_argument_var_names.contains(pred)); m_argument_var_names.insert(pred, var_names); @@ -430,7 +428,7 @@ namespace datalog { } - void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) { if (relation_name_cnt > 0) { ensure_engine(); @@ -440,9 +438,9 @@ namespace datalog { } } - func_decl * context::mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, + func_decl * context::mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, func_decl* orig_pred) { - func_decl* new_pred = + func_decl* new_pred = m.mk_fresh_func_decl(prefix, suffix, arity, domain, m.mk_bool_sort()); register_predicate(new_pred, true); @@ -465,7 +463,7 @@ namespace datalog { scoped_proof_mode _scp(m, generate_proof_trace()?PGM_ENABLED:PGM_DISABLED); while (m_rule_fmls_head < m_rule_fmls.size()) { expr* fml = m_rule_fmls[m_rule_fmls_head].get(); - proof* p = generate_proof_trace()?m.mk_asserted(fml):0; + proof* p = generate_proof_trace()?m.mk_asserted(fml):nullptr; rm.mk_rule(fml, p, m_rule_set, m_rule_names[m_rule_fmls_head]); ++m_rule_fmls_head; } @@ -475,10 +473,10 @@ namespace datalog { // // Update a rule with a new. // It requires basic subsumption. - // + // void context::update_rule(expr* rl, symbol const& name) { datalog::rule_manager& rm = get_rule_manager(); - proof* p = 0; + proof* p = nullptr; if (generate_proof_trace()) { p = m.mk_asserted(rl); } @@ -493,16 +491,16 @@ namespace datalog { // The new rule is inserted last: rule_ref r(m_rule_set.get_rule(size_before), rm); rule_ref_vector const& rls = m_rule_set.get_rules(); - rule* old_rule = 0; + rule* old_rule = nullptr; for (unsigned i = 0; i < size_before; ++i) { if (rls[i]->name() == name) { - if (old_rule) { + if (old_rule) { std::stringstream strm; strm << "Rule " << name << " occurs twice. It cannot be modified"; m_rule_set.del_rule(r); throw default_exception(strm.str()); } - old_rule = rls[i]; + old_rule = rls[i]; } } if (old_rule) { @@ -558,7 +556,7 @@ namespace datalog { ensure_engine(); m_engine->add_cover(level, pred, property); } - + void context::add_invariant(func_decl* pred, expr *property) { ensure_engine(); @@ -568,40 +566,29 @@ namespace datalog { void context::check_rules(rule_set& r) { m_rule_properties.set_generate_proof(generate_proof_trace()); switch(get_engine()) { - case DATALOG_ENGINE: + case DATALOG_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_quantifier_free(); m_rule_properties.check_uninterpreted_free(); - m_rule_properties.check_nested_free(); + m_rule_properties.check_nested_free(); m_rule_properties.check_infinite_sorts(); break; case SPACER_ENGINE: - case PDR_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); m_rule_properties.check_uninterpreted_free(); break; - case QPDR_ENGINE: - m_rule_properties.collect(r); - m_rule_properties.check_for_negated_predicates(); - m_rule_properties.check_uninterpreted_free(); - break; case BMC_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_for_negated_predicates(); - break; + break; case QBMC_ENGINE: - m_rule_properties.collect(r); - m_rule_properties.check_existential_tail(); - m_rule_properties.check_for_negated_predicates(); - break; - case TAB_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); break; - case DUALITY_ENGINE: + case TAB_ENGINE: m_rule_properties.collect(r); m_rule_properties.check_existential_tail(); m_rule_properties.check_for_negated_predicates(); @@ -663,7 +650,7 @@ namespace datalog { add_fact(pred, rfact); } } - + void context::add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]) { if (pred->get_arity() != num_args) { std::ostringstream out; @@ -695,7 +682,7 @@ namespace datalog { reopen(); } } - + void context::reopen() { SASSERT(m_closed); m_rule_set.reopen(); @@ -708,7 +695,7 @@ namespace datalog { transformer.register_plugin(plugin); transform_rules(transformer); } - + void context::transform_rules(rule_transformer& transf) { SASSERT(m_closed); //we must finish adding rules before we start transforming them TRACE("dl", display_rules(tout);); @@ -737,7 +724,7 @@ namespace datalog { } void context::collect_params(param_descrs& p) { - fixedpoint_params::collect_param_descrs(p); + fp_params::collect_param_descrs(p); insert_timeout(p); } @@ -745,8 +732,8 @@ namespace datalog { m_params_ref.copy(p); if (m_engine.get()) m_engine->updt_params(); m_generate_proof_trace = m_params->generate_proof_trace(); - m_unbound_compressor = m_params->datalog_unbound_compressor(); - m_default_relation = m_params->datalog_default_relation(); + m_unbound_compressor = m_params->datalog_unbound_compressor(); + m_default_relation = m_params->datalog_default_relation(); } expr_ref context::get_background_assertion() { @@ -761,7 +748,7 @@ namespace datalog { void context::assert_expr(expr* e) { TRACE("dl", tout << mk_ismt2_pp(e, m) << "\n";); - m_background.push_back(e); + m_background.push_back(e); } void context::cleanup() { @@ -781,19 +768,14 @@ namespace datalog { DL_ENGINE get_engine() const { return m_engine_type; } void operator()(expr* e) { - if (is_quantifier(e)) { - m_engine_type = QPDR_ENGINE; - } - else if (m_engine_type != QPDR_ENGINE) { if (a.is_int_real(e)) { - m_engine_type = PDR_ENGINE; + m_engine_type = SPACER_ENGINE; } else if (is_var(e) && m.is_bool(e)) { - m_engine_type = PDR_ENGINE; + m_engine_type = SPACER_ENGINE; } else if (dt.is_datatype(m.get_sort(e))) { - m_engine_type = PDR_ENGINE; - } + m_engine_type = SPACER_ENGINE; } } }; @@ -803,19 +785,13 @@ namespace datalog { return; } symbol e = m_params->engine(); - + if (e == symbol("datalog")) { m_engine_type = DATALOG_ENGINE; } else if (e == symbol("spacer")) { m_engine_type = SPACER_ENGINE; } - else if (e == symbol("pdr")) { - m_engine_type = PDR_ENGINE; - } - else if (e == symbol("qpdr")) { - m_engine_type = QPDR_ENGINE; - } else if (e == symbol("bmc")) { m_engine_type = BMC_ENGINE; } @@ -828,9 +804,6 @@ namespace datalog { else if (e == symbol("clp")) { m_engine_type = CLP_ENGINE; } - else if (e == symbol("duality")) { - m_engine_type = DUALITY_ENGINE; - } else if (e == symbol("ddnf")) { m_engine_type = DDNF_ENGINE; } @@ -838,7 +811,7 @@ namespace datalog { if (m_engine_type == LAST_ENGINE) { expr_fast_mark1 mark; engine_type_proc proc(m); - m_engine_type = DATALOG_ENGINE; + m_engine_type = DATALOG_ENGINE; for (unsigned i = 0; m_engine_type == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) { rule * r = m_rule_set.get_rule(i); quick_for_each_expr(proc, mark, r->get_head()); @@ -861,13 +834,11 @@ namespace datalog { lbool context::query(expr* query) { m_mc = mk_skip_model_converter(); m_last_status = OK; - m_last_answer = 0; - m_last_ground_answer = 0; + m_last_answer = nullptr; + m_last_ground_answer = nullptr; switch (get_engine()) { case DATALOG_ENGINE: case SPACER_ENGINE: - case PDR_ENGINE: - case QPDR_ENGINE: case BMC_ENGINE: case QBMC_ENGINE: case TAB_ENGINE: @@ -875,11 +846,6 @@ namespace datalog { case DDNF_ENGINE: flush_add_rules(); break; - case DUALITY_ENGINE: - // this lets us use duality with SAS 2013 abstraction - if(quantify_arrays()) - flush_add_rules(); - break; default: UNREACHABLE(); } @@ -890,13 +856,11 @@ namespace datalog { lbool context::query_from_lvl (expr* query, unsigned lvl) { m_mc = mk_skip_model_converter(); m_last_status = OK; - m_last_answer = 0; - m_last_ground_answer = 0; + m_last_answer = nullptr; + m_last_ground_answer = nullptr; switch (get_engine()) { case DATALOG_ENGINE: case SPACER_ENGINE: - case PDR_ENGINE: - case QPDR_ENGINE: case BMC_ENGINE: case QBMC_ENGINE: case TAB_ENGINE: @@ -929,15 +893,15 @@ namespace datalog { m_rel = dynamic_cast(m_engine.get()); } - } + } } - lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { - m_last_answer = 0; + lbool context::rel_query(unsigned num_rels, func_decl * const* rels) { + m_last_answer = nullptr; ensure_engine(); return m_engine->query(num_rels, rels); } - + expr* context::get_answer_as_formula() { if (m_last_answer) { return m_last_answer.get(); @@ -990,7 +954,7 @@ namespace datalog { void context::display(std::ostream & out) const { display_rules(out); - if (m_rel) m_rel->display_facts(out); + if (m_rel) m_rel->display_facts(out); } void context::display_profile(std::ostream& out) const { @@ -1026,10 +990,10 @@ namespace datalog { bool context::result_contains_fact(relation_fact const& f) { return m_rel && m_rel->result_contains_fact(f); } - + // NB: algebraic data-types declarations will not be printed. - static void collect_free_funcs(unsigned sz, expr* const* exprs, + static void collect_free_funcs(unsigned sz, expr* const* exprs, ast_pp_util& v, mk_fresh_name& fresh_names) { v.collect(sz, exprs); @@ -1041,7 +1005,7 @@ namespace datalog { fresh_names.add(e); } } - + void context::get_raw_rule_formulas(expr_ref_vector& rules, svector& names, unsigned_vector &bounds) { for (unsigned i = 0; i < m_rule_fmls.size(); ++i) { expr_ref r = bind_vars(m_rule_fmls[i].get(), true); @@ -1054,12 +1018,12 @@ namespace datalog { void context::get_rules_as_formulas(expr_ref_vector& rules, expr_ref_vector& queries, svector& names) { expr_ref fml(m); rule_manager& rm = get_rule_manager(); - + // ensure that rules are all using bound variables. for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { m_free_vars(m_rule_fmls[i].get()); if (!m_free_vars.empty()) { - rm.mk_rule(m_rule_fmls[i].get(), 0, m_rule_set, m_rule_names[i]); + rm.mk_rule(m_rule_fmls[i].get(), nullptr, m_rule_set, m_rule_names[i]); m_rule_fmls[i] = m_rule_fmls.back(); m_rule_names[i] = m_rule_names.back(); m_rule_fmls.pop_back(); @@ -1103,10 +1067,20 @@ namespace datalog { } for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) { rules.push_back(m_rule_fmls[i].get()); - names.push_back(m_rule_names[i]); + names.push_back(m_rule_names[i]); } } - + + static std::ostream& display_symbol(std::ostream& out, symbol const& nm) { + if (is_smt2_quoted_symbol(nm)) { + out << mk_smt2_quoted_symbol(nm); + } + else { + out << nm; + } + return out; + } + void context::display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out) { ast_manager& m = get_manager(); ast_pp_util visitor(m); @@ -1135,7 +1109,7 @@ namespace datalog { for (unsigned i = 0; i < sz; ++i) { func_decl* f = visitor.coll.get_func_decls()[i]; if (f->get_family_id() != null_family_id) { - // + // } else if (is_predicate(f) && use_fixedpoint_extensions) { rels.insert(f); @@ -1148,13 +1122,13 @@ namespace datalog { if (!use_fixedpoint_extensions) { out << "(set-logic HORN)\n"; } + for (func_decl * f : rels) + visitor.remove_decl(f); visitor.display_decls(out); - func_decl_set::iterator it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - func_decl* f = *it; + + for (func_decl * f : rels) display_rel_decl(out, f); - } if (use_fixedpoint_extensions && do_declare_vars) { declare_vars(rules, fresh_names, out); @@ -1169,7 +1143,7 @@ namespace datalog { PP(axioms[i]); out << ")\n"; } - for (unsigned i = 0; i < rules.size(); ++i) { + for (unsigned i = 0; i < rules.size(); ++i) { out << (use_fixedpoint_extensions?"(rule ":"(assert "); expr* r = rules[i].get(); symbol nm = names[i]; @@ -1182,16 +1156,10 @@ namespace datalog { while (fresh_names.contains(nm)) { std::ostringstream s; s << nm << "!"; - nm = symbol(s.str().c_str()); + nm = symbol(s.str().c_str()); } fresh_names.add(nm); - if (is_smt2_quoted_symbol(nm)) { - out << mk_smt2_quoted_symbol(nm); - } - else { - out << nm; - } - out << ")"; + display_symbol(out, nm) << ")"; } out << ")\n"; } @@ -1214,12 +1182,13 @@ namespace datalog { args.push_back(m.mk_var(j, m_free_vars[j])); } qfn = m.mk_implies(q, m.mk_app(fn, args.size(), args.c_ptr())); - + out << "(assert "; PP(qfn); out << ")\n"; } - out << "(query " << fn->get_name() << ")\n"; + out << "(query "; + display_symbol(out, fn->get_name()) << ")\n"; } } else { @@ -1238,8 +1207,9 @@ namespace datalog { void context::display_rel_decl(std::ostream& out, func_decl* f) { smt2_pp_environment_dbg env(m); - out << "(declare-rel " << f->get_name() << " ("; - for (unsigned i = 0; i < f->get_arity(); ++i) { + out << "(declare-rel "; + display_symbol(out, f->get_name()) << " ("; + for (unsigned i = 0; i < f->get_arity(); ++i) { ast_smt2_pp(out, f->get_domain(i), env); if (i + 1 < f->get_arity()) { out << " "; @@ -1269,12 +1239,12 @@ namespace datalog { void context::declare_vars(expr_ref_vector& rules, mk_fresh_name& fresh_names, std::ostream& out) { // // replace bound variables in rules by 'var declarations' - // First remove quantifers, then replace bound variables + // First remove quantifers, then replace bound variables // by fresh constants. - // + // smt2_pp_environment_dbg env(m); var_subst vsubst(m, false); - + expr_ref_vector fresh_vars(m), subst(m); expr_ref res(m); obj_map var_idxs; @@ -1287,7 +1257,7 @@ namespace datalog { quantifier* q = to_quantifier(r); if (!q->is_forall()) { continue; - } + } if (has_quantifiers(q->get_expr())) { continue; } @@ -1317,7 +1287,7 @@ namespace datalog { fresh_vars.push_back(m.mk_const(name, s)); out << "(declare-var " << name << " "; ast_smt2_pp(out, s, env); - out << ")\n"; + out << ")\n"; } subst.push_back(fresh_vars[vars[max_var]].get()); } @@ -1329,4 +1299,3 @@ namespace datalog { }; - diff --git a/src/muz/base/dl_context.h b/src/muz/base/dl_context.h index 129277514..6e6bc80dc 100644 --- a/src/muz/base/dl_context.h +++ b/src/muz/base/dl_context.h @@ -44,7 +44,7 @@ Revision History: #include "muz/base/bind_variables.h" #include "muz/base/rule_properties.h" -struct fixedpoint_params; +struct fp_params; namespace datalog { @@ -61,7 +61,7 @@ namespace datalog { class relation_manager; typedef sort * relation_sort; - typedef uint64 table_element; + typedef uint64_t table_element; typedef svector table_fact; typedef app * relation_element; @@ -98,7 +98,7 @@ namespace datalog { relation_fact(ast_manager & m) : app_ref_vector(m) {} relation_fact(ast_manager & m, unsigned sz) : app_ref_vector(m) { resize(sz); } relation_fact(context & ctx); - + iterator begin() const { return c_ptr(); } iterator end() const { return c_ptr()+size(); } @@ -110,7 +110,7 @@ namespace datalog { class rel_context_base : public engine_base { public: rel_context_base(ast_manager& m, char const* name): engine_base(m, name) {} - virtual ~rel_context_base() {} + ~rel_context_base() override {} virtual relation_manager & get_rmanager() = 0; virtual const relation_manager & get_rmanager() const = 0; virtual relation_base & get_relation(func_decl * pred) = 0; @@ -126,7 +126,7 @@ namespace datalog { virtual bool has_facts(func_decl * pred) const = 0; virtual void store_relation(func_decl * pred, relation_base * rel) = 0; virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) = 0; - virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names) = 0; virtual bool output_profile() const = 0; virtual void collect_non_empty_predicates(func_decl_set& preds) = 0; @@ -146,9 +146,9 @@ namespace datalog { context const& ctx; public: contains_pred(context& ctx): ctx(ctx) {} - virtual ~contains_pred() {} - - virtual bool operator()(expr* e) { + ~contains_pred() override {} + + bool operator()(expr* e) override { return ctx.is_predicate(e); } }; @@ -170,7 +170,7 @@ namespace datalog { register_engine_base& m_register_engine; smt_params & m_fparams; params_ref m_params_ref; - fixedpoint_params* m_params; + fp_params* m_params; bool m_generate_proof_trace; // cached configuration parameter bool m_unbound_compressor; // cached configuration parameter symbol m_default_relation; // cached configuration parameter @@ -227,7 +227,7 @@ namespace datalog { void push(); void pop(); - + bool saturation_was_run() const { return m_saturation_was_run; } void notify_saturation_was_run() { m_saturation_was_run = true; } @@ -236,7 +236,7 @@ namespace datalog { ast_manager & get_manager() const { return m; } rule_manager & get_rule_manager() { return m_rule_manager; } smt_params & get_fparams() const { return m_fparams; } - fixedpoint_params const& get_params() const { return *m_params; } + fp_params const& get_params() const { return *m_params; } DL_ENGINE get_engine() { configure_engine(); return m_engine_type; } register_engine_base& get_register_engine() { return m_register_engine; } th_rewriter& get_rewriter() { return m_rewriter; } @@ -251,7 +251,7 @@ namespace datalog { symbol default_table() const; symbol default_relation() const; void set_default_relation(symbol const& s); - symbol default_table_checker() const; + symbol default_table_checker() const; symbol check_relation() const; bool default_table_checked() const; bool dbg_fpr_nonempty_relation_signature() const; @@ -275,7 +275,7 @@ namespace datalog { bool compress_unbound() const; bool quantify_arrays() const; bool instantiate_quantifiers() const; - bool xform_bit_blast() const; + bool xform_bit_blast() const; bool xform_slice() const; bool xform_coi() const; bool array_blast() const; @@ -291,9 +291,9 @@ namespace datalog { void register_variable(func_decl* var); /* - Replace constants that have been registered as + Replace constants that have been registered as variables by de-Bruijn indices and corresponding - universal (if is_forall is true) or existential + universal (if is_forall is true) or existential quantifier. */ expr_ref bind_vars(expr* fml, bool is_forall); @@ -303,7 +303,7 @@ namespace datalog { /** Register datalog relation. - If named is true, we associate the predicate with its name, so that it can be + If named is true, we associate the predicate with its name, so that it can be retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced e.g. by rule transformations do not need to be named. */ @@ -326,22 +326,22 @@ namespace datalog { /** \brief If a predicate name has a \c func_decl object assigned, return pointer to it; otherwise return 0. - + Not all \c func_decl object used as relation identifiers need to be assigned to their names. Generally, the names coming from the parses are registered here. */ func_decl * try_get_predicate_decl(symbol const& pred_name) const { - func_decl * res = 0; + func_decl * res = nullptr; m_preds_by_name.find(pred_name, res); return res; - } + } /** \brief Create a fresh head predicate declaration. */ - func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, - unsigned arity, sort * const * domain, func_decl* orig_pred=0); + func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix, + unsigned arity, sort * const * domain, func_decl* orig_pred=nullptr); /** @@ -351,27 +351,27 @@ namespace datalog { /** \brief Return number of a symbol in a DK_UINT64 kind sort (\see register_sort() ) */ - finite_element get_constant_number(relation_sort srt, uint64 el); + finite_element get_constant_number(relation_sort srt, uint64_t el); /** \brief Output name of constant with number \c num in sort \c sort. */ - void print_constant_name(relation_sort sort, uint64 num, std::ostream & out); + void print_constant_name(relation_sort sort, uint64_t num, std::ostream & out); - bool try_get_sort_constant_count(relation_sort srt, uint64 & constant_count); + bool try_get_sort_constant_count(relation_sort srt, uint64_t & constant_count); - uint64 get_sort_size_estimate(relation_sort srt); + uint64_t get_sort_size_estimate(relation_sort srt); /** \brief Assign names of variables used in the declaration of a predicate. - These names are used when printing out the relations to make the output conform + These names are used when printing out the relations to make the output conform to the one of bddbddb. */ - void set_argument_names(const func_decl * pred, svector var_names); + void set_argument_names(const func_decl * pred, const svector & var_names); symbol get_argument_name(const func_decl * pred, unsigned arg_index); - void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, symbol const * relation_names); void set_output_predicate(func_decl * pred) { m_rule_set.set_output_predicate(pred); } @@ -385,9 +385,9 @@ namespace datalog { void add_fact(func_decl * pred, const relation_fact & fact); bool has_facts(func_decl * pred) const; - + void add_rule(rule_ref& r); - + void assert_expr(expr* e); expr_ref get_background_assertion(); unsigned get_num_assertions() { return m_background.size(); } @@ -397,7 +397,7 @@ namespace datalog { Method exposed from API for adding rules. */ void add_rule(expr* rl, symbol const& name, unsigned bound = UINT_MAX); - + /** Update a named rule. @@ -421,9 +421,9 @@ namespace datalog { at 'level+1', 'level+2' etc, and include level=-1. */ expr_ref get_cover_delta(int level, func_decl* pred); - + /** - Add a property of predicate 'pred' at 'level'. + Add a property of predicate 'pred' at 'level'. It gets pushed forward when possible. */ void add_cover(int level, func_decl* pred, expr* property); @@ -432,7 +432,7 @@ namespace datalog { Add an invariant of predicate 'pred'. */ void add_invariant (func_decl *pred, expr *property); - + /** \brief Check rule subsumption. */ @@ -471,15 +471,15 @@ namespace datalog { proof_converter_ref& get_proof_converter() { return m_pc; } void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); } - void transform_rules(rule_transformer& transf); + void transform_rules(rule_transformer& transf); void transform_rules(rule_transformer::plugin* plugin); void replace_rules(rule_set const& rs); void record_transformed_rules(); - void apply_default_transformation(); + void apply_default_transformation(); void collect_params(param_descrs& r); - + void updt_params(params_ref const& p); void display_rules(std::ostream & out) const { @@ -507,7 +507,7 @@ namespace datalog { /** \brief check if query 'q' is satisfied under asserted rules and background. - If successful, return OK and into \c result assign a relation with all + If successful, return OK and into \c result assign a relation with all tuples matching the query. Otherwise return reason for failure and do not modify \c result. @@ -515,7 +515,7 @@ namespace datalog { starting from zero. The caller becomes an owner of the relation object returned in \c result. The - relation object, however, should not outlive the datalog context since it is + relation object, however, should not outlive the datalog context since it is linked to a relation plugin in the context. */ @@ -524,7 +524,7 @@ namespace datalog { lbool query_from_lvl (expr* q, unsigned lvl); /** \brief retrieve model from inductive invariant that shows query is unsat. - + \pre engine == 'pdr' || engine == 'duality' - this option is only supported for PDR mode and Duality mode. */ @@ -532,7 +532,7 @@ namespace datalog { /** \brief retrieve proof from derivation of the query. - + \pre engine == 'pdr' || engine == 'duality'- this option is only supported for PDR mode and Duality mode. */ @@ -588,12 +588,25 @@ namespace datalog { rel_context_base* get_rel_context() { ensure_engine(); return m_rel; } + void add_callback(void *state, + const datalog::t_new_lemma_eh new_lemma_eh, + const datalog::t_predecessor_eh predecessor_eh, + const datalog::t_unfold_eh unfold_eh) { + ensure_engine(); + m_engine->add_callback(state, new_lemma_eh, predecessor_eh, unfold_eh); + } + + void add_constraint (expr *c, unsigned lvl){ + ensure_engine(); + m_engine->add_constraint(c, lvl); + } + private: /** Just reset all tables. */ - void reset_tables(); + void reset_tables(); void flush_add_rules(); @@ -614,4 +627,3 @@ namespace datalog { }; #endif /* DL_CONTEXT_H_ */ - diff --git a/src/muz/base/dl_costs.cpp b/src/muz/base/dl_costs.cpp index f97a4d451..70688cd59 100644 --- a/src/muz/base/dl_costs.cpp +++ b/src/muz/base/dl_costs.cpp @@ -128,7 +128,7 @@ namespace datalog { // // ----------------------------------- - cost_recorder::cost_recorder() : m_obj(0) { + cost_recorder::cost_recorder() : m_obj(nullptr) { m_stopwatch = alloc(stopwatch); m_stopwatch->start(); } @@ -141,7 +141,7 @@ namespace datalog { } void cost_recorder::start(accounted_object * obj) { - uint64 curr_time = static_cast(m_stopwatch->get_current_seconds()*1000); + uint64_t curr_time = static_cast(m_stopwatch->get_current_seconds()*1000); if(m_obj) { costs::time_type time_delta = static_cast(curr_time-m_last_time); costs & c = m_obj->get_current_costs(); @@ -149,7 +149,7 @@ namespace datalog { c.milliseconds+=time_delta; m_obj->m_being_recorded = false; } - m_running = obj!=0; + m_running = obj!=nullptr; m_obj = obj; m_last_time = curr_time; if(obj) { diff --git a/src/muz/base/dl_costs.h b/src/muz/base/dl_costs.h index fd5a99f15..6a5b5e5d6 100644 --- a/src/muz/base/dl_costs.h +++ b/src/muz/base/dl_costs.h @@ -63,7 +63,7 @@ namespace datalog { costs m_processed_cost; bool m_being_recorded; protected: - accounted_object() : m_context(0), m_parent_object(0), m_being_recorded(false) {} + accounted_object() : m_context(nullptr), m_parent_object(nullptr), m_being_recorded(false) {} ~accounted_object(); public: @@ -95,7 +95,7 @@ namespace datalog { stopwatch * m_stopwatch; bool m_running; - uint64 m_last_time; + uint64_t m_last_time; public: cost_recorder(); ~cost_recorder(); @@ -107,7 +107,7 @@ namespace datalog { before separately. */ void start(accounted_object *); - void finish() { start(0); } + void finish() { start(nullptr); } }; }; diff --git a/src/muz/base/dl_engine_base.h b/src/muz/base/dl_engine_base.h index 576ed7f6b..d6099e04c 100644 --- a/src/muz/base/dl_engine_base.h +++ b/src/muz/base/dl_engine_base.h @@ -25,18 +25,19 @@ Revision History: namespace datalog { enum DL_ENGINE { DATALOG_ENGINE, - PDR_ENGINE, SPACER_ENGINE, - QPDR_ENGINE, BMC_ENGINE, QBMC_ENGINE, TAB_ENGINE, CLP_ENGINE, - DUALITY_ENGINE, DDNF_ENGINE, LAST_ENGINE }; + typedef void (*t_new_lemma_eh)(void *state, expr *lemma, unsigned level); + typedef void (*t_predecessor_eh)(void *state); + typedef void (*t_unfold_eh)(void *state); + class engine_base { ast_manager& m; std::string m_name; @@ -103,6 +104,15 @@ namespace datalog { virtual proof_ref get_proof() { return proof_ref(m.mk_asserted(m.mk_true()), m); } + virtual void add_callback(void *state, + const t_new_lemma_eh new_lemma_eh, + const t_predecessor_eh predecessor_eh, + const t_unfold_eh unfold_eh) { + throw default_exception(std::string("add_lemma_exchange_callbacks is not supported for ") + m_name); + } + virtual void add_constraint (expr *c, unsigned lvl){ + throw default_exception(std::string("add_constraint is not supported for ") + m_name); + } virtual void updt_params() {} virtual void cancel() {} virtual void cleanup() {} diff --git a/src/muz/base/dl_rule.cpp b/src/muz/base/dl_rule.cpp index 4f832c4c9..dd6728486 100644 --- a/src/muz/base/dl_rule.cpp +++ b/src/muz/base/dl_rule.cpp @@ -41,7 +41,7 @@ Revision History: #include "ast/rewriter/expr_replacer.h" #include "ast/rewriter/bool_rewriter.h" #include "ast/rewriter/expr_safe_replace.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/scoped_proof.h" #include "ast/datatype_decl_plugin.h" #include "ast/ast_util.h" @@ -81,7 +81,7 @@ namespace datalog { } var_idx_set& rule_manager::collect_vars(expr* e) { - return collect_vars(e, 0); + return collect_vars(e, nullptr); } var_idx_set& rule_manager::collect_vars(expr* e1, expr* e2) { @@ -274,11 +274,11 @@ namespace datalog { bind_variables(query, false, q); quantifier_hoister qh(m); - qh.pull_quantifier(false, q, 0, &names); + qh.pull_quantifier(false, q, nullptr, &names); // retrieve free variables. m_free_vars(q); vars.append(m_free_vars.size(), m_free_vars.c_ptr()); - if (vars.contains(static_cast(0))) { + if (vars.contains(static_cast(nullptr))) { var_subst sub(m, false); expr_ref_vector args(m); // [s0, 0, s2, ..] @@ -306,7 +306,7 @@ namespace datalog { } body.push_back(to_app(q)); flatten_body(body); - func_decl* body_pred = 0; + func_decl* body_pred = nullptr; for (unsigned i = 0; i < body.size(); i++) { if (is_uninterp(body[i].get())) { body_pred = body[i]->get_decl(); @@ -326,8 +326,8 @@ namespace datalog { rules.set_output_predicate(qpred); if (m_ctx.get_model_converter()) { - filter_model_converter* mc = alloc(filter_model_converter, m); - mc->insert(qpred); + generic_model_converter* mc = alloc(generic_model_converter, m, "dl_rule"); + mc->hide(qpred); m_ctx.add_model_converter(mc); } @@ -472,7 +472,7 @@ namespace datalog { r->m_head = head; r->m_name = name; r->m_tail_size = n; - r->m_proof = 0; + r->m_proof = nullptr; m.inc_ref(r->m_head); app * * uninterp_tail = r->m_tail; //grows upwards @@ -482,7 +482,7 @@ namespace datalog { bool has_neg = false; for (unsigned i = 0; i < n; i++) { - bool is_neg = (is_negated != 0 && is_negated[i]); + bool is_neg = (is_negated != nullptr && is_negated[i]); app * curr = tail[i]; if (is_neg && !m_ctx.is_predicate(curr)) { @@ -548,7 +548,7 @@ namespace datalog { r->m_tail_size = n; r->m_positive_cnt = source->m_positive_cnt; r->m_uninterp_cnt = source->m_uninterp_cnt; - r->m_proof = 0; + r->m_proof = nullptr; m.inc_ref(r->m_head); for (unsigned i = 0; i < n; i++) { r->m_tail[i] = source->m_tail[i]; @@ -978,7 +978,7 @@ namespace datalog { subst_vals.push_back(m.mk_var(next_fresh_var++, var_srt)); } else { - subst_vals.push_back(0); + subst_vals.push_back(nullptr); } } diff --git a/src/muz/base/dl_rule.h b/src/muz/base/dl_rule.h index ea0e64e4f..2537adbb0 100644 --- a/src/muz/base/dl_rule.h +++ b/src/muz/base/dl_rule.h @@ -54,7 +54,7 @@ namespace datalog { bool m_found; func_decl* m_func; uninterpreted_function_finder_proc(ast_manager& m): - m(m), m_dt(m), m_dl(m), m_found(false), m_func(0) {} + m(m), m_dt(m), m_dl(m), m_found(false), m_func(nullptr) {} void operator()(var * n) { } void operator()(quantifier * n) { } void operator()(app * n) { @@ -71,7 +71,7 @@ namespace datalog { } } } - void reset() { m_found = false; m_func = 0; } + void reset() { m_found = false; m_func = nullptr; } bool found(func_decl*& f) const { f = m_func; return m_found; } }; @@ -209,7 +209,7 @@ namespace datalog { \remark A tail may contain negation. tail[i] is assumed to be negated if is_neg != 0 && is_neg[i] == true */ - rule * mk(app * head, unsigned n, app * const * tail, bool const * is_neg = 0, + rule * mk(app * head, unsigned n, app * const * tail, bool const * is_neg = nullptr, symbol const& name = symbol::null, bool normalize = true); /** diff --git a/src/muz/base/dl_rule_set.cpp b/src/muz/base/dl_rule_set.cpp index 65c940091..acd200d44 100644 --- a/src/muz/base/dl_rule_set.cpp +++ b/src/muz/base/dl_rule_set.cpp @@ -282,7 +282,7 @@ namespace datalog { m_rule_manager(ctx.get_rule_manager()), m_rules(m_rule_manager), m_deps(ctx), - m_stratifier(0), + m_stratifier(nullptr), m_refs(ctx.get_manager()) { } @@ -291,7 +291,7 @@ namespace datalog { m_rule_manager(other.m_rule_manager), m_rules(m_rule_manager), m_deps(other.m_context), - m_stratifier(0), + m_stratifier(nullptr), m_refs(m_context.get_manager()) { add_rules(other); if (other.m_stratifier) { @@ -307,7 +307,7 @@ namespace datalog { m_rules.reset(); reset_dealloc_values(m_head2rules); m_deps.reset(); - m_stratifier = 0; + m_stratifier = nullptr; m_output_preds.reset(); m_orig2pred.reset(); m_pred2orig.reset(); @@ -401,7 +401,7 @@ namespace datalog { m_deps.populate(*this); m_stratifier = alloc(rule_stratifier, m_deps); if (!stratified_negation()) { - m_stratifier = 0; + m_stratifier = nullptr; m_deps.reset(); return false; } @@ -410,7 +410,7 @@ namespace datalog { void rule_set::reopen() { if (is_closed()) { - m_stratifier = 0; + m_stratifier = nullptr; m_deps.reset(); } } diff --git a/src/muz/base/dl_rule_transformer.h b/src/muz/base/dl_rule_transformer.h index dc930ae2b..2bcc1da5c 100644 --- a/src/muz/base/dl_rule_transformer.h +++ b/src/muz/base/dl_rule_transformer.h @@ -87,7 +87,7 @@ namespace datalog { (higher priority plugins will be applied first). */ plugin(unsigned priority, bool can_destratify_negation = false) : m_priority(priority), - m_can_destratify_negation(can_destratify_negation), m_transformer(0) {} + m_can_destratify_negation(can_destratify_negation), m_transformer(nullptr) {} public: virtual ~plugin() {} diff --git a/src/muz/base/dl_util.cpp b/src/muz/base/dl_util.cpp index fad424b90..6c52d0537 100644 --- a/src/muz/base/dl_util.cpp +++ b/src/muz/base/dl_util.cpp @@ -31,10 +31,14 @@ Revision History: #include "muz/base/dl_rule.h" #include "muz/base/dl_util.h" #include "util/stopwatch.h" +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include namespace datalog { - verbose_action::verbose_action(char const* msg, unsigned lvl): m_lvl(lvl), m_sw(0) { + verbose_action::verbose_action(char const* msg, unsigned lvl): m_lvl(lvl), m_sw(nullptr) { IF_VERBOSE(m_lvl, (verbose_stream() << msg << "...").flush(); m_sw = alloc(stopwatch); @@ -87,7 +91,7 @@ namespace datalog { else { SASSERT(is_var(arg)); int vidx = to_var(arg)->get_idx(); - var * new_var = 0; + var * new_var = nullptr; if (!varidx2var.find(vidx, new_var)) { new_var = m.mk_var(next_idx, to_var(arg)->get_sort()); next_idx++; @@ -165,7 +169,7 @@ namespace datalog { } expr * arg = f->get_arg(i); - uint64 sym_num; + uint64_t sym_num; SASSERT(is_app(arg)); VERIFY( ctx.get_decl_util().is_numeral_ext(to_app(arg), sym_num) ); relation_sort sort = pred_decl->get_domain(i); @@ -385,24 +389,32 @@ namespace datalog { public: skip_model_converter() {} - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { return alloc(skip_model_converter); } + void operator()(model_ref&) override {} + + void display(std::ostream & out) override { } + + void get_units(obj_map& units) override {} + }; model_converter* mk_skip_model_converter() { return alloc(skip_model_converter); } class skip_proof_converter : public proof_converter { - virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { + + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { SASSERT(num_source == 1); - result = source[0]; + return proof_ref(source[0], m); } - virtual proof_converter * translate(ast_translation & translator) { + proof_converter * translate(ast_translation & translator) override { return alloc(skip_proof_converter); } + void display(std::ostream & out) override { out << "(skip-proof-converter)\n"; } }; proof_converter* mk_skip_proof_converter() { return alloc(skip_proof_converter); } @@ -428,7 +440,7 @@ namespace datalog { unsigned tgt_sz = max_var_idx+1; unsigned tgt_ofs = tgt_sz-1; - tgt.resize(tgt_sz, 0); + tgt.resize(tgt_sz, nullptr); for(unsigned i=0; i=0; i--) { out << (len-1-i) <<"->"; - if(cont.get(i)==0) { + if(cont.get(i)==nullptr) { out << "{none}"; } else { @@ -513,10 +525,9 @@ namespace datalog { } void collect_and_transform(const unsigned_vector & src, const unsigned_vector & translation, - unsigned_vector & res) { - unsigned n = src.size(); - for(unsigned i=0; iofs) ? (dot_index-ofs) : std::string::npos; return name.substr(ofs, count); } - bool string_to_uint64(const char * s, uint64 & res) { + bool string_to_uint64(const char * s, uint64_t & res) { #if _WINDOWS - int converted = sscanf_s(s, "%I64u", &res); + int converted = sscanf_s(s, "%" SCNu64, &res); #else - int converted = sscanf(s, "%llu", &res); + int converted = sscanf(s, "%" SCNu64, &res); #endif if(converted==0) { return false; @@ -634,9 +643,9 @@ namespace datalog { return true; } - bool read_uint64(const char * & s, uint64 & res) { - static const uint64 max_but_one_digit = ULLONG_MAX/10; - static const uint64 max_but_one_digit_safe = (ULLONG_MAX-9)/10; + bool read_uint64(const char * & s, uint64_t & res) { + static const uint64_t max_but_one_digit = ULLONG_MAX/10; + static const uint64_t max_but_one_digit_safe = (ULLONG_MAX-9)/10; if(*s<'0' || *s>'9') { return false; @@ -664,7 +673,7 @@ namespace datalog { return true; } - std::string to_string(uint64 num) { + std::string to_string(uint64_t num) { std::stringstream stm; stm< - void project_out_vector_columns(T & container, const unsigned_vector removed_cols) { + void project_out_vector_columns(T & container, const unsigned_vector & removed_cols) { project_out_vector_columns(container, removed_cols.size(), removed_cols.c_ptr()); } @@ -342,7 +342,7 @@ namespace datalog { } template - void permutate_by_cycle(T & container, const unsigned_vector permutation_cycle) { + void permutate_by_cycle(T & container, const unsigned_vector & permutation_cycle) { permutate_by_cycle(container, permutation_cycle.size(), permutation_cycle.c_ptr()); } @@ -578,13 +578,13 @@ namespace datalog { // // ----------------------------------- - void get_file_names(std::string directory, std::string extension, bool traverse_subdirs, + void get_file_names(std::string directory, const std::string & extension, bool traverse_subdirs, string_vector & res); - bool file_exists(std::string name); - bool is_directory(std::string name); + bool file_exists(const std::string & name); + bool is_directory(const std::string & name); - std::string get_file_name_without_extension(std::string name); + std::string get_file_name_without_extension(const std::string & name); // ----------------------------------- // @@ -602,8 +602,8 @@ namespace datalog { \brief If it is possible to convert the beginning of \c s to uint64, store the result of conversion and return true; otherwise return false. */ - bool string_to_uint64(const char * s, uint64 & res); - std::string to_string(uint64 num); + bool string_to_uint64(const char * s, uint64_t & res); + std::string to_string(uint64_t num); /** \brief Read the sequence of decimal digits starting at \c s and interpret it as uint64. If successful, \c res will contain the read number and \c s will point @@ -612,7 +612,7 @@ namespace datalog { overflows, \c points to the character which caused the overflow and false is returned. */ - bool read_uint64(const char * & s, uint64 & res); + bool read_uint64(const char * & s, uint64_t & res); }; #endif /* DL_UTIL_H_ */ diff --git a/src/muz/base/fixedpoint_params.pyg b/src/muz/base/fp_params.pyg similarity index 53% rename from src/muz/base/fixedpoint_params.pyg rename to src/muz/base/fp_params.pyg index 0c2f03460..c96a12ca2 100644 --- a/src/muz/base/fixedpoint_params.pyg +++ b/src/muz/base/fp_params.pyg @@ -1,53 +1,53 @@ -def_module_params('fixedpoint', +def_module_params('fp', description='fixedpoint parameters', export=True, params=(('timeout', UINT, UINT_MAX, 'set timeout'), - ('engine', SYMBOL, 'auto-config', - 'Select: auto-config, datalog, duality, pdr, bmc, spacer'), - ('datalog.default_table', SYMBOL, 'sparse', + ('engine', SYMBOL, 'auto-config', + 'Select: auto-config, datalog, bmc, spacer'), + ('datalog.default_table', SYMBOL, 'sparse', 'default table implementation: sparse, hashtable, bitvector, interval'), - ('datalog.default_relation', SYMBOL, 'pentagon', + ('datalog.default_relation', SYMBOL, 'pentagon', 'default relation implementation: external_relation, pentagon'), - ('datalog.generate_explanations', BOOL, False, + ('datalog.generate_explanations', BOOL, False, 'produce explanations for produced facts when using the datalog engine'), - ('datalog.use_map_names', BOOL, True, + ('datalog.use_map_names', BOOL, True, "use names from map files when displaying tuples"), - ('datalog.magic_sets_for_queries', BOOL, False, + ('datalog.magic_sets_for_queries', BOOL, False, "magic set transformation will be used for queries"), - ('datalog.explanations_on_relation_level', BOOL, False, - 'if true, explanations are generated as history of each relation, ' + - 'rather than per fact (generate_explanations must be set to true for ' + + ('datalog.explanations_on_relation_level', BOOL, False, + 'if true, explanations are generated as history of each relation, ' + + 'rather than per fact (generate_explanations must be set to true for ' + 'this option to have any effect)'), - ('datalog.unbound_compressor', BOOL, True, - "auxiliary relations will be introduced to avoid unbound variables " + + ('datalog.unbound_compressor', BOOL, True, + "auxiliary relations will be introduced to avoid unbound variables " + "in rule heads"), - ('datalog.similarity_compressor', BOOL, True, - "rules that differ only in values of constants will be merged into " + + ('datalog.similarity_compressor', BOOL, True, + "rules that differ only in values of constants will be merged into " + "a single rule"), - ('datalog.similarity_compressor_threshold', UINT, 11, - "if similarity_compressor is on, this value determines how many " + + ('datalog.similarity_compressor_threshold', UINT, 11, + "if similarity_compressor is on, this value determines how many " + "similar rules there must be in order for them to be merged"), - ('datalog.all_or_nothing_deltas', BOOL, False, + ('datalog.all_or_nothing_deltas', BOOL, False, "compile rules so that it is enough for the delta relation in " + - "union and widening operations to determine only whether the " + + "union and widening operations to determine only whether the " + "updated relation was modified or not"), - ('datalog.compile_with_widening', BOOL, False, + ('datalog.compile_with_widening', BOOL, False, "widening will be used to compile recursive rules"), - ('datalog.default_table_checked', BOOL, False, "if true, the detault " + + ('datalog.default_table_checked', BOOL, False, "if true, the default " + 'table will be default_table inside a wrapper that checks that its results ' + 'are the same as of default_table_checker table'), ('datalog.default_table_checker', SYMBOL, 'null', "see default_table_checked"), ('datalog.check_relation',SYMBOL,'null', "name of default relation to check. " + "operations on the default relation will be verified using SMT solving"), - ('datalog.initial_restart_timeout', UINT, 0, - "length of saturation run before the first restart (in ms), " + + ('datalog.initial_restart_timeout', UINT, 0, + "length of saturation run before the first restart (in ms), " + "zero means no restarts"), - ('datalog.output_profile', BOOL, False, - "determines whether profile information should be " + + ('datalog.output_profile', BOOL, False, + "determines whether profile information should be " + "output when outputting Datalog rules or instructions"), - ('datalog.print.tuples', BOOL, True, + ('datalog.print.tuples', BOOL, True, "determines whether tuples for output predicates should be output"), - ('datalog.profile_timeout_milliseconds', UINT, 0, + ('datalog.profile_timeout_milliseconds', UINT, 0, "instructions and rules that took less than the threshold " + "will not be printed when printed the instruction/rule list"), ('datalog.dbg_fpr_nonempty_relation_signature', BOOL, False, @@ -56,152 +56,126 @@ def_module_params('fixedpoint', "table columns, if it would have been empty otherwise"), ('datalog.subsumption', BOOL, True, "if true, removes/filters predicates with total transitions"), - ('duality.full_expand', BOOL, False, 'Fully expand derivation trees'), - ('duality.no_conj', BOOL, False, 'No forced covering (conjectures)'), - ('duality.feasible_edges', BOOL, True, - 'Don\'t expand definitley infeasible edges'), - ('duality.use_underapprox', BOOL, False, 'Use underapproximations'), - ('duality.stratified_inlining', BOOL, False, 'Use stratified inlining'), - ('duality.recursion_bound', UINT, UINT_MAX, - 'Recursion bound for stratified inlining'), - ('duality.profile', BOOL, False, 'profile run time'), - ('duality.mbqi', BOOL, True, 'use model-based quantifier instantiation'), - ('duality.batch_expand', BOOL, False, 'use batch expansion'), - ('duality.conjecture_file', STRING, '', 'save conjectures to file'), - ('pdr.bfs_model_search', BOOL, True, - "use BFS strategy for expanding model search"), - ('pdr.farkas', BOOL, True, - "use lemma generator based on Farkas (for linear real arithmetic)"), ('generate_proof_trace', BOOL, False, "trace for 'sat' answer as proof object"), - ('pdr.flexible_trace', BOOL, False, - "allow PDR generate long counter-examples " + - "by extending candidate trace within search area"), - ('pdr.flexible_trace_depth', UINT, UINT_MAX, - 'Controls the depth (below the current level) at which flexible trace can be applied'), - ('pdr.use_model_generalizer', BOOL, False, - "use model for backwards propagation (instead of symbolic simulation)"), - ('pdr.validate_result', BOOL, False, + ('spacer.push_pob', BOOL, False, "push blocked pobs to higher level"), + ('spacer.push_pob_max_depth', UINT, UINT_MAX, + 'Maximum depth at which push_pob is enabled'), + ('validate', BOOL, False, "validate result (by proof checking or model checking)"), - ('pdr.simplify_formulas_pre', BOOL, False, - "simplify derived formulas before inductive propagation"), - ('pdr.simplify_formulas_post', BOOL, False, - "simplify derived formulas after inductive propagation"), - ('pdr.use_multicore_generalizer', BOOL, False, - "extract multiple cores for blocking states"), - ('pdr.use_inductive_generalizer', BOOL, True, + ('spacer.simplify_lemmas_pre', BOOL, False, + "simplify derived lemmas before inductive propagation"), + ('spacer.simplify_lemmas_post', BOOL, False, + "simplify derived lemmas after inductive propagation"), + ('spacer.use_inductive_generalizer', BOOL, True, "generalize lemmas using induction strengthening"), - ('pdr.use_arith_inductive_generalizer', BOOL, False, - "generalize lemmas using arithmetic heuristics for induction strengthening"), - ('pdr.use_convex_closure_generalizer', BOOL, False, - "generalize using convex closures of lemmas"), - ('pdr.use_convex_interior_generalizer', BOOL, False, - "generalize using convex interiors of lemmas"), - ('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " + - "cache (2) for model search"), - ('pdr.inductive_reachability_check', BOOL, False, - "assume negation of the cube on the previous level when " + - "checking for reachability (not only during cube weakening)"), - ('pdr.max_num_contexts', UINT, 500, "maximal number of contexts to create"), - ('pdr.try_minimize_core', BOOL, False, - "try to reduce core size (before inductive minimization)"), - ('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'), - ('print_fixedpoint_extensions', BOOL, True, - "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " + + ('spacer.max_num_contexts', UINT, 500, "maximal number of contexts to create"), + ('print_fixedpoint_extensions', BOOL, True, + "use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " + "when printing rules"), - ('print_low_level_smt2', BOOL, False, - "use (faster) low-level SMT2 printer (the printer is scalable " + + ('print_low_level_smt2', BOOL, False, + "use (faster) low-level SMT2 printer (the printer is scalable " + "but the result may not be as readable)"), - ('print_with_variable_declarations', BOOL, True, + ('print_with_variable_declarations', BOOL, True, "use variable declarations when displaying rules " + "(instead of attempting to use original names)"), ('print_answer', BOOL, False, 'print answer instance(s) to query'), - ('print_certificate', BOOL, False, + ('print_certificate', BOOL, False, 'print certificate for reachability or non-reachability'), - ('print_boogie_certificate', BOOL, False, + ('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a ' + 'format understood by Boogie'), ('print_statistics', BOOL, False, 'print statistics'), - ('print_aig', SYMBOL, '', + ('print_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'), - ('tab.selection', SYMBOL, 'weight', + ('tab.selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'), - ('xform.bit_blast', BOOL, False, + ('xform.bit_blast', BOOL, False, 'bit-blast bit-vectors'), - ('xform.magic', BOOL, False, + ('xform.magic', BOOL, False, "perform symbolic magic set transformation"), - ('xform.scale', BOOL, False, - "add scaling variable to linear real arithemtic clauses"), + ('xform.scale', BOOL, False, + "add scaling variable to linear real arithmetic clauses"), ('xform.inline_linear', BOOL, True, "try linear inlining method"), ('xform.inline_eager', BOOL, True, "try eager inlining of rules"), - ('xform.inline_linear_branch', BOOL, False, + ('xform.inline_linear_branch', BOOL, False, "try linear inlining method with potential expansion"), ('xform.compress_unbound', BOOL, True, "compress tails with unbound variables"), ('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"), - ('xform.unfold_rules', UINT, 0, - "unfold rules statically using iterative squarring"), + ('xform.unfold_rules', UINT, 0, + "unfold rules statically using iterative squaring"), ('xform.slice', BOOL, True, "simplify clause set using slicing"), - ('xform.karr', BOOL, False, + ('xform.karr', BOOL, False, "Add linear invariants to clauses using Karr's method"), - ('spacer.use_eqclass', BOOL, False, "Generalizes equalities to equivalence classes"), - ('xform.transform_arrays', BOOL, False, + ('spacer.use_euf_gen', BOOL, False, 'Generalize lemmas and pobs using implied equalities'), + ('xform.transform_arrays', BOOL, False, "Rewrites arrays equalities and applies select over store"), - ('xform.instantiate_arrays', BOOL, False, + ('xform.instantiate_arrays', BOOL, False, "Transforms P(a) into P(i, a[i] a)"), - ('xform.instantiate_arrays.enforce', BOOL, False, + ('xform.instantiate_arrays.enforce', BOOL, False, "Transforms P(a) into P(i, a[i]), discards a from predicate"), - ('xform.instantiate_arrays.nb_quantifier', UINT, 1, + ('xform.instantiate_arrays.nb_quantifier', UINT, 1, "Gives the number of quantifiers per array"), - ('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing", + ('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing", "=> GetId(i) = i, => GetId(i) = true"), - ('xform.quantify_arrays', BOOL, False, + ('xform.quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"), - ('xform.instantiate_quantifiers', BOOL, False, + ('xform.instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"), ('xform.coalesce_rules', BOOL, False, "coalesce rules"), ('xform.tail_simplifier_pve', BOOL, True, "propagate_variable_equivalences"), ('xform.subsumption_checker', BOOL, True, "Enable subsumption checker (no support for model conversion)"), ('xform.coi', BOOL, True, "use cone of influence simplification"), - ('duality.enable_restarts', BOOL, False, 'DUALITY: enable restarts'), - ('spacer.order_children', UINT, 0, 'SPACER: order of enqueuing children in non-linear rules : 0 (original), 1 (reverse)'), - ('spacer.eager_reach_check', BOOL, True, 'SPACER: eagerly check if a query is reachable using reachability facts of predecessors'), + ('spacer.order_children', UINT, 0, 'SPACER: order of enqueuing children in non-linear rules : 0 (original), 1 (reverse), 2 (random)'), ('spacer.use_lemma_as_cti', BOOL, False, 'SPACER: use a lemma instead of a CTI in flexible_trace'), - ('spacer.reset_obligation_queue', BOOL, True, 'SPACER: reset obligation queue when entering a new level'), - ('spacer.init_reach_facts', BOOL, True, 'SPACER: initialize reachability facts with false'), + ('spacer.reset_pob_queue', BOOL, True, 'SPACER: reset pob obligation queue when entering a new level'), ('spacer.use_array_eq_generalizer', BOOL, True, 'SPACER: attempt to generalize lemmas with array equalities'), - ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), - ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"), - ('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"), - ('spacer.skip_propagate', BOOL, False, "Skip propagate/pushing phase. Turns PDR into a BMC that returns either reachable or unknown"), + ('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'), + ('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"), + ('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"), + ('spacer.propagate', BOOL, True, 'Enable propagate/pushing phase'), ('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"), ('spacer.elim_aux', BOOL, True, "Eliminate auxiliary variables in reachability facts"), - ('spacer.reach_as_init', BOOL, True, "Extend initial rules with computed reachability facts"), ('spacer.blast_term_ite', BOOL, True, "Expand non-Boolean ite-terms"), - ('spacer.nondet_tie_break', BOOL, False, "Break ties in obligation queue non-deterministicly"), ('spacer.reach_dnf', BOOL, True, "Restrict reachability facts to DNF"), ('bmc.linear_unrolling_depth', UINT, UINT_MAX, "Maximal level to explore"), - ('spacer.split_farkas_literals', BOOL, False, "Split Farkas literals"), - ('spacer.native_mbp', BOOL, False, "Use native mbp of Z3"), + ('spacer.iuc.split_farkas_literals', BOOL, False, "Split Farkas literals"), + ('spacer.native_mbp', BOOL, True, "Use native mbp of Z3"), ('spacer.eq_prop', BOOL, True, "Enable equality and bound propagation in arithmetic"), ('spacer.weak_abs', BOOL, True, "Weak abstraction"), ('spacer.restarts', BOOL, False, "Enable reseting obligation queue"), ('spacer.restart_initial_threshold', UINT, 10, "Intial threshold for restarts"), ('spacer.random_seed', UINT, 0, "Random seed to be used by SMT solver"), - ('spacer.ground_cti', BOOL, True, "Require CTI to be ground"), - ('spacer.vs.dump_benchmarks', BOOL, False, 'dump benchmarks in virtual solver'), - ('spacer.vs.dump_min_time', DOUBLE, 5.0, 'min time to dump benchmark'), - ('spacer.vs.recheck', BOOL, False, 're-check locally during benchmark dumping'), - ('spacer.mbqi', BOOL, True, 'use model-based quantifier instantiation'), + + ('spacer.mbqi', BOOL, True, 'Enable mbqi'), ('spacer.keep_proxy', BOOL, True, 'keep proxy variables (internal parameter)'), - ('spacer.instantiate', BOOL, True, 'instantiate quantified lemmas'), - ('spacer.qlemmas', BOOL, True, 'allow quantified lemmas in frames'), - ('spacer.new_unsat_core', BOOL, True, 'use the new implementation of unsat-core-generation'), - ('spacer.minimize_unsat_core', BOOL, False, 'compute unsat-core by min-cut'), - ('spacer.farkas_optimized', BOOL, True, 'use the optimized farkas plugin, which performs gaussian elimination'), - ('spacer.farkas_a_const', BOOL, True, 'if the unoptimized farkas plugin is used, use the constants from A while constructing unsat_cores'), - ('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'), - ('spacer.reuse_pobs', BOOL, True, 'reuse POBs'), - ('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints') + ('spacer.q3', BOOL, True, 'Allow quantified lemmas in frames'), + ('spacer.q3.instantiate', BOOL, True, 'Instantiate quantified lemmas'), + ('spacer.iuc', UINT, 1, + '0 = use old implementation of unsat-core-generation, ' + + '1 = use new implementation of IUC generation, ' + + '2 = use new implementation of IUC + min-cut optimization'), + ('spacer.iuc.arith', UINT, 1, + '0 = use simple Farkas plugin, ' + + '1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation),' + + '2 = use Gaussian elimination optimization (broken), 3 = use additive IUC plugin'), + ('spacer.iuc.old_hyp_reducer', BOOL, False, 'use old hyp reducer instead of new implementation, for debugging only'), + ('spacer.validate_lemmas', BOOL, False, 'Validate each lemma after generalization'), + ('spacer.reuse_pobs', BOOL, True, 'Reuse pobs'), + ('spacer.ground_pobs', BOOL, True, 'Ground pobs by using values from a model'), + ('spacer.iuc.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut (for debugging)'), + ('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes (debugging)'), + ('spacer.simplify_pob', BOOL, False, 'simplify pobs by removing redundant constraints'), + ('spacer.q3.use_qgen', BOOL, False, 'use quantified lemma generalizer'), + ('spacer.q3.qgen.normalize', BOOL, True, 'normalize cube before quantified generalization'), + ('spacer.p3.share_lemmas', BOOL, False, 'Share frame lemmas'), + ('spacer.p3.share_invariants', BOOL, False, "Share invariants lemmas"), + ('spacer.min_level', UINT, 0, 'Minimal level to explore'), + ('spacer.print_json', SYMBOL, '', 'Print pobs tree in JSON format to a given file'), + ('spacer.ctp', BOOL, True, 'Enable counterexample-to-pushing'), + ('spacer.use_inc_clause', BOOL, True, 'Use incremental clause to represent trans'), + ('spacer.dump_benchmarks', BOOL, False, 'Dump SMT queries as benchmarks'), + ('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'), + )) - - - diff --git a/src/muz/base/hnf.cpp b/src/muz/base/hnf.cpp index 7db30a9f0..f0cbc0620 100644 --- a/src/muz/base/hnf.cpp +++ b/src/muz/base/hnf.cpp @@ -153,7 +153,7 @@ public: m_fresh_predicates.reset(); m_todo.push_back(n); m_proofs.push_back(p); - m_produce_proofs = p != 0; + m_produce_proofs = p != nullptr; while (!m_todo.empty() && checkpoint()) { fml = m_todo.back(); pr = m_proofs.back(); @@ -269,7 +269,7 @@ private: expr* const* args = _or->get_args(); for (unsigned i = 0; i < sz; ++i) { m_todo.push_back(bind_variables(m.mk_implies(args[i], head))); - m_proofs.push_back(0); + m_proofs.push_back(nullptr); } if (premise) { @@ -284,7 +284,7 @@ private: m_proofs[m_proofs.size()-sz+i] = m.mk_and_elim(p2, i); } } - fml = 0; + fml = nullptr; return; } @@ -330,7 +330,7 @@ private: bool is_disj = false; expr_ref_vector _body(m); unsigned num_disj = 0; - expr* const* disjs = 0; + expr* const* disjs = nullptr; if (!contains_predicate(b)) { return; } @@ -356,7 +356,7 @@ private: negate_args = false; } if (is_disj) { - app* old_head = 0; + app* old_head = nullptr; if (m_memoize_disj.find(b, old_head)) { body = old_head; } @@ -369,7 +369,7 @@ private: e = m.mk_not(e); } m_todo.push_back(bind_variables(m.mk_implies(e, head))); - m_proofs.push_back(0); + m_proofs.push_back(nullptr); if (produce_proofs()) { defs.push_back(m.mk_def_intro(m_todo.back())); m_proofs[m_proofs.size()-1] = defs.back(); @@ -423,7 +423,7 @@ private: if (!is_predicate(e)) { app_ref head = mk_fresh_head(e); m_todo.push_back(bind_variables(m.mk_implies(e, head))); - m_proofs.push_back(0); + m_proofs.push_back(nullptr); body = m.update_quantifier(q, head); if (produce_proofs()) { proof* def_intro = m.mk_def_intro(m_todo.back()); diff --git a/src/muz/base/rule_properties.cpp b/src/muz/base/rule_properties.cpp index 21317a07c..315765acc 100644 --- a/src/muz/base/rule_properties.cpp +++ b/src/muz/base/rule_properties.cpp @@ -146,12 +146,10 @@ void rule_properties::check_existential_tail() { else if (is_quantifier(e)) { tocheck.push_back(to_quantifier(e)->get_expr()); } - else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && - m.is_true(e1)) { + else if (m.is_eq(e, e1, e2) && m.is_true(e1)) { todo.push_back(e2); } - else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) && - m.is_true(e2)) { + else if (m.is_eq(e, e1, e2) && m.is_true(e2)) { todo.push_back(e1); } else { diff --git a/src/muz/bmc/dl_bmc_engine.cpp b/src/muz/bmc/dl_bmc_engine.cpp index 3368c7640..97432f544 100644 --- a/src/muz/bmc/dl_bmc_engine.cpp +++ b/src/muz/bmc/dl_bmc_engine.cpp @@ -33,7 +33,7 @@ Revision History: #include "muz/transforms/dl_mk_rule_inliner.h" #include "ast/scoped_proof.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { @@ -228,10 +228,9 @@ namespace datalog { expr_ref eval_q(model_ref& model, func_decl* f, unsigned i) { func_decl_ref fn = mk_q_func_decl(f); - expr_ref t(m), result(m); + expr_ref t(m); t = m.mk_app(mk_q_func_decl(f).get(), mk_q_num(i)); - model->eval(t, result); - return result; + return (*model)(t); } expr_ref eval_q(model_ref& model, expr* t, unsigned i) { @@ -240,8 +239,7 @@ namespace datalog { num = mk_q_num(i); expr* nums[1] = { num }; vs(t, 1, nums, tmp); - model->eval(tmp, result); - return result; + return (*model)(tmp); } lbool get_model() { @@ -258,7 +256,7 @@ namespace datalog { func_decl* pred = b.m_query_pred; dl_decl_util util(m); T = m.mk_const(symbol("T"), mk_index_sort()); - md->eval(T, vl); + vl = (*md)(T); VERIFY (m_bv.is_numeral(vl, num, bv_size)); SASSERT(num.is_unsigned()); level = num.get_unsigned(); @@ -270,7 +268,7 @@ namespace datalog { TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); expr_ref_vector sub(m); rule_vector const& rls = b.m_rules.get_predicate_rules(pred); - rule* r = 0; + rule* r = nullptr; unsigned i = 0; for (; i < rls.size(); ++i) { rule_i = m.mk_app(mk_q_rule(pred, i), mk_q_num(level).get()); @@ -489,7 +487,7 @@ namespace datalog { proof_ref get_proof(model_ref& md, func_decl* pred, app* prop, unsigned level) { if (m.canceled()) { - return proof_ref(0, m); + return proof_ref(nullptr, m); } TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); rule_manager& rm = b.m_ctx.get_rule_manager(); @@ -500,13 +498,12 @@ namespace datalog { // find the rule that was triggered by evaluating the arguments to prop. rule_vector const& rls = b.m_rules.get_predicate_rules(pred); - rule* r = 0; + rule* r = nullptr; for (unsigned i = 0; i < rls.size(); ++i) { func_decl_ref rule_i = mk_level_rule(pred, i, level); TRACE("bmc", rls[i]->display(b.m_ctx, tout << "Checking rule " << mk_pp(rule_i, m) << " ");); prop_r = m.mk_app(rule_i, prop->get_num_args(), prop->get_args()); - md->eval(prop_r, prop_v); - if (m.is_true(prop_v)) { + if (md->is_true(prop_r)) { r = rls[i]; break; } @@ -527,8 +524,7 @@ namespace datalog { return pr; } for (unsigned j = 0; j < sub.size(); ++j) { - md->eval(sub[j].get(), tmp); - sub[j] = tmp; + sub[j] = (*md)(sub[j].get()); } svector > positions; @@ -606,7 +602,7 @@ namespace datalog { binding.push_back(m.mk_app(f, args.size(), args.c_ptr())); } else { - binding.push_back(0); + binding.push_back(nullptr); } } return binding; @@ -642,7 +638,7 @@ namespace datalog { names.push_back(symbol(i)); } else { - binding.push_back(0); + binding.push_back(nullptr); } } sorts.reverse(); @@ -686,7 +682,7 @@ namespace datalog { } else { expr * const * no_pats = &new_body; - result = n.m.update_quantifier(old_q, 0, 0, 1, no_pats, new_body); + result = n.m.update_quantifier(old_q, 0, nullptr, 1, no_pats, new_body); } return true; } @@ -986,7 +982,7 @@ namespace datalog { sort_ref_vector new_sorts(m); family_id dfid = m.mk_family_id("datatype"); datatype_decl_plugin* dtp = static_cast(m.get_plugin(dfid)); - VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, 0, new_sorts)); + VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, nullptr, new_sorts)); it = b.m_rules.begin_grouped_rules(); for (unsigned i = 0; it != end; ++it, ++i) { @@ -1012,7 +1008,7 @@ namespace datalog { unsigned sz = r->get_uninterpreted_tail_size(); max_arity = std::max(sz, max_arity); } - cnstrs.push_back(mk_constructor_decl(symbol("Z#"), symbol("Z#?"), 0, 0)); + cnstrs.push_back(mk_constructor_decl(symbol("Z#"), symbol("Z#?"), 0, nullptr)); for (unsigned i = 0; i + 1 < max_arity; ++i) { std::stringstream _name; @@ -1028,7 +1024,7 @@ namespace datalog { cnstrs.push_back(mk_constructor_decl(name, is_name, accs.size(), accs.c_ptr())); } dts.push_back(mk_datatype_decl(dtu, symbol("Path"), 0, nullptr, cnstrs.size(), cnstrs.c_ptr())); - VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, 0, new_sorts)); + VERIFY (dtp->mk_datatypes(dts.size(), dts.c_ptr(), 0, nullptr, new_sorts)); m_path_sort = new_sorts[0].get(); } } @@ -1062,8 +1058,7 @@ namespace datalog { return pr; } for (unsigned j = 0; j < sub.size(); ++j) { - md->eval(sub[j].get(), tmp); - sub[j] = tmp; + sub[j] = (*md)(sub.get(j)); } rule_ref rl(b.m_ctx.get_rule_manager()); rl = rules[i]; @@ -1090,7 +1085,7 @@ namespace datalog { } } UNREACHABLE(); - return proof_ref(0, m); + return proof_ref(nullptr, m); } // instantiation of algebraic data-types takes care of the rest. @@ -1116,7 +1111,7 @@ namespace datalog { bool check_model(model_ref& md, expr* trace) { expr_ref trace_val(m), eq(m); - md->eval(trace, trace_val); + trace_val = (*md)(trace); eq = m.mk_eq(trace, trace_val); b.m_solver.push(); b.m_solver.assert_expr(eq); @@ -1135,8 +1130,8 @@ namespace datalog { void mk_answer(model_ref& md, expr_ref& trace, expr_ref& path) { IF_VERBOSE(2, model_smt2_pp(verbose_stream(), m, *md, 0);); - md->eval(trace, trace); - md->eval(path, path); + trace = (*md)(trace); + path = (*md)(path); IF_VERBOSE(2, verbose_stream() << mk_pp(trace, m) << "\n"; for (unsigned i = 0; i < b.m_solver.size(); ++i) { verbose_stream() << mk_pp(b.m_solver.get_formula(i), m) << "\n"; @@ -1201,7 +1196,7 @@ namespace datalog { TRACE("bmc", tout << "Predicate: " << pred->get_name() << "\n";); expr_ref_vector sub(m); rule_vector const& rls = b.m_rules.get_predicate_rules(pred); - rule* r = 0; + rule* r = nullptr; unsigned i = 0; for (; i < rls.size(); ++i) { expr_ref rule_i = mk_level_rule(pred, i, level); @@ -1446,7 +1441,7 @@ namespace datalog { lbool bmc::query(expr* query) { m_solver.reset(); - m_answer = 0; + m_answer = nullptr; m_ctx.ensure_opened(); m_rules.reset(); datalog::rule_manager& rule_manager = m_ctx.get_rule_manager(); diff --git a/src/muz/bmc/dl_bmc_engine.h b/src/muz/bmc/dl_bmc_engine.h index fd5ce92e6..a5824fa82 100644 --- a/src/muz/bmc/dl_bmc_engine.h +++ b/src/muz/bmc/dl_bmc_engine.h @@ -55,18 +55,18 @@ namespace datalog { public: bmc(context& ctx); - ~bmc(); + ~bmc() override; - lbool query(expr* query); + lbool query(expr* query) override; - void display_certificate(std::ostream& out) const; + void display_certificate(std::ostream& out) const override; - void collect_statistics(statistics& st) const; + void collect_statistics(statistics& st) const override; - void reset_statistics(); - void get_rules_along_trace(datalog::rule_ref_vector& rules); + void reset_statistics() override; + void get_rules_along_trace(datalog::rule_ref_vector& rules) override; - expr_ref get_answer(); + expr_ref get_answer() override; // direct access to (new) non-linear compiler. void compile(rule_set const& rules, expr_ref_vector& fmls, unsigned level); diff --git a/src/muz/clp/clp_context.cpp b/src/muz/clp/clp_context.cpp index d1ce80e12..8ed017214 100644 --- a/src/muz/clp/clp_context.cpp +++ b/src/muz/clp/clp_context.cpp @@ -197,7 +197,7 @@ namespace datalog { proof_ref get_proof() const { - return proof_ref(0, m); + return proof_ref(nullptr, m); } }; diff --git a/src/muz/clp/clp_context.h b/src/muz/clp/clp_context.h index 44f99d564..378fdd473 100644 --- a/src/muz/clp/clp_context.h +++ b/src/muz/clp/clp_context.h @@ -32,12 +32,12 @@ namespace datalog { imp* m_imp; public: clp(context& ctx); - ~clp(); - virtual lbool query(expr* query); - virtual void reset_statistics(); - virtual void collect_statistics(statistics& st) const; - virtual void display_certificate(std::ostream& out) const; - virtual expr_ref get_answer(); + ~clp() override; + lbool query(expr* query) override; + void reset_statistics() override; + void collect_statistics(statistics& st) const override; + void display_certificate(std::ostream& out) const override; + expr_ref get_answer() override; }; }; diff --git a/src/muz/ddnf/ddnf.cpp b/src/muz/ddnf/ddnf.cpp index da1f5392b..a4fe5f0fa 100644 --- a/src/muz/ddnf/ddnf.cpp +++ b/src/muz/ddnf/ddnf.cpp @@ -206,7 +206,7 @@ namespace datalog { return find(t); } - tbv* allocate(uint64 v, unsigned hi, unsigned lo) { + tbv* allocate(uint64_t v, unsigned hi, unsigned lo) { return m_tbv.allocate(v, hi, lo); } @@ -437,7 +437,7 @@ namespace datalog { } } - tbv* allocate(unsigned num_bits, uint64 v, unsigned hi, unsigned lo) { + tbv* allocate(unsigned num_bits, uint64_t v, unsigned hi, unsigned lo) { return get(num_bits).allocate(v, hi, lo); } void insert(unsigned num_bits, tbv const& t) { @@ -462,7 +462,7 @@ namespace datalog { private: ddnf_mgr* insert(unsigned n) { - ddnf_mgr* m = 0; + ddnf_mgr* m = nullptr; if (!m_mgrs.find(n, m)) { m = alloc(ddnf_mgr, n); m_mgrs.insert(n, m); @@ -673,7 +673,7 @@ namespace datalog { void dump_rules(rule_set& rules) { init_ctx(rules); - m_inner_ctx.display_smt2(0, 0, std::cout); + m_inner_ctx.display_smt2(0, nullptr, std::cout); } lbool execute_rules(rule_set& rules) { @@ -715,7 +715,7 @@ namespace datalog { compile_expr(r.get_tail(i), tmp); body.push_back(to_app(tmp)); } - rule* r_new = rm.mk(head, body.size(), body.c_ptr(), 0, r.name(), false); + rule* r_new = rm.mk(head, body.size(), body.c_ptr(), nullptr, r.name(), false); new_rules.add_rule(r_new); IF_VERBOSE(20, r_new->display(m_ctx, verbose_stream());); if (old_rules.is_output_predicate(r.get_decl())) { @@ -775,11 +775,11 @@ namespace datalog { return bv.mk_sort(nb); } UNREACHABLE(); - return 0; + return nullptr; } void compile_expr(expr* e, expr_ref& result) { - expr* r = 0; + expr* r = nullptr; if (m_cache.find(e, r)) { result = r; return; @@ -847,7 +847,7 @@ namespace datalog { } void compile_eq(expr* e, expr_ref& result, var* v, unsigned hi, unsigned lo, expr* c) { - tbv* t = 0; + tbv* t = nullptr; // TBD: hi, lo are ignored. VERIFY(m_expr2tbv.find(e, t)); var_ref w(m); diff --git a/src/muz/ddnf/ddnf.h b/src/muz/ddnf/ddnf.h index 29e9b1555..14c9f3cc7 100644 --- a/src/muz/ddnf/ddnf.h +++ b/src/muz/ddnf/ddnf.h @@ -35,12 +35,12 @@ namespace datalog { imp* m_imp; public: ddnf(context& ctx); - ~ddnf(); - virtual lbool query(expr* query); - virtual void reset_statistics(); - virtual void collect_statistics(statistics& st) const; - virtual void display_certificate(std::ostream& out) const; - virtual expr_ref get_answer(); + ~ddnf() override; + lbool query(expr* query) override; + void reset_statistics() override; + void collect_statistics(statistics& st) const override; + void display_certificate(std::ostream& out) const override; + expr_ref get_answer() override; }; class ddnf_node; diff --git a/src/muz/duality/CMakeLists.txt b/src/muz/duality/CMakeLists.txt deleted file mode 100644 index f005b88b1..000000000 --- a/src/muz/duality/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -z3_add_component(duality_intf - SOURCES - duality_dl_interface.cpp - COMPONENT_DEPENDENCIES - duality - muz - transforms -) diff --git a/src/muz/duality/duality_dl_interface.cpp b/src/muz/duality/duality_dl_interface.cpp deleted file mode 100755 index 5ce4ef957..000000000 --- a/src/muz/duality/duality_dl_interface.cpp +++ /dev/null @@ -1,622 +0,0 @@ -/*++ - Copyright (c) 2013 Microsoft Corporation - - Module Name: - - duality_dl_interface.cpp - - Abstract: - - SMT2 interface for Duality - - Author: - - Krystof Hoder (t-khoder) 2011-9-22. - Modified by Ken McMIllan (kenmcmil) 2013-4-18. - - Revision History: - - --*/ - -#include "muz/base/dl_context.h" -#include "muz/transforms/dl_mk_coi_filter.h" -#include "muz/transforms/dl_mk_interp_tail_simplifier.h" -#include "muz/transforms/dl_mk_subsumption_checker.h" -#include "muz/transforms/dl_mk_rule_inliner.h" -#include "muz/base/dl_rule.h" -#include "muz/base/dl_rule_transformer.h" -#include "parsers/smt2/smt2parser.h" -#include "muz/duality/duality_dl_interface.h" -#include "muz/base/dl_rule_set.h" -#include "muz/transforms/dl_mk_slice.h" -#include "muz/transforms/dl_mk_unfold.h" -#include "muz/transforms/dl_mk_coalesce.h" -#include "ast/expr_abstract.h" -#include "model/model_smt2_pp.h" -#include "model/model_v2_pp.h" -#include "muz/base/fixedpoint_params.hpp" -#include "ast/used_vars.h" -#include "ast/func_decl_dependencies.h" -#include "muz/transforms/dl_transforms.h" - -// template class symbol_table; - -#ifdef WIN32 -#pragma warning(disable:4996) -#pragma warning(disable:4800) -#pragma warning(disable:4267) -#pragma warning(disable:4101) -#endif - -#include "duality/duality.h" -#include "duality/duality_profiling.h" - -// using namespace Duality; - -namespace Duality { - - enum DualityStatus {StatusModel, StatusRefutation, StatusUnknown, StatusNull}; - - class duality_data { - public: - context ctx; - RPFP::LogicSolver *ls; - RPFP *rpfp; - - DualityStatus status; - std::vector clauses; - std::vector > clause_labels; - hash_map map; // edges to clauses - Solver *old_rs; - Solver::Counterexample cex; - - duality_data(ast_manager &_m) : ctx(_m,config(params_ref())) { - ls = 0; - rpfp = 0; - status = StatusNull; - old_rs = 0; - } - ~duality_data(){ - if(old_rs) - dealloc(old_rs); - if(rpfp) - dealloc(rpfp); - if(ls) - dealloc(ls); - } - }; - - - dl_interface::dl_interface(datalog::context& dl_ctx) : - engine_base(dl_ctx.get_manager(), "duality"), - m_ctx(dl_ctx) - - { - _d = 0; - // dl_ctx.get_manager().toggle_proof_mode(PGM_FINE); - } - - - dl_interface::~dl_interface() { - if(_d) - dealloc(_d); - } - - - // - // Check if the new rules are weaker so that we can - // re-use existing context. - // -#if 0 - void dl_interface::check_reset() { - // TODO - datalog::rule_ref_vector const& new_rules = m_ctx.get_rules().get_rules(); - datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); - bool is_subsumed = !old_rules.empty(); - for (unsigned i = 0; is_subsumed && i < new_rules.size(); ++i) { - is_subsumed = false; - for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { - if (m_ctx.check_subsumes(*old_rules[j], *new_rules[i])) { - is_subsumed = true; - } - } - if (!is_subsumed) { - TRACE("pdr", new_rules[i]->display(m_ctx, tout << "Fresh rule ");); - m_context->reset(); - } - } - m_old_rules.reset(); - m_old_rules.add_rules(new_rules.size(), new_rules.c_ptr()); - } -#endif - - - lbool dl_interface::query(::expr * query) { - - // we restore the initial state in the datalog context - m_ctx.ensure_opened(); - - // if there is old data, get the cex and dispose (later) - duality_data *old_data = _d; - Solver *old_rs = 0; - if(old_data){ - old_rs = old_data->old_rs; - old_rs->GetCounterexample().swap(old_data->cex); - } - - scoped_proof generate_proofs_please(m_ctx.get_manager()); - - // make a new problem and solver - _d = alloc(duality_data,m_ctx.get_manager()); - _d->ctx.set("mbqi",m_ctx.get_params().duality_mbqi()); - _d->ls = alloc(RPFP::iZ3LogicSolver,_d->ctx); - _d->rpfp = alloc(RPFP,_d->ls); - - - - expr_ref_vector rules(m_ctx.get_manager()); - svector< ::symbol> names; - unsigned_vector bounds; - // m_ctx.get_rules_as_formulas(rules, names); - - - // If using SAS 2013 abstractiion, we need to perform some transforms - expr_ref query_ref(m_ctx.get_manager()); - if(m_ctx.quantify_arrays()){ - datalog::rule_manager& rm = m_ctx.get_rule_manager(); - rm.mk_query(query, m_ctx.get_rules()); - apply_default_transformation(m_ctx); - datalog::rule_set &rs = m_ctx.get_rules(); - if(m_ctx.get_rules().get_output_predicates().empty()) - query_ref = m_ctx.get_manager().mk_false(); - else { - func_decl_ref query_pred(m_ctx.get_manager()); - query_pred = m_ctx.get_rules().get_output_predicate(); - ptr_vector sorts; - unsigned nargs = query_pred.get()->get_arity(); - expr_ref_vector vars(m_ctx.get_manager()); - for(unsigned i = 0; i < nargs; i++){ - ::sort *s = query_pred.get()->get_domain(i); - vars.push_back(m_ctx.get_manager().mk_var(nargs-1-i,s)); - } - query_ref = m_ctx.get_manager().mk_app(query_pred.get(),nargs,vars.c_ptr()); - query = query_ref.get(); - } - unsigned nrules = rs.get_num_rules(); - for(unsigned i = 0; i < nrules; i++){ - expr_ref f(m_ctx.get_manager()); - rm.to_formula(*rs.get_rule(i), f); - rules.push_back(f); - } - } - else - m_ctx.get_raw_rule_formulas(rules, names, bounds); - - // get all the rules as clauses - std::vector &clauses = _d->clauses; - clauses.clear(); - for (unsigned i = 0; i < rules.size(); ++i) { - expr e(_d->ctx,rules[i].get()); - clauses.push_back(e); - } - - std::vector b_sorts; - std::vector b_names; - used_vars uv; - uv.process(query); - unsigned nuv = uv.get_max_found_var_idx_plus_1(); - for(int i = nuv-1; i >= 0; i--){ // var indices are backward - ::sort * s = uv.get(i); - if(!s) - s = _d->ctx.m().mk_bool_sort(); // missing var, whatever - b_sorts.push_back(sort(_d->ctx,s)); - b_names.push_back(symbol(_d->ctx,::symbol(i))); // names? - } - -#if 0 - // turn the query into a clause - expr q(_d->ctx,m_ctx.bind_variables(query,false)); - - std::vector b_sorts; - std::vector b_names; - if (q.is_quantifier() && !q.is_quantifier_forall()) { - int bound = q.get_quantifier_num_bound(); - for(int j = 0; j < bound; j++){ - b_sorts.push_back(q.get_quantifier_bound_sort(j)); - b_names.push_back(q.get_quantifier_bound_name(j)); - } - q = q.arg(0); - } -#else - expr q(_d->ctx,query); -#endif - - expr qc = implies(q,_d->ctx.bool_val(false)); - qc = _d->ctx.make_quant(Forall,b_sorts,b_names,qc); - clauses.push_back(qc); - bounds.push_back(UINT_MAX); - - // get the background axioms - unsigned num_asserts = m_ctx.get_num_assertions(); - for (unsigned i = 0; i < num_asserts; ++i) { - expr e(_d->ctx,m_ctx.get_assertion(i)); - _d->rpfp->AssertAxiom(e); - } - - // make sure each predicate is the head of at least one clause - func_decl_set heads; - for(unsigned i = 0; i < clauses.size(); i++){ - expr cl = clauses[i]; - - while(true){ - if(cl.is_app()){ - decl_kind k = cl.decl().get_decl_kind(); - if(k == Implies) - cl = cl.arg(1); - else { - heads.insert(cl.decl()); - break; - } - } - else if(cl.is_quantifier()) - cl = cl.body(); - else break; - } - } - ast_ref_vector const &pinned = m_ctx.get_pinned(); - for(unsigned i = 0; i < pinned.size(); i++){ - ::ast *fa = pinned[i]; - if(is_func_decl(fa)){ - ::func_decl *fd = to_func_decl(fa); - if (m_ctx.is_predicate(fd)) { - func_decl f(_d->ctx, fd); - if (!heads.contains(fd)) { - int arity = f.arity(); - std::vector args; - for (int j = 0; j < arity; j++) - args.push_back(_d->ctx.fresh_func_decl("X", f.domain(j))()); - expr c = implies(_d->ctx.bool_val(false), f(args)); - c = _d->ctx.make_quant(Forall, args, c); - clauses.push_back(c); - bounds.push_back(UINT_MAX); - } - } - } - } - unsigned rb = m_ctx.get_params().duality_recursion_bound(); - std::vector std_bounds; - for(unsigned i = 0; i < bounds.size(); i++){ - unsigned b = bounds[i]; - if (b == UINT_MAX) b = rb; - std_bounds.push_back(b); - } - - // creates 1-1 map between clauses and rpfp edges - _d->rpfp->FromClauses(clauses,&std_bounds); - - // populate the edge-to-clause map - for(unsigned i = 0; i < _d->rpfp->edges.size(); ++i) - _d->map[_d->rpfp->edges[i]] = i; - - // create a solver object - - Solver *rs = Solver::Create("duality", _d->rpfp); - - if(old_rs) - rs->LearnFrom(old_rs); // new solver gets hints from old solver - - // set its options - IF_VERBOSE(1, rs->SetOption("report","1");); - rs->SetOption("full_expand",m_ctx.get_params().duality_full_expand() ? "1" : "0"); - rs->SetOption("no_conj",m_ctx.get_params().duality_no_conj() ? "1" : "0"); - rs->SetOption("feasible_edges",m_ctx.get_params().duality_feasible_edges() ? "1" : "0"); - rs->SetOption("use_underapprox",m_ctx.get_params().duality_use_underapprox() ? "1" : "0"); - rs->SetOption("stratified_inlining",m_ctx.get_params().duality_stratified_inlining() ? "1" : "0"); - rs->SetOption("batch_expand",m_ctx.get_params().duality_batch_expand() ? "1" : "0"); - rs->SetOption("conjecture_file",m_ctx.get_params().duality_conjecture_file()); - rs->SetOption("enable_restarts",m_ctx.get_params().duality_enable_restarts() ? "1" : "0"); -#if 0 - if(rb != UINT_MAX){ - std::ostringstream os; os << rb; - rs->SetOption("recursion_bound", os.str()); - } -#endif - - // Solve! - bool ans; - try { - ans = rs->Solve(); - } - catch (Duality::solver::cancel_exception &exn){ - throw default_exception(Z3_CANCELED_MSG); - } - catch (Duality::Solver::Incompleteness &exn){ - throw default_exception("incompleteness"); - } - - // profile! - - if(m_ctx.get_params().duality_profile()) - print_profile(std::cout); - - // save the result and counterexample if there is one - _d->status = ans ? StatusModel : StatusRefutation; - _d->cex.swap(rs->GetCounterexample()); // take ownership of cex - _d->old_rs = rs; // save this for later hints - - if(old_data){ - dealloc(old_data); // this deallocates the old solver if there is one - } - - // dealloc(rs); this is now owned by data - - // true means the RPFP problem is SAT, so the query is UNSAT - // but we return undef if the UNSAT result is bounded - if(ans){ - if(rs->IsResultRecursionBounded()){ -#if 0 - m_ctx.set_status(datalog::BOUNDED); - return l_undef; -#else - return l_false; -#endif - } - return l_false; - } - return l_true; - } - - expr_ref dl_interface::get_cover_delta(int level, ::func_decl* pred_orig) { - SASSERT(false); - return expr_ref(m_ctx.get_manager()); - } - - void dl_interface::add_cover(int level, ::func_decl* pred, ::expr* property) { - SASSERT(false); - } - - unsigned dl_interface::get_num_levels(::func_decl* pred) { - SASSERT(false); - return 0; - } - - void dl_interface::collect_statistics(::statistics& st) const { - } - - void dl_interface::reset_statistics() { - } - - static hash_set *local_func_decls; - - static void print_proof(dl_interface *d, std::ostream& out, RPFP *tree, RPFP::Node *root) { - context &ctx = d->dd()->ctx; - RPFP::Node &node = *root; - RPFP::Edge &edge = *node.Outgoing; - - // first, prove the children (that are actually used) - - for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!tree->Empty(edge.Children[i])){ - print_proof(d,out,tree,edge.Children[i]); - } - } - - // print the label and the proved fact - - out << "(step s!" << node.number; - out << " (" << node.Name.name(); - for(unsigned i = 0; i < edge.F.IndParams.size(); i++) - out << " " << tree->Eval(&edge,edge.F.IndParams[i]); - out << ")\n"; - - // print the rule number - - out << " rule!" << node.Outgoing->map->number; - - // print the substitution - - out << " (subst\n"; - RPFP::Edge *orig_edge = edge.map; - int orig_clause = d->dd()->map[orig_edge]; - expr &t = d->dd()->clauses[orig_clause]; - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - hash_map subst; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - out << " (= " << skolem << " " << tree->Eval(&edge,skolem) << ")\n"; - expr local_skolem = tree->Localize(&edge,skolem); - (*local_func_decls).insert(local_skolem.decl()); - } - } - out << " )\n"; - - out << " (labels"; - std::vector labels; - tree->GetLabels(&edge,labels); - for(unsigned j = 0; j < labels.size(); j++){ - out << " " << labels[j]; - } - - out << " )\n"; - - // reference the proofs of all the children, in syntactic order - // "true" means the child is not needed - - out << " (ref "; - for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!tree->Empty(edge.Children[i])) - out << " s!" << edge.Children[i]->number; - else - out << " true"; - } - out << " )"; - out << ")\n"; - } - - - void dl_interface::display_certificate(std::ostream& out) const { - ((dl_interface *)this)->display_certificate_non_const(out); - } - - void dl_interface::display_certificate_non_const(std::ostream& out) { - if(_d->status == StatusModel){ - ast_manager &m = m_ctx.get_manager(); - model_ref md = get_model(); - out << "(fixedpoint \n"; - model_smt2_pp(out, m, *md.get(), 0); - out << ")\n"; - } - else if(_d->status == StatusRefutation){ - out << "(derivation\n"; - // negation of the query is the last clause -- prove it - hash_set locals; - local_func_decls = &locals; - print_proof(this,out,_d->cex.get_tree(),_d->cex.get_root()); - out << ")\n"; - out << "(model \n\""; - ::model mod(m_ctx.get_manager()); - model orig_model = _d->cex.get_tree()->dualModel; - for(unsigned i = 0; i < orig_model.num_consts(); i++){ - func_decl cnst = orig_model.get_const_decl(i); - if (locals.find(cnst) == locals.end()) { - expr thing = orig_model.get_const_interp(cnst); - mod.register_decl(to_func_decl(cnst.raw()), to_expr(thing.raw())); - } - } - for(unsigned i = 0; i < orig_model.num_funcs(); i++){ - func_decl cnst = orig_model.get_func_decl(i); - if (locals.find(cnst) == locals.end()) { - func_interp thing = orig_model.get_func_interp(cnst); - ::func_interp *thing_raw = thing; - mod.register_decl(to_func_decl(cnst.raw()), thing_raw->copy()); - } - } - model_v2_pp(out,mod); - out << "\")\n"; - } - } - - expr_ref dl_interface::get_answer() { - SASSERT(false); - return expr_ref(m_ctx.get_manager()); - } - - void dl_interface::cancel() { -#if 0 - if(_d && _d->ls) - _d->ls->cancel(); -#else - // HACK: duality can't cancel at all times, we just exit here - std::cout << "(error \"duality canceled\")\nunknown\n"; - abort(); -#endif - } - - void dl_interface::cleanup() { - } - - void dl_interface::updt_params() { - } - - model_ref dl_interface::get_model() { - ast_manager &m = m_ctx.get_manager(); - model_ref md(alloc(::model, m)); - std::vector &nodes = _d->rpfp->nodes; - expr_ref_vector conjs(m); - for (unsigned i = 0; i < nodes.size(); ++i) { - RPFP::Node *node = nodes[i]; - func_decl &pred = node->Name; - expr_ref prop(m); - prop = to_expr(node->Annotation.Formula); - std::vector ¶ms = node->Annotation.IndParams; - expr_ref q(m); - expr_ref_vector sig_vars(m); - for (unsigned j = 0; j < params.size(); ++j) - sig_vars.push_back(params[params.size()-j-1]); // TODO: why backwards? - expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q); - if (params.empty()) { - md->register_decl(pred, q); - } - else { - ::func_interp* fi = alloc(::func_interp, m, params.size()); - fi->set_else(q); - md->register_decl(pred, fi); - } - } - return md; - } - - static proof_ref extract_proof(dl_interface *d, RPFP *tree, RPFP::Node *root) { - context &ctx = d->dd()->ctx; - ast_manager &mgr = ctx.m(); - RPFP::Node &node = *root; - RPFP::Edge &edge = *node.Outgoing; - RPFP::Edge *orig_edge = edge.map; - - // first, prove the children (that are actually used) - - proof_ref_vector prems(mgr); - ::vector substs; - int orig_clause = d->dd()->map[orig_edge]; - expr &t = d->dd()->clauses[orig_clause]; - prems.push_back(mgr.mk_asserted(ctx.uncook(t))); - - substs.push_back(expr_ref_vector(mgr)); - if (t.is_quantifier() && t.is_quantifier_forall()) { - int bound = t.get_quantifier_num_bound(); - std::vector sorts; - std::vector names; - hash_map subst; - for(int j = 0; j < bound; j++){ - sort the_sort = t.get_quantifier_bound_sort(j); - symbol name = t.get_quantifier_bound_name(j); - expr skolem = ctx.constant(symbol(ctx,name),sort(ctx,the_sort)); - expr val = tree->Eval(&edge,skolem); - expr_ref thing(ctx.uncook(val),mgr); - substs[0].push_back(thing); - expr local_skolem = tree->Localize(&edge,skolem); - (*local_func_decls).insert(local_skolem.decl()); - } - } - - svector > pos; - for(unsigned i = 0; i < edge.Children.size(); i++){ - if(!tree->Empty(edge.Children[i])){ - pos.push_back(std::pair(i+1,0)); - proof_ref prem = extract_proof(d,tree,edge.Children[i]); - prems.push_back(prem); - substs.push_back(expr_ref_vector(mgr)); - } - } - - func_decl f = node.Name; - std::vector args; - for(unsigned i = 0; i < edge.F.IndParams.size(); i++) - args.push_back(tree->Eval(&edge,edge.F.IndParams[i])); - expr conc = f(args); - - - ::vector< ::proof *> pprems; - for(unsigned i = 0; i < prems.size(); i++) - pprems.push_back(prems[i].get()); - - proof_ref res(mgr.mk_hyper_resolve(pprems.size(),&pprems[0], ctx.uncook(conc), pos, substs),mgr); - return res; - - } - - proof_ref dl_interface::get_proof() { - if(_d->status == StatusRefutation){ - hash_set locals; - local_func_decls = &locals; - return extract_proof(this,_d->cex.get_tree(),_d->cex.get_root()); - } - else - return proof_ref(m_ctx.get_manager()); - } -} diff --git a/src/muz/duality/duality_dl_interface.h b/src/muz/duality/duality_dl_interface.h deleted file mode 100644 index 506642217..000000000 --- a/src/muz/duality/duality_dl_interface.h +++ /dev/null @@ -1,80 +0,0 @@ -/*++ - Copyright (c) 2013 Microsoft Corporation - - Module Name: - - duality_dl_interface.h - - Abstract: - - SMT2 interface for Duality - - Author: - - Krystof Hoder (t-khoder) 2011-9-22. - Modified by Ken McMIllan (kenmcmil) 2013-4-18. - - Revision History: - - --*/ - -#ifndef DUALITY_DL_INTERFACE_H_ -#define DUALITY_DL_INTERFACE_H_ - -#include "util/lbool.h" -#include "muz/base/dl_rule.h" -#include "muz/base/dl_rule_set.h" -#include "muz/base/dl_engine_base.h" -#include "util/statistics.h" - -namespace datalog { - class context; -} - -namespace Duality { - - class duality_data; - - class dl_interface : public datalog::engine_base { - duality_data *_d; - datalog::context &m_ctx; - - public: - dl_interface(datalog::context& ctx); - ~dl_interface(); - - lbool query(expr* query); - - void cancel(); - - void cleanup(); - - void display_certificate(std::ostream& out) const; - - void collect_statistics(statistics& st) const; - - void reset_statistics(); - - expr_ref get_answer(); - - unsigned get_num_levels(func_decl* pred); - - expr_ref get_cover_delta(int level, func_decl* pred); - - void add_cover(int level, func_decl* pred, expr* property); - - void updt_params(); - - model_ref get_model(); - - proof_ref get_proof(); - - duality_data *dd(){return _d;} - - private: - void display_certificate_non_const(std::ostream& out); - }; -} - - -#endif diff --git a/src/muz/fp/CMakeLists.txt b/src/muz/fp/CMakeLists.txt index 0c5f5e915..4837df81b 100644 --- a/src/muz/fp/CMakeLists.txt +++ b/src/muz/fp/CMakeLists.txt @@ -8,9 +8,7 @@ z3_add_component(fp bmc clp ddnf - duality_intf muz - pdr rel spacer tab diff --git a/src/muz/fp/datalog_parser.cpp b/src/muz/fp/datalog_parser.cpp index 7191f1931..a23d654b0 100644 --- a/src/muz/fp/datalog_parser.cpp +++ b/src/muz/fp/datalog_parser.cpp @@ -104,11 +104,13 @@ public: m_ok = (m_file != NULL) && (err == 0); #else m_file = fopen(fname, "rb"); - m_ok = (m_file != NULL); + m_ok = (m_file != nullptr); #endif } ~line_reader() { - fclose(m_file); + if (m_file != nullptr){ + fclose(m_file); + } } bool operator()() { return m_ok; } @@ -171,7 +173,7 @@ class char_reader { public: char_reader(char const* file): m_line_reader(file), - m_line(0) + m_line(nullptr) {} bool operator()() { return m_line_reader(); } @@ -184,7 +186,7 @@ public: m_line = m_line_reader.get_line(); } if (!(m_line[0])) { - m_line = 0; + m_line = nullptr; return '\n'; } char result = m_line[0]; @@ -266,8 +268,8 @@ public: } dlexer(): - m_input(0), - m_reader(0), + m_input(nullptr), + m_reader(nullptr), m_prev_char(0), m_curr_char(0), m_line(1), @@ -494,27 +496,27 @@ public: { } - virtual bool parse_file(char const * filename) { + bool parse_file(char const * filename) override { reset(); - if (filename != 0) { + if (filename != nullptr) { set_path(filename); char_reader reader(filename); if (!reader()) { get_err() << "ERROR: could not open file '" << filename << "'.\n"; return false; } - return parse_stream(0, &reader); + return parse_stream(nullptr, &reader); } else { - return parse_stream(&std::cin, 0); + return parse_stream(&std::cin, nullptr); } } - virtual bool parse_string(char const * string) { + bool parse_string(char const * string) override { reset(); std::string s(string); std::istringstream is(s); - return parse_stream(&is, 0); + return parse_stream(&is, nullptr); } protected: @@ -701,7 +703,7 @@ protected: if(is_predicate_declaration) { return unexpected(tok, "predicate declaration should not end with '.'"); } - add_rule(pred, 0, 0, 0); + add_rule(pred, 0, nullptr, nullptr); return m_lexer->next_token(); case TK_LEFT_ARROW: return parse_body(pred); @@ -777,7 +779,7 @@ protected: dtoken parse_infix(dtoken tok1, char const* td, app_ref& pred) { symbol td1(td); expr_ref v1(m_manager), v2(m_manager); - sort* s = 0; + sort* s = nullptr; dtoken tok2 = m_lexer->next_token(); if (tok2 != TK_NEQ && tok2 != TK_GT && tok2 != TK_LT && tok2 != TK_EQ) { return unexpected(tok2, "built-in infix operator"); @@ -790,12 +792,12 @@ protected: symbol td2(td); if (tok1 == TK_ID) { - expr* _v1 = 0; + expr* _v1 = nullptr; m_vars.find(td1.bare_str(), _v1); v1 = _v1; } if (tok3 == TK_ID) { - expr* _v2 = 0; + expr* _v2 = nullptr; m_vars.find(td2.bare_str(), _v2); v2 = _v2; } @@ -842,8 +844,8 @@ protected: svector arg_names; func_decl* f = m_context.try_get_predicate_decl(s); tok = parse_args(tok, f, args, arg_names); - is_predicate_declaration = f==0; - if (f==0) { + is_predicate_declaration = f==nullptr; + if (f==nullptr) { //we're in a declaration unsigned arity = args.size(); ptr_vector domain; @@ -884,7 +886,7 @@ protected: tok = m_lexer->next_token(); while (tok != TK_EOS && tok != TK_ERROR) { symbol alias; - sort* s = 0; + sort* s = nullptr; if(!f) { //we're in a predicate declaration @@ -951,7 +953,7 @@ protected: symbol data (m_lexer->get_token_data()); if (is_var(data.bare_str())) { unsigned idx = 0; - expr* v = 0; + expr* v = nullptr; if (!m_vars.find(data.bare_str(), v)) { idx = m_num_vars++; v = m_manager.mk_var(idx, s); @@ -979,7 +981,7 @@ protected: if(!num.is_uint64()) { return unexpected(tok, "integer expected"); } - uint64 int_num = num.get_uint64(); + uint64_t int_num = num.get_uint64(); app * numeral = mk_symbol_const(int_num, s); args.push_back(numeral); @@ -1014,7 +1016,7 @@ protected: dlexer lexer; { flet lexer_let(m_lexer, &lexer); - m_lexer->set_stream(0, &stream); + m_lexer->set_stream(nullptr, &stream); tok = m_lexer->next_token(); if(parsing_domain) { tok = parse_domains(tok); @@ -1072,7 +1074,7 @@ protected: } } - sort * register_finite_sort(symbol name, uint64 domain_size, context::sort_kind k) { + sort * register_finite_sort(symbol name, uint64_t domain_size, context::sort_kind k) { if(m_sort_dict.contains(name.bare_str())) { throw default_exception(default_exception::fmt(), "sort %s already declared", name.bare_str()); } @@ -1102,7 +1104,7 @@ protected: app* mk_const(symbol const& name, sort* s) { app * res; if(m_arith.is_int(s)) { - uint64 val; + uint64_t val; if(!string_to_uint64(name.bare_str(), val)) { throw default_exception(default_exception::fmt(), "Invalid integer: \"%s\"", name.bare_str()); } @@ -1117,7 +1119,7 @@ protected: /** \brief Make a constant for DK_SYMBOL sort out of an integer */ - app* mk_symbol_const(uint64 el, sort* s) { + app* mk_symbol_const(uint64_t el, sort* s) { app * res; if(m_arith.is_int(s)) { res = m_arith.mk_numeral(rational(el, rational::ui64()), s); @@ -1128,7 +1130,7 @@ protected: } return res; } - app* mk_const(uint64 el, sort* s) { + app* mk_const(uint64_t el, sort* s) { unsigned idx = m_context.get_constant_number(s, el); app * res = m_decl_util.mk_numeral(idx, s); return res; @@ -1137,7 +1139,7 @@ protected: table_element mk_table_const(symbol const& name, sort* s) { return m_context.get_constant_number(s, name); } - table_element mk_table_const(uint64 el, sort* s) { + table_element mk_table_const(uint64_t el, sort* s) { return m_context.get_constant_number(s, el); } }; @@ -1169,9 +1171,9 @@ protected: // ----------------------------------- class wpa_parser_impl : public wpa_parser, dparser { - typedef svector uint64_vector; - typedef hashtable > uint64_set; - typedef map > num2sym; + typedef svector uint64_vector; + typedef hashtable > uint64_set; + typedef map > num2sym; typedef map sym2nums; num2sym m_number_names; @@ -1186,7 +1188,7 @@ class wpa_parser_impl : public wpa_parser, dparser { bool m_use_map_names; uint64_set& ensure_sort_content(symbol sort_name) { - sym2nums::entry * e = m_sort_contents.insert_if_not_there2(sort_name, 0); + sym2nums::entry * e = m_sort_contents.insert_if_not_there2(sort_name, nullptr); if(!e->get_data().m_value) { e->get_data().m_value = alloc(uint64_set); } @@ -1200,13 +1202,13 @@ public: m_short_sort(ctx.get_manager()), m_use_map_names(ctx.use_map_names()) { } - ~wpa_parser_impl() { + ~wpa_parser_impl() override { reset_dealloc_values(m_sort_contents); } void reset() { } - virtual bool parse_directory(char const * path) { + bool parse_directory(char const * path) override { bool result = false; try { result = parse_directory_core(path); @@ -1261,7 +1263,7 @@ private: return true; } - bool inp_num_to_element(sort * s, uint64 num, table_element & res) { + bool inp_num_to_element(sort * s, uint64_t num, table_element & res) { if(s==m_bool_sort.get() || s==m_short_sort.get()) { res = mk_table_const(num, s); return true; @@ -1303,7 +1305,7 @@ private: } } - void parse_rules_file(std::string fname) { + void parse_rules_file(const std::string & fname) { SASSERT(file_exists(fname)); flet flet_cur_file(m_current_file, fname); @@ -1312,10 +1314,10 @@ private: dlexer lexer; m_lexer = &lexer; - m_lexer->set_stream(&stm, 0); + m_lexer->set_stream(&stm, nullptr); dtoken tok = m_lexer->next_token(); tok = parse_decls(tok); - m_lexer = 0; + m_lexer = nullptr; } bool parse_rel_line(char * full_line, uint64_vector & args) { @@ -1332,7 +1334,7 @@ private: if(*ptr==0) { break; } - uint64 num; + uint64_t num; if(!read_uint64(ptr, num)) { throw default_exception(default_exception::fmt(), "number expected on line %d in file %s", m_current_line, m_current_file.c_str()); @@ -1347,7 +1349,7 @@ private: return true; } - void parse_rel_file(std::string fname) { + void parse_rel_file(const std::string & fname) { SASSERT(file_exists(fname)); IF_VERBOSE(10, verbose_stream() << "Parsing relation file " << fname << "\n";); @@ -1389,7 +1391,7 @@ private: bool fact_fail = false; fact.reset(); for(unsigned i=0;im_key; uint64_set & sort_content = *sit->m_value; //the +1 is for a zero element which happens to appear in the problem files - uint64 domain_size = sort_content.size()+1; + uint64_t domain_size = sort_content.size()+1; // sort * s; if(!m_use_map_names) { /* s = */ register_finite_sort(sort_name, domain_size, context::SK_UINT64); @@ -1428,7 +1430,7 @@ private: uint64_set::iterator cit = sort_content.begin(); uint64_set::iterator cend = sort_content.end(); for(; cit!=cend; ++cit) { - uint64 const_num = *cit; + uint64_t const_num = *cit; inp_num_to_element(s, const_num); } */ @@ -1443,7 +1445,7 @@ private: *ptr=0; } - bool parse_map_line(char * full_line, uint64 & num, symbol & name) { + bool parse_map_line(char * full_line, uint64_t & num, symbol & name) { cut_off_comment(full_line); if(full_line[0]==0) { return false; @@ -1496,7 +1498,7 @@ private: return true; } - void parse_map_file(std::string fname) { + void parse_map_file(const std::string & fname) { SASSERT(file_exists(fname)); IF_VERBOSE(10, verbose_stream() << "Parsing map file " << fname << "\n";); @@ -1515,7 +1517,7 @@ private: m_current_line++; char * full_line = rdr.get_line(); - uint64 num; + uint64_t num; symbol el_name; if(!parse_map_line(full_line, num, el_name)) { diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index 2610f821c..4605826ba 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -30,23 +30,23 @@ Notes: #include "util/scoped_ctrl_c.h" #include "util/scoped_timer.h" #include "util/trail.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include struct dl_context { smt_params m_fparams; params_ref m_params_ref; - fixedpoint_params m_params; + fp_params m_params; cmd_context & m_cmd; datalog::register_engine m_register_engine; dl_collected_cmds* m_collected_cmds; unsigned m_ref_count; datalog::dl_decl_plugin* m_decl_plugin; - scoped_ptr m_context; + scoped_ptr m_context; trail_stack m_trail; - fixedpoint_params const& get_params() { + fp_params const& get_params() { init(); return m_context->get_params(); } @@ -56,20 +56,20 @@ struct dl_context { m_cmd(ctx), m_collected_cmds(collected_cmds), m_ref_count(0), - m_decl_plugin(0), + m_decl_plugin(nullptr), m_trail(*this) {} - + void inc_ref() { ++m_ref_count; } - + void dec_ref() { --m_ref_count; if (0 == m_ref_count) { dealloc(this); } } - + void init() { ast_manager& m = m_cmd.m(); if (!m_context) { @@ -83,12 +83,12 @@ struct dl_context { else { m_decl_plugin = alloc(datalog::dl_decl_plugin); m.register_plugin(symbol("datalog_relation"), m_decl_plugin); - } + } } } - + void reset() { - m_context = 0; + m_context = nullptr; } void register_predicate(func_decl* pred, unsigned num_kinds, symbol const* kinds) { @@ -97,9 +97,9 @@ struct dl_context { m_trail.push(push_back_vector(m_collected_cmds->m_rels)); } dlctx().register_predicate(pred, false); - dlctx().set_predicate_representation(pred, num_kinds, kinds); + dlctx().set_predicate_representation(pred, num_kinds, kinds); } - + void add_rule(expr * rule, symbol const& name, unsigned bound) { init(); if (m_collected_cmds) { @@ -112,7 +112,7 @@ struct dl_context { else { m_context->add_rule(rule, name, bound); } - } + } bool collect_query(func_decl* q) { if (m_collected_cmds) { @@ -127,7 +127,7 @@ struct dl_context { m_collected_cmds->m_queries.push_back(qr); m_trail.push(push_back_vector(m_collected_cmds->m_queries)); return true; - } + } else { return false; } @@ -142,7 +142,7 @@ struct dl_context { m_trail.pop_scope(1); dlctx().pop(); } - + datalog::context & dlctx() { init(); return *m_context; @@ -162,14 +162,14 @@ class dl_rule_cmd : public cmd { public: dl_rule_cmd(dl_context * dl_ctx): cmd("rule"), - m_dl_ctx(dl_ctx), + m_dl_ctx(dl_ctx), m_arg_idx(0), - m_t(0), + m_t(nullptr), m_bound(UINT_MAX) {} - virtual char const * get_usage() const { return "(forall (q) (=> (and body) head)) :optional-name :optional-recursion-bound"; } - virtual char const * get_descr(cmd_context & ctx) const { return "add a Horn rule."; } - virtual unsigned get_arity() const { return VAR_ARITY; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + char const * get_usage() const override { return "(forall (q) (=> (and body) head)) :optional-name :optional-recursion-bound"; } + char const * get_descr(cmd_context & ctx) const override { return "add a Horn rule."; } + unsigned get_arity() const override { return VAR_ARITY; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { switch(m_arg_idx) { case 0: return CPK_EXPR; case 1: return CPK_SYMBOL; @@ -177,23 +177,23 @@ public: default: return CPK_SYMBOL; } } - virtual void set_next_arg(cmd_context & ctx, expr * t) { + void set_next_arg(cmd_context & ctx, expr * t) override { m_t = t; m_arg_idx++; } - virtual void set_next_arg(cmd_context & ctx, symbol const & s) { + void set_next_arg(cmd_context & ctx, symbol const & s) override { m_name = s; m_arg_idx++; } - virtual void set_next_arg(cmd_context & ctx, unsigned bound) { + void set_next_arg(cmd_context & ctx, unsigned bound) override { m_bound = bound; m_arg_idx++; } - virtual void reset(cmd_context & ctx) { m_dl_ctx->reset(); prepare(ctx); m_t = nullptr; } - virtual void prepare(cmd_context& ctx) { m_arg_idx = 0; m_name = symbol::null; m_bound = UINT_MAX; } - virtual void finalize(cmd_context & ctx) { + void reset(cmd_context & ctx) override { m_dl_ctx->reset(); prepare(ctx); m_t = nullptr; } + void prepare(cmd_context& ctx) override { m_arg_idx = 0; m_name = symbol::null; m_bound = UINT_MAX; } + void finalize(cmd_context & ctx) override { } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { if (!m_t) throw cmd_exception("invalid rule, expected formula"); m_dl_ctx->add_rule(m_t, m_name, m_bound); } @@ -206,19 +206,19 @@ public: dl_query_cmd(dl_context * dl_ctx): parametric_cmd("query"), m_dl_ctx(dl_ctx), - m_target(0) { + m_target(nullptr) { } - virtual char const * get_usage() const { return "predicate"; } - virtual char const * get_main_descr() const { - return "pose a query to a predicate based on the Horn rules."; + char const * get_usage() const override { return "predicate"; } + char const * get_main_descr() const override { + return "pose a query to a predicate based on the Horn rules."; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { - if (m_target == 0) return CPK_FUNC_DECL; + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { + if (m_target == nullptr) return CPK_FUNC_DECL; return parametric_cmd::next_arg_kind(ctx); } - virtual void set_next_arg(cmd_context & ctx, func_decl* t) { + void set_next_arg(cmd_context & ctx, func_decl* t) override { m_target = t; if (t->get_family_id() != null_family_id) { throw cmd_exception("Invalid query argument, expected uinterpreted function name, but argument is interpreted"); @@ -229,23 +229,23 @@ public: } } - virtual void prepare(cmd_context & ctx) { + void prepare(cmd_context & ctx) override { ctx.m(); // ensure manager is initialized. parametric_cmd::prepare(ctx); - m_target = 0; + m_target = nullptr; } - virtual void execute(cmd_context& ctx) { - if (m_target == 0) { + void execute(cmd_context& ctx) override { + if (m_target == nullptr) { throw cmd_exception("invalid query command, argument expected"); } if (m_dl_ctx->collect_query(m_target)) { return; } datalog::context& dlctx = m_dl_ctx->dlctx(); - set_background(ctx); + set_background(ctx); dlctx.updt_params(m_params); - unsigned timeout = m_dl_ctx->get_params().timeout(); + unsigned timeout = m_dl_ctx->get_params().timeout(); cancel_eh eh(ctx.m().limit()); bool query_exn = false; lbool status = l_undef; @@ -271,12 +271,12 @@ public: ctx.regular_stream() << "unsat\n"; print_certificate(ctx); break; - case l_true: + case l_true: ctx.regular_stream() << "sat\n"; print_answer(ctx); print_certificate(ctx); break; - case l_undef: + case l_undef: if (dlctx.get_status() == datalog::BOUNDED){ ctx.regular_stream() << "bounded\n"; print_certificate(ctx); @@ -287,7 +287,7 @@ public: case datalog::INPUT_ERROR: ctx.regular_stream() << "input error\n"; break; - + case datalog::MEMOUT: ctx.regular_stream() << "memory bounds exceeded\n"; break; @@ -295,12 +295,12 @@ public: case datalog::TIMEOUT: ctx.regular_stream() << "timeout\n"; break; - + case datalog::APPROX: ctx.regular_stream() << "approximated relations\n"; break; - case datalog::OK: + case datalog::OK: (void)query_exn; SASSERT(query_exn); break; @@ -318,13 +318,13 @@ public: } dlctx.cleanup(); print_statistics(ctx); - m_target = 0; + m_target = nullptr; } - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { m_dl_ctx->dlctx().collect_params(p); } - + private: void set_background(cmd_context& ctx) { @@ -356,8 +356,8 @@ private: statistics st; datalog::context& dlctx = m_dl_ctx->dlctx(); dlctx.collect_statistics(st); - st.update("time", ctx.get_seconds()); - st.display_smt2(ctx.regular_stream()); + st.update("time", ctx.get_seconds()); + st.display_smt2(ctx.regular_stream()); } } @@ -385,30 +385,30 @@ public: m_dl_ctx(dl_ctx), m_domain(0) {} - virtual char const * get_usage() const { return " ( ...) *"; } - virtual char const * get_descr(cmd_context & ctx) const { return "declare new relation"; } - virtual unsigned get_arity() const { return VAR_ARITY; } + char const * get_usage() const override { return " ( ...) *"; } + char const * get_descr(cmd_context & ctx) const override { return "declare new relation"; } + unsigned get_arity() const override { return VAR_ARITY; } - virtual void prepare(cmd_context & ctx) { + void prepare(cmd_context & ctx) override { ctx.m(); // ensure manager is initialized. - m_arg_idx = 0; - m_query_arg_idx = 0; + m_arg_idx = 0; + m_query_arg_idx = 0; m_domain.reset(); m_kinds.reset(); } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { switch(m_query_arg_idx++) { case 0: return CPK_SYMBOL; // relation name case 1: return CPK_SORT_LIST; // arguments default: return CPK_SYMBOL; // optional representation specification } } - virtual void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) { + void set_next_arg(cmd_context & ctx, unsigned num, sort * const * slist) override { m_domain.reset(); m_domain.append(num, slist); m_arg_idx++; } - virtual void set_next_arg(cmd_context & ctx, symbol const & s) { + void set_next_arg(cmd_context & ctx, symbol const & s) override { if(m_arg_idx==0) { m_rel_name = s; } @@ -418,7 +418,7 @@ public: } m_arg_idx++; } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { if(m_arg_idx<2) { throw cmd_exception("at least 2 arguments expected"); } @@ -443,36 +443,36 @@ public: m_arg_idx(0), m_dl_ctx(dl_ctx) {} - - virtual char const * get_usage() const { return " "; } - virtual char const * get_descr(cmd_context & ctx) const { return "declare constant as variable"; } - virtual unsigned get_arity() const { return 2; } - virtual void prepare(cmd_context & ctx) { + char const * get_usage() const override { return " "; } + char const * get_descr(cmd_context & ctx) const override { return "declare constant as variable"; } + unsigned get_arity() const override { return 2; } + + void prepare(cmd_context & ctx) override { ctx.m(); // ensure manager is initialized. - m_arg_idx = 0; + m_arg_idx = 0; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { SASSERT(m_arg_idx <= 1); if (m_arg_idx == 0) { - return CPK_SYMBOL; + return CPK_SYMBOL; } - return CPK_SORT; + return CPK_SORT; } - virtual void set_next_arg(cmd_context & ctx, sort* s) { + void set_next_arg(cmd_context & ctx, sort* s) override { m_var_sort = s; ++m_arg_idx; } - virtual void set_next_arg(cmd_context & ctx, symbol const & s) { - m_var_name = s; + void set_next_arg(cmd_context & ctx, symbol const & s) override { + m_var_name = s; ++m_arg_idx; } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { ast_manager& m = ctx.m(); - func_decl_ref var(m.mk_func_decl(m_var_name, 0, static_cast(0), m_var_sort), m); + func_decl_ref var(m.mk_func_decl(m_var_name, 0, static_cast(nullptr), m_var_sort), m); ctx.insert(var); m_dl_ctx->dlctx().register_variable(var); } @@ -489,10 +489,10 @@ public: m_dl_ctx(dl_ctx) {} - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "push the fixedpoint context"; } - virtual unsigned get_arity() const { return 0; } - virtual void execute(cmd_context & ctx) { + char const * get_usage() const override { return ""; } + char const * get_descr(cmd_context & ctx) const override { return "push the fixedpoint context"; } + unsigned get_arity() const override { return 0; } + void execute(cmd_context & ctx) override { m_dl_ctx->push(); } }; @@ -508,10 +508,10 @@ public: m_dl_ctx(dl_ctx) {} - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "pop the fixedpoint context"; } - virtual unsigned get_arity() const { return 0; } - virtual void execute(cmd_context & ctx) { + char const * get_usage() const override { return ""; } + char const * get_descr(cmd_context & ctx) const override { return "pop the fixedpoint context"; } + unsigned get_arity() const override { return 0; } + void execute(cmd_context & ctx) override { m_dl_ctx->pop(); } }; @@ -523,12 +523,12 @@ static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_c ctx.insert(alloc(dl_query_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx)); ctx.insert(alloc(dl_declare_var_cmd, dl_ctx)); - ctx.insert(alloc(dl_push_cmd, dl_ctx)); + ctx.insert(alloc(dl_push_cmd, dl_ctx)); ctx.insert(alloc(dl_pop_cmd, dl_ctx)); } void install_dl_cmds(cmd_context & ctx) { - install_dl_cmds_aux(ctx, 0); + install_dl_cmds_aux(ctx, nullptr); } void install_dl_collect_cmds(dl_collected_cmds& collected_cmds, cmd_context & ctx) { diff --git a/src/muz/fp/dl_register_engine.cpp b/src/muz/fp/dl_register_engine.cpp index b56a07a7c..28a2a1c5e 100644 --- a/src/muz/fp/dl_register_engine.cpp +++ b/src/muz/fp/dl_register_engine.cpp @@ -21,19 +21,14 @@ Revision History: #include "muz/clp/clp_context.h" #include "muz/tab/tab_context.h" #include "muz/rel/rel_context.h" -#include "muz/pdr/pdr_dl_interface.h" #include "muz/ddnf/ddnf.h" -#include "muz/duality/duality_dl_interface.h" #include "muz/spacer/spacer_dl_interface.h" namespace datalog { - register_engine::register_engine(): m_ctx(0) {} + register_engine::register_engine(): m_ctx(nullptr) {} engine_base* register_engine::mk_engine(DL_ENGINE engine_type) { switch(engine_type) { - case PDR_ENGINE: - case QPDR_ENGINE: - return alloc(pdr::dl_interface, *m_ctx); case SPACER_ENGINE: return alloc(spacer::dl_interface, *m_ctx); case DATALOG_ENGINE: @@ -45,16 +40,14 @@ namespace datalog { return alloc(tab, *m_ctx); case CLP_ENGINE: return alloc(clp, *m_ctx); - case DUALITY_ENGINE: - return alloc(Duality::dl_interface, *m_ctx); case DDNF_ENGINE: return alloc(ddnf, *m_ctx); case LAST_ENGINE: UNREACHABLE(); - return 0; + return nullptr; } UNREACHABLE(); - return 0; + return nullptr; } } diff --git a/src/muz/fp/dl_register_engine.h b/src/muz/fp/dl_register_engine.h index 11f778346..e401c141a 100644 --- a/src/muz/fp/dl_register_engine.h +++ b/src/muz/fp/dl_register_engine.h @@ -27,8 +27,8 @@ namespace datalog { context* m_ctx; public: register_engine(); - engine_base* mk_engine(DL_ENGINE engine_type); - void set_context(context* ctx) { m_ctx = ctx; } + engine_base* mk_engine(DL_ENGINE engine_type) override; + void set_context(context* ctx) override { m_ctx = ctx; } }; } diff --git a/src/muz/fp/horn_tactic.cpp b/src/muz/fp/horn_tactic.cpp index 5db57a12c..eef95915d 100644 --- a/src/muz/fp/horn_tactic.cpp +++ b/src/muz/fp/horn_tactic.cpp @@ -25,9 +25,9 @@ Revision History: #include "ast/rewriter/expr_replacer.h" #include "muz/base/dl_rule_transformer.h" #include "muz/transforms/dl_mk_slice.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "muz/transforms/dl_transforms.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "ast/ast_util.h" #include "ast/rewriter/var_subst.h" @@ -65,13 +65,13 @@ class horn_tactic : public tactic { void normalize(expr_ref& f) { bool is_positive = true; - expr* e = 0; + expr* e = nullptr; while (true) { if (is_forall(f) && is_positive) { f = to_quantifier(f)->get_expr(); } else if (is_exists(f) && !is_positive) { - f = to_quantifier(f)->get_expr(); + f = to_quantifier(f)->get_expr(); } else if (m.is_not(f, e)) { is_positive = !is_positive; @@ -84,7 +84,7 @@ class horn_tactic : public tactic { if (!is_positive) { f = m.mk_not(f); } - + } bool is_predicate(expr* a) { @@ -141,10 +141,10 @@ class horn_tactic : public tactic { ast_mark mark; expr_ref_vector args(m), body(m); expr_ref head(m); - expr* a = 0, *a1 = 0; + expr* a = nullptr, *a1 = nullptr; flatten_or(tmp, args); for (unsigned i = 0; i < args.size(); ++i) { - a = args[i].get(); + a = args[i].get(); check_predicate(mark, a); if (m.is_not(a, a1)) { body.push_back(a1); @@ -176,17 +176,13 @@ class horn_tactic : public tactic { return expr_ref(m.mk_implies(body, head), m); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("horn", *g); bool produce_proofs = g->proofs_enabled(); - if (produce_proofs) { + if (produce_proofs) { if (!m_ctx.generate_proof_trace()) { params_ref params = m_ctx.get_params().p; params.set_bool("generate_proof_trace", true); @@ -212,7 +208,7 @@ class horn_tactic : public tactic { case IS_QUERY: queries.push_back(f); break; - default: + default: msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n"; TRACE("horn", tout << msg.str();); throw tactic_exception(msg.str().c_str()); @@ -229,24 +225,28 @@ class horn_tactic : public tactic { } queries.reset(); queries.push_back(q); - filter_model_converter* mc1 = alloc(filter_model_converter, m); - mc1->insert(to_app(q)->get_decl()); - mc = mc1; + generic_model_converter* mc1 = alloc(generic_model_converter, m, "horn"); + mc1->hide(q); + g->add(mc1); } SASSERT(queries.size() == 1); q = queries[0].get(); + proof_converter_ref pc = g->pc(); + model_converter_ref mc; if (m_is_simplify) { simplify(q, g, result, mc, pc); } else { verify(q, g, result, mc, pc); } + g->set(pc.get()); + g->set(mc.get()); } - void verify(expr* q, + void verify(expr* q, goal_ref const& g, - goal_ref_buffer & result, - model_converter_ref & mc, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc) { lbool is_reachable = l_undef; @@ -270,14 +270,14 @@ class horn_tactic : public tactic { if (produce_proofs) { proof_ref proof = m_ctx.get_proof(); pc = proof2proof_converter(m, proof); - g->assert_expr(m.mk_false(), proof, 0); + g->assert_expr(m.mk_false(), proof, nullptr); } else { g->assert_expr(m.mk_false()); } - break; + break; } - case l_false: { + case l_false: { // goal is sat g->reset(); if (produce_models) { @@ -290,11 +290,11 @@ class horn_tactic : public tactic { mc = mc2; } } - break; + break; } - case l_undef: + case l_undef: // subgoal is unchanged. - break; + break; } TRACE("horn", g->display(tout);); SASSERT(g->is_well_sorted()); @@ -314,20 +314,20 @@ class horn_tactic : public tactic { } } - void simplify(expr* q, + void simplify(expr* q, goal_ref const& g, - goal_ref_buffer & result, - model_converter_ref & mc, + goal_ref_buffer & result, + model_converter_ref & mc, proof_converter_ref & pc) { - expr_ref fml(m); + expr_ref fml(m); func_decl* query_pred = to_app(q)->get_decl(); m_ctx.set_output_predicate(query_pred); m_ctx.get_rules(); // flush adding rules. apply_default_transformation(m_ctx); - + if (m_ctx.xform_slice()) { datalog::rule_transformer transformer(m_ctx); datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); @@ -351,7 +351,7 @@ class horn_tactic : public tactic { g->assert_expr(fml); } } - + }; bool m_is_simplify; @@ -365,50 +365,47 @@ public: m_imp = alloc(imp, t, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(horn_tactic, m_is_simplify, m, m_params); } - - virtual ~horn_tactic() { + + ~horn_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - - virtual void collect_param_descrs(param_descrs & r) { + + void collect_param_descrs(param_descrs & r) override { m_imp->collect_param_descrs(r); } - - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - - virtual void collect_statistics(statistics & st) const { + + void collect_statistics(statistics & st) const override { m_imp->collect_statistics(st); st.copy(m_stats); } - virtual void reset_statistics() { + void reset_statistics() override { m_stats.reset(); m_imp->reset_statistics(); } - - virtual void cleanup() { + + void cleanup() override { ast_manager & m = m_imp->m; m_imp->collect_statistics(m_stats); dealloc(m_imp); m_imp = alloc(imp, m_is_simplify, m, m_params); - + } - + }; @@ -419,4 +416,3 @@ tactic * mk_horn_tactic(ast_manager & m, params_ref const & p) { tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(horn_tactic, true, m, p)); } - diff --git a/src/muz/pdr/CMakeLists.txt b/src/muz/pdr/CMakeLists.txt deleted file mode 100644 index ca2992b97..000000000 --- a/src/muz/pdr/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -z3_add_component(pdr - SOURCES - pdr_closure.cpp - pdr_context.cpp - pdr_dl_interface.cpp - pdr_farkas_learner.cpp - pdr_generalizers.cpp - pdr_manager.cpp - pdr_prop_solver.cpp - pdr_reachable_cache.cpp - pdr_smt_context_manager.cpp - pdr_sym_mux.cpp - pdr_util.cpp - COMPONENT_DEPENDENCIES - arith_tactics - core_tactics - muz - smt_tactic - transforms -) diff --git a/src/muz/pdr/pdr_closure.cpp b/src/muz/pdr/pdr_closure.cpp deleted file mode 100644 index 82caf285b..000000000 --- a/src/muz/pdr/pdr_closure.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - pdr_closure.cpp - -Abstract: - - Utility functions for computing closures. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-9-1. - -Revision History: - ---*/ - -#include "muz/pdr/pdr_closure.h" -#include "muz/pdr/pdr_context.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/ast_util.h" - -namespace pdr { - - expr_ref scaler::operator()(expr* e, expr* k, obj_map* translate) { - m_cache[0].reset(); - m_cache[1].reset(); - m_translate = translate; - m_k = k; - return scale(e, false); - } - - expr_ref scaler::scale(expr* e, bool is_mul) { - expr* r; - if (m_cache[is_mul].find(e, r)) { - return expr_ref(r, m); - } - if (!is_app(e)) { - return expr_ref(e, m); - } - app* ap = to_app(e); - if (m_translate && m_translate->find(ap->get_decl(), r)) { - return expr_ref(r, m); - } - if (!is_mul && a.is_numeral(e)) { - return expr_ref(a.mk_mul(m_k, e), m); - } - expr_ref_vector args(m); - bool is_mul_rec = is_mul || a.is_mul(e); - for (unsigned i = 0; i < ap->get_num_args(); ++i) { - args.push_back(scale(ap->get_arg(i), is_mul_rec)); - } - expr_ref result(m); - result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); - m_cache[is_mul].insert(e, result); - return result; - } - - expr_ref scaler::undo_k(expr* e, expr* k) { - expr_safe_replace sub(m); - th_rewriter rw(m); - expr_ref result(e, m); - sub.insert(k, a.mk_numeral(rational(1), false)); - sub(result); - rw(result); - return result; - } - - - closure::closure(pred_transformer& p, bool is_closure): - m(p.get_manager()), m_pt(p), a(m), - m_is_closure(is_closure), m_sigma(m), m_trail(m) {} - - - void closure::add_variables(unsigned num_vars, expr_ref_vector& fmls) { - manager& pm = m_pt.get_pdr_manager(); - SASSERT(num_vars > 0); - while (m_vars.size() < num_vars) { - m_vars.resize(m_vars.size()+1); - m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real())); - } - - unsigned sz = m_pt.sig_size(); - - for (unsigned i = 0; i < sz; ++i) { - expr* var; - ptr_vector vars; - func_decl* fn0 = m_pt.sig(i); - func_decl* fn1 = pm.o2n(fn0, 0); - sort* srt = fn0->get_range(); - if (a.is_int_real(srt)) { - for (unsigned j = 0; j < num_vars; ++j) { - if (!m_vars[j].find(fn1, var)) { - var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt); - m_trail.push_back(var); - m_vars[j].insert(fn1, var); - } - vars.push_back(var); - } - fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr()))); - } - } - if (m_is_closure) { - for (unsigned i = 0; i < num_vars; ++i) { - fmls.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); - } - } - else { - // is interior: - for (unsigned i = 0; i < num_vars; ++i) { - fmls.push_back(a.mk_gt(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real()))); - } - } - fmls.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(num_vars, m_sigma.c_ptr()))); - } - - expr_ref closure::close_fml(expr* e) { - expr* e0, *e1, *e2; - expr_ref result(m); - if (a.is_lt(e, e1, e2)) { - result = a.mk_le(e1, e2); - } - else if (a.is_gt(e, e1, e2)) { - result = a.mk_ge(e1, e2); - } - else if (m.is_not(e, e0) && a.is_ge(e0, e1, e2)) { - result = a.mk_le(e1, e2); - } - else if (m.is_not(e, e0) && a.is_le(e0, e1, e2)) { - result = a.mk_ge(e1, e2); - } - else if (a.is_ge(e) || a.is_le(e) || m.is_eq(e) || - (m.is_not(e, e0) && (a.is_gt(e0) || a.is_lt(e0)))) { - result = e; - } - else { - IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";); - result = m.mk_true(); - } - return result; - } - - expr_ref closure::close_conjunction(expr* fml) { - expr_ref_vector fmls(m); - flatten_and(fml, fmls); - for (unsigned i = 0; i < fmls.size(); ++i) { - fmls[i] = close_fml(fmls[i].get()); - } - return expr_ref(mk_and(fmls), m); - } - - expr_ref closure::relax(unsigned i, expr* fml) { - scaler sc(m); - expr_ref result = sc(fml, m_sigma[i].get(), &m_vars[i]); - return close_conjunction(result); - } - - expr_ref closure::operator()(expr_ref_vector const& As) { - if (As.empty()) { - return expr_ref(m.mk_false(), m); - } - if (As.size() == 1) { - return expr_ref(As[0], m); - } - expr_ref_vector fmls(m); - expr_ref B(m); - add_variables(As.size(), fmls); - for (unsigned i = 0; i < As.size(); ++i) { - fmls.push_back(relax(i, As[i])); - } - B = mk_and(fmls); - return B; - } - -} diff --git a/src/muz/pdr/pdr_closure.h b/src/muz/pdr/pdr_closure.h deleted file mode 100644 index 8af53376b..000000000 --- a/src/muz/pdr/pdr_closure.h +++ /dev/null @@ -1,67 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - pdr_closure.h - -Abstract: - - Utility functions for computing closures. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-9-1. - -Revision History: - ---*/ - -#ifndef PDR_CLOSURE_H_ -#define PDR_CLOSURE_H_ - -#include "ast/arith_decl_plugin.h" - -namespace pdr { - - // Arithmetic scaling functor. - // Variables are replaced using - // m_translate. Constants are replaced by - // multiplication with a variable 'k' (scale factor). - class scaler { - ast_manager& m; - arith_util a; - obj_map m_cache[2]; - expr* m_k; - obj_map* m_translate; - public: - scaler(ast_manager& m): m(m), a(m), m_translate(0) {} - expr_ref operator()(expr* e, expr* k, obj_map* translate = 0); - expr_ref undo_k(expr* e, expr* k); - private: - expr_ref scale(expr* e, bool is_mul); - }; - - class pred_transformer; - - class closure { - ast_manager& m; - pred_transformer& m_pt; - arith_util a; - bool m_is_closure; - expr_ref_vector m_sigma; - expr_ref_vector m_trail; - vector > m_vars; - - expr_ref relax(unsigned i, expr* fml); - expr_ref close_conjunction(expr* fml); - expr_ref close_fml(expr* fml); - void add_variables(unsigned num_vars, expr_ref_vector& fmls); - public: - closure(pred_transformer& pt, bool is_closure); - expr_ref operator()(expr_ref_vector const& As); - - }; -} - -#endif diff --git a/src/muz/pdr/pdr_context.cpp b/src/muz/pdr/pdr_context.cpp deleted file mode 100644 index 1d3232deb..000000000 --- a/src/muz/pdr/pdr_context.cpp +++ /dev/null @@ -1,2421 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_context.cpp - -Abstract: - - PDR predicate transformers and search context. - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-20. - -Revision History: - - Based on pdr_dl.cpp by - Krystof Hoder (t-khoder) 2011-9-19. - -Notes: - - ---*/ - - -#include -#include "muz/base/dl_util.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/rewriter_def.h" -#include "ast/rewriter/var_subst.h" -#include "util/util.h" -#include "muz/pdr/pdr_prop_solver.h" -#include "muz/pdr/pdr_context.h" -#include "muz/pdr/pdr_generalizers.h" -#include "ast/for_each_expr.h" -#include "muz/base/dl_rule_set.h" -#include "smt/tactic/unit_subsumption_tactic.h" -#include "model/model_smt2_pp.h" -#include "muz/transforms/dl_mk_rule_inliner.h" -#include "ast/ast_smt2_pp.h" -#include "qe/qe_lite.h" -#include "ast/ast_ll_pp.h" -#include "ast/proofs/proof_checker.h" -#include "smt/smt_value_sort.h" -#include "muz/base/dl_boogie_proof.h" -#include "ast/scoped_proof.h" -#include "tactic/core/blast_term_ite_tactic.h" -#include "model/model_implicant.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/ast_util.h" - -namespace pdr { - - - static const unsigned infty_level = UINT_MAX; - - static bool is_infty_level(unsigned lvl) { return lvl == infty_level; } - - static unsigned next_level(unsigned lvl) { return is_infty_level(lvl)?lvl:(lvl+1); } - - struct pp_level { - unsigned m_level; - pp_level(unsigned l): m_level(l) {} - }; - - static std::ostream& operator<<(std::ostream& out, pp_level const& p) { - if (is_infty_level(p.m_level)) { - return out << "oo"; - } - else { - return out << p.m_level; - } - } - - // ---------------- - // pred_tansformer - - pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): - pm(pm), m(pm.get_manager()), - ctx(ctx), m_head(head, m), - m_sig(m), m_solver(pm, head->get_name()), - m_invariants(m), m_transition(m), m_initial_state(m), - m_reachable(pm, (datalog::PDR_CACHE_MODE)ctx.get_params().pdr_cache_mode()) {} - - pred_transformer::~pred_transformer() { - rule2inst::iterator it2 = m_rule2inst.begin(), end2 = m_rule2inst.end(); - for (; it2 != end2; ++it2) { - dealloc(it2->m_value); - } - rule2expr::iterator it3 = m_rule2transition.begin(), end3 = m_rule2transition.end(); - for (; it3 != end3; ++it3) { - m.dec_ref(it3->m_value); - } - } - - std::ostream& pred_transformer::display(std::ostream& out) const { - if (!rules().empty()) out << "rules\n"; - datalog::rule_manager& rm = ctx.get_context().get_rule_manager(); - for (unsigned i = 0; i < rules().size(); ++i) { - rm.display_smt2(*rules()[i], out) << "\n"; - } - out << "transition\n" << mk_pp(transition(), m) << "\n"; - return out; - } - - void pred_transformer::collect_statistics(statistics& st) const { - m_solver.collect_statistics(st); - m_reachable.collect_statistics(st); - st.update("PDR num propagations", m_stats.m_num_propagations); - unsigned np = m_invariants.size(); - for (unsigned i = 0; i < m_levels.size(); ++i) { - np += m_levels[i].size(); - } - st.update("PDR num properties", np); - } - - void pred_transformer::reset_statistics() { - m_solver.reset_statistics(); - m_reachable.reset_statistics(); - m_stats.reset(); - } - - void pred_transformer::init_sig() { - if (m_sig.empty()) { - for (unsigned i = 0; i < m_head->get_arity(); ++i) { - sort * arg_sort = m_head->get_domain(i); - std::stringstream name_stm; - name_stm << m_head->get_name() << '_' << i; - func_decl_ref stm(m); - stm = m.mk_func_decl(symbol(name_stm.str().c_str()), 0, (sort*const*)0, arg_sort); - m_sig.push_back(pm.get_o_pred(stm, 0)); - } - } - } - - void pred_transformer::ensure_level(unsigned level) { - if (is_infty_level(level)) { - return; - } - while (m_levels.size() <= level) { - m_solver.add_level(); - m_levels.push_back(expr_ref_vector(m)); - } - } - - bool pred_transformer::is_reachable(expr* state) { - return m_reachable.is_reachable(state); - } - - datalog::rule const& pred_transformer::find_rule(model_core const& model) const { - TRACE("pdr_verbose", - datalog::rule_manager& rm = ctx.get_context().get_rule_manager(); - for (auto const& kv : m_tag2rule) { - expr* pred = kv.m_key; - tout << mk_pp(pred, m) << ":\n"; - if (kv.m_value) rm.display_smt2(*kv.m_value, tout) << "\n"; - } - ); - - if (m_tag2rule.size() == 1) { - return *m_tag2rule.begin()->m_value; - } - - expr_ref vl(m); - for (auto const& kv : m_tag2rule) { - expr* pred = kv.m_key; - if (model.eval(to_app(pred)->get_decl(), vl) && m.is_true(vl)) { - return *kv.m_value; - } - } - throw default_exception("could not find rule"); - } - - void pred_transformer::find_predecessors(datalog::rule const& r, ptr_vector& preds) const { - preds.reset(); - unsigned tail_sz = r.get_uninterpreted_tail_size(); - for (unsigned ti = 0; ti < tail_sz; ti++) { - preds.push_back(r.get_tail(ti)->get_decl()); - } - } - - - void pred_transformer::remove_predecessors(expr_ref_vector& literals) { - // remove tags - for (unsigned i = 0; i < literals.size(); ) { - expr* l = literals[i].get(); - m.is_not(l, l); - if (m_tag2rule.contains(l)) { - literals[i] = literals.back(); - literals.pop_back(); - } - else { - ++i; - } - } - } - - void pred_transformer::simplify_formulas(tactic& tac, expr_ref_vector& v) { - goal_ref g(alloc(goal, m, false, false, false)); - for (expr* e : v) g->assert_expr(e); - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); - goal_ref_buffer result; - tac(g, result, mc, pc, core); - SASSERT(result.size() == 1); - goal* r = result[0]; - v.reset(); - for (unsigned j = 0; j < r->size(); ++j) v.push_back(r->form(j)); - } - - void pred_transformer::simplify_formulas() { - tactic_ref us = mk_unit_subsumption_tactic(m); - simplify_formulas(*us, m_invariants); - for (auto & fmls : m_levels) - simplify_formulas(*us, fmls); - } - - expr_ref pred_transformer::get_formulas(unsigned level, bool add_axioms) { - expr_ref_vector res(m); - if (add_axioms) { - res.push_back(pm.get_background()); - res.push_back((level == 0)?initial_state():transition()); - } - res.append(m_invariants); - for (unsigned i = level; i < m_levels.size(); ++i) { - res.append(m_levels[i]); - } - return pm.mk_and(res); - } - - expr_ref pred_transformer::get_propagation_formula(decl2rel const& pts, unsigned level) { - expr_ref result(m), tmp1(m), tmp2(m); - expr_ref_vector conj(m); - if (level == 0) { - conj.push_back(initial_state()); - } - else { - conj.push_back(transition()); - } - conj.push_back(get_formulas(level, true)); - obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); - for (; level > 0 && it != end; ++it) { - expr* tag = it->m_key; - datalog::rule const* r = it->m_value; - if (!r) continue; - find_predecessors(*r, m_predicates); - for (unsigned i = 0; i < m_predicates.size(); ++i) { - func_decl* d = m_predicates[i]; - pred_transformer & pt = *pts.find(d); - tmp1 = pt.get_formulas(level-1, false); - TRACE("pdr", tout << mk_pp(tmp1, m) << "\n";); - pm.formula_n2o(tmp1, tmp2, i, false); - conj.push_back(m.mk_implies(tag, tmp2)); - } - } - return pm.mk_and(conj); - } - - bool pred_transformer::propagate_to_next_level(unsigned src_level) { - unsigned tgt_level = next_level(src_level); - ensure_level(next_level(tgt_level)); - expr_ref_vector& src = m_levels[src_level]; - - CTRACE("pdr", !src.empty(), - tout << "propagating " << src_level << " to " << tgt_level; - tout << " for relation " << head()->get_name() << "\n";); - - for (unsigned i = 0; i < src.size(); ) { - expr * curr = src[i].get(); - unsigned stored_lvl = 0; - VERIFY(m_prop2level.find(curr, stored_lvl)); - SASSERT(stored_lvl >= src_level); - bool assumes_level; - if (stored_lvl > src_level) { - TRACE("pdr", tout << "at level: "<< stored_lvl << " " << mk_pp(curr, m) << "\n";); - src[i] = src.back(); - src.pop_back(); - } - else if (is_invariant(tgt_level, curr, false, assumes_level)) { - - add_property(curr, assumes_level?tgt_level:infty_level); - TRACE("pdr", tout << "is invariant: "<< pp_level(tgt_level) << " " << mk_pp(curr, m) << "\n";); - src[i] = src.back(); - src.pop_back(); - ++m_stats.m_num_propagations; - } - else { - TRACE("pdr", tout << "not propagated: " << mk_pp(curr, m) << "\n";); - ++i; - } - } - IF_VERBOSE(3, verbose_stream() << "propagate: " << pp_level(src_level) << "\n"; - for (unsigned i = 0; i < src.size(); ++i) { - verbose_stream() << mk_pp(src[i].get(), m) << "\n"; - }); - return src.empty(); - } - - bool pred_transformer::add_property1(expr * lemma, unsigned lvl) { - if (is_infty_level(lvl)) { - if (!m_invariants.contains(lemma)) { - TRACE("pdr", tout << "property1: " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); - m_invariants.push_back(lemma); - m_prop2level.insert(lemma, lvl); - m_solver.add_formula(lemma); - return true; - } - else { - TRACE("pdr", tout << "already contained: " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); - return false; - } - } - ensure_level(lvl); - unsigned old_level; - if (!m_prop2level.find(lemma, old_level) || old_level < lvl) { - TRACE("pdr", tout << "property1: " << pp_level(lvl) << " " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); - m_levels[lvl].push_back(lemma); - m_prop2level.insert(lemma, lvl); - m_solver.add_level_formula(lemma, lvl); - return true; - } - else { - TRACE("pdr", tout << "old-level: " << pp_level(old_level) << " " << head()->get_name() << " " << mk_pp(lemma, m) << "\n";); - return false; - } - } - - void pred_transformer::add_child_property(pred_transformer& child, expr* lemma, unsigned lvl) { - ensure_level(lvl); - expr_ref_vector fmls(m); - mk_assumptions(child.head(), lemma, fmls); - for (unsigned i = 0; i < fmls.size(); ++i) { - TRACE("pdr", tout << "child property: " << mk_pp(fmls[i].get(), m) << "\n";); - if (is_infty_level(lvl)) { - m_solver.add_formula(fmls[i].get()); - } - else { - m_solver.add_level_formula(fmls[i].get(), lvl); - } - } - } - - void pred_transformer::add_property(expr* lemma, unsigned lvl) { - expr_ref_vector lemmas(m); - flatten_and(lemma, lemmas); - for (unsigned i = 0; i < lemmas.size(); ++i) { - expr* lemma_i = lemmas[i].get(); - if (add_property1(lemma_i, lvl)) { - IF_VERBOSE(2, verbose_stream() << pp_level(lvl) << " " << mk_pp(lemma_i, m) << "\n";); - for (unsigned j = 0; j < m_use.size(); ++j) { - m_use[j]->add_child_property(*this, lemma_i, next_level(lvl)); - } - } - } - } - - expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) { - expr_ref result(m.mk_true(), m), v(m), c(m); - if (level == -1) { - result = pm.mk_and(m_invariants); - } - else if ((unsigned)level < m_levels.size()) { - result = pm.mk_and(m_levels[level]); - } - // replace local constants by bound variables. - expr_substitution sub(m); - 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(c, v); - } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - (*rep)(result); - - // adjust result according to model converter. - unsigned arity = m_head->get_arity(); - model_ref md = alloc(model, m); - if (arity == 0) { - md->register_decl(m_head, result); - } - else { - func_interp* fi = alloc(func_interp, m, arity); - fi->set_else(result); - md->register_decl(m_head, fi); - } - model_converter_ref mc = ctx.get_model_converter(); - apply(mc, md, 0); - if (p_orig->get_arity() == 0) { - result = md->get_const_interp(p_orig); - } - else { - result = md->get_func_interp(p_orig)->get_interp(); - } - return result; - } - - void pred_transformer::add_cover(unsigned level, expr* property) { - // replace bound variables by local constants. - expr_ref result(property, m), v(m), c(m); - expr_substitution sub(m); - 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); - } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - (*rep)(result); - TRACE("pdr", tout << "cover:\n" << mk_pp(result, m) << "\n";); - // add the property. - add_property(result, level); - } - - void pred_transformer::propagate_to_infinity(unsigned invariant_level) { - expr_ref inv = get_formulas(invariant_level, false); - add_property(inv, infty_level); - // cleanup - for (unsigned i = invariant_level; i < m_levels.size(); ++i) { - m_levels[i].reset(); - } - } - - lbool pred_transformer::is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level) { - TRACE("pdr", - tout << "is-reachable: " << head()->get_name() << " level: " << n.level() << "\n"; - tout << mk_pp(n.state(), m) << "\n";); - ensure_level(n.level()); - model_ref model; - prop_solver::scoped_level _sl(m_solver, n.level()); - m_solver.set_core(core); - m_solver.set_model(&model); - lbool is_sat = m_solver.check_conjunction_as_assumptions(n.state()); - if (is_sat == l_true && core) { - core->reset(); - TRACE("pdr", tout << "updating model\n"; - model_smt2_pp(tout, m, *model, 0); - tout << mk_pp(n.state(), m) << "\n";); - n.set_model(model); - } - else if (is_sat == l_false) { - uses_level = m_solver.assumes_level(); - } - m_solver.set_model(0); - return is_sat; - } - - bool pred_transformer::is_invariant(unsigned level, expr* states, bool inductive, bool& assumes_level, expr_ref_vector* core) { - expr_ref_vector conj(m); - expr_ref tmp(m); - - conj.push_back(m.mk_not(states)); - - if (inductive) { - mk_assumptions(head(), states, conj); - } - tmp = pm.mk_and(conj); - prop_solver::scoped_level _sl(m_solver, level); - m_solver.set_core(core); - m_solver.set_model(0); - lbool r = m_solver.check_conjunction_as_assumptions(tmp); - if (r == l_false) { - assumes_level = m_solver.assumes_level(); - } - return r == l_false; - } - - bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& lits, bool& assumes_level) { - manager& pm = get_pdr_manager(); - expr_ref_vector conj(m), core(m); - expr_ref fml(m), states(m); - states = m.mk_not(pm.mk_and(lits)); - mk_assumptions(head(), states, conj); - fml = pm.mk_and(conj); - prop_solver::scoped_level _sl(m_solver, level); - m_solver.set_core(&core); - m_solver.set_subset_based_core(true); - m_solver.set_model(0); - lbool res = m_solver.check_assumptions_and_formula(lits, fml); - if (res == l_false) { - lits.reset(); - lits.append(core); - assumes_level = m_solver.assumes_level(); - } - return res == l_false; - } - - void pred_transformer::mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result) { - expr_ref tmp1(m), tmp2(m); - obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); - for (; it != end; ++it) { - expr* pred = it->m_key; - datalog::rule const* r = it->m_value; - if (!r) continue; - find_predecessors(*r, m_predicates); - for (unsigned i = 0; i < m_predicates.size(); i++) { - func_decl* d = m_predicates[i]; - if (d == head) { - tmp1 = m.mk_implies(pred, fml); - pm.formula_n2o(tmp1, tmp2, i); - result.push_back(tmp2); - } - } - } - } - - void pred_transformer::initialize(decl2rel const& pts) { - m_initial_state = m.mk_false(); - m_transition = m.mk_true(); - init_rules(pts, m_initial_state, m_transition); - th_rewriter rw(m); - rw(m_transition); - rw(m_initial_state); - - m_solver.add_formula(m_transition); - m_solver.add_level_formula(m_initial_state, 0); - TRACE("pdr", - tout << "Initial state: " << mk_pp(m_initial_state, m) << "\n"; - tout << "Transition: " << mk_pp(m_transition, m) << "\n";); - SASSERT(is_app(m_initial_state)); - m_reachable.add_init(to_app(m_initial_state)); - } - - void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition) { - expr_ref_vector transitions(m); - ptr_vector tr_rules; - datalog::rule const* rule; - expr_ref_vector disj(m); - app_ref pred(m); - for (unsigned i = 0; i < rules().size(); ++i) { - init_rule(pts, *rules()[i], init, tr_rules, transitions); - } - switch(transitions.size()) { - case 0: - transition = m.mk_false(); - break; - case 1: - // create a dummy tag. - pred = m.mk_fresh_const(head()->get_name().str().c_str(), m.mk_bool_sort()); - rule = tr_rules[0]; - m_tag2rule.insert(pred, rule); - m_rule2tag.insert(rule, pred.get()); - transitions.push_back(pred); - transition = pm.mk_and(transitions); - break; - default: - for (unsigned i = 0; i < transitions.size(); ++i) { - pred = m.mk_fresh_const(head()->get_name().str().c_str(), m.mk_bool_sort()); - rule = tr_rules[i]; - m_tag2rule.insert(pred, rule); - m_rule2tag.insert(rule, pred); - disj.push_back(pred); - transitions[i] = m.mk_implies(pred, transitions[i].get()); - } - transitions.push_back(m.mk_or(disj.size(), disj.c_ptr())); - transition = pm.mk_and(transitions); - break; - } - } - - void pred_transformer::init_rule( - decl2rel const& pts, - datalog::rule const& rule, - expr_ref& init, - ptr_vector& rules, - expr_ref_vector& transitions) - { - // Predicates that are variable representatives. Other predicates at - // positions the variables occur are made equivalent with these. - expr_ref_vector conj(m); - app_ref_vector var_reprs(m); - ptr_vector aux_vars; - - unsigned ut_size = rule.get_uninterpreted_tail_size(); - unsigned t_size = rule.get_tail_size(); - SASSERT(ut_size <= t_size); - init_atom(pts, rule.get_head(), var_reprs, conj, UINT_MAX); - for (unsigned i = 0; i < ut_size; ++i) { - if (rule.is_neg_tail(i)) { - char const* msg = "PDR does not supported negated predicates in rule tails"; - IF_VERBOSE(0, verbose_stream() << msg << "\n";); - throw default_exception(msg); - } - init_atom(pts, rule.get_tail(i), var_reprs, conj, i); - } - for (unsigned i = ut_size; i < t_size; ++i) { - ground_free_vars(rule.get_tail(i), var_reprs, aux_vars); - } - SASSERT(check_filled(var_reprs)); - expr_ref_vector tail(m); - for (unsigned i = ut_size; i < t_size; ++i) { - tail.push_back(rule.get_tail(i)); - } - flatten_and(tail); - for (unsigned i = 0; i < tail.size(); ++i) { - expr_ref tmp(m); - var_subst vs(m, false); - vs(tail[i].get(), var_reprs.size(), (expr*const*)var_reprs.c_ptr(), tmp); - conj.push_back(tmp); - TRACE("pdr", tout << mk_pp(tail[i].get(), m) << "\n" << mk_pp(tmp, m) << "\n";); - if (!is_ground(tmp)) { - std::stringstream msg; - msg << "PDR cannot solve non-ground tails: " << tmp; - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - } - expr_ref fml = pm.mk_and(conj); - th_rewriter rw(m); - rw(fml); - if (ctx.is_dl() || ctx.is_utvpi()) { - blast_term_ite(fml); - } - TRACE("pdr", tout << mk_pp(fml, m) << "\n";); - SASSERT(is_ground(fml)); - if (m.is_false(fml)) { - // no-op. - } - else { - if (ut_size == 0) { - init = m.mk_or(init, fml); - } - transitions.push_back(fml); - m.inc_ref(fml); - m_rule2transition.insert(&rule, fml.get()); - rules.push_back(&rule); - } - m_rule2inst.insert(&rule, alloc(app_ref_vector, var_reprs)); - m_rule2vars.insert(&rule, aux_vars); - TRACE("pdr", - tout << rule.get_decl()->get_name() << "\n"; - for (unsigned i = 0; i < var_reprs.size(); ++i) { - tout << mk_pp(var_reprs[i].get(), m) << " "; - } - if (!var_reprs.empty()) tout << "\n";); - } - - bool pred_transformer::check_filled(app_ref_vector const& v) const { - for (unsigned i = 0; i < v.size(); ++i) { - if (!v[i]) return false; - } - return true; - } - - // create constants for free variables in tail. - void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars) { - expr_free_vars fv; - fv(e); - while (vars.size() < fv.size()) { - vars.push_back(0); - } - for (unsigned i = 0; i < fv.size(); ++i) { - if (fv[i] && !vars[i].get()) { - vars[i] = m.mk_fresh_const("aux", fv[i]); - aux_vars.push_back(vars[i].get()); - } - } - } - - // create names for variables used in relations. - void pred_transformer::init_atom( - decl2rel const& pts, - app * atom, - app_ref_vector& var_reprs, - expr_ref_vector& conj, - unsigned tail_idx - ) - { - unsigned arity = atom->get_num_args(); - func_decl* head = atom->get_decl(); - pred_transformer& pt = *pts.find(head); - for (unsigned i = 0; i < arity; i++) { - app_ref rep(m); - - if (tail_idx == UINT_MAX) { - rep = m.mk_const(pm.o2n(pt.sig(i), 0)); - } - else { - rep = m.mk_const(pm.o2o(pt.sig(i), 0, tail_idx)); - } - - expr * arg = atom->get_arg(i); - if (is_var(arg)) { - var * v = to_var(arg); - unsigned var_idx = v->get_idx(); - if (var_idx >= var_reprs.size()) { - var_reprs.resize(var_idx+1); - } - expr * repr = var_reprs[var_idx].get(); - if (repr) { - conj.push_back(m.mk_eq(rep, repr)); - } - else { - var_reprs[var_idx] = rep; - } - } - else { - SASSERT(is_app(arg)); - conj.push_back(m.mk_eq(rep, arg)); - } - } - } - - void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r) { - r.push_back(pm.get_background()); - r.push_back((lvl == 0)?initial_state():transition()); - for (unsigned i = 0; i < rules().size(); ++i) { - add_premises(pts, lvl, *rules()[i], r); - } - } - - void pred_transformer::close(expr* e) { - m_reachable.add_reachable(e); - } - - void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r) { - find_predecessors(rule, m_predicates); - for (unsigned i = 0; i < m_predicates.size(); ++i) { - expr_ref tmp(m); - func_decl* head = m_predicates[i]; - pred_transformer& pt = *pts.find(head); - expr_ref inv = pt.get_formulas(lvl, false); - if (!m.is_true(inv)) { - pm.formula_n2o(inv, tmp, i, true); - r.push_back(tmp); - } - } - } - - void pred_transformer::inherit_properties(pred_transformer& other) { - SASSERT(m_head == other.m_head); - obj_map::iterator it = other.m_prop2level.begin(); - obj_map::iterator end = other.m_prop2level.end(); - for (; it != end; ++it) { - IF_VERBOSE(2, verbose_stream() << "(pdr-inherit: " << mk_pp(it->m_key, m) << ")\n";); - add_property(it->m_key, it->m_value); - } - } - - // ---------------- - // model_node - - void model_node::set_closed() { - TRACE("pdr", tout << state() << "\n";); - pt().close(state()); - m_closed = true; - } - - void model_node::set_open() { - SASSERT(m_closed); - m_closed = false; - model_node* p = parent(); - while (p && p->is_closed()) { - p->m_closed = false; - p = p->parent(); - } - } - - void model_node::check_pre_closed() { - for (unsigned i = 0; i < children().size(); ++i) { - if (children()[i]->is_open()) return; - } - set_pre_closed(); - model_node* p = parent(); - while (p && p->is_1closed()) { - p->set_pre_closed(); - p = p->parent(); - } - } - - static bool is_ini(datalog::rule const& r) { - return r.get_uninterpreted_tail_size() == 0; - } - - datalog::rule* model_node::get_rule() { - if (m_rule) { - return const_cast(m_rule); - } - // only initial states are not set by the PDR search. - SASSERT(m_model.get()); - if (!m_model.get()) { - std::stringstream msg; - msg << "no model for node " << state(); - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - - datalog::rule const& rl1 = pt().find_rule(*m_model); - if (is_ini(rl1)) { - set_rule(&rl1); - return const_cast(m_rule); - } - ast_manager& m = pt().get_manager(); - // otherwise, the initial state is reachable. - ptr_vector const& rules = pt().rules(); - ptr_vector ini_rules; - expr_ref_vector tags(m); - expr_ref ini_tags(m), ini_state(m); - for (unsigned i = 0; i < rules.size(); ++i) { - datalog::rule* rl = rules[i]; - if (is_ini(*rl)) { - tags.push_back(pt().rule2tag(rl)); - } - } - SASSERT(!tags.empty()); - ini_tags = ::mk_or(tags); - ini_state = m.mk_and(ini_tags, pt().initial_state(), state()); - model_ref mdl; - pt().get_solver().set_model(&mdl); - TRACE("pdr", tout << ini_state << "\n";); - if (l_true != pt().get_solver().check_conjunction_as_assumptions(ini_state)) { - std::stringstream msg; - msg << "Unsatisfiable initial state: " << ini_state << "\n"; - display(msg, 2); - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - SASSERT(mdl.get()); - datalog::rule const& rl2 = pt().find_rule(*mdl); - SASSERT(is_ini(rl2)); - set_rule(&rl2); - pt().get_solver().set_model(0); - return const_cast(m_rule); - } - - - void model_node::mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding) { - ast_manager& m = pt().get_manager(); - expr_ref_vector conjs(m); - obj_map model; - flatten_and(state(), conjs); - for (unsigned i = 0; i < conjs.size(); ++i) { - expr* e = conjs[i].get(), *e1, *e2; - if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { - if (m.is_value(e2)) { - model.insert(e1, e2); - } - else if (m.is_value(e1)) { - model.insert(e2, e1); - } - } - else if (m.is_not(e, e1)) { - model.insert(e1, m.mk_false()); - } - else { - model.insert(e, m.mk_true()); - } - } - r0 = get_rule(); - app_ref_vector& inst = pt().get_inst(r0); - TRACE("pdr", tout << state() << " instance: " << inst.size() << "\n";); - for (unsigned i = 0; i < inst.size(); ++i) { - expr* v; - if (model.find(inst[i].get(), v)) { - binding.push_back(v); - } - else { - binding.push_back(m.mk_var(i, m.get_sort(inst[i].get()))); - } - } - r1 = r0; - if (!inst.empty()) { - r1.get_manager().substitute(r1, binding.size(), binding.c_ptr()); - } - } - - - - std::ostream& model_node::display(std::ostream& out, unsigned indent) { - for (unsigned i = 0; i < indent; ++i) out << " "; - out << m_level << " " << m_pt.head()->get_name() << " " << (m_closed?"closed":"open") << "\n"; - for (unsigned i = 0; i < indent; ++i) out << " "; - out << " " << mk_pp(m_state, m_state.get_manager(), indent) << " " << m_state->get_id() << "\n"; - for (unsigned i = 0; i < children().size(); ++i) { - children()[i]->display(out, indent + 1); - } - return out; - } - - unsigned model_node::index() const { - model_node* p = parent(); - if (!p) return 0; - for (unsigned i = 0; i < p->children().size(); ++i) { - if (this == p->children()[i]) return i; - } - UNREACHABLE(); - return 0; - } - - - void model_node::dequeue(model_node*& root) { - TRACE("pdr", tout << this << " root: " << root << " " << state() << "\n";); - if (!m_next && !m_prev) return; - SASSERT(m_next); - SASSERT(m_prev); - SASSERT(children().empty()); - if (this == m_next) { - SASSERT(m_prev == this); - if (root == this) { - root = 0; - } - } - else { - m_next->m_prev = m_prev; - m_prev->m_next = m_next; - if (this == root) { - root = m_next; - } - } - TRACE("pdr", tout << "new root: " << root << "\n";); - m_prev = 0; - m_next = 0; - } - - - void model_node::enqueue(model_node* n) { - TRACE("pdr", tout << n << " " << n->state() << "\n";); - SASSERT(!n->m_next); - SASSERT(!n->m_prev); - if (this == n) { - m_next = n; - m_prev = n; - } - else { - n->m_next = m_next; - m_next->m_prev = n; - m_next = n; - n->m_prev = this; - } - } - // ---------------- - // model_search - - /** - \brief Dequeue the next goal. - */ - model_node* model_search::next() { - if (!m_goal) { - return 0; - } - else { - model_node* result = m_goal; - result->dequeue(m_goal); - return result; - } - } - - - void model_search::add_leaf(model_node& n) { - SASSERT(n.children().empty()); - model_nodes ns; - model_nodes& nodes = cache(n).insert_if_not_there2(n.state(), ns)->get_data().m_value; - if (nodes.contains(&n)) { - return; - } - nodes.push_back(&n); - TRACE("pdr_verbose", tout << "add: " << n.level() << ": " << &n << " " << n.state() << "\n";); - if (nodes.size() == 1) { - set_leaf(n); - } - else { - n.set_pre_closed(); - } - } - - void model_search::set_leaf(model_node& n) { - erase_children(n, true); - SASSERT(n.is_open()); - enqueue_leaf(&n); - } - - void model_search::enqueue_leaf(model_node* n) { - TRACE("pdr_verbose", tout << "node: " << n << " " << n->state() << " goal: " << m_goal << "\n";); - SASSERT(n->is_open()); - if (!m_goal) { - m_goal = n; - m_goal->enqueue(n); - } - else if (m_bfs) { - m_goal->enqueue(n); - } - else { - m_goal->next()->enqueue(n); - } - } - - void model_search::set_root(model_node* root) { - reset(); - m_root = root; - SASSERT(cache(*root).empty()); - cache(*root).insert(root->state(), 1); - set_leaf(*root); - } - - obj_map >& model_search::cache(model_node const& n) { - unsigned l = n.orig_level(); - if (l >= m_cache.size()) { - m_cache.resize(l + 1); - } - return m_cache[l]; - } - - void model_search::erase_children(model_node& n, bool backtrack) { - ptr_vector todo, nodes; - todo.append(n.children()); - remove_goal(n); - n.reset(); - while (!todo.empty()) { - model_node* m = todo.back(); - todo.pop_back(); - nodes.push_back(m); - todo.append(m->children()); - remove_node(*m, backtrack); - } - std::for_each(nodes.begin(), nodes.end(), delete_proc()); - } - - void model_search::remove_node(model_node& n, bool backtrack) { - TRACE("pdr_verbose", tout << "remove: " << n.level() << ": " << &n << " " << n.state() << "\n";); - model_nodes& nodes = cache(n).find(n.state()); - nodes.erase(&n); - remove_goal(n); - // TBD: siblings would also fail if n is not a goal. - if (!nodes.empty() && backtrack && nodes[0]->children().empty() && nodes[0]->is_closed()) { - TRACE("pdr_verbose", for (unsigned i = 0; i < nodes.size(); ++i) n.display(tout << &n << "\n", 2);); - model_node* n1 = nodes[0]; - n1->set_open(); - enqueue_leaf(n1); - } - - if (!nodes.empty() && n.get_model_ptr() && backtrack) { - model_ref mr(n.get_model_ptr()); - nodes[0]->set_model(mr); - } - if (nodes.empty()) { - cache(n).remove(n.state()); - } - } - - void model_search::remove_goal(model_node& n) { - n.dequeue(m_goal); - } - - void model_search::well_formed() { - // each open leaf is in the set of m_goal. - ptr_vector nodes; - nodes.push_back(&get_root()); - for (unsigned i = 0; i < nodes.size(); ++i) { - model_node* n = nodes[i]; - if (!n->children().empty()) { - nodes.append(n->children()); - } - else if (n->is_open() && !n->is_goal() && n->parent()) { - TRACE("pdr", n->display(tout << "node " << n << " not found among leaves\n", 0); display(tout);); - UNREACHABLE(); - return; - } - } - if (m_goal) { - model_node* n = m_goal; - do { - if (!n->is_open() || !n->children().empty()) { - TRACE("pdr", n->display(tout << "invalid leaf\n", 0); - display(tout);); - UNREACHABLE(); - return; - } - n = n->next(); - } - while (m_goal != n); - } - - // each state appears in at most one goal per level. - bool found = true; - for (unsigned l = 0; m_goal && found; ++l) { - found = false; - obj_hashtable open_states; - model_node* n = m_goal; - do { - if (n->level() == l) { - found = true; - if (n->is_open()) { - if (open_states.contains(n->state())) { - TRACE("pdr", n->display(tout << "repeated leaf\n", 0); display(tout);); - UNREACHABLE(); - } - open_states.insert(n->state()); - } - } - n = n->next(); - } - while (m_goal != n); - } - // a node is open if and only if it contains an - // open child which is a goal. - for (unsigned i = 0; i < nodes.size(); ++i) { - model_node* n = nodes[i]; - if (!n->children().empty() && n->parent()) { - found = false; - for (unsigned j = 0; !found && j < n->children().size(); ++j) { - found = n->children()[j]->is_open(); - } - if (n->is_open() != found) { - TRACE("pdr", n->display(tout << "node in inconsistent state\n", 0); display(tout);); - UNREACHABLE(); - } - } - } - } - - unsigned model_search::num_goals() const { - model_node* n = m_goal; - unsigned num = 0; - if (n) { - do { - ++num; - n = n->next(); - } - while (n != m_goal); - } - return num; - } - - std::ostream& model_search::display(std::ostream& out) const { - if (m_root) { - m_root->display(out, 0); - } - out << "goals " << num_goals() << "\n"; - model_node* n = m_goal; - if (n) { - do { - n->display(out, 1); - n = n->next(); - } - while (n != m_goal); - } - return out; - } - - /** - \brief Ensure that all nodes in the tree have associated models. - get_trace and get_proof_trace rely on models to extract rules. - */ - void model_search::update_models() { - obj_map models; - obj_map rules; - ptr_vector todo; - todo.push_back(m_root); - while (!todo.empty()) { - model_node* n = todo.back(); - if (n->get_model_ptr()) { - models.insert(n->state(), n->get_model_ptr()); - rules.insert(n->state(), n->get_rule()); - } - todo.pop_back(); - todo.append(n->children().size(), n->children().c_ptr()); - } - - todo.push_back(m_root); - while (!todo.empty()) { - model_node* n = todo.back(); - model* md = 0; - if (!n->get_model_ptr()) { - if (models.find(n->state(), md)) { - TRACE("pdr", tout << n->state() << "\n";); - model_ref mr(md); - n->set_model(mr); - datalog::rule const* rule = rules.find(n->state()); - n->set_rule(rule); - } - else { - TRACE("pdr", tout << "no model for " << n->state() << "\n";); - IF_VERBOSE(1, n->display(verbose_stream() << "no model:\n", 0); - verbose_stream() << n->state() << "\n";); - } - } - else { - TRACE("pdr", tout << n->state() << "\n";); - } - todo.pop_back(); - todo.append(n->children().size(), n->children().c_ptr()); - } - } - - /** - Extract trace comprising of constraints - and predicates that are satisfied from facts to the query. - The resulting trace - */ - expr_ref model_search::get_trace(context const& ctx) { - pred_transformer& pt = get_root().pt(); - ast_manager& m = pt.get_manager(); - manager& pm = pt.get_pdr_manager(); - datalog::context& dctx = ctx.get_context(); - datalog::rule_manager& rm = dctx.get_rule_manager(); - expr_ref_vector constraints(m), predicates(m); - expr_ref tmp(m); - ptr_vector children; - unsigned deltas[2]; - datalog::rule_ref rule(rm), r0(rm); - model_node* n = m_root; - datalog::rule_counter& vc = rm.get_counter(); - substitution subst(m); - unifier unif(m); - rule = n->get_rule(); - unsigned max_var = vc.get_max_rule_var(*rule); - predicates.push_back(rule->get_head()); - children.push_back(n); - bool first = true; - update_models(); - while (!children.empty()) { - SASSERT(children.size() == predicates.size()); - expr_ref_vector binding(m); - n = children.back(); - children.pop_back(); - TRACE("pdr", n->display(tout, 0);); - n->mk_instantiate(r0, rule, binding); - - max_var = std::max(max_var, vc.get_max_rule_var(*rule)); - subst.reset(); - subst.reserve(2, max_var+1); - deltas[0] = 0; - deltas[1] = max_var+1; - - VERIFY(unif(predicates.back(), rule->get_head(), subst)); - for (unsigned i = 0; i < constraints.size(); ++i) { - subst.apply(2, deltas, expr_offset(constraints[i].get(), 0), tmp); - dctx.get_rewriter()(tmp); - constraints[i] = tmp; - } - for (unsigned i = 0; i < predicates.size(); ++i) { - subst.apply(2, deltas, expr_offset(predicates[i].get(), 0), tmp); - predicates[i] = tmp; - } - if (!first) { - constraints.push_back(predicates.back()); - } - first = false; - predicates.pop_back(); - for (unsigned i = rule->get_uninterpreted_tail_size(); i < rule->get_tail_size(); ++i) { - subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); - constraints.push_back(tmp); - } - for (unsigned i = 0; i < constraints.size(); ++i) { - max_var = std::max(vc.get_max_var(constraints[i].get()), max_var); - } - if (n->children().empty()) { - // nodes whose states are repeated - // in the search tree do not have children. - continue; - } - - SASSERT(n->children().size() == rule->get_uninterpreted_tail_size()); - - for (unsigned i = 0; i < rule->get_uninterpreted_tail_size(); ++i) { - subst.apply(2, deltas, expr_offset(rule->get_tail(i), 1), tmp); - predicates.push_back(tmp); - } - for (unsigned i = 0; i < predicates.size(); ++i) { - max_var = std::max(vc.get_max_var(predicates[i].get()), max_var); - } - - children.append(n->children()); - } - expr_safe_replace repl(m); - for (unsigned i = 0; i < constraints.size(); ++i) { - expr* e = constraints[i].get(), *e1, *e2; - if (m.is_eq(e, e1, e2) && is_var(e1) && is_ground(e2)) { - repl.insert(e1, e2); - } - else if (m.is_eq(e, e1, e2) && is_var(e2) && is_ground(e1)) { - repl.insert(e2, e1); - } - } - expr_ref_vector result(m); - for (unsigned i = 0; i < constraints.size(); ++i) { - expr_ref tmp(m); - tmp = constraints[i].get(); - repl(tmp); - dctx.get_rewriter()(tmp); - if (!m.is_true(tmp)) { - result.push_back(tmp); - } - } - return pm.mk_and(result); - } - - proof_ref model_search::get_proof_trace(context const& ctx) { - pred_transformer& pt = get_root().pt(); - ast_manager& m = pt.get_manager(); - datalog::context& dctx = ctx.get_context(); - datalog::rule_manager& rm = dctx.get_rule_manager(); - datalog::rule_unifier unif(dctx); - datalog::dl_decl_util util(m); - datalog::rule_ref r0(rm), r1(rm); - obj_map cache; - obj_map rules; - ptr_vector todo; - proof_ref_vector trail(m); - datalog::rule_ref_vector rules_trail(rm); - proof* pr = 0; - unif.set_normalize(true); - todo.push_back(m_root); - update_models(); - while (!todo.empty()) { - model_node* n = todo.back(); - TRACE("pdr", n->display(tout, 0);); - if (cache.find(n->state(), pr)) { - todo.pop_back(); - continue; - } - ptr_vector pfs; - ptr_vector rls; - ptr_vector const& chs = n->children(); - pfs.push_back(0); - rls.push_back(0); - for (unsigned i = 0; i < chs.size(); ++i) { - if (cache.find(chs[i]->state(), pr)) { - pfs.push_back(pr); - rls.push_back(rules.find(chs[i]->state())); - } - else { - todo.push_back(chs[i]); - } - } - if (pfs.size() != 1 + chs.size()) { - continue; - } - proof_ref rl(m); - expr_ref_vector binding(m); - n->mk_instantiate(r0, r1, binding); - proof_ref p1(m), p2(m); - p1 = r0->get_proof(); - IF_VERBOSE(0, if (!p1) r0->display(dctx, verbose_stream());); - SASSERT(p1); - pfs[0] = p1; - rls[0] = r1; - TRACE("pdr", - tout << "root: " << mk_pp(p1.get(), m) << "\n"; - for (unsigned i = 0; i < binding.size(); ++i) { - tout << mk_pp(binding[i].get(), m) << "\n"; - } - for (unsigned i = 1; i < pfs.size(); ++i) { - tout << mk_pp(pfs[i], m) << "\n"; - } - ); - datalog::rule_ref reduced_rule(rm), r3(rm); - reduced_rule = rls[0]; - // check if binding is identity. - bool binding_is_id = true; - for (unsigned i = 0; binding_is_id && i < binding.size(); ++i) { - expr* v = binding[i].get(); - binding_is_id = is_var(v) && to_var(v)->get_idx() == i; - } - if (rls.size() > 1 || !binding_is_id) { - expr_ref tmp(m); - vector substs; - svector > positions; - substs.push_back(binding); // TODO base substitution. - for (unsigned i = 1; i < rls.size(); ++i) { - datalog::rule& src = *rls[i]; - bool unified = unif.unify_rules(*reduced_rule, 0, src); - if (!unified) { - IF_VERBOSE(0, - verbose_stream() << "Could not unify rules: "; - reduced_rule->display(dctx, verbose_stream()); - src.display(dctx, verbose_stream());); - } - expr_ref_vector sub1 = unif.get_rule_subst(*reduced_rule, true); - TRACE("pdr", - for (unsigned k = 0; k < sub1.size(); ++k) { - tout << mk_pp(sub1[k].get(), m) << " "; - } - tout << "\n"; - ); - - for (unsigned j = 0; j < substs.size(); ++j) { - for (unsigned k = 0; k < substs[j].size(); ++k) { - var_subst(m, false)(substs[j][k].get(), sub1.size(), sub1.c_ptr(), tmp); - substs[j][k] = tmp; - } - while (substs[j].size() < sub1.size()) { - substs[j].push_back(sub1[substs[j].size()].get()); - } - } - - positions.push_back(std::make_pair(i,0)); - substs.push_back(unif.get_rule_subst(src, false)); - VERIFY(unif.apply(*reduced_rule.get(), 0, src, r3)); - reduced_rule = r3; - } - - expr_ref fml_concl(m); - rm.to_formula(*reduced_rule.get(), fml_concl); - p1 = m.mk_hyper_resolve(pfs.size(), pfs.c_ptr(), fml_concl, positions, substs); - - } - cache.insert(n->state(), p1); - rules.insert(n->state(), reduced_rule); - trail.push_back(p1); - rules_trail.push_back(reduced_rule); - todo.pop_back(); - } - return proof_ref(cache.find(m_root->state()), m); - } - - model_search::~model_search() { - TRACE("pdr", tout << "\n";); - reset(); - } - - void model_search::reset() { - if (m_root) { - erase_children(*m_root, false); - remove_node(*m_root, false); - dealloc(m_root); - m_root = 0; - } - m_cache.reset(); - } - - void model_search::backtrack_level(bool uses_level, model_node& n) { - SASSERT(m_root); - if (uses_level && m_root->level() > n.level()) { - IF_VERBOSE(2, verbose_stream() << "Increase level " << n.level() << "\n";); - n.increase_level(); - enqueue_leaf(&n); - } - else { - model_node* p = n.parent(); - if (p) { - set_leaf(*p); - } - } - DEBUG_CODE(well_formed();); - } - - // ---------------- - // context - - context::context( - smt_params& fparams, - fixedpoint_params const& params, - ast_manager& m - ) - : m_fparams(fparams), - m_params(params), - m(m), - m_context(0), - m_pm(m_fparams, params.pdr_max_num_contexts(), m), - m_query_pred(m), - m_query(0), - m_search(m_params.pdr_bfs_model_search()), - m_last_result(l_undef), - m_inductive_lvl(0), - m_expanded_lvl(0) - { - } - - context::~context() { - reset_core_generalizers(); - reset(); - } - - void context::reset(decl2rel& rels) { - decl2rel::iterator it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } - rels.reset(); - } - - void context::reset(bool full) { - TRACE("pdr", tout << "reset\n";); - reset(m_rels); - if (full) { - reset(m_rels_tmp); - } - m_search.reset(); - m_query = 0; - m_last_result = l_undef; - m_inductive_lvl = 0; - } - - void context::init_rules(datalog::rule_set& rules, decl2rel& rels) { - m_context = &rules.get_context(); - // Allocate collection of predicate transformers - datalog::rule_set::decl2rules::iterator dit = rules.begin_grouped_rules(), dend = rules.end_grouped_rules(); - decl2rel::obj_map_entry* e; - for (; dit != dend; ++dit) { - func_decl* pred = dit->m_key; - TRACE("pdr", tout << mk_pp(pred, m) << "\n";); - SASSERT(!rels.contains(pred)); - e = rels.insert_if_not_there2(pred, alloc(pred_transformer, *this, get_pdr_manager(), pred)); - datalog::rule_vector const& pred_rules = *dit->m_value; - for (unsigned i = 0; i < pred_rules.size(); ++i) { - e->get_data().m_value->add_rule(pred_rules[i]); - } - } - TRACE("pdr", tout << "adding rules\n";); - datalog::rule_set::iterator rit = rules.begin(), rend = rules.end(); - for (; rit != rend; ++rit) { - datalog::rule* r = *rit; - pred_transformer* pt; - unsigned utz = r->get_uninterpreted_tail_size(); - for (unsigned i = 0; i < utz; ++i) { - func_decl* pred = r->get_decl(i); - if (!rels.find(pred, pt)) { - pt = alloc(pred_transformer, *this, get_pdr_manager(), pred); - rels.insert(pred, pt); - } - } - } - // Initialize use list dependencies - TRACE("pdr", tout << "initialize use list dependencies\n";); - decl2rel::iterator it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - func_decl* pred = it->m_key; - pred_transformer* pt = it->m_value, *pt_user; - obj_hashtable const& deps = rules.get_dependencies().get_deps(pred); - obj_hashtable::iterator itf = deps.begin(), endf = deps.end(); - for (; itf != endf; ++itf) { - TRACE("pdr", tout << mk_pp(pred, m) << " " << mk_pp(*itf, m) << "\n";); - pt_user = rels.find(*itf); - pt_user->add_use(pt); - } - } - - TRACE("pdr", tout << "initialize predicate transformers\n";); - // Initialize the predicate transformers. - it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - SASSERT(it->m_value); - pred_transformer& rel = *it->m_value; - rel.initialize(rels); - TRACE("pdr", rel.display(tout); ); - } - } - - void context::update_rules(datalog::rule_set& rules) { - TRACE("pdr", tout << "update rules\n";); - reset(m_rels_tmp); - init_core_generalizers(rules); - init_rules(rules, m_rels_tmp); - decl2rel::iterator it = m_rels_tmp.begin(), end = m_rels_tmp.end(); - for (; it != end; ++it) { - pred_transformer* pt = 0; - if (m_rels.find(it->m_key, pt)) { - it->m_value->inherit_properties(*pt); - } - } - reset(false); - it = m_rels_tmp.begin(), end = m_rels_tmp.end(); - for (; it != end; ++it) { - m_rels.insert(it->m_key, it->m_value); - } - m_rels_tmp.reset(); - TRACE("pdr", tout << "done update rules\n";); - } - - unsigned context::get_num_levels(func_decl* p) { - pred_transformer* pt = 0; - if (m_rels.find(p, pt)) { - return pt->get_num_levels(); - } - else { - IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); - return 0; - } - } - - expr_ref context::get_cover_delta(int level, func_decl* p_orig, func_decl* p) { - pred_transformer* pt = 0; - 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";); - return expr_ref(m.mk_true(), m); - } - } - - void context::add_cover(int level, func_decl* p, expr* property) { - pred_transformer* pt = 0; - if (!m_rels.find(p, pt)) { - pt = alloc(pred_transformer, *this, get_pdr_manager(), p); - m_rels.insert(p, pt); - 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); - } - - class context::classifier_proc { - ast_manager& m; - arith_util a; - bool m_is_bool; - bool m_is_bool_arith; - bool m_has_arith; - bool m_is_dl; - bool m_is_utvpi; - public: - classifier_proc(ast_manager& m, datalog::rule_set& rules): - m(m), a(m), m_is_bool(true), m_is_bool_arith(true), m_has_arith(false), m_is_dl(false), m_is_utvpi(false) { - classify(rules); - } - void operator()(expr* e) { - if (m_is_bool) { - if (!m.is_bool(e)) { - m_is_bool = false; - } - else if (is_var(e)) { - // no-op. - } - else if (!is_app(e)) { - m_is_bool = false; - } - else if (to_app(e)->get_num_args() > 0 && - to_app(e)->get_family_id() != m.get_basic_family_id()) { - m_is_bool = false; - } - } - - m_has_arith = m_has_arith || a.is_int_real(e); - - if (m_is_bool_arith) { - if (!m.is_bool(e) && !a.is_int_real(e)) { - m_is_bool_arith = false; - } - else if (is_var(e)) { - // no-op - } - else if (!is_app(e)) { - m_is_bool_arith = false; - } - else if (to_app(e)->get_num_args() > 0 && - to_app(e)->get_family_id() != m.get_basic_family_id() && - to_app(e)->get_family_id() != a.get_family_id()) { - m_is_bool_arith = false; - } - } - } - - bool is_bool() const { return m_is_bool; } - - bool is_bool_arith() const { return m_is_bool_arith; } - - bool is_dl() const { return m_is_dl; } - - bool is_utvpi() const { return m_is_utvpi; } - - private: - - void classify(datalog::rule_set& rules) { - expr_fast_mark1 mark; - datalog::rule_set::iterator it = rules.begin(), end = rules.end(); - for (; it != end; ++it) { - datalog::rule& r = *(*it); - classify_pred(mark, r.get_head()); - unsigned utsz = r.get_uninterpreted_tail_size(); - for (unsigned i = 0; i < utsz; ++i) { - classify_pred(mark, r.get_tail(i)); - } - for (unsigned i = utsz; i < r.get_tail_size(); ++i) { - quick_for_each_expr(*this, mark, r.get_tail(i)); - } - } - mark.reset(); - - m_is_dl = false; - m_is_utvpi = false; - if (m_has_arith) { - ptr_vector forms; - for (it = rules.begin(); it != end; ++it) { - datalog::rule& r = *(*it); - unsigned utsz = r.get_uninterpreted_tail_size(); - forms.push_back(r.get_head()); - for (unsigned i = utsz; i < r.get_tail_size(); ++i) { - forms.push_back(r.get_tail(i)); - } - } - m_is_dl = is_difference_logic(m, forms.size(), forms.c_ptr()); - m_is_utvpi = m_is_dl || is_utvpi_logic(m, forms.size(), forms.c_ptr()); - } - } - - void classify_pred(expr_fast_mark1& mark, app* pred) { - for (unsigned i = 0; i < pred->get_num_args(); ++i) { - quick_for_each_expr(*this, mark, pred->get_arg(i)); - } - } - }; - - void context::validate_proof() { - std::stringstream msg; - proof_ref pr = get_proof(); - proof_checker checker(m); - expr_ref_vector side_conditions(m); - bool ok = checker.check(pr, side_conditions); - if (!ok) { - msg << "proof validation failed"; - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - for (unsigned i = 0; i < side_conditions.size(); ++i) { - expr* cond = side_conditions[i].get(); - expr_ref tmp(m); - - tmp = m.mk_not(cond); - IF_VERBOSE(2, verbose_stream() << "checking side-condition:\n" << mk_pp(cond, m) << "\n";); - smt::kernel solver(m, get_fparams()); - solver.assert_expr(tmp); - lbool res = solver.check(); - if (res != l_false) { - msg << "rule validation failed when checking: " << mk_pp(cond, m); - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - } - } - - void context::validate_search() { - expr_ref tr = m_search.get_trace(*this); - TRACE("pdr", tout << tr << "\n";); - smt::kernel solver(m, get_fparams()); - solver.assert_expr(tr); - lbool res = solver.check(); - if (res != l_true) { - std::stringstream msg; - msg << "rule validation failed when checking: " << tr; - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - } - - void context::validate_model() { - std::stringstream msg; - expr_ref_vector refs(m); - expr_ref tmp(m); - model_ref model; - vector rs; - model_converter_ref mc; - get_level_property(m_inductive_lvl, refs, rs); - inductive_property ex(m, mc, rs); - ex.to_model(model); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - var_subst vs(m, false); - expr_free_vars fv; - for (; it != end; ++it) { - ptr_vector const& rules = it->m_value->rules(); - for (unsigned i = 0; i < rules.size(); ++i) { - datalog::rule& r = *rules[i]; - model->eval(r.get_head(), tmp); - expr_ref_vector fmls(m); - fmls.push_back(m.mk_not(tmp)); - unsigned utsz = r.get_uninterpreted_tail_size(); - unsigned tsz = r.get_tail_size(); - for (unsigned j = 0; j < utsz; ++j) { - model->eval(r.get_tail(j), tmp); - fmls.push_back(tmp); - } - for (unsigned j = utsz; j < tsz; ++j) { - fmls.push_back(r.get_tail(j)); - } - tmp = m.mk_and(fmls.size(), fmls.c_ptr()); - svector names; - fv(tmp); - fv.set_default_sort(m.mk_bool_sort()); - for (unsigned i = 0; i < fv.size(); ++i) { - names.push_back(symbol(i)); - } - fv.reverse(); - if (!fv.empty()) { - tmp = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), tmp); - } - smt::kernel solver(m, get_fparams()); - solver.assert_expr(tmp); - lbool res = solver.check(); - TRACE("pdr", tout << tmp << " " << res << "\n";); - if (res != l_false) { - msg << "rule validation failed when checking: " << mk_pp(tmp, m); - IF_VERBOSE(0, verbose_stream() << msg.str() << "\n";); - throw default_exception(msg.str()); - } - } - } - } - - void context::validate() { - if (!m_params.pdr_validate_result()) { - return; - } - switch(m_last_result) { - case l_true: - if (m_params.generate_proof_trace()) { - validate_proof(); - } - validate_search(); - break; - case l_false: - validate_model(); - break; - default: - break; - } - } - - void context::reset_core_generalizers() { - std::for_each(m_core_generalizers.begin(), m_core_generalizers.end(), delete_proc()); - m_core_generalizers.reset(); - } - - void context::init_core_generalizers(datalog::rule_set& rules) { - reset_core_generalizers(); - classifier_proc classify(m, rules); - bool use_mc = m_params.pdr_use_multicore_generalizer(); - if (use_mc) { - m_core_generalizers.push_back(alloc(core_multi_generalizer, *this, 0)); - } - if (!classify.is_bool()) { - m.toggle_proof_mode(PGM_ENABLED); - m_fparams.m_arith_bound_prop = BP_NONE; - m_fparams.m_arith_auto_config_simplex = true; - m_fparams.m_arith_propagate_eqs = false; - m_fparams.m_arith_eager_eq_axioms = false; - if (m_params.pdr_utvpi() && - !m_params.pdr_use_convex_closure_generalizer() && - !m_params.pdr_use_convex_interior_generalizer()) { - if (classify.is_dl()) { - m_fparams.m_arith_mode = AS_DIFF_LOGIC; - m_fparams.m_arith_eq2ineq = true; - } - else if (classify.is_utvpi()) { - IF_VERBOSE(1, verbose_stream() << "UTVPI\n";); - m_fparams.m_arith_mode = AS_UTVPI; - m_fparams.m_arith_eq2ineq = true; - } - else { - m_fparams.m_arith_mode = AS_ARITH; - m_fparams.m_arith_eq2ineq = false; - } - } - } - if (m_params.pdr_use_convex_closure_generalizer()) { - m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, true)); - } - if (m_params.pdr_use_convex_interior_generalizer()) { - m_core_generalizers.push_back(alloc(core_convex_hull_generalizer, *this, false)); - } - if (!use_mc && m_params.pdr_use_inductive_generalizer()) { - m_core_generalizers.push_back(alloc(core_bool_inductive_generalizer, *this, 0)); - } - if (m_params.pdr_inductive_reachability_check()) { - m_core_generalizers.push_back(alloc(core_induction_generalizer, *this)); - } - if (m_params.pdr_use_arith_inductive_generalizer()) { - m_core_generalizers.push_back(alloc(core_arith_inductive_generalizer, *this)); - } - - } - - void context::get_level_property(unsigned lvl, expr_ref_vector& res, vector& rs) { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer* r = it->m_value; - if (r->head() == m_query_pred) { - continue; - } - expr_ref conj = r->get_formulas(lvl, false); - m_pm.formula_n2o(0, false, conj); - res.push_back(conj); - ptr_vector sig(r->head()->get_arity(), r->sig()); - rs.push_back(relation_info(m, r->head(), sig, conj)); - } - } - - void context::simplify_formulas() { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer* r = it->m_value; - r->simplify_formulas(); - } - } - - lbool context::solve() { - TRACE("pdr", tout << "solve\n";); - m_last_result = l_undef; - try { - solve_impl(); - UNREACHABLE(); - } - catch (model_exception) { - IF_VERBOSE(1, verbose_stream() << "\n"; m_search.display(verbose_stream());); - m_last_result = l_true; - validate(); - - IF_VERBOSE(1, - if (m_params.print_boogie_certificate()) { - display_certificate(verbose_stream()); - }); - - return l_true; - } - catch (inductive_exception) { - simplify_formulas(); - m_last_result = l_false; - TRACE("pdr", display_certificate(tout);); - IF_VERBOSE(1, { - expr_ref_vector refs(m); - vector rs; - get_level_property(m_inductive_lvl, refs, rs); - model_converter_ref mc; - inductive_property ex(m, mc, rs); - verbose_stream() << ex.to_string(); - }); - - // upgrade invariants that are known to be inductive. - decl2rel::iterator it = m_rels.begin (), end = m_rels.end (); - for (; m_inductive_lvl > 0 && it != end; ++it) { - if (it->m_value->head() != m_query_pred) { - it->m_value->propagate_to_infinity (m_inductive_lvl); - } - } - validate(); - return l_false; - } - catch (unknown_exception) { - return l_undef; - } - UNREACHABLE(); - return l_undef; - } - - void context::checkpoint() { - if (m.canceled()) { - throw default_exception(Z3_CANCELED_MSG); - } - } - - /** - \brief retrieve answer. - */ - expr_ref context::get_answer() { - switch(m_last_result) { - case l_true: return mk_sat_answer(); - case l_false: return mk_unsat_answer(); - default: return expr_ref(m.mk_true(), m); - } - } - - model_ref context::get_model() { - SASSERT(m_last_result == l_false); - expr_ref_vector refs(m); - vector rs; - model_ref md; - get_level_property(m_inductive_lvl, refs, rs); - inductive_property ex(m, m_mc, rs); - ex.to_model(md); - return md; - } - - proof_ref context::get_proof() const { - scoped_proof _sc(m); - proof_ref proof(m); - SASSERT(m_last_result == l_true); - proof = m_search.get_proof_trace(*this); - TRACE("pdr", tout << "PDR trace: " << proof << "\n";); - apply(m, m_pc.get(), proof); - TRACE("pdr", tout << "PDR trace: " << proof << "\n";); - // proof_utils::push_instantiations_up(proof); - // TRACE("pdr", tout << "PDR up: " << proof << "\n";); - return proof; - } - - - /** - \brief Retrieve satisfying assignment with explanation. - */ - expr_ref context::mk_sat_answer() const { - if (m_params.generate_proof_trace()) { - proof_ref pr = get_proof(); - return expr_ref(pr.get(), m); - } - return m_search.get_trace(*this); - } - - expr_ref context::mk_unsat_answer() { - expr_ref_vector refs(m); - vector rs; - get_level_property(m_inductive_lvl, refs, rs); - inductive_property ex(m, const_cast(m_mc), rs); - return ex.to_expr(); - } - - void context::solve_impl() { - if (!m_rels.find(m_query_pred, m_query)) { - throw inductive_exception(); - } - unsigned lvl = 0; - bool reachable; - while (true) { - checkpoint(); - m_expanded_lvl = lvl; - reachable = check_reachability(lvl); - if (reachable) { - throw model_exception(); - } - if (lvl != 0) { - propagate(lvl); - } - lvl++; - m_stats.m_max_depth = std::max(m_stats.m_max_depth, lvl); - IF_VERBOSE(1,verbose_stream() << "Entering level "<level() << "\n";); - checkpoint(); - expand_node(*node); - } - return root->is_closed(); - } - - void context::close_node(model_node& n) { - n.set_closed(); - model_node* p = n.parent(); - while (p && p->is_1closed()) { - p->set_closed(); - p = p->parent(); - } - } - - - void context::expand_node(model_node& n) { - SASSERT(n.is_open()); - expr_ref_vector cube(m); - - if (n.level() < m_expanded_lvl) { - m_expanded_lvl = n.level(); - } - - pred_transformer::scoped_farkas sf (n.pt(), m_params.pdr_farkas()); - if (n.pt().is_reachable(n.state())) { - TRACE("pdr", tout << "reachable\n";); - close_node(n); - } - else { - bool uses_level = true; - switch (expand_state(n, cube, uses_level)) { - case l_true: - if (n.level() == 0) { - TRACE("pdr", n.display(tout << "reachable at level 0\n", 0);); - close_node(n); - } - else { - TRACE("pdr", n.display(tout, 0);); - create_children(n); - } - break; - case l_false: { - core_generalizer::cores cores; - cores.push_back(std::make_pair(cube, uses_level)); - TRACE("pdr", tout << "cube:\n"; - for (unsigned j = 0; j < cube.size(); ++j) tout << mk_pp(cube[j].get(), m) << "\n";); - for (unsigned i = 0; !cores.empty() && i < m_core_generalizers.size(); ++i) { - core_generalizer::cores new_cores; - for (unsigned j = 0; j < cores.size(); ++j) { - (*m_core_generalizers[i])(n, cores[j].first, cores[j].second, new_cores); - } - cores.reset(); - cores.append(new_cores); - } - bool found_invariant = false; - for (unsigned i = 0; i < cores.size(); ++i) { - expr_ref_vector const& core = cores[i].first; - uses_level = cores[i].second; - found_invariant = !uses_level || found_invariant; - expr_ref ncore(m_pm.mk_not_and(core), m); - TRACE("pdr", tout << "invariant state: " << (uses_level?"":"(inductive) ") << mk_pp(ncore, m) << "\n";); - n.pt().add_property(ncore, uses_level?n.level():infty_level); - } - CASSERT("pdr",n.level() == 0 || check_invariant(n.level()-1)); - m_search.backtrack_level(!found_invariant && m_params.pdr_flexible_trace(), n); - break; - } - case l_undef: { - TRACE("pdr", tout << "unknown state: " << mk_pp(m_pm.mk_and(cube), m) << "\n";); - IF_VERBOSE(1, verbose_stream() << "unknown state\n";); - throw unknown_exception(); - } - } - } - } - - // - // check if predicate transformer has a satisfiable predecessor state. - // returns either a satisfiable predecessor state or - // return a property that blocks state and is implied by the - // predicate transformer (or some unfolding of it). - // - lbool context::expand_state(model_node& n, expr_ref_vector& result, bool& uses_level) { - TRACE("pdr", - tout << "expand_state: " << n.pt().head()->get_name(); - tout << " level: " << n.level() << "\n"; - tout << mk_pp(n.state(), m) << "\n";); - - return n.pt().is_reachable(n, &result, uses_level); - } - - void context::propagate(unsigned max_prop_lvl) { - if (m_params.pdr_simplify_formulas_pre()) { - simplify_formulas(); - } - for (unsigned lvl = m_expanded_lvl; lvl <= max_prop_lvl; lvl++) { - checkpoint(); - bool all_propagated = true; - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - checkpoint(); - pred_transformer& r = *it->m_value; - all_propagated = r.propagate_to_next_level(lvl) && all_propagated; - } - CASSERT("pdr", check_invariant(lvl)); - - if (all_propagated && lvl == max_prop_lvl) { - m_inductive_lvl = lvl; - throw inductive_exception(); - } - } - if (m_params.pdr_simplify_formulas_post()) { - simplify_formulas(); - } - } - - - /** - \brief create children states from model cube. - - Introduce the shorthands: - - - T(x0,x1,x) for transition - - phi(x) for n.state() - - M(x0,x1,x) for n.model() - - Assumptions: - M => phi & T - - In other words, - 1. phi & T is implied by M - - Goal is to find phi0(x0), phi1(x1) such that: - - phi(x) & phi0(x0) & phi1(x1) => T(x0, x1, x) - - Strategy: - - - Extract literals from T & phi using ternary simulation with M. - - resulting formula is Phi. - - - perform cheap existential quantifier elimination on - Phi <- exists x . Phi(x0,x1,x) - (e.g., destructive equality resolution) - - - Sub-strategy 1: rename remaining x to fresh variables. - - Sub-strategy 2: replace remaining x to M(x). - - - For each literal L in result: - - - if L is x0 pure, add L to L0 - - if L is x1 pure, add L to L1 - - if L mixes x0, x1, add x1 = M(x1) to L1, add L(x1 |-> M(x1)) to L0 - - - Create sub-goals for L0 and L1. - - */ - void context::create_children(model_node& n) { - SASSERT(n.level() > 0); - bool use_model_generalizer = m_params.pdr_use_model_generalizer(); - scoped_no_proof _sc(m); - - pred_transformer& pt = n.pt(); - model_ref M = n.get_model_ptr(); - SASSERT(M.get()); - datalog::rule const& r = pt.find_rule(*M); - expr* T = pt.get_transition(r); - expr* phi = n.state(); - - n.set_rule(&r); - - - TRACE("pdr", - tout << "Model:\n"; - model_smt2_pp(tout, m, *M, 0); - tout << "\n"; - tout << "Transition:\n" << mk_pp(T, m) << "\n"; - tout << "Phi:\n" << mk_pp(phi, m) << "\n";); - - model_implicant mev(m); - expr_ref_vector mdl(m), forms(m), Phi(m); - forms.push_back(T); - forms.push_back(phi); - flatten_and(forms); - ptr_vector forms1(forms.size(), forms.c_ptr()); - if (use_model_generalizer) { - Phi.append(mev.minimize_model(forms1, M)); - } - else { - Phi.append(mev.minimize_literals(forms1, M)); - } - ptr_vector preds; - pt.find_predecessors(r, preds); - pt.remove_predecessors(Phi); - - app_ref_vector vars(m); - unsigned sig_size = pt.head()->get_arity(); - for (unsigned i = 0; i < sig_size; ++i) { - vars.push_back(m.mk_const(m_pm.o2n(pt.sig(i), 0))); - } - ptr_vector& aux_vars = pt.get_aux_vars(r); - vars.append(aux_vars.size(), aux_vars.c_ptr()); - - scoped_ptr rep; - qe_lite qe(m, m_params.p); - expr_ref phi1 = m_pm.mk_and(Phi); - qe(vars, phi1); - TRACE("pdr", tout << "Eliminated\n" << mk_pp(phi1, m) << "\n";); - if (!use_model_generalizer) { - reduce_disequalities(*M, 3, phi1); - TRACE("pdr", tout << "Reduced-eq\n" << mk_pp(phi1, m) << "\n";); - } - get_context().get_rewriter()(phi1); - - TRACE("pdr", - tout << "Vars:\n"; - for (unsigned i = 0; i < vars.size(); ++i) { - tout << mk_pp(vars[i].get(), m) << "\n"; - } - tout << "Literals\n"; - tout << mk_pp(m_pm.mk_and(Phi), m) << "\n"; - tout << "Reduced\n" << mk_pp(phi1, m) << "\n";); - - if (!vars.empty()) { - // also fresh names for auxiliary variables in body? - expr_substitution sub(m); - expr_ref tmp(m); - proof_ref pr(m); - pr = m.mk_asserted(m.mk_true()); - for (unsigned i = 0; i < vars.size(); ++i) { - tmp = mev.eval(M, vars[i].get()); - sub.insert(vars[i].get(), tmp, pr); - } - if (!rep) rep = mk_expr_simp_replacer(m); - rep->set_substitution(&sub); - (*rep)(phi1); - TRACE("pdr", tout << "Projected:\n" << mk_pp(phi1, m) << "\n";); - } - Phi.reset(); - flatten_and(phi1, Phi); - unsigned_vector indices; - vector child_states; - child_states.resize(preds.size(), expr_ref_vector(m)); - for (unsigned i = 0; i < Phi.size(); ++i) { - m_pm.collect_indices(Phi[i].get(), indices); - if (indices.size() == 0) { - IF_VERBOSE(3, verbose_stream() << "Skipping " << mk_pp(Phi[i].get(), m) << "\n";); - } - else if (indices.size() == 1) { - child_states[indices.back()].push_back(Phi[i].get()); - } - else { - expr_substitution sub(m); - expr_ref tmp(m); - proof_ref pr(m); - pr = m.mk_asserted(m.mk_true()); - vector > vars; - m_pm.collect_variables(Phi[i].get(), vars); - SASSERT(vars.size() == indices.back()+1); - for (unsigned j = 1; j < indices.size(); ++j) { - ptr_vector const& vs = vars[indices[j]]; - for (unsigned k = 0; k < vs.size(); ++k) { - tmp = mev.eval(M, vs[k]); - sub.insert(vs[k], tmp, pr); - child_states[indices[j]].push_back(m.mk_eq(vs[k], tmp)); - } - } - tmp = Phi[i].get(); - if (!rep) rep = mk_expr_simp_replacer(m); - rep->set_substitution(&sub); - (*rep)(tmp); - child_states[indices[0]].push_back(tmp); - } - - } - - expr_ref n_cube(m); - for (unsigned i = 0; i < preds.size(); ++i) { - pred_transformer& pt = *m_rels.find(preds[i]); - SASSERT(pt.head() == preds[i]); - expr_ref o_cube = m_pm.mk_and(child_states[i]); - m_pm.formula_o2n(o_cube, n_cube, i); - model_node* child = alloc(model_node, &n, n_cube, pt, n.level()-1); - ++m_stats.m_num_nodes; - m_search.add_leaf(*child); - IF_VERBOSE(2, verbose_stream() << "Predecessor: " << mk_pp(n_cube, m) << " " << (child->is_closed()?"closed":"open") << "\n";); - m_stats.m_max_depth = std::max(m_stats.m_max_depth, child->depth()); - } - n.check_pre_closed(); - TRACE("pdr", m_search.display(tout);); - } - - void context::collect_statistics(statistics& st) const { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (it = m_rels.begin(); it != end; ++it) { - it->m_value->collect_statistics(st); - } - st.update("PDR num unfoldings", m_stats.m_num_nodes); - st.update("PDR max depth", m_stats.m_max_depth); - st.update("PDR inductive level", m_inductive_lvl); - m_pm.collect_statistics(st); - - for (unsigned i = 0; i < m_core_generalizers.size(); ++i) { - m_core_generalizers[i]->collect_statistics(st); - } - } - - void context::reset_statistics() { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (it = m_rels.begin(); it != end; ++it) { - it->m_value->reset_statistics(); - } - m_stats.reset(); - m_pm.reset_statistics(); - - for (unsigned i = 0; i < m_core_generalizers.size(); ++i) { - m_core_generalizers[i]->reset_statistics(); - } - - } - - - std::ostream& context::display(std::ostream& out) const { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - it->m_value->display(out); - } - m_search.display(out); - return out; - } - - bool context::check_invariant(unsigned lvl) { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - checkpoint(); - if (!check_invariant(lvl, it->m_key)) { - return false; - } - } - return true; - } - - bool context::check_invariant(unsigned lvl, func_decl* fn) { - smt::kernel ctx(m, m_fparams); - pred_transformer& pt = *m_rels.find(fn); - expr_ref_vector conj(m); - expr_ref inv = pt.get_formulas(next_level(lvl), false); - if (m.is_true(inv)) return true; - pt.add_premises(m_rels, lvl, conj); - conj.push_back(m.mk_not(inv)); - expr_ref fml(m.mk_and(conj.size(), conj.c_ptr()), m); - ctx.assert_expr(fml); - lbool result = ctx.check(); - TRACE("pdr", tout << "Check invariant level: " << lvl << " " << result << "\n" << mk_pp(fml, m) << "\n";); - return result == l_false; - } - - void context::display_certificate(std::ostream& strm) { - switch(m_last_result) { - case l_false: { - expr_ref_vector refs(m); - vector rs; - get_level_property(m_inductive_lvl, refs, rs); - inductive_property ex(m, const_cast(m_mc), rs); - strm << ex.to_string(); - break; - } - case l_true: { - if (m_params.print_boogie_certificate()) { - datalog::boogie_proof bp(m); - bp.set_proof(get_proof()); - bp.set_model(0); - bp.pp(strm); - } - else { - strm << mk_pp(mk_sat_answer(), m); - } - break; - } - case l_undef: { - strm << "unknown"; - break; - } - } - } - -} diff --git a/src/muz/pdr/pdr_context.h b/src/muz/pdr/pdr_context.h deleted file mode 100644 index f160cff6a..000000000 --- a/src/muz/pdr/pdr_context.h +++ /dev/null @@ -1,448 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_context.h - -Abstract: - - PDR for datalog - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-20. - -Revision History: - ---*/ - -#ifndef PDR_CONTEXT_H_ -#define PDR_CONTEXT_H_ - -#ifdef _CYGWIN -#undef min -#undef max -#endif -#include -#include "muz/pdr/pdr_manager.h" -#include "muz/pdr/pdr_prop_solver.h" -#include "muz/pdr/pdr_reachable_cache.h" -#include "muz/base/fixedpoint_params.hpp" - - -namespace datalog { - class rule_set; - class context; -}; - -namespace pdr { - - class pred_transformer; - class model_node; - class context; - - typedef obj_map rule2inst; - typedef obj_map decl2rel; - - - // - // Predicate transformer state. - // A predicate transformer corresponds to the - // set of rules that have the same head predicates. - // - - class pred_transformer { - - struct stats { - unsigned m_num_propagations; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - typedef obj_map rule2expr; - typedef obj_map > rule2apps; - - manager& pm; // pdr-manager - ast_manager& m; // manager - context& ctx; - - func_decl_ref m_head; // predicate - func_decl_ref_vector m_sig; // signature - ptr_vector m_use; // places where 'this' is referenced. - ptr_vector m_rules; // rules used to derive transformer - prop_solver m_solver; // solver context - vector m_levels; // level formulas - expr_ref_vector m_invariants; // properties that are invariant. - obj_map m_prop2level; // map property to level where it occurs. - obj_map m_tag2rule; // map tag predicate to rule. - rule2expr m_rule2tag; // map rule to predicate tag. - rule2inst m_rule2inst; // map rules to instantiations of indices - rule2expr m_rule2transition; // map rules to transition - rule2apps m_rule2vars; // map rule to auxiliary variables - expr_ref m_transition; // transition relation. - expr_ref m_initial_state; // initial state. - reachable_cache m_reachable; - ptr_vector m_predicates; - stats m_stats; - - void init_sig(); - void ensure_level(unsigned level); - bool add_property1(expr * lemma, unsigned lvl); // add property 'p' to state at level lvl. - void add_child_property(pred_transformer& child, expr* lemma, unsigned lvl); - void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result); - - // Initialization - void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition); - void init_rule(decl2rel const& pts, datalog::rule const& rule, expr_ref& init, - ptr_vector& rules, expr_ref_vector& transition); - void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx); - - void simplify_formulas(tactic& tac, expr_ref_vector& fmls); - - // Debugging - bool check_filled(app_ref_vector const& v) const; - - void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); - - public: - pred_transformer(context& ctx, manager& pm, func_decl* head); - ~pred_transformer(); - - void add_rule(datalog::rule* r) { m_rules.push_back(r); } - void add_use(pred_transformer* pt) { if (!m_use.contains(pt)) m_use.insert(pt); } - void initialize(decl2rel const& pts); - - func_decl* head() const { return m_head; } - ptr_vector const& rules() const { return m_rules; } - func_decl* sig(unsigned i) { init_sig(); return m_sig[i].get(); } // signature - func_decl* const* sig() { init_sig(); return m_sig.c_ptr(); } - unsigned sig_size() { init_sig(); return m_sig.size(); } - expr* transition() const { return m_transition; } - expr* initial_state() const { return m_initial_state; } - expr* rule2tag(datalog::rule const* r) { return m_rule2tag.find(r); } - unsigned get_num_levels() { return m_levels.size(); } - expr_ref get_cover_delta(func_decl* p_orig, int level); - void add_cover(unsigned level, expr* property); - context& get_context() { return ctx; } - - std::ostream& display(std::ostream& strm) const; - - void collect_statistics(statistics& st) const; - void reset_statistics(); - - bool is_reachable(expr* state); - void remove_predecessors(expr_ref_vector& literals); - void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; - datalog::rule const& find_rule(model_core const& model) const; - expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); } - ptr_vector& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); } - - bool propagate_to_next_level(unsigned level); - void propagate_to_infinity(unsigned level); - void add_property(expr * lemma, unsigned lvl); // add property 'p' to state at level. - - lbool is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level); - bool is_invariant(unsigned level, expr* co_state, bool inductive, bool& assumes_level, expr_ref_vector* core = 0); - bool check_inductive(unsigned level, expr_ref_vector& state, bool& assumes_level); - - expr_ref get_formulas(unsigned level, bool add_axioms); - - void simplify_formulas(); - - expr_ref get_propagation_formula(decl2rel const& pts, unsigned level); - - manager& get_pdr_manager() const { return pm; } - ast_manager& get_manager() const { return m; } - - void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r); - - void close(expr* e); - - app_ref_vector& get_inst(datalog::rule const* r) { return *m_rule2inst.find(r);} - - void inherit_properties(pred_transformer& other); - - void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars); - - prop_solver& get_solver() { return m_solver; } - prop_solver const& get_solver() const { return m_solver; } - - void set_use_farkas(bool f) { get_solver().set_use_farkas(f); } - bool get_use_farkas() const { return get_solver().get_use_farkas(); } - class scoped_farkas { - bool m_old; - pred_transformer& m_p; - public: - scoped_farkas(pred_transformer& p, bool v): m_old(p.get_use_farkas()), m_p(p) { - p.set_use_farkas(v); - } - ~scoped_farkas() { m_p.set_use_farkas(m_old); } - }; - - }; - - - // structure for counter-example search. - class model_node { - model_node* m_parent; - model_node* m_next; - model_node* m_prev; - pred_transformer& m_pt; - expr_ref m_state; - model_ref m_model; - ptr_vector m_children; - unsigned m_level; - unsigned m_orig_level; - unsigned m_depth; - bool m_closed; - datalog::rule const* m_rule; - public: - model_node(model_node* parent, expr_ref& state, pred_transformer& pt, unsigned level): - m_parent(parent), m_next(0), m_prev(0), m_pt(pt), m_state(state), m_model(0), - m_level(level), m_orig_level(level), m_depth(0), m_closed(false), m_rule(0) { - model_node* p = m_parent; - if (p) { - p->m_children.push_back(this); - SASSERT(p->m_level == level+1); - SASSERT(p->m_level > 0); - m_depth = p->m_depth+1; - if (p && p->is_closed()) { - p->set_open(); - } - } - } - void set_model(model_ref& m) { m_model = m; } - unsigned level() const { return m_level; } - unsigned orig_level() const { return m_orig_level; } - unsigned depth() const { return m_depth; } - void increase_level() { ++m_level; } - expr_ref const& state() const { return m_state; } - ptr_vector const& children() { return m_children; } - pred_transformer& pt() const { return m_pt; } - model_node* parent() const { return m_parent; } - model* get_model_ptr() const { return m_model.get(); } - model const& get_model() const { return *m_model; } - unsigned index() const; - - bool is_closed() const { return m_closed; } - bool is_open() const { return !is_closed(); } - - bool is_1closed() { - if (is_closed()) return true; - if (m_children.empty()) return false; - for (unsigned i = 0; i < m_children.size(); ++i) { - if (m_children[i]->is_open()) return false; - } - return true; - } - - void check_pre_closed(); - void set_closed(); - void set_open(); - void set_pre_closed() { TRACE("pdr", tout << state() << "\n";); m_closed = true; } - void reset() { m_children.reset(); } - - void set_rule(datalog::rule const* r) { m_rule = r; } - datalog::rule* get_rule(); - - void mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding); - - std::ostream& display(std::ostream& out, unsigned indent); - - void dequeue(model_node*& root); - void enqueue(model_node* n); - model_node* next() const { return m_next; } - bool is_goal() const { return 0 != next(); } - }; - - class model_search { - typedef ptr_vector model_nodes; - bool m_bfs; - model_node* m_root; - model_node* m_goal; - vector > m_cache; - obj_map& cache(model_node const& n); - void erase_children(model_node& n, bool backtrack); - void remove_node(model_node& n, bool backtrack); - void enqueue_leaf(model_node* n); // add leaf to priority queue. - void update_models(); - void set_leaf(model_node& n); // Set node as leaf, remove children. - unsigned num_goals() const; - - public: - model_search(bool bfs): m_bfs(bfs), m_root(0), m_goal(0) {} - ~model_search(); - - void reset(); - model_node* next(); - void add_leaf(model_node& n); // add fresh node. - - void set_root(model_node* n); - model_node& get_root() const { return *m_root; } - std::ostream& display(std::ostream& out) const; - expr_ref get_trace(context const& ctx); - proof_ref get_proof_trace(context const& ctx); - void backtrack_level(bool uses_level, model_node& n); - void remove_goal(model_node& n); - - void well_formed(); - }; - - struct model_exception { }; - struct inductive_exception {}; - - - // 'state' is unsatisfiable at 'level' with 'core'. - // Minimize or weaken core. - class core_generalizer { - protected: - context& m_ctx; - public: - typedef vector > cores; - core_generalizer(context& ctx): m_ctx(ctx) {} - virtual ~core_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) = 0; - virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - new_cores.push_back(std::make_pair(core, uses_level)); - if (!core.empty()) { - (*this)(n, new_cores.back().first, new_cores.back().second); - } - } - virtual void collect_statistics(statistics& st) const {} - virtual void reset_statistics() {} - }; - - class context { - - struct stats { - unsigned m_num_nodes; - unsigned m_max_depth; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - smt_params& m_fparams; - fixedpoint_params const& m_params; - ast_manager& m; - datalog::context* m_context; - manager m_pm; - decl2rel m_rels; // Map from relation predicate to fp-operator. - decl2rel m_rels_tmp; - func_decl_ref m_query_pred; - pred_transformer* m_query; - mutable model_search m_search; - lbool m_last_result; - unsigned m_inductive_lvl; - unsigned m_expanded_lvl; - ptr_vector m_core_generalizers; - stats m_stats; - model_converter_ref m_mc; - proof_converter_ref m_pc; - - // Functions used by search. - void solve_impl(); - bool check_reachability(unsigned level); - void propagate(unsigned max_prop_lvl); - void close_node(model_node& n); - void check_pre_closed(model_node& n); - void expand_node(model_node& n); - lbool expand_state(model_node& n, expr_ref_vector& cube, bool& uses_level); - void create_children(model_node& n); - expr_ref mk_sat_answer() const; - expr_ref mk_unsat_answer(); - - // Generate inductive property - void get_level_property(unsigned lvl, expr_ref_vector& res, vector & rs); - - - // Initialization - class classifier_proc; - void init_core_generalizers(datalog::rule_set& rules); - - bool check_invariant(unsigned lvl); - bool check_invariant(unsigned lvl, func_decl* fn); - - void checkpoint(); - - void init_rules(datalog::rule_set& rules, decl2rel& transformers); - - void simplify_formulas(); - - void reset_core_generalizers(); - - void reset(decl2rel& rels); - - void validate(); - void validate_proof(); - void validate_search(); - void validate_model(); - - public: - - /** - Initial values of predicates are stored in corresponding relations in dctx. - - We check whether there is some reachable state of the relation checked_relation. - */ - context( - smt_params& fparams, - fixedpoint_params const& params, - ast_manager& m); - - ~context(); - - smt_params& get_fparams() const { return m_fparams; } - fixedpoint_params const& get_params() const { return m_params; } - ast_manager& get_manager() const { return m; } - manager& get_pdr_manager() { return m_pm; } - decl2rel const& get_pred_transformers() const { return m_rels; } - pred_transformer& get_pred_transformer(func_decl* p) const { return *m_rels.find(p); } - datalog::context& get_context() const { SASSERT(m_context); return *m_context; } - expr_ref get_answer(); - - bool is_dl() const { return m_fparams.m_arith_mode == AS_DIFF_LOGIC; } - bool is_utvpi() const { return m_fparams.m_arith_mode == AS_UTVPI; } - - void collect_statistics(statistics& st) const; - void reset_statistics(); - - std::ostream& display(std::ostream& strm) const; - - void display_certificate(std::ostream& strm); - - lbool solve(); - - void reset(bool full = true); - - void set_query(func_decl* q) { m_query_pred = q; } - - void set_unsat() { m_last_result = l_false; } - - void set_model_converter(model_converter_ref& mc) { m_mc = mc; } - - model_converter_ref get_model_converter() { return m_mc; } - - void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; } - - void update_rules(datalog::rule_set& rules); - - void set_axioms(expr* axioms) { m_pm.set_background(axioms); } - - 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); - - model_ref get_model(); - - proof_ref get_proof() const; - - model_node& get_root() const { return m_search.get_root(); } - - }; - -}; - -#endif diff --git a/src/muz/pdr/pdr_dl_interface.cpp b/src/muz/pdr/pdr_dl_interface.cpp deleted file mode 100644 index 87bc68bd9..000000000 --- a/src/muz/pdr/pdr_dl_interface.cpp +++ /dev/null @@ -1,225 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_dl.cpp - -Abstract: - - SMT2 interface for the datalog PDR - -Author: - - Krystof Hoder (t-khoder) 2011-9-22. - -Revision History: - ---*/ - -#include "muz/base/dl_context.h" -#include "muz/transforms/dl_mk_coi_filter.h" -#include "muz/base/dl_rule.h" -#include "muz/base/dl_rule_transformer.h" -#include "muz/pdr/pdr_context.h" -#include "muz/pdr/pdr_dl_interface.h" -#include "muz/base/dl_rule_set.h" -#include "muz/transforms/dl_mk_slice.h" -#include "muz/transforms/dl_mk_unfold.h" -#include "muz/transforms/dl_mk_coalesce.h" -#include "muz/transforms/dl_transforms.h" -#include "ast/scoped_proof.h" -#include "model/model_smt2_pp.h" - -using namespace pdr; - -dl_interface::dl_interface(datalog::context& ctx) : - engine_base(ctx.get_manager(), "pdr"), - m_ctx(ctx), - m_pdr_rules(ctx), - m_old_rules(ctx), - m_context(0), - m_refs(ctx.get_manager()) { - m_context = alloc(pdr::context, ctx.get_fparams(), ctx.get_params(), ctx.get_manager()); -} - - -dl_interface::~dl_interface() { - dealloc(m_context); -} - - -// -// Check if the new rules are weaker so that we can -// re-use existing context. -// -void dl_interface::check_reset() { - datalog::rule_set const& new_rules = m_ctx.get_rules(); - datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules(); - bool is_subsumed = !old_rules.empty(); - for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++i) { - is_subsumed = false; - for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) { - if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) { - is_subsumed = true; - } - } - if (!is_subsumed) { - TRACE("pdr", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule ");); - m_context->reset(); - } - } - m_old_rules.replace_rules(new_rules); -} - - -lbool dl_interface::query(expr * query) { - //we restore the initial state in the datalog context - m_ctx.ensure_opened(); - m_refs.reset(); - m_pred2slice.reset(); - ast_manager& m = m_ctx.get_manager(); - datalog::rule_manager& rm = m_ctx.get_rule_manager(); - datalog::rule_set& rules0 = m_ctx.get_rules(); - - datalog::rule_set old_rules(rules0); - func_decl_ref query_pred(m); - rm.mk_query(query, rules0); - expr_ref bg_assertion = m_ctx.get_background_assertion(); - - check_reset(); - - TRACE("pdr", - if (!m.is_true(bg_assertion)) { - tout << "axioms:\n"; - tout << mk_pp(bg_assertion, m) << "\n"; - } - tout << "query: " << mk_pp(query, m) << "\n"; - tout << "rules:\n"; - m_ctx.display_rules(tout); - ); - - - apply_default_transformation(m_ctx); - - if (m_ctx.get_params().xform_slice()) { - datalog::rule_transformer transformer(m_ctx); - datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx); - transformer.register_plugin(slice); - m_ctx.transform_rules(transformer); - - // track sliced predicates. - obj_map const& preds = slice->get_predicates(); - obj_map::iterator it = preds.begin(); - obj_map::iterator end = preds.end(); - for (; it != end; ++it) { - m_pred2slice.insert(it->m_key, it->m_value); - m_refs.push_back(it->m_key); - m_refs.push_back(it->m_value); - } - } - - if (m_ctx.get_params().xform_unfold_rules() > 0) { - unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules(); - datalog::rule_transformer transf1(m_ctx), transf2(m_ctx); - transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx)); - transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx)); - if (m_ctx.get_params().xform_coalesce_rules()) { - m_ctx.transform_rules(transf1); - } - while (num_unfolds > 0) { - m_ctx.transform_rules(transf2); - --num_unfolds; - } - } - - const datalog::rule_set& rules = m_ctx.get_rules(); - if (rules.get_output_predicates().empty()) { - m_context->set_unsat(); - return l_false; - } - - query_pred = rules.get_output_predicate(); - - TRACE("pdr", - tout << "rules:\n"; - m_ctx.display_rules(tout); - m_ctx.display_smt2(0, 0, tout); - ); - - IF_VERBOSE(2, m_ctx.display_rules(verbose_stream());); - m_pdr_rules.replace_rules(rules); - m_pdr_rules.close(); - m_ctx.record_transformed_rules(); - m_ctx.reopen(); - m_ctx.replace_rules(old_rules); - - - scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode. - - m_context->set_proof_converter(m_ctx.get_proof_converter()); - m_context->set_model_converter(m_ctx.get_model_converter()); - m_context->set_query(query_pred); - m_context->set_axioms(bg_assertion); - m_context->update_rules(m_pdr_rules); - - if (m_pdr_rules.get_rules().empty()) { - m_context->set_unsat(); - IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(),0);); - return l_false; - } - - return m_context->solve(); - -} - -expr_ref dl_interface::get_cover_delta(int level, func_decl* pred_orig) { - func_decl* pred = pred_orig; - m_pred2slice.find(pred_orig, pred); - SASSERT(pred); - return m_context->get_cover_delta(level, pred_orig, pred); -} - -void dl_interface::add_cover(int level, func_decl* pred, expr* property) { - if (m_ctx.get_params().xform_slice()) { - throw default_exception("Covers are incompatible with slicing. Disable slicing before using covers"); - } - m_context->add_cover(level, pred, property); -} - -unsigned dl_interface::get_num_levels(func_decl* pred) { - m_pred2slice.find(pred, pred); - SASSERT(pred); - return m_context->get_num_levels(pred); -} - -void dl_interface::collect_statistics(statistics& st) const { - m_context->collect_statistics(st); -} - -void dl_interface::reset_statistics() { - m_context->reset_statistics(); -} - -void dl_interface::display_certificate(std::ostream& out) const { - m_context->display_certificate(out); -} - -expr_ref dl_interface::get_answer() { - return m_context->get_answer(); -} - - - -void dl_interface::updt_params() { - dealloc(m_context); - m_context = alloc(pdr::context, m_ctx.get_fparams(), m_ctx.get_params(), m_ctx.get_manager()); -} - -model_ref dl_interface::get_model() { - return m_context->get_model(); -} - -proof_ref dl_interface::get_proof() { - return m_context->get_proof(); -} diff --git a/src/muz/pdr/pdr_dl_interface.h b/src/muz/pdr/pdr_dl_interface.h deleted file mode 100644 index 884f89e4b..000000000 --- a/src/muz/pdr/pdr_dl_interface.h +++ /dev/null @@ -1,78 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_dl_interface.h - -Abstract: - - SMT2 interface for the datalog PDR - -Author: - - Krystof Hoder (t-khoder) 2011-9-22. - -Revision History: - ---*/ - -#ifndef PDR_DL_INTERFACE_H_ -#define PDR_DL_INTERFACE_H_ - -#include "util/lbool.h" -#include "muz/base/dl_rule.h" -#include "muz/base/dl_rule_set.h" -#include "muz/base/dl_util.h" -#include "muz/base/dl_engine_base.h" -#include "util/statistics.h" - -namespace datalog { - class context; -} - -namespace pdr { - - class context; - - class dl_interface : public datalog::engine_base { - datalog::context& m_ctx; - datalog::rule_set m_pdr_rules; - datalog::rule_set m_old_rules; - context* m_context; - obj_map m_pred2slice; - ast_ref_vector m_refs; - - void check_reset(); - - public: - dl_interface(datalog::context& ctx); - ~dl_interface(); - - virtual lbool query(expr* query); - - virtual void display_certificate(std::ostream& out) const; - - virtual void collect_statistics(statistics& st) const; - - virtual void reset_statistics(); - - virtual expr_ref get_answer(); - - virtual unsigned get_num_levels(func_decl* pred); - - virtual expr_ref get_cover_delta(int level, func_decl* pred); - - virtual void add_cover(int level, func_decl* pred, expr* property); - - virtual void updt_params(); - - virtual model_ref get_model(); - - virtual proof_ref get_proof(); - - }; -} - - -#endif diff --git a/src/muz/pdr/pdr_farkas_learner.cpp b/src/muz/pdr/pdr_farkas_learner.cpp deleted file mode 100644 index 4a77d2f5f..000000000 --- a/src/muz/pdr/pdr_farkas_learner.cpp +++ /dev/null @@ -1,1015 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_farkas_learner.cpp - -Abstract: - - Proviced abstract interface and some inplementations of algorithms - for strenghtning lemmas - -Author: - - Krystof Hoder (t-khoder) 2011-11-1. - -Revision History: - ---*/ - -#include "ast/ast_smt2_pp.h" -#include "ast/array_decl_plugin.h" -#include "ast/rewriter/bool_rewriter.h" -#include "ast/dl_decl_plugin.h" -#include "ast/for_each_expr.h" -#include "muz/base/dl_util.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/rewriter_def.h" -#include "muz/pdr/pdr_util.h" -#include "muz/pdr/pdr_farkas_learner.h" -#include "ast/rewriter/th_rewriter.h" -#include "ast/ast_ll_pp.h" -#include "tactic/arith/arith_bounds_tactic.h" -#include "ast/proofs/proof_utils.h" -#include "ast/reg_decl_plugins.h" - - -namespace pdr { - - class farkas_learner::constr { - - ast_manager& m; - arith_util a; - app_ref_vector m_ineqs; - vector m_coeffs; - - unsigned m_time; - unsigned_vector m_roots, m_size, m_his, m_reps, m_ts; - - void mk_coerce(expr*& e1, expr*& e2) { - if (a.is_int(e1) && a.is_real(e2)) { - e1 = a.mk_to_real(e1); - } - else if (a.is_int(e2) && a.is_real(e1)) { - e2 = a.mk_to_real(e2); - } - } - - app* mk_add(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_add(e1, e2); - } - - app* mk_mul(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_mul(e1, e2); - } - - app* mk_le(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_le(e1, e2); - } - - app* mk_ge(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_ge(e1, e2); - } - - app* mk_gt(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_gt(e1, e2); - } - - app* mk_lt(expr* e1, expr* e2) { - mk_coerce(e1, e2); - return a.mk_lt(e1, e2); - } - - void mul(rational const& c, expr* e, expr_ref& res) { - expr_ref tmp(m); - if (c.is_one()) { - tmp = e; - } - else { - tmp = mk_mul(a.mk_numeral(c, c.is_int() && a.is_int(e)), e); - } - res = mk_add(res, tmp); - } - - bool is_int_sort(app* c) { - SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); - SASSERT(a.is_int(c->get_arg(0)) || a.is_real(c->get_arg(0))); - return a.is_int(c->get_arg(0)); - } - - bool is_int_sort() { - SASSERT(!m_ineqs.empty()); - return is_int_sort(m_ineqs[0].get()); - } - - void normalize_coeffs() { - rational l(1); - for (unsigned i = 0; i < m_coeffs.size(); ++i) { - l = lcm(l, denominator(m_coeffs[i])); - } - if (!l.is_one()) { - for (unsigned i = 0; i < m_coeffs.size(); ++i) { - m_coeffs[i] *= l; - } - } - } - - app* mk_one() { - return a.mk_numeral(rational(1), true); - } - - app* fix_sign(bool is_pos, app* c) { - expr* x, *y; - SASSERT(m.is_eq(c) || a.is_le(c) || a.is_lt(c) || a.is_gt(c) || a.is_ge(c)); - bool is_int = is_int_sort(c); - if (is_int && is_pos && (a.is_lt(c, x, y) || a.is_gt(c, y, x))) { - return mk_le(mk_add(x, mk_one()), y); - } - if (is_int && !is_pos && (a.is_le(c, x, y) || a.is_ge(c, y, x))) { - // !(x <= y) <=> x > y <=> x >= y + 1 - return mk_ge(x, mk_add(y, mk_one())); - } - if (is_pos) { - return c; - } - if (a.is_le(c, x, y)) return mk_gt(x, y); - if (a.is_lt(c, x, y)) return mk_ge(x, y); - if (a.is_ge(c, x, y)) return mk_lt(x, y); - if (a.is_gt(c, x, y)) return mk_le(x, y); - UNREACHABLE(); - return c; - } - - public: - constr(ast_manager& m) : m(m), a(m), m_ineqs(m), m_time(0) {} - - void reset() { - m_ineqs.reset(); - m_coeffs.reset(); - } - - /** add a multiple of constraint c to the current constr */ - void add(rational const & coef, app * c) { - bool is_pos = true; - expr* e; - while (m.is_not(c, e)) { - is_pos = !is_pos; - c = to_app(e); - } - - if (!coef.is_zero() && !m.is_true(c)) { - m_coeffs.push_back(coef); - m_ineqs.push_back(fix_sign(is_pos, c)); - } - } - - // - // Extract the complement of premises multiplied by Farkas coefficients. - // - void get(expr_ref& res) { - if (m_coeffs.empty()) { - res = m.mk_false(); - return; - } - bool is_int = is_int_sort(); - if (is_int) { - normalize_coeffs(); - } - TRACE("pdr", - for (unsigned i = 0; i < m_coeffs.size(); ++i) { - tout << m_coeffs[i] << ": " << mk_pp(m_ineqs[i].get(), m) << "\n"; - } - ); - - res = extract_consequence(0, m_coeffs.size()); - -#if 1 - // partition equalities into variable disjoint sets. - // take the conjunction of these instead of the - // linear combination. - partition_ineqs(); - expr_ref_vector lits(m); - unsigned lo = 0; - for (unsigned i = 0; i < m_his.size(); ++i) { - unsigned hi = m_his[i]; - lits.push_back(extract_consequence(lo, hi)); - lo = hi; - } - res = mk_or(lits); - IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } }); -#endif - } - - private: - - // partition inequalities into variable disjoint sets. - void partition_ineqs() { - m_reps.reset(); - m_his.reset(); - ++m_time; - for (unsigned i = 0; i < m_ineqs.size(); ++i) { - m_reps.push_back(process_term(m_ineqs[i].get())); - } - unsigned head = 0; - while (head < m_ineqs.size()) { - unsigned r = find(m_reps[head]); - unsigned tail = head; - for (unsigned i = head+1; i < m_ineqs.size(); ++i) { - if (find(m_reps[i]) == r) { - ++tail; - if (tail != i) { - SASSERT(tail < i); - std::swap(m_reps[tail], m_reps[i]); - app_ref tmp(m); - tmp = m_ineqs[i].get(); - m_ineqs[i] = m_ineqs[tail].get(); - m_ineqs[tail] = tmp; - std::swap(m_coeffs[tail], m_coeffs[i]); - } - } - } - head = tail + 1; - m_his.push_back(head); - } - } - - unsigned find(unsigned idx) { - if (m_ts.size() <= idx) { - m_roots.resize(idx+1); - m_size.resize(idx+1); - m_ts.resize(idx+1); - m_roots[idx] = idx; - m_ts[idx] = m_time; - m_size[idx] = 1; - return idx; - } - if (m_ts[idx] != m_time) { - m_size[idx] = 1; - m_ts[idx] = m_time; - m_roots[idx] = idx; - return idx; - } - while (true) { - if (m_roots[idx] == idx) { - return idx; - } - idx = m_roots[idx]; - } - } - - void merge(unsigned i, unsigned j) { - i = find(i); - j = find(j); - if (i == j) { - return; - } - if (m_size[i] > m_size[j]) { - std::swap(i, j); - } - m_roots[i] = j; - m_size[j] += m_size[i]; - } - - unsigned process_term(expr* e) { - unsigned r = e->get_id(); - ptr_vector todo; - ast_mark mark; - todo.push_back(e); - while (!todo.empty()) { - e = todo.back(); - todo.pop_back(); - if (mark.is_marked(e)) { - continue; - } - mark.mark(e, true); - if (is_uninterp(e)) { - merge(r, e->get_id()); - } - if (is_app(e)) { - app* a = to_app(e); - for (unsigned i = 0; i < a->get_num_args(); ++i) { - todo.push_back(a->get_arg(i)); - } - } - } - return r; - } - - expr_ref extract_consequence(unsigned lo, unsigned hi) { - bool is_int = is_int_sort(); - app_ref zero(a.mk_numeral(rational::zero(), is_int), m); - expr_ref res(m); - res = zero; - bool is_strict = false; - bool is_eq = true; - expr* x, *y; - for (unsigned i = lo; i < hi; ++i) { - app* c = m_ineqs[i].get(); - if (m.is_eq(c, x, y)) { - mul(m_coeffs[i], x, res); - mul(-m_coeffs[i], y, res); - } - if (a.is_lt(c, x, y) || a.is_gt(c, y, x)) { - mul(m_coeffs[i], x, res); - mul(-m_coeffs[i], y, res); - is_strict = true; - is_eq = false; - } - if (a.is_le(c, x, y) || a.is_ge(c, y, x)) { - mul(m_coeffs[i], x, res); - mul(-m_coeffs[i], y, res); - is_eq = false; - } - } - - zero = a.mk_numeral(rational::zero(), a.is_int(res)); - if (is_eq) { - res = m.mk_eq(res, zero); - } - else if (is_strict) { - res = mk_lt(res, zero); - } - else { - res = mk_le(res, zero); - } - res = m.mk_not(res); - th_rewriter rw(m); - params_ref params; - params.set_bool("gcd_rounding", true); - rw.updt_params(params); - proof_ref pr(m); - expr_ref result(m); - rw(res, result, pr); - fix_dl(result); - return result; - } - - // patch: swap addends to make static - // features recognize difference constraint. - void fix_dl(expr_ref& r) { - expr* e; - if (m.is_not(r, e)) { - r = e; - fix_dl(r); - r = m.mk_not(r); - return; - } - expr* e1, *e2, *e3, *e4; - if ((m.is_eq(r, e1, e2) || a.is_lt(r, e1, e2) || a.is_gt(r, e1, e2) || - a.is_le(r, e1, e2) || a.is_ge(r, e1, e2))) { - if (a.is_add(e1, e3, e4) && a.is_mul(e3)) { - r = m.mk_app(to_app(r)->get_decl(), a.mk_add(e4,e3), e2); - } - } - } - }; - - farkas_learner::farkas_learner(smt_params& params, ast_manager& outer_mgr) - : m_proof_params(get_proof_params(params)), - m_pr(PGM_ENABLED), - m_constr(0), - m_combine_farkas_coefficients(true), - p2o(m_pr, outer_mgr), - o2p(outer_mgr, m_pr) - { - reg_decl_plugins(m_pr); - m_ctx = alloc(smt::kernel, m_pr, m_proof_params); - } - - farkas_learner::~farkas_learner() { - dealloc(m_constr); - } - - smt_params farkas_learner::get_proof_params(smt_params& orig_params) { - smt_params res(orig_params); - res.m_arith_bound_prop = BP_NONE; - // temp hack to fix the build - // res.m_conflict_resolution_strategy = CR_ALL_DECIDED; - res.m_arith_auto_config_simplex = true; - res.m_arith_propagate_eqs = false; - res.m_arith_eager_eq_axioms = false; - res.m_arith_eq_bounds = false; - return res; - } - - class farkas_learner::equality_expander_cfg : public default_rewriter_cfg - { - ast_manager& m; - arith_util m_ar; - public: - equality_expander_cfg(ast_manager& m) : m(m), m_ar(m) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - expr * x, *y; - if (m.is_eq(s, x, y) && (m_ar.is_int(x) || m_ar.is_real(x))) { - t = m.mk_and(m_ar.mk_ge(x, y), m_ar.mk_le(x, y)); - return true; - } - else { - return false; - } - } - - }; - - class collect_pure_proc { - func_decl_set& m_symbs; - public: - collect_pure_proc(func_decl_set& s):m_symbs(s) {} - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - m_symbs.insert(a->get_decl()); - } - } - void operator()(var*) {} - void operator()(quantifier*) {} - }; - - - bool farkas_learner::get_lemma_guesses(expr * A_ext, expr * B_ext, expr_ref_vector& lemmas) - { - expr_ref A(o2p(A_ext), m_pr); - expr_ref B(o2p(B_ext), m_pr); - proof_ref pr(m_pr); - expr_ref tmp(m_pr); - expr_ref_vector ilemmas(m_pr); - equality_expander_cfg ee_rwr_cfg(m_pr); - rewriter_tpl ee_rwr(m_pr, false, ee_rwr_cfg); - - lemmas.reset(); - - ee_rwr(A, A); - ee_rwr(B, B); - - expr_set bs; - expr_ref_vector blist(m_pr); - flatten_and(B, blist); - for (unsigned i = 0; i < blist.size(); ++i) { - bs.insert(blist[i].get()); - } - - - if (!m_ctx) { - m_ctx = alloc(smt::kernel, m_pr, m_proof_params); - } - - m_ctx->push(); - m_ctx->assert_expr(A); - expr_set::iterator it = bs.begin(), end = bs.end(); - for (; it != end; ++it) { - m_ctx->assert_expr(*it); - } - lbool res = m_ctx->check(); - bool is_unsat = res == l_false; - if (is_unsat) { - pr = m_ctx->get_proof(); - get_lemmas(m_ctx->get_proof(), bs, ilemmas); - for (unsigned i = 0; i < ilemmas.size(); ++i) { - lemmas.push_back(p2o(ilemmas[i].get())); - } - } - m_ctx->pop(1); - - IF_VERBOSE(3, { - for (unsigned i = 0; i < ilemmas.size(); ++i) { - verbose_stream() << "B': " << mk_pp(ilemmas[i].get(), m_pr) << "\n"; - } - }); - - TRACE("farkas_learner", - tout << (is_unsat?"unsat":"sat") << "\n"; - tout << "A: " << mk_pp(A_ext, m_ctx->m()) << "\n"; - tout << "B: " << mk_pp(B_ext, m_ctx->m()) << "\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - tout << "B': " << mk_pp(ilemmas[i].get(), m_pr) << "\n"; - }); - DEBUG_CODE( - if (is_unsat) { - m_ctx->push(); - m_ctx->assert_expr(A); - for (unsigned i = 0; i < ilemmas.size(); ++i) { - m_ctx->assert_expr(ilemmas[i].get()); - } - lbool res2 = m_ctx->check(); - SASSERT(l_false == res2); - m_ctx->pop(1); - } - ); - return is_unsat; - } - - // - // Perform simple subsumption check of lemmas. - // - void farkas_learner::simplify_lemmas(expr_ref_vector& lemmas) { - ast_manager& m = lemmas.get_manager(); - goal_ref g(alloc(goal, m, false, false, false)); - TRACE("farkas_simplify_lemmas", - for (unsigned i = 0; i < lemmas.size(); ++i) { - tout << mk_pp(lemmas[i].get(), m) << "\n"; - }); - - for (unsigned i = 0; i < lemmas.size(); ++i) { - g->assert_expr(lemmas[i].get()); - } - expr_ref tmp(m); - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); - goal_ref_buffer result; - tactic_ref simplifier = mk_arith_bounds_tactic(m); - (*simplifier)(g, result, mc, pc, core); - lemmas.reset(); - SASSERT(result.size() == 1); - goal* r = result[0]; - for (unsigned i = 0; i < r->size(); ++i) { - lemmas.push_back(r->form(i)); - } - TRACE("farkas_simplify_lemmas", - tout << "simplified:\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - tout << mk_pp(lemmas[i].get(), m) << "\n"; - }); - } - - - void farkas_learner::combine_constraints(unsigned n, app * const * lits, rational const * coeffs, expr_ref& res) - { - ast_manager& m = res.get_manager(); - if (m_combine_farkas_coefficients) { - if (!m_constr) { - m_constr = alloc(constr, m); - } - m_constr->reset(); - for (unsigned i = 0; i < n; ++i) { - m_constr->add(coeffs[i], lits[i]); - } - m_constr->get(res); - } - else { - bool_rewriter rw(m); - rw.mk_or(n, (expr*const*)(lits), res); - res = m.mk_not(res); - } - } - - class farkas_learner::constant_replacer_cfg : public default_rewriter_cfg - { - const obj_map& m_translation; - public: - constant_replacer_cfg(const obj_map& translation) - : m_translation(translation) - { } - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - return m_translation.find(s, t); - } - }; - - // every uninterpreted symbol is in symbs - class is_pure_expr_proc { - func_decl_set const& m_symbs; - public: - struct non_pure {}; - - is_pure_expr_proc(func_decl_set const& s):m_symbs(s) {} - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - if (!m_symbs.contains(a->get_decl())) { - throw non_pure(); - } - } - } - void operator()(var*) {} - void operator()(quantifier*) {} - }; - - bool farkas_learner::is_pure_expr(func_decl_set const& symbs, expr* e) const { - is_pure_expr_proc proc(symbs); - try { - for_each_expr(proc, e); - } - catch (is_pure_expr_proc::non_pure) { - return false; - } - return true; - }; - - - /** - Revised version of Farkas strengthener. - 1. Mark B-pure nodes as derivations that depend only on B. - 2. Collect B-influenced nodes - 3. (optional) Permute B-pure units over resolution steps to narrow dependencies on B. - 4. Weaken B-pure units for resolution with Farkas Clauses. - 5. Add B-pure units elsewhere. - - Rules: - - hypothesis h |- h - - H |- false - - lemma ---------- - |- not H - - Th |- L \/ C H |- not L - - th-lemma ------------------------- - H |- C - - Note: C is false for theory axioms, C is unit literal for propagation. - - - rewrite |- t = s - - H |- t = s - - monotonicity ---------------- - H |- f(t) = f(s) - - H |- t = s H' |- s = u - - trans ---------------------- - H, H' |- t = u - - H |- C \/ L H' |- not L - - unit_resolve ------------------------ - H, H' |- C - - H |- a ~ b H' |- a - - mp -------------------- - H, H' |- b - - - def-axiom |- C - - - asserted |- f - - Mark nodes by: - - Hypotheses - - Dependency on bs - - Dependency on A - - A node is unit derivable from bs if: - - It has no hypotheses. - - It depends on bs. - - It does not depend on A. - - NB: currently unit derivable is not symmetric: A clause can be - unit derivable, but a unit literal with hypotheses is not. - This is clearly wrong, because hypotheses are just additional literals - in a clausal version. - - NB: the routine is not interpolating, though an interpolating variant would - be preferrable because then we can also use it for model propagation. - - We collect the unit derivable nodes from bs. - These are the weakenings of bs, besides the - units under Farkas. - - */ - -#define INSERT(_x_) if (!lemma_set.contains(_x_)) { lemma_set.insert(_x_); lemmas.push_back(_x_); } - - void farkas_learner::get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas) { - ast_manager& m = lemmas.get_manager(); - bool_rewriter brwr(m); - func_decl_set Bsymbs; - collect_pure_proc collect_proc(Bsymbs); - expr_set::iterator it = bs.begin(), end = bs.end(); - for (; it != end; ++it) { - for_each_expr(collect_proc, *it); - } - - proof_ref pr(root, m); - proof_utils::reduce_hypotheses(pr); - proof_utils::permute_unit_resolution(pr); - IF_VERBOSE(3, verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n";); - - ptr_vector hyprefs; - obj_map hypmap; - obj_hashtable lemma_set; - ast_mark b_depend, a_depend, visited, b_closed; - expr_set* empty_set = alloc(expr_set); - hyprefs.push_back(empty_set); - ptr_vector todo; - TRACE("pdr_verbose", tout << mk_pp(pr, m) << "\n";); - todo.push_back(pr); - while (!todo.empty()) { - proof* p = todo.back(); - SASSERT(m.is_proof(p)); - if (visited.is_marked(p)) { - todo.pop_back(); - continue; - } - bool all_visit = true; - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - expr* arg = p->get_arg(i); - SASSERT(m.is_proof(arg)); - if (!visited.is_marked(arg)) { - all_visit = false; - todo.push_back(to_app(arg)); - } - } - if (!all_visit) { - continue; - } - visited.mark(p, true); - todo.pop_back(); - - // retrieve hypotheses and dependencies on A, bs. - bool b_dep = false, a_dep = false; - expr_set* hyps = empty_set; - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - expr* arg = p->get_arg(i); - a_dep = a_dep || a_depend.is_marked(arg); - b_dep = b_dep || b_depend.is_marked(arg); - expr_set* hyps2 = hypmap.find(arg); - if (hyps != hyps2 && !hyps2->empty()) { - if (hyps->empty()) { - hyps = hyps2; - } - else { - expr_set* hyps3 = alloc(expr_set); - set_union(*hyps3, *hyps); - set_union(*hyps3, *hyps2); - hyps = hyps3; - hyprefs.push_back(hyps); - } - } - } - hypmap.insert(p, hyps); - a_depend.mark(p, a_dep); - b_depend.mark(p, b_dep); - -#define IS_B_PURE(_p) (b_depend.is_marked(_p) && !a_depend.is_marked(_p) && hypmap.find(_p)->empty()) - - - // Add lemmas that depend on bs, have no hypotheses, don't depend on A. - if ((!hyps->empty() || a_depend.is_marked(p)) && - b_depend.is_marked(p) && !is_farkas_lemma(m, p)) { - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - app* arg = to_app(p->get_arg(i)); - if (IS_B_PURE(arg)) { - expr* fact = m.get_fact(arg); - if (is_pure_expr(Bsymbs, fact)) { - TRACE("farkas_learner", - tout << "Add: " << mk_pp(m.get_fact(arg), m) << "\n"; - tout << mk_pp(arg, m) << "\n"; - ); - INSERT(fact); - } - else { - get_asserted(p, bs, b_closed, lemma_set, lemmas); - b_closed.mark(p, true); - } - } - } - } - - switch(p->get_decl_kind()) { - case PR_ASSERTED: - if (bs.contains(m.get_fact(p))) { - b_depend.mark(p, true); - } - else { - a_depend.mark(p, true); - } - break; - case PR_HYPOTHESIS: { - SASSERT(hyps == empty_set); - hyps = alloc(expr_set); - hyps->insert(m.get_fact(p)); - hyprefs.push_back(hyps); - hypmap.insert(p, hyps); - break; - } - case PR_DEF_AXIOM: { - if (!is_pure_expr(Bsymbs, m.get_fact(p))) { - a_depend.mark(p, true); - } - break; - } - case PR_LEMMA: { - expr_set* hyps2 = alloc(expr_set); - hyprefs.push_back(hyps2); - set_union(*hyps2, *hyps); - hyps = hyps2; - expr* fml = m.get_fact(p); - hyps->remove(fml); - if (m.is_or(fml)) { - for (unsigned i = 0; i < to_app(fml)->get_num_args(); ++i) { - expr* f = to_app(fml)->get_arg(i); - expr_ref hyp(m); - brwr.mk_not(f, hyp); - hyps->remove(hyp); - } - } - hypmap.insert(p, hyps); - break; - } - case PR_TH_LEMMA: { - if (!is_farkas_lemma(m, p)) break; - - SASSERT(m.has_fact(p)); - unsigned prem_cnt = m.get_num_parents(p); - func_decl * d = p->get_decl(); - SASSERT(d->get_num_parameters() >= prem_cnt+2); - SASSERT(d->get_parameter(0).get_symbol() == "arith"); - SASSERT(d->get_parameter(1).get_symbol() == "farkas"); - parameter const* params = d->get_parameters() + 2; - - app_ref_vector lits(m); - expr_ref tmp(m); - unsigned num_b_pures = 0; - rational coef; - vector coeffs; - - TRACE("farkas_learner", - for (unsigned i = 0; i < prem_cnt; ++i) { - VERIFY(params[i].is_rational(coef)); - proof* prem = to_app(p->get_arg(i)); - bool b_pure = IS_B_PURE(prem); - tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; - } - tout << mk_pp(m.get_fact(p), m) << "\n"; - ); - - // NB. Taking 'abs' of coefficients is a workaround. - // The Farkas coefficient extraction in arith_core must be wrong. - // The coefficients would be always positive relative to the theory lemma. - - for(unsigned i = 0; i < prem_cnt; ++i) { - expr * prem_e = p->get_arg(i); - SASSERT(is_app(prem_e)); - proof * prem = to_app(prem_e); - - if(IS_B_PURE(prem)) { - ++num_b_pures; - } - else { - VERIFY(params[i].is_rational(coef)); - lits.push_back(to_app(m.get_fact(prem))); - coeffs.push_back(abs(coef)); - } - } - params += prem_cnt; - if (prem_cnt + 2 < d->get_num_parameters()) { - unsigned num_args = 1; - expr* fact = m.get_fact(p); - expr* const* args = &fact; - if (m.is_or(fact)) { - app* _or = to_app(fact); - num_args = _or->get_num_args(); - args = _or->get_args(); - } - SASSERT(prem_cnt + 2 + num_args == d->get_num_parameters()); - for (unsigned i = 0; i < num_args; ++i) { - expr* prem_e = args[i]; - brwr.mk_not(prem_e, tmp); - VERIFY(params[i].is_rational(coef)); - SASSERT(is_app(tmp)); - lits.push_back(to_app(tmp)); - coeffs.push_back(abs(coef)); - } - - } - SASSERT(coeffs.size() == lits.size()); - if (num_b_pures > 0) { - expr_ref res(m); - combine_constraints(coeffs.size(), lits.c_ptr(), coeffs.c_ptr(), res); - TRACE("farkas_learner", tout << "Add: " << mk_pp(res, m) << "\n";); - INSERT(res); - b_closed.mark(p, true); - } - } - default: - break; - } - } - - std::for_each(hyprefs.begin(), hyprefs.end(), delete_proc()); - simplify_lemmas(lemmas); - } - - void farkas_learner::get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences) { - TRACE("farkas_learner", tout << "get consequences\n";); - m_combine_farkas_coefficients = false; - get_lemmas(root, bs, consequences); - m_combine_farkas_coefficients = true; - } - - void farkas_learner::get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas) { - ast_manager& m = lemmas.get_manager(); - ast_mark visited; - proof* p0 = p; - ptr_vector todo; - todo.push_back(p); - - while (!todo.empty()) { - p = todo.back(); - todo.pop_back(); - if (visited.is_marked(p) || b_closed.is_marked(p)) { - continue; - } - visited.mark(p, true); - for (unsigned i = 0; i < m.get_num_parents(p); ++i) { - expr* arg = p->get_arg(i); - SASSERT(m.is_proof(arg)); - todo.push_back(to_app(arg)); - } - if (p->get_decl_kind() == PR_ASSERTED && - bs.contains(m.get_fact(p))) { - expr* fact = m.get_fact(p); - (void)p0; - TRACE("farkas_learner", - tout << mk_ll_pp(p0,m) << "\n"; - tout << "Add: " << mk_pp(p,m) << "\n";); - INSERT(fact); - b_closed.mark(p, true); - } - } - } - - - bool farkas_learner::is_farkas_lemma(ast_manager& m, expr* e) { - app * a; - func_decl* d; - symbol sym; - return - is_app(e) && - (a = to_app(e), d = a->get_decl(), true) && - PR_TH_LEMMA == a->get_decl_kind() && - d->get_num_parameters() >= 2 && - d->get_parameter(0).is_symbol(sym) && sym == "arith" && - d->get_parameter(1).is_symbol(sym) && sym == "farkas" && - d->get_num_parameters() >= m.get_num_parents(to_app(e)) + 2; - }; - - - void farkas_learner::test() { - smt_params params; - enable_trace("farkas_learner"); - - bool res; - ast_manager m; - reg_decl_plugins(m); - arith_util a(m); - pdr::farkas_learner fl(params, m); - expr_ref_vector lemmas(m); - - sort_ref int_s(a.mk_int(), m); - expr_ref x(m.mk_const(symbol("x"), int_s), m); - expr_ref y(m.mk_const(symbol("y"), int_s), m); - expr_ref z(m.mk_const(symbol("z"), int_s), m); - expr_ref u(m.mk_const(symbol("u"), int_s), m); - expr_ref v(m.mk_const(symbol("v"), int_s), m); - - // A: x > y >= z - // B: x < z - // Farkas: x <= z - expr_ref A(m.mk_and(a.mk_gt(x,y), a.mk_ge(y,z)),m); - expr_ref B(a.mk_gt(z,x),m); - res = fl.get_lemma_guesses(A, B, lemmas); - std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; - - // A: x > y >= z + 2 - // B: x = 1, z = 8 - // Farkas: x <= z + 2 - expr_ref one(a.mk_numeral(rational(1), true), m); - expr_ref two(a.mk_numeral(rational(2), true), m); - expr_ref eight(a.mk_numeral(rational(8), true), m); - A = m.mk_and(a.mk_gt(x,y),a.mk_ge(y,a.mk_add(z,two))); - B = m.mk_and(m.mk_eq(x,one), m.mk_eq(z, eight)); - res = fl.get_lemma_guesses(A, B, lemmas); - std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; - - // A: x > y >= z \/ x >= u > z - // B: z > x + 1 - // Farkas: z >= x - A = m.mk_or(m.mk_and(a.mk_gt(x,y),a.mk_ge(y,z)),m.mk_and(a.mk_ge(x,u),a.mk_gt(u,z))); - B = a.mk_gt(z, a.mk_add(x,one)); - res = fl.get_lemma_guesses(A, B, lemmas); - std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; - - // A: (x > y >= z \/ x >= u > z \/ u > v) - // B: z > x + 1 & not (u > v) - // Farkas: z >= x & not (u > v) - A = m.mk_or(m.mk_and(a.mk_gt(x,y),a.mk_ge(y,z)),m.mk_and(a.mk_ge(x,u),a.mk_gt(u,z)), a.mk_gt(u, v)); - B = m.mk_and(a.mk_gt(z, a.mk_add(x,one)), m.mk_not(a.mk_gt(u, v))); - res = fl.get_lemma_guesses(A, B, lemmas); - std::cout << "\nres: " << res << "\nlemmas: " << pp_cube(lemmas, m) << "\n"; - - } - - void farkas_learner::collect_statistics(statistics& st) const { - if (m_ctx) { - m_ctx->collect_statistics(st); - } - } - - -}; - diff --git a/src/muz/pdr/pdr_farkas_learner.h b/src/muz/pdr/pdr_farkas_learner.h deleted file mode 100644 index a5f3482e6..000000000 --- a/src/muz/pdr/pdr_farkas_learner.h +++ /dev/null @@ -1,128 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_farkas_learner.h - -Abstract: - - SMT2 interface for the datalog PDR - -Author: - - Krystof Hoder (t-khoder) 2011-11-1. - -Revision History: - ---*/ - -#ifndef PDR_FARKAS_LEARNER_H_ -#define PDR_FARKAS_LEARNER_H_ - -#include "ast/arith_decl_plugin.h" -#include "ast/ast_translation.h" -#include "ast/bv_decl_plugin.h" -#include "smt/smt_kernel.h" -#include "ast/rewriter/bool_rewriter.h" -#include "muz/pdr/pdr_util.h" -#include "smt/params/smt_params.h" -#include "tactic/tactic.h" - -namespace pdr { - -class farkas_learner { - class farkas_collector; - class constant_replacer_cfg; - class equality_expander_cfg; - class constr; - - typedef obj_hashtable expr_set; - - smt_params m_proof_params; - ast_manager m_pr; - scoped_ptr m_ctx; - constr* m_constr; - - // - // true: produce a combined constraint by applying Farkas coefficients. - // false: produce a conjunction of the negated literals from the theory lemmas. - // - bool m_combine_farkas_coefficients; - - - static smt_params get_proof_params(smt_params& orig_params); - - // - // all ast objects passed to private functions have m_proof_mgs as their ast_manager - // - - ast_translation p2o; /** Translate expression from inner ast_manager to outer one */ - ast_translation o2p; /** Translate expression from outer ast_manager to inner one */ - - - /** All ast opbjects here are in the m_proof_mgs */ - void get_lemma_guesses_internal(proof * p, expr* A, expr * B, expr_ref_vector& lemmas); - - bool farkas2lemma(proof * fstep, expr* A, expr * B, expr_ref& res); - - void combine_constraints(unsigned cnt, app * const * constrs, rational const * coeffs, expr_ref& res); - - bool try_ensure_lemma_in_language(expr_ref& lemma, expr* A, const func_decl_set& lang); - - bool is_farkas_lemma(ast_manager& m, expr* e); - - void get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable& lemma_set, expr_ref_vector& lemmas); - - bool is_pure_expr(func_decl_set const& symbs, expr* e) const; - - static void test(); - -public: - farkas_learner(smt_params& params, ast_manager& m); - - ~farkas_learner(); - - /** - All ast objects have the ast_manager which was passed as - an argument to the constructor (i.e. m_outer_mgr) - - B is a conjunction of literals. - A && B is unsat, equivalently A => ~B is valid - Find a weakened B' such that - A && B' is unsat and B' uses vocabulary (and constants) in common with A. - return lemmas to weaken B. - */ - - bool get_lemma_guesses(expr * A, expr * B, expr_ref_vector& lemmas); - - /** - Traverse a proof and retrieve lemmas using the vocabulary from bs. - */ - void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas); - - /** - Traverse a proof and retrieve consequences of A that are used to establish ~B. - The assumption is that: - - A => \/ ~consequences[i] and \/ ~consequences[i] => ~B - - e.g., the second implication can be rewritten as: - - B => /\ consequences[i] - */ - void get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences); - - /** - \brief Simplify lemmas using subsumption. - */ - void simplify_lemmas(expr_ref_vector& lemmas); - - void collect_statistics(statistics& st) const; - -}; - - -} - -#endif diff --git a/src/muz/pdr/pdr_generalizers.cpp b/src/muz/pdr/pdr_generalizers.cpp deleted file mode 100644 index 094379a0b..000000000 --- a/src/muz/pdr/pdr_generalizers.cpp +++ /dev/null @@ -1,777 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_generalizers.cpp - -Abstract: - - Generalizers of satisfiable states and unsat cores. - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-20. - -Revision History: - ---*/ - - -#include "muz/pdr/pdr_context.h" -#include "muz/pdr/pdr_farkas_learner.h" -#include "muz/pdr/pdr_generalizers.h" -#include "ast/expr_abstract.h" -#include "ast/rewriter/var_subst.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "model/model_smt2_pp.h" - - -namespace pdr { - - - // ------------------------ - // core_bool_inductive_generalizer - - // main propositional induction generalizer. - // drop literals one by one from the core and check if the core is still inductive. - // - void core_bool_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - if (core.size() <= 1) { - return; - } - ast_manager& m = core.get_manager(); - TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; }); - unsigned num_failures = 0, i = 0, old_core_size = core.size(); - ptr_vector processed; - - while (i < core.size() && 1 < core.size() && (!m_failure_limit || num_failures <= m_failure_limit)) { - expr_ref lit(m); - lit = core[i].get(); - core[i] = m.mk_true(); - if (n.pt().check_inductive(n.level(), core, uses_level)) { - num_failures = 0; - for (i = 0; i < core.size() && processed.contains(core[i].get()); ++i); - } - else { - core[i] = lit; - processed.push_back(lit); - ++num_failures; - ++i; - } - } - IF_VERBOSE(2, verbose_stream() << "old size: " << old_core_size << " new size: " << core.size() << "\n";); - TRACE("pdr", tout << "old size: " << old_core_size << " new size: " << core.size() << "\n";); - } - - - void core_multi_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - UNREACHABLE(); - } - - /** - \brief Find minimal cores. - Apply a simple heuristic: find a minimal core, then find minimal cores that exclude at least one - literal from each of the literals in the minimal cores. - */ - void core_multi_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - ast_manager& m = core.get_manager(); - expr_ref_vector old_core(m), core0(core); - bool uses_level1 = uses_level; - m_gen(n, core0, uses_level1); - new_cores.push_back(std::make_pair(core0, uses_level1)); - obj_hashtable core_exprs, core1_exprs; - set_union(core_exprs, core0); - for (unsigned i = 0; i < old_core.size(); ++i) { - expr* lit = old_core[i].get(); - if (core_exprs.contains(lit)) { - expr_ref_vector core1(old_core); - core1[i] = core1.back(); - core1.pop_back(); - uses_level1 = uses_level; - m_gen(n, core1, uses_level1); - SASSERT(core1.size() <= old_core.size()); - if (core1.size() < old_core.size()) { - new_cores.push_back(std::make_pair(core1, uses_level1)); - core1_exprs.reset(); - set_union(core1_exprs, core1); - set_intersection(core_exprs, core1_exprs); - } - } - } - } - - // ------------------------ - // core_farkas_generalizer - - // - // for each disjunct of core: - // weaken predecessor. - // - - core_farkas_generalizer::core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p): - core_generalizer(ctx), - m_farkas_learner(p, m) - {} - - void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - ast_manager& m = n.pt().get_manager(); - if (core.empty()) return; - expr_ref A(m), B(mk_and(core)), C(m); - expr_ref_vector Bs(m); - flatten_or(B, Bs); - A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level()); - - bool change = false; - for (unsigned i = 0; i < Bs.size(); ++i) { - expr_ref_vector lemmas(m); - C = Bs[i].get(); - if (m_farkas_learner.get_lemma_guesses(A, B, lemmas)) { - TRACE("pdr", - tout << "Old core:\n" << mk_pp(B, m) << "\n"; - tout << "New core:\n" << mk_and(lemmas) << "\n";); - Bs[i] = mk_and(lemmas); - change = true; - } - } - if (change) { - C = mk_or(Bs); - TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";); - core.reset(); - flatten_and(C, core); - uses_level = true; - } - } - - void core_farkas_generalizer::collect_statistics(statistics& st) const { - m_farkas_learner.collect_statistics(st); - } - - - core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure): - core_generalizer(ctx), - m(ctx.get_manager()), - m_is_closure(is_closure) { - } - - void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - // method3(n, core, uses_level, new_cores); - method1(n, core, uses_level, new_cores); - } - - void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - UNREACHABLE(); - } - - // use the entire region as starting point for generalization. - // - // Constraints: - // add_variables: y = y1 + y2 - // core: Ay <= b -> conv1: A*y1 <= b*sigma1 - // sigma1 > 0 - // sigma2 > 0 - // 1 = sigma1 + sigma2 - // A'y <= b' -> conv2: A'*y2 <= b'*sigma2 - // - // If Constraints & Transition(y0, y) is unsat, then - // update with new core. - // - void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - expr_ref_vector conv2(m), fmls(m), fml1_2(m); - bool change = false; - - if (core.empty()) { - new_cores.push_back(std::make_pair(core, uses_level)); - return; - } - closure cl(n.pt(), m_is_closure); - - expr_ref fml1 = mk_and(core); - expr_ref fml2 = n.pt().get_formulas(n.level(), false); - fml1_2.push_back(fml1); - fml1_2.push_back(0); - flatten_and(fml2, fmls); - for (unsigned i = 0; i < fmls.size(); ++i) { - fml2 = m.mk_not(fmls[i].get()); - fml1_2[1] = fml2; - expr_ref state = cl(fml1_2); - TRACE("pdr", - tout << "Check states:\n" << mk_pp(state, m) << "\n"; - tout << "Old states:\n" << mk_pp(fml2, m) << "\n"; - ); - model_node nd(0, state, n.pt(), n.level()); - pred_transformer::scoped_farkas sf(n.pt(), true); - bool uses_level1 = uses_level; - if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) { - new_cores.push_back(std::make_pair(conv2, uses_level1)); - change = true; - expr_ref state1 = mk_and(conv2); - TRACE("pdr", - tout << mk_pp(state, m) << "\n"; - tout << "Generalized to:\n" << mk_pp(state1, m) << "\n";); - IF_VERBOSE(0, - verbose_stream() << mk_pp(state, m) << "\n"; - verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";); - } - } - if (!m_is_closure || !change) { - new_cores.push_back(std::make_pair(core, uses_level)); - } - } - - /* - Extract the lemmas from the transition relation that were used to establish unsatisfiability. - Take convex closures of conbinations of these lemmas. - */ - void core_convex_hull_generalizer::method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) { - TRACE("dl", tout << "method: generalize consequences of F(R)\n"; - for (unsigned i = 0; i < core.size(); ++i) { - tout << "B:" << mk_pp(core[i], m) << "\n"; - }); - bool uses_level1; - expr_ref_vector core1(m); - core1.append(core); - expr_ref_vector consequences(m); - { - n.pt().get_solver().set_consequences(&consequences); - pred_transformer::scoped_farkas sf (n.pt(), true); - VERIFY(l_false == n.pt().is_reachable(n, &core1, uses_level1)); - n.pt().get_solver().set_consequences(0); - } - IF_VERBOSE(0, - verbose_stream() << "Consequences: " << consequences.size() << "\n"; - for (unsigned i = 0; i < consequences.size(); ++i) { - verbose_stream() << mk_pp(consequences[i].get(), m) << "\n"; - } - verbose_stream() << "core: " << core1.size() << "\n"; - for (unsigned i = 0; i < core1.size(); ++i) { - verbose_stream() << mk_pp(core1[i].get(), m) << "\n"; - }); - - expr_ref tmp(m); - - // Check that F(R) => \/ consequences - { - expr_ref_vector cstate(m); - for (unsigned i = 0; i < consequences.size(); ++i) { - cstate.push_back(m.mk_not(consequences[i].get())); - } - tmp = m.mk_and(cstate.size(), cstate.c_ptr()); - model_node nd(0, tmp, n.pt(), n.level()); - pred_transformer::scoped_farkas sf (n.pt(), false); - VERIFY(l_false == n.pt().is_reachable(nd, &core1, uses_level1)); - } - - // Create disjunction. - tmp = m.mk_and(core.size(), core.c_ptr()); - - // Check that \/ consequences => not (core) - if (!is_unsat(consequences, tmp)) { - IF_VERBOSE(0, verbose_stream() << "Consequences don't contradict the core\n";); - return; - } - IF_VERBOSE(0, verbose_stream() << "Consequences contradict core\n";); - - if (!strengthen_consequences(n, consequences, tmp)) { - return; - } - - IF_VERBOSE(0, verbose_stream() << "consequences strengthened\n";); - // Use the resulting formula to find Farkas lemmas from core. - } - - bool core_convex_hull_generalizer::strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B) { - expr_ref A(m), tmp(m), convA(m); - unsigned sz = As.size(); - closure cl(n.pt(), m_is_closure); - for (unsigned i = 0; i < As.size(); ++i) { - expr_ref_vector Hs(m); - Hs.push_back(As[i].get()); - for (unsigned j = i + 1; j < As.size(); ++j) { - Hs.push_back(As[j].get()); - bool unsat = false; - A = cl(Hs); - tmp = As[i].get(); - As[i] = A; - unsat = is_unsat(As, B); - As[i] = tmp; - if (unsat) { - IF_VERBOSE(0, verbose_stream() << "New convex: " << mk_pp(convA, m) << "\n";); - convA = A; - As[j] = As.back(); - As.pop_back(); - --j; - } - else { - Hs.pop_back(); - } - } - if (Hs.size() > 1) { - As[i] = convA; - } - } - return sz > As.size(); - } - - - bool core_convex_hull_generalizer::is_unsat(expr_ref_vector const& As, expr* B) { - smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); - expr_ref disj(m); - disj = m.mk_or(As.size(), As.c_ptr()); - ctx.assert_expr(disj); - ctx.assert_expr(B); - std::cout << "Checking\n" << mk_pp(disj, m) << "\n" << mk_pp(B, m) << "\n"; - return l_false == ctx.check(); - } - - - // --------------------------------- - // core_arith_inductive_generalizer - // NB. this is trying out some ideas for generalization in - // an ad hoc specialized way. arith_inductive_generalizer should - // not be used by default. It is a place-holder for a general purpose - // extrapolator of a lattice basis. - - core_arith_inductive_generalizer::core_arith_inductive_generalizer(context& ctx): - core_generalizer(ctx), - m(ctx.get_manager()), - a(m), - m_refs(m) {} - - void core_arith_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - if (core.size() <= 1) { - return; - } - reset(); - expr_ref e(m), t1(m), t2(m), t3(m); - rational r; - - TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; }); - - svector eqs; - get_eqs(core, eqs); - - if (eqs.empty()) { - return; - } - - expr_ref_vector new_core(m); - new_core.append(core); - - for (unsigned eq = 0; eq < eqs.size(); ++eq) { - rational r = eqs[eq].m_value; - expr* x = eqs[eq].m_term; - unsigned k = eqs[eq].m_i; - unsigned l = eqs[eq].m_j; - - new_core[l] = m.mk_true(); - new_core[k] = m.mk_true(); - - for (unsigned i = 0; i < new_core.size(); ++i) { - if (substitute_alias(r, x, new_core[i].get(), e)) { - new_core[i] = e; - } - } - if (abs(r) >= rational(2) && a.is_int(x)) { - new_core[k] = m.mk_eq(a.mk_mod(x, a.mk_numeral(rational(2), true)), a.mk_numeral(rational(0), true)); - new_core[l] = a.mk_le(x, a.mk_numeral(rational(0), true)); - } - } - - bool inductive = n.pt().check_inductive(n.level(), new_core, uses_level); - - IF_VERBOSE(1, - verbose_stream() << (inductive?"":"non") << "inductive\n"; - verbose_stream() << "old\n"; - for (unsigned j = 0; j < core.size(); ++j) { - verbose_stream() << mk_pp(core[j].get(), m) << "\n"; - } - verbose_stream() << "new\n"; - for (unsigned j = 0; j < new_core.size(); ++j) { - verbose_stream() << mk_pp(new_core[j].get(), m) << "\n"; - }); - - if (inductive) { - core.reset(); - core.append(new_core); - } - } - - void core_arith_inductive_generalizer::insert_bound(bool is_lower, expr* x, rational const& r, unsigned i) { - if (r.is_neg()) { - expr_ref e(m); - e = a.mk_uminus(x); - m_refs.push_back(e); - x = e; - is_lower = !is_lower; - } - - vector bound; - bound.push_back(std::make_pair(x, i)); - if (is_lower) { - m_lb.insert(abs(r), bound); - } - else { - m_ub.insert(abs(r), bound); - } - } - - void core_arith_inductive_generalizer::reset() { - m_refs.reset(); - m_lb.reset(); - m_ub.reset(); - } - - void core_arith_inductive_generalizer::get_eqs(expr_ref_vector const& core, svector& eqs) { - expr* e1, *x, *y; - expr_ref e(m); - rational r; - - for (unsigned i = 0; i < core.size(); ++i) { - e = core[i]; - if (m.is_not(e, e1) && a.is_le(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) { - // not (<= x r) <=> x >= r + 1 - insert_bound(true, x, r + rational(1), i); - } - else if (m.is_not(e, e1) && a.is_ge(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) { - // not (>= x r) <=> x <= r - 1 - insert_bound(false, x, r - rational(1), i); - } - else if (a.is_le(e, x, y) && a.is_numeral(y, r)) { - insert_bound(false, x, r, i); - } - else if (a.is_ge(e, x, y) && a.is_numeral(y, r)) { - insert_bound(true, x, r, i); - } - } - bounds_t::iterator it = m_lb.begin(), end = m_lb.end(); - for (; it != end; ++it) { - rational r = it->m_key; - vector & terms1 = it->m_value; - vector terms2; - if (r >= rational(2) && m_ub.find(r, terms2)) { - for (unsigned i = 0; i < terms1.size(); ++i) { - bool done = false; - for (unsigned j = 0; !done && j < terms2.size(); ++j) { - expr* t1 = terms1[i].first; - expr* t2 = terms2[j].first; - if (t1 == t2) { - eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second)); - done = true; - } - else { - e = m.mk_eq(t1, t2); - th_rewriter rw(m); - rw(e); - if (m.is_true(e)) { - eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second)); - done = true; - } - } - } - } - } - } - } - - bool core_arith_inductive_generalizer::substitute_alias(rational const& r, expr* x, expr* e, expr_ref& result) { - rational r2; - expr* y, *z, *e1; - if (m.is_not(e, e1) && substitute_alias(r, x, e1, result)) { - result = m.mk_not(result); - return true; - } - if (a.is_le(e, y, z) && a.is_numeral(z, r2)) { - if (r == r2) { - result = a.mk_le(y, x); - return true; - } - if (r == r2 + rational(1)) { - result = a.mk_lt(y, x); - return true; - } - if (r == r2 - rational(1)) { - result = a.mk_le(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x)))); - return true; - } - - } - if (a.is_ge(e, y, z) && a.is_numeral(z, r2)) { - if (r == r2) { - result = a.mk_ge(y, x); - return true; - } - if (r2 == r + rational(1)) { - result = a.mk_gt(y, x); - return true; - } - if (r2 == r - rational(1)) { - result = a.mk_ge(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x)))); - return true; - } - } - return false; - } - - - // - // < F, phi, i + 1> - // | - // < G, psi, i > - // - // where: - // - // p(x) <- F(x,y,p,q) - // q(x) <- G(x,y) - // - // Hyp: - // Q_k(x) => phi(x) j <= k <= i - // Q_k(x) => R_k(x) j <= k <= i + 1 - // Q_k(x) <=> Trans(Q_{k-1}) j < k <= i + 1 - // Conclusion: - // Q_{i+1}(x) => phi(x) - // - class core_induction_generalizer::imp { - context& m_ctx; - manager& pm; - ast_manager& m; - - // - // Create predicate Q_level - // - func_decl_ref mk_pred(unsigned level, func_decl* f) { - func_decl_ref result(m); - std::ostringstream name; - name << f->get_name() << "_" << level; - symbol sname(name.str().c_str()); - result = m.mk_func_decl(sname, f->get_arity(), f->get_domain(), f->get_range()); - return result; - } - - // - // Create formula exists y . z . F[Q_{level-1}, x, y, z] - // - expr_ref mk_transition_rule( - expr_ref_vector const& reps, - unsigned level, - datalog::rule const& rule) - { - expr_ref_vector conj(m), sub(m); - expr_ref result(m); - svector names; - unsigned ut_size = rule.get_uninterpreted_tail_size(); - unsigned t_size = rule.get_tail_size(); - if (0 == level && 0 < ut_size) { - result = m.mk_false(); - return result; - } - app* atom = rule.get_head(); - SASSERT(atom->get_num_args() == reps.size()); - - for (unsigned i = 0; i < reps.size(); ++i) { - expr* arg = atom->get_arg(i); - if (is_var(arg)) { - unsigned idx = to_var(arg)->get_idx(); - if (idx >= sub.size()) sub.resize(idx+1); - if (sub[idx].get()) { - conj.push_back(m.mk_eq(sub[idx].get(), reps[i])); - } - else { - sub[idx] = reps[i]; - } - } - else { - conj.push_back(m.mk_eq(arg, reps[i])); - } - } - for (unsigned i = 0; 0 < level && i < ut_size; i++) { - app* atom = rule.get_tail(i); - func_decl* head = atom->get_decl(); - func_decl_ref fn = mk_pred(level-1, head); - conj.push_back(m.mk_app(fn, atom->get_num_args(), atom->get_args())); - } - for (unsigned i = ut_size; i < t_size; i++) { - conj.push_back(rule.get_tail(i)); - } - result = mk_and(conj); - if (!sub.empty()) { - expr_ref tmp = result; - var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), result); - } - expr_free_vars fv; - fv(result); - fv.set_default_sort(m.mk_bool_sort()); - for (unsigned i = 0; i < fv.size(); ++i) { - names.push_back(symbol(fv.size() - i - 1)); - } - if (!fv.empty()) { - fv.reverse(); - result = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), result); - } - return result; - } - - expr_ref bind_head(expr_ref_vector const& reps, expr* fml) { - expr_ref result(m); - expr_abstract(m, 0, reps.size(), reps.c_ptr(), fml, result); - ptr_vector sorts; - svector names; - unsigned sz = reps.size(); - for (unsigned i = 0; i < sz; ++i) { - sorts.push_back(m.get_sort(reps[sz-i-1])); - names.push_back(symbol(sz-i-1)); - } - if (sz > 0) { - result = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), result); - } - return result; - } - - expr_ref_vector mk_reps(pred_transformer& pt) { - expr_ref_vector reps(m); - expr_ref rep(m); - for (unsigned i = 0; i < pt.head()->get_arity(); ++i) { - rep = m.mk_const(pm.o2n(pt.sig(i), 0)); - reps.push_back(rep); - } - return reps; - } - - // - // extract transition axiom: - // - // forall x . p_lvl(x) <=> exists y z . F[p_{lvl-1}(y), q_{lvl-1}(z), x] - // - expr_ref mk_transition_axiom(pred_transformer& pt, unsigned level) { - expr_ref fml(m.mk_false(), m), tr(m); - expr_ref_vector reps = mk_reps(pt); - ptr_vector const& rules = pt.rules(); - for (unsigned i = 0; i < rules.size(); ++i) { - tr = mk_transition_rule(reps, level, *rules[i]); - fml = (i == 0)?tr.get():m.mk_or(fml, tr); - } - func_decl_ref fn = mk_pred(level, pt.head()); - fml = m.mk_iff(m.mk_app(fn, reps.size(), reps.c_ptr()), fml); - fml = bind_head(reps, fml); - return fml; - } - - // - // Create implication: - // Q_level(x) => phi(x) - // - expr_ref mk_predicate_property(unsigned level, pred_transformer& pt, expr* phi) { - expr_ref_vector reps = mk_reps(pt); - func_decl_ref fn = mk_pred(level, pt.head()); - expr_ref fml(m); - fml = m.mk_implies(m.mk_app(fn, reps.size(), reps.c_ptr()), phi); - fml = bind_head(reps, fml); - return fml; - } - - - - public: - imp(context& ctx): m_ctx(ctx), pm(ctx.get_pdr_manager()), m(ctx.get_manager()) {} - - // - // not exists y . F(x,y) - // - expr_ref mk_blocked_transition(pred_transformer& pt, unsigned level) { - SASSERT(level > 0); - expr_ref fml(m.mk_true(), m); - expr_ref_vector reps = mk_reps(pt), fmls(m); - ptr_vector const& rules = pt.rules(); - for (unsigned i = 0; i < rules.size(); ++i) { - fmls.push_back(m.mk_not(mk_transition_rule(reps, level, *rules[i]))); - } - fml = mk_and(fmls); - TRACE("pdr", tout << mk_pp(fml, m) << "\n";); - return fml; - } - - expr_ref mk_induction_goal(pred_transformer& pt, unsigned level, unsigned depth) { - SASSERT(level >= depth); - expr_ref_vector conjs(m); - ptr_vector pts; - unsigned_vector levels; - // negated goal - expr_ref phi = mk_blocked_transition(pt, level); - conjs.push_back(m.mk_not(mk_predicate_property(level, pt, phi))); - pts.push_back(&pt); - levels.push_back(level); - // Add I.H. - for (unsigned lvl = level-depth; lvl < level; ++lvl) { - if (lvl > 0) { - expr_ref psi = mk_blocked_transition(pt, lvl); - conjs.push_back(mk_predicate_property(lvl, pt, psi)); - pts.push_back(&pt); - levels.push_back(lvl); - } - } - // Transitions: - for (unsigned qhead = 0; qhead < pts.size(); ++qhead) { - pred_transformer& qt = *pts[qhead]; - unsigned lvl = levels[qhead]; - - // Add transition definition and properties at level. - conjs.push_back(mk_transition_axiom(qt, lvl)); - conjs.push_back(mk_predicate_property(lvl, qt, qt.get_formulas(lvl, true))); - - // Enqueue additional hypotheses - ptr_vector const& rules = qt.rules(); - if (lvl + depth < level || lvl == 0) { - continue; - } - for (unsigned i = 0; i < rules.size(); ++i) { - datalog::rule& r = *rules[i]; - unsigned ut_size = r.get_uninterpreted_tail_size(); - for (unsigned j = 0; j < ut_size; ++j) { - func_decl* f = r.get_tail(j)->get_decl(); - pred_transformer* rt = m_ctx.get_pred_transformers().find(f); - bool found = false; - for (unsigned k = 0; !found && k < levels.size(); ++k) { - found = (rt == pts[k] && levels[k] + 1 == lvl); - } - if (!found) { - levels.push_back(lvl-1); - pts.push_back(rt); - } - } - } - } - - expr_ref result = mk_and(conjs); - TRACE("pdr", tout << mk_pp(result, m) << "\n";); - return result; - } - }; - - // - // Instantiate Peano induction schema. - // - void core_induction_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) { - model_node* p = n.parent(); - if (p == 0) { - return; - } - unsigned depth = 2; - imp imp(m_ctx); - ast_manager& m = core.get_manager(); - expr_ref goal = imp.mk_induction_goal(p->pt(), p->level(), depth); - smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p); - ctx.assert_expr(goal); - lbool r = ctx.check(); - TRACE("pdr", tout << r << "\n"; - for (unsigned i = 0; i < core.size(); ++i) { - tout << mk_pp(core[i].get(), m) << "\n"; - }); - if (r == l_false) { - core.reset(); - expr_ref phi = imp.mk_blocked_transition(p->pt(), p->level()); - core.push_back(m.mk_not(phi)); - uses_level = true; - } - } -}; - diff --git a/src/muz/pdr/pdr_generalizers.h b/src/muz/pdr/pdr_generalizers.h deleted file mode 100644 index e0feda310..000000000 --- a/src/muz/pdr/pdr_generalizers.h +++ /dev/null @@ -1,110 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_generalizers.h - -Abstract: - - Generalizer plugins. - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-22. - -Revision History: - ---*/ - -#ifndef PDR_GENERALIZERS_H_ -#define PDR_GENERALIZERS_H_ - -#include "muz/pdr/pdr_context.h" -#include "muz/pdr/pdr_closure.h" -#include "ast/arith_decl_plugin.h" - -namespace pdr { - - class core_bool_inductive_generalizer : public core_generalizer { - unsigned m_failure_limit; - public: - core_bool_inductive_generalizer(context& ctx, unsigned failure_limit) : core_generalizer(ctx), m_failure_limit(failure_limit) {} - virtual ~core_bool_inductive_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); - }; - - template - class r_map : public map { - }; - - class core_arith_inductive_generalizer : public core_generalizer { - typedef std::pair term_loc_t; - typedef r_map > bounds_t; - - ast_manager& m; - arith_util a; - expr_ref_vector m_refs; - bounds_t m_lb; - bounds_t m_ub; - - struct eq { - expr* m_term; - rational m_value; - unsigned m_i; - unsigned m_j; - eq(expr* t, rational const& r, unsigned i, unsigned j): m_term(t), m_value(r), m_i(i), m_j(j) {} - }; - void reset(); - void insert_bound(bool is_lower, expr* x, rational const& r, unsigned i); - void get_eqs(expr_ref_vector const& core, svector& eqs); - bool substitute_alias(rational const&r, expr* x, expr* e, expr_ref& result); - public: - core_arith_inductive_generalizer(context& ctx); - virtual ~core_arith_inductive_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); - }; - - class core_farkas_generalizer : public core_generalizer { - farkas_learner m_farkas_learner; - public: - core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p); - virtual ~core_farkas_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); - virtual void collect_statistics(statistics& st) const; - }; - - - class core_convex_hull_generalizer : public core_generalizer { - ast_manager& m; - obj_map m_models; - bool m_is_closure; - void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); - void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); - bool strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B); - bool is_unsat(expr_ref_vector const& As, expr* B); - public: - core_convex_hull_generalizer(context& ctx, bool is_closure); - virtual ~core_convex_hull_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); - virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); - }; - - class core_multi_generalizer : public core_generalizer { - core_bool_inductive_generalizer m_gen; - public: - core_multi_generalizer(context& ctx, unsigned max_failures): core_generalizer(ctx), m_gen(ctx, max_failures) {} - virtual ~core_multi_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); - virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores); - }; - - class core_induction_generalizer : public core_generalizer { - class imp; - public: - core_induction_generalizer(context& ctx): core_generalizer(ctx) {} - virtual ~core_induction_generalizer() {} - virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level); - }; -}; -#endif diff --git a/src/muz/pdr/pdr_manager.cpp b/src/muz/pdr/pdr_manager.cpp deleted file mode 100644 index 077d27427..000000000 --- a/src/muz/pdr/pdr_manager.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_manager.cpp - -Abstract: - - A manager class for PDR, taking care of creating of AST - objects and conversions between them. - -Author: - - Krystof Hoder (t-khoder) 2011-8-25. - -Revision History: - ---*/ - -#include -#include "muz/pdr/pdr_manager.h" -#include "ast/ast_smt2_pp.h" -#include "ast/for_each_expr.h" -#include "ast/has_free_vars.h" -#include "ast/rewriter/expr_replacer.h" -#include "ast/expr_abstract.h" -#include "model/model2expr.h" -#include "model/model_smt2_pp.h" -#include "tactic/model_converter.h" - -namespace pdr { - - class collect_decls_proc { - func_decl_set& m_bound_decls; - func_decl_set& m_aux_decls; - public: - collect_decls_proc(func_decl_set& bound_decls, func_decl_set& aux_decls): - m_bound_decls(bound_decls), - m_aux_decls(aux_decls) { - } - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - func_decl* f = a->get_decl(); - if (!m_bound_decls.contains(f)) { - m_aux_decls.insert(f); - } - } - } - void operator()(var* v) {} - void operator()(quantifier* q) {} - }; - - typedef hashtable symbol_set; - - expr_ref inductive_property::fixup_clause(expr* fml) const { - expr_ref_vector disjs(m); - flatten_or(fml, disjs); - expr_ref result(m); - bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result); - return result; - } - - expr_ref inductive_property::fixup_clauses(expr* fml) const { - expr_ref_vector conjs(m); - expr_ref result(m); - flatten_and(fml, conjs); - for (unsigned i = 0; i < conjs.size(); ++i) { - conjs[i] = fixup_clause(conjs[i].get()); - } - bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result); - return result; - } - - std::string inductive_property::to_string() const { - std::stringstream stm; - model_ref md; - expr_ref result(m); - to_model(md); - model_smt2_pp(stm, m, *md.get(), 0); - return stm.str(); - } - - void inductive_property::to_model(model_ref& md) const { - md = alloc(model, m); - vector const& rs = m_relation_info; - expr_ref_vector conjs(m); - for (unsigned i = 0; i < rs.size(); ++i) { - relation_info ri(rs[i]); - func_decl * pred = ri.m_pred; - expr_ref prop = fixup_clauses(ri.m_body); - func_decl_ref_vector const& sig = ri.m_vars; - expr_ref q(m); - expr_ref_vector sig_vars(m); - for (unsigned j = 0; j < sig.size(); ++j) { - sig_vars.push_back(m.mk_const(sig[sig.size()-j-1])); - } - expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q); - if (sig.empty()) { - md->register_decl(pred, q); - } - else { - func_interp* fi = alloc(func_interp, m, sig.size()); - fi->set_else(q); - md->register_decl(pred, fi); - } - } - TRACE("pdr", model_smt2_pp(tout, m, *md, 0);); - apply(const_cast(m_mc), md, 0); - } - - expr_ref inductive_property::to_expr() const { - model_ref md; - expr_ref result(m); - to_model(md); - model2expr(md, result); - return result; - } - - - void inductive_property::display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const { - func_decl_set bound_decls, aux_decls; - collect_decls_proc collect_decls(bound_decls, aux_decls); - - for (unsigned i = 0; i < m_relation_info.size(); ++i) { - bound_decls.insert(m_relation_info[i].m_pred); - func_decl_ref_vector const& sig = m_relation_info[i].m_vars; - for (unsigned j = 0; j < sig.size(); ++j) { - bound_decls.insert(sig[j]); - } - for_each_expr(collect_decls, m_relation_info[i].m_body); - } - for (unsigned i = 0; i < rules.size(); ++i) { - bound_decls.insert(rules[i]->get_decl()); - } - for (unsigned i = 0; i < rules.size(); ++i) { - unsigned u_sz = rules[i]->get_uninterpreted_tail_size(); - unsigned t_sz = rules[i]->get_tail_size(); - for (unsigned j = u_sz; j < t_sz; ++j) { - for_each_expr(collect_decls, rules[i]->get_tail(j)); - } - } - smt2_pp_environment_dbg env(m); - func_decl_set::iterator it = aux_decls.begin(), end = aux_decls.end(); - for (; it != end; ++it) { - func_decl* f = *it; - ast_smt2_pp(out, f, env); - out << "\n"; - } - - out << to_string() << "\n"; - for (unsigned i = 0; i < rules.size(); ++i) { - out << "(push)\n"; - out << "(assert (not\n"; - rm.display_smt2(*rules[i], out); - out << "))\n"; - out << "(check-sat)\n"; - out << "(pop)\n"; - } - } - - manager::manager(smt_params& fparams, unsigned max_num_contexts, ast_manager& manager) : - m(manager), - m_fparams(fparams), - m_brwr(m), - m_mux(m), - m_background(m.mk_true(), m), - m_contexts(fparams, max_num_contexts, m), - m_next_unique_num(0) - { - } - - - void manager::add_new_state(func_decl * s) { - SASSERT(s->get_arity()==0); //we currently don't support non-constant states - decl_vector vect; - SASSERT(o_index(0)==1); //we assume this in the number of retrieved symbols - m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect); - m_o0_preds.push_back(vect[o_index(0)]); - } - - func_decl * manager::get_o_pred(func_decl* s, unsigned idx) - { - func_decl * res = m_mux.try_get_by_prefix(s, o_index(idx)); - if(res) { return res; } - add_new_state(s); - res = m_mux.try_get_by_prefix(s, o_index(idx)); - SASSERT(res); - return res; - } - - func_decl * manager::get_n_pred(func_decl* s) - { - func_decl * res = m_mux.try_get_by_prefix(s, n_index()); - if(res) { return res; } - add_new_state(s); - res = m_mux.try_get_by_prefix(s, n_index()); - SASSERT(res); - return res; - } - - void manager::mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res) { - m_brwr.mk_and(mdl.size(), mdl.c_ptr(), res); - } - - void manager::mk_core_into_cube(const expr_ref_vector & core, expr_ref & res) { - m_brwr.mk_and(core.size(), core.c_ptr(), res); - } - - void manager::mk_cube_into_lemma(expr * cube, expr_ref & res) { - m_brwr.mk_not(cube, res); - } - - void manager::mk_lemma_into_cube(expr * lemma, expr_ref & res) { - m_brwr.mk_not(lemma, res); - } - - expr_ref manager::mk_and(unsigned sz, expr* const* exprs) { - expr_ref result(m); - m_brwr.mk_and(sz, exprs, result); - return result; - } - - expr_ref manager::mk_or(unsigned sz, expr* const* exprs) { - expr_ref result(m); - m_brwr.mk_or(sz, exprs, result); - return result; - } - - expr_ref manager::mk_not_and(expr_ref_vector const& conjs) { - expr_ref result(m), e(m); - expr_ref_vector es(conjs); - flatten_and(es); - for (unsigned i = 0; i < es.size(); ++i) { - m_brwr.mk_not(es[i].get(), e); - es[i] = e; - } - m_brwr.mk_or(es.size(), es.c_ptr(), result); - return result; - } - - void manager::get_or(expr* e, expr_ref_vector& result) { - result.push_back(e); - for (unsigned i = 0; i < result.size(); ) { - e = result[i].get(); - if (m.is_or(e)) { - result.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - result[i] = result.back(); - result.pop_back(); - } - else { - ++i; - } - } - } - - bool manager::try_get_state_and_value_from_atom(expr * atom0, app *& state, app_ref& value) - { - if(!is_app(atom0)) { - return false; - } - app * atom = to_app(atom0); - expr * arg1; - expr * arg2; - app * candidate_state; - app_ref candidate_value(m); - if(m.is_not(atom, arg1)) { - if(!is_app(arg1)) { - return false; - } - candidate_state = to_app(arg1); - candidate_value = m.mk_false(); - } - else if(m.is_eq(atom, arg1, arg2)) { - if(!is_app(arg1) || !is_app(arg2)) { - return false; - } - if(!m_mux.is_muxed(to_app(arg1)->get_decl())) { - std::swap(arg1, arg2); - } - candidate_state = to_app(arg1); - candidate_value = to_app(arg2); - } - else { - candidate_state = atom; - candidate_value = m.mk_true(); - } - if(!m_mux.is_muxed(candidate_state->get_decl())) { - return false; - } - state = candidate_state; - value = candidate_value; - return true; - } - - bool manager::try_get_state_decl_from_atom(expr * atom, func_decl *& state) { - app_ref dummy_value_holder(m); - app * s; - if(try_get_state_and_value_from_atom(atom, s, dummy_value_holder)) { - state = s->get_decl(); - return true; - } - else { - return false; - } - } - - bool manager::implication_surely_holds(expr * lhs, expr * rhs, expr * bg) { - smt::kernel sctx(m, get_fparams()); - if(bg) { - sctx.assert_expr(bg); - } - sctx.assert_expr(lhs); - expr_ref neg_rhs(m.mk_not(rhs),m); - sctx.assert_expr(neg_rhs); - lbool smt_res = sctx.check(); - return smt_res==l_false; - } - -}; diff --git a/src/muz/pdr/pdr_manager.h b/src/muz/pdr/pdr_manager.h deleted file mode 100644 index 9fc98940d..000000000 --- a/src/muz/pdr/pdr_manager.h +++ /dev/null @@ -1,304 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_manager.h - -Abstract: - - A manager class for PDR, taking care of creating of AST - objects and conversions between them. - -Author: - - Krystof Hoder (t-khoder) 2011-8-25. - -Revision History: - ---*/ - -#ifndef PDR_MANAGER_H_ -#define PDR_MANAGER_H_ - -#include -#include -#include "ast/rewriter/bool_rewriter.h" -#include "ast/rewriter/expr_replacer.h" -#include "ast/expr_substitution.h" -#include "util/map.h" -#include "util/ref_vector.h" -#include "smt/smt_kernel.h" -#include "muz/pdr/pdr_util.h" -#include "muz/pdr/pdr_sym_mux.h" -#include "muz/pdr/pdr_farkas_learner.h" -#include "muz/pdr/pdr_smt_context_manager.h" -#include "muz/base/dl_rule.h" - - -namespace smt { - class context; -} - -namespace pdr { - - struct relation_info { - func_decl_ref m_pred; - func_decl_ref_vector m_vars; - expr_ref m_body; - relation_info(ast_manager& m, func_decl* pred, ptr_vector const& vars, expr* b): - m_pred(pred, m), m_vars(m, vars.size(), vars.c_ptr()), m_body(b, m) {} - relation_info(relation_info const& other): m_pred(other.m_pred), m_vars(other.m_vars), m_body(other.m_body) {} - }; - - class unknown_exception {}; - - class inductive_property { - ast_manager& m; - model_converter_ref m_mc; - vector m_relation_info; - expr_ref fixup_clauses(expr* property) const; - expr_ref fixup_clause(expr* clause) const; - public: - inductive_property(ast_manager& m, model_converter_ref& mc, vector const& relations): - m(m), - m_mc(mc), - m_relation_info(relations) {} - - std::string to_string() const; - - expr_ref to_expr() const; - - void to_model(model_ref& md) const; - - void display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const; - }; - - class manager - { - ast_manager& m; - smt_params& m_fparams; - - mutable bool_rewriter m_brwr; - - sym_mux m_mux; - expr_ref m_background; - decl_vector m_o0_preds; - pdr::smt_context_manager m_contexts; - - /** whenever we need an unique number, we get this one and increase */ - unsigned m_next_unique_num; - - - unsigned n_index() const { return 0; } - unsigned o_index(unsigned i) const { return i+1; } - - void add_new_state(func_decl * s); - - public: - manager(smt_params& fparams, unsigned max_num_contexts, ast_manager & manager); - - ast_manager& get_manager() const { return m; } - smt_params& get_fparams() const { return m_fparams; } - bool_rewriter& get_brwr() const { return m_brwr; } - - expr_ref mk_and(unsigned sz, expr* const* exprs); - expr_ref mk_and(expr_ref_vector const& exprs) { - return mk_and(exprs.size(), exprs.c_ptr()); - } - expr_ref mk_and(expr* a, expr* b) { - expr* args[2] = { a, b }; - return mk_and(2, args); - } - expr_ref mk_or(unsigned sz, expr* const* exprs); - expr_ref mk_or(expr_ref_vector const& exprs) { - return mk_or(exprs.size(), exprs.c_ptr()); - } - - expr_ref mk_not_and(expr_ref_vector const& exprs); - - void get_or(expr* e, expr_ref_vector& result); - - //"o" predicates stand for the old states and "n" for the new states - func_decl * get_o_pred(func_decl * s, unsigned idx); - func_decl * get_n_pred(func_decl * s); - - /** - Marks symbol as non-model which means it will not appear in models collected by - get_state_cube_from_model function. - This is to take care of auxiliary symbols introduced by the disjunction relations - to relativize lemmas coming from disjuncts. - */ - void mark_as_non_model(func_decl * p) { - m_mux.mark_as_non_model(p); - } - - - func_decl * const * begin_o0_preds() const { return m_o0_preds.begin(); } - func_decl * const * end_o0_preds() const { return m_o0_preds.end(); } - - bool is_state_pred(func_decl * p) const { return m_mux.is_muxed(p); } - func_decl * to_o0(func_decl * p) { return m_mux.conv(m_mux.get_primary(p), 0, o_index(0)); } - - bool is_o(func_decl * p, unsigned idx) const { - return m_mux.has_index(p, o_index(idx)); - } - bool is_o(expr* e, unsigned idx) const { - return is_app(e) && is_o(to_app(e)->get_decl(), idx); - } - bool is_o(func_decl * p) const { - unsigned idx; - return m_mux.try_get_index(p, idx) && idx!=n_index(); - } - bool is_o(expr* e) const { - return is_app(e) && is_o(to_app(e)->get_decl()); - } - bool is_n(func_decl * p) const { - return m_mux.has_index(p, n_index()); - } - bool is_n(expr* e) const { - return is_app(e) && is_n(to_app(e)->get_decl()); - } - - /** true if p should not appead in models propagates into child relations */ - bool is_non_model_sym(func_decl * p) const - { return m_mux.is_non_model_sym(p); } - - - /** true if f doesn't contain any n predicates */ - bool is_o_formula(expr * f) const { - return !m_mux.contains(f, n_index()); - } - - /** true if f contains only o state preds of index o_idx */ - bool is_o_formula(expr * f, unsigned o_idx) const { - return m_mux.is_homogenous_formula(f, o_index(o_idx)); - } - /** true if f doesn't contain any o predicates */ - bool is_n_formula(expr * f) const { - return m_mux.is_homogenous_formula(f, n_index()); - } - - func_decl * o2n(func_decl * p, unsigned o_idx) { - return m_mux.conv(p, o_index(o_idx), n_index()); - } - func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) { - return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx)); - } - func_decl * n2o(func_decl * p, unsigned o_idx) { - return m_mux.conv(p, n_index(), o_index(o_idx)); - } - - void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true) - { m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous); } - - void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true) - { m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); } - - void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) - { m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous); } - - void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous=true) - { m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous); } - - /** - Return true if all state symbols which e contains are of one kind (either "n" or one of "o"). - */ - bool is_homogenous_formula(expr * e) const { - return m_mux.is_homogenous_formula(e); - } - - /** - Collect indices used in expression. - */ - void collect_indices(expr* e, unsigned_vector& indices) const { - m_mux.collect_indices(e, indices); - } - - /** - Collect used variables of each index. - */ - void collect_variables(expr* e, vector >& vars) const { - m_mux.collect_variables(e, vars); - } - - /** - Return true iff both s1 and s2 are either "n" or "o" of the same index. - If one (or both) of them are not state symbol, return false. - */ - bool have_different_state_kinds(func_decl * s1, func_decl * s2) const { - unsigned i1, i2; - return m_mux.try_get_index(s1, i1) && m_mux.try_get_index(s2, i2) && i1!=i2; - } - - /** - Increase indexes of state symbols in formula by dist. - The 'N' index becomes 'O' index with number dist-1. - */ - void formula_shift(expr * src, expr_ref & tgt, unsigned dist) { - SASSERT(n_index()==0); - SASSERT(o_index(0)==1); - m_mux.shift_formula(src, dist, tgt); - } - - void mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res); - void mk_core_into_cube(const expr_ref_vector & core, expr_ref & res); - void mk_cube_into_lemma(expr * cube, expr_ref & res); - void mk_lemma_into_cube(expr * lemma, expr_ref & res); - - /** - Remove from vec all atoms that do not have an "o" state. - The order of elements in vec may change. - An assumption is that atoms having "o" state of given index - do not have "o" states of other indexes or "n" states. - */ - void filter_o_atoms(expr_ref_vector& vec, unsigned o_idx) const - { m_mux.filter_idx(vec, o_index(o_idx)); } - void filter_n_atoms(expr_ref_vector& vec) const - { m_mux.filter_idx(vec, n_index()); } - - /** - Partition literals into o_lits and others. - */ - void partition_o_atoms(expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, - unsigned o_idx) const { - m_mux.partition_o_idx(lits, o_lits, other, o_index(o_idx)); - } - - void filter_out_non_model_atoms(expr_ref_vector& vec) const - { m_mux.filter_non_model_lits(vec); } - - bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value); - bool try_get_state_decl_from_atom(expr * atom, func_decl *& state); - - - std::string pp_model(const model_core & mdl) const - { return m_mux.pp_model(mdl); } - - - void set_background(expr* b) { m_background = b; } - - expr* get_background() const { return m_background; } - - - /** - Return true if we can show that lhs => rhs. The function can have false negatives - (i.e. when smt::context returns unknown), but no false positives. - - bg is background knowledge and can be null - */ - bool implication_surely_holds(expr * lhs, expr * rhs, expr * bg=0); - - unsigned get_unique_num() { return m_next_unique_num++; } - - pdr::smt_context* mk_fresh() { return m_contexts.mk_fresh(); } - - void collect_statistics(statistics& st) const { m_contexts.collect_statistics(st); } - - void reset_statistics() { m_contexts.reset_statistics(); } - }; -} - -#endif diff --git a/src/muz/pdr/pdr_prop_solver.cpp b/src/muz/pdr/pdr_prop_solver.cpp deleted file mode 100644 index 3055985f4..000000000 --- a/src/muz/pdr/pdr_prop_solver.cpp +++ /dev/null @@ -1,459 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - prop_solver.cpp - -Abstract: - - SMT solver abstraction for PDR. - -Author: - - Krystof Hoder (t-khoder) 2011-8-17. - -Revision History: - ---*/ - -#include -#include "model/model.h" -#include "muz/pdr/pdr_util.h" -#include "muz/pdr/pdr_prop_solver.h" -#include "ast/ast_smt2_pp.h" -#include "muz/base/dl_util.h" -#include "model/model_pp.h" -#include "smt/params/smt_params.h" -#include "ast/datatype_decl_plugin.h" -#include "ast/bv_decl_plugin.h" -#include "muz/pdr/pdr_farkas_learner.h" -#include "ast/ast_smt2_pp.h" -#include "ast/rewriter/expr_replacer.h" - -// -// Auxiliary structure to introduce propositional names for assumptions that are not -// propositional. It is to work with the smt::context's restriction -// that assumptions be propositional literals. -// - -namespace pdr { - - class prop_solver::safe_assumptions { - prop_solver& s; - ast_manager& m; - expr_ref_vector m_atoms; - expr_ref_vector m_assumptions; - obj_map m_proxies2expr; - obj_map m_expr2proxies; - unsigned m_num_proxies; - - app * mk_proxy(expr* literal) { - app* res; - SASSERT(!is_var(literal)); //it doesn't make sense to introduce names to variables - if (m_expr2proxies.find(literal, res)) { - return res; - } - SASSERT(s.m_proxies.size() >= m_num_proxies); - if (m_num_proxies == s.m_proxies.size()) { - std::stringstream name; - name << "pdr_proxy_" << s.m_proxies.size(); - res = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); - s.m_proxies.push_back(res); - s.m_aux_symbols.insert(res->get_decl()); - } - else { - res = s.m_proxies[m_num_proxies].get(); - } - ++m_num_proxies; - m_expr2proxies.insert(literal, res); - m_proxies2expr.insert(res, literal); - expr_ref implies(m.mk_or(m.mk_not(res), literal), m); - s.m_ctx->assert_expr(implies); - m_assumptions.push_back(implies); - TRACE("pdr_verbose", tout << "name asserted " << mk_pp(implies, m) << "\n";); - return res; - } - - void mk_safe(expr_ref_vector& conjs) { - flatten_and(conjs); - expand_literals(conjs); - for (unsigned i = 0; i < conjs.size(); ++i) { - expr * lit = conjs[i].get(); - expr * lit_core = lit; - m.is_not(lit, lit_core); - SASSERT(!m.is_true(lit)); - if (!is_uninterp(lit_core) || to_app(lit_core)->get_num_args() != 0) { - conjs[i] = mk_proxy(lit); - } - } - m_assumptions.append(conjs); - } - - expr* apply_accessor( - ptr_vector const& acc, - unsigned j, - func_decl* f, - expr* c) { - if (is_app(c) && to_app(c)->get_decl() == f) { - return to_app(c)->get_arg(j); - } - else { - return m.mk_app(acc[j], c); - } - } - - void expand_literals(expr_ref_vector& conjs) { - arith_util arith(m); - datatype_util dt(m); - bv_util bv(m); - expr* e1, *e2, *c, *val; - rational r; - unsigned bv_size; - - TRACE("pdr", - tout << "begin expand\n"; - for (unsigned i = 0; i < conjs.size(); ++i) { - tout << mk_pp(conjs[i].get(), m) << "\n"; - }); - - for (unsigned i = 0; i < conjs.size(); ++i) { - expr* e = conjs[i].get(); - if (m.is_eq(e, e1, e2) && arith.is_int_real(e1)) { - conjs[i] = arith.mk_le(e1,e2); - if (i+1 == conjs.size()) { - conjs.push_back(arith.mk_ge(e1, e2)); - } - else { - conjs.push_back(conjs[i+1].get()); - conjs[i+1] = arith.mk_ge(e1, e2); - } - ++i; - } - else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) || - (m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))){ - func_decl* f = to_app(val)->get_decl(); - func_decl* r = dt.get_constructor_recognizer(f); - conjs[i] = m.mk_app(r, c); - ptr_vector const& acc = *dt.get_constructor_accessors(f); - for (unsigned j = 0; j < acc.size(); ++j) { - conjs.push_back(m.mk_eq(apply_accessor(acc, j, f, c), to_app(val)->get_arg(j))); - } - } - else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) || - (m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) { - rational two(2); - for (unsigned j = 0; j < bv_size; ++j) { - parameter p(j); - //expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c); - expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c)); - if ((r % two).is_zero()) { - e = m.mk_not(e); - } - r = div(r, two); - if (j == 0) { - conjs[i] = e; - } - else { - conjs.push_back(e); - } - } - } - } - TRACE("pdr", - tout << "end expand\n"; - for (unsigned i = 0; i < conjs.size(); ++i) { - tout << mk_pp(conjs[i].get(), m) << "\n"; - }); - } - - public: - safe_assumptions(prop_solver& s, expr_ref_vector const& assumptions): - s(s), m(s.m), m_atoms(assumptions), m_assumptions(m), m_num_proxies(0) { - mk_safe(m_atoms); - } - - ~safe_assumptions() { - } - - expr_ref_vector const& atoms() const { return m_atoms; } - - unsigned assumptions_size() const { return m_assumptions.size(); } - - expr* assumptions(unsigned i) const { return m_assumptions[i]; } - - void undo_proxies(expr_ref_vector& es) { - expr_ref e(m); - expr* r; - for (unsigned i = 0; i < es.size(); ++i) { - e = es[i].get(); - if (is_app(e) && m_proxies2expr.find(to_app(e), r)) { - es[i] = r; - } - } - } - - void elim_proxies(expr_ref_vector& es) { - expr_substitution sub(m, false, m.proofs_enabled()); - proof_ref pr(m); - if (m.proofs_enabled()) { - pr = m.mk_asserted(m.mk_true()); - } - obj_map::iterator it = m_proxies2expr.begin(), end = m_proxies2expr.end(); - for (; it != end; ++it) { - sub.insert(it->m_key, m.mk_true(), pr); - } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - replace_proxies(*rep, es); - } - private: - - void replace_proxies(expr_replacer& rep, expr_ref_vector& es) { - expr_ref e(m); - for (unsigned i = 0; i < es.size(); ++i) { - e = es[i].get(); - rep(e); - es[i] = e; - if (m.is_true(e)) { - es[i] = es.back(); - es.pop_back(); - --i; - } - } - } - }; - - - prop_solver::prop_solver(manager& pm, symbol const& name) : - m_fparams(pm.get_fparams()), - m(pm.get_manager()), - m_pm(pm), - m_name(name), - m_ctx(pm.mk_fresh()), - m_pos_level_atoms(m), - m_neg_level_atoms(m), - m_proxies(m), - m_core(0), - m_model(0), - m_consequences(0), - m_subset_based_core(false), - m_use_farkas(false), - m_in_level(false), - m_current_level(0) - { - m_ctx->assert_expr(m_pm.get_background()); - } - - void prop_solver::add_level() { - unsigned idx = level_cnt(); - std::stringstream name; - name << m_name << "#level_" << idx; - func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, 0,m.mk_bool_sort()); - m_aux_symbols.insert(lev_pred); - m_level_preds.push_back(lev_pred); - - app_ref pos_la(m.mk_const(lev_pred), m); - app_ref neg_la(m.mk_not(pos_la.get()), m); - - m_pos_level_atoms.push_back(pos_la); - m_neg_level_atoms.push_back(neg_la); - - m_level_atoms_set.insert(pos_la.get()); - m_level_atoms_set.insert(neg_la.get()); - } - - void prop_solver::ensure_level(unsigned lvl) { - while (lvl>=level_cnt()) { - add_level(); - } - } - - unsigned prop_solver::level_cnt() const { - return m_level_preds.size(); - } - - void prop_solver::push_level_atoms(unsigned level, expr_ref_vector& tgt) const { - unsigned lev_cnt = level_cnt(); - for (unsigned i=0; i=level; - app * lev_atom = active ? m_neg_level_atoms[i] : m_pos_level_atoms[i]; - tgt.push_back(lev_atom); - } - } - - void prop_solver::add_formula(expr * form) { - SASSERT(!m_in_level); - m_ctx->assert_expr(form); - IF_VERBOSE(21, verbose_stream() << "$ asserted " << mk_pp(form, m) << "\n";); - TRACE("pdr", tout << "add_formula: " << mk_pp(form, m) << "\n";); - } - - void prop_solver::add_level_formula(expr * form, unsigned level) { - ensure_level(level); - app * lev_atom = m_pos_level_atoms[level].get(); - app_ref lform(m.mk_or(form, lev_atom), m); - add_formula(lform.get()); - } - - - lbool prop_solver::check_safe_assumptions( - safe_assumptions& safe, - const expr_ref_vector& atoms) - { - flet _model(m_fparams.m_model, m_model != 0); - expr_ref_vector expr_atoms(m); - expr_atoms.append(atoms.size(), atoms.c_ptr()); - - if (m_in_level) { - push_level_atoms(m_current_level, expr_atoms); - } - - lbool result = m_ctx->check(expr_atoms); - - TRACE("pdr", - tout << mk_pp(m_pm.mk_and(expr_atoms), m) << "\n"; - tout << result << "\n";); - - if (result == l_true && m_model) { - m_ctx->get_model(*m_model); - TRACE("pdr_verbose", model_pp(tout, **m_model); ); - } - - if (result == l_false) { - unsigned core_size = m_ctx->get_unsat_core_size(); - m_assumes_level = false; - for (unsigned i = 0; i < core_size; ++i) { - if (m_level_atoms_set.contains(m_ctx->get_unsat_core_expr(i))) { - m_assumes_level = true; - break; - } - } - } - - if (result == l_false && - m_core && - m.proofs_enabled() && - m_use_farkas && - !m_subset_based_core) { - extract_theory_core(safe); - } - else if (result == l_false && m_core) { - extract_subset_core(safe); - SASSERT(expr_atoms.size() >= m_core->size()); - } - m_core = 0; - m_model = 0; - m_subset_based_core = false; - return result; - } - - void prop_solver::extract_subset_core(safe_assumptions& safe) { - unsigned core_size = m_ctx->get_unsat_core_size(); - m_core->reset(); - for (unsigned i = 0; i < core_size; ++i) { - expr * core_expr = m_ctx->get_unsat_core_expr(i); - SASSERT(is_app(core_expr)); - - if (m_level_atoms_set.contains(core_expr)) { - continue; - } - if (m_ctx->is_aux_predicate(core_expr)) { - continue; - } - m_core->push_back(to_app(core_expr)); - } - - safe.undo_proxies(*m_core); - - TRACE("pdr", - tout << "core_exprs: "; - for (unsigned i = 0; i < core_size; ++i) { - tout << mk_pp(m_ctx->get_unsat_core_expr(i), m) << " "; - } - tout << "\n"; - tout << "core: " << mk_pp(m_pm.mk_and(*m_core), m) << "\n"; - ); - } - - - void prop_solver::extract_theory_core(safe_assumptions& safe) { - proof_ref pr(m); - pr = m_ctx->get_proof(); - IF_VERBOSE(21, verbose_stream() << mk_ismt2_pp(pr, m) << "\n";); - farkas_learner fl(m_fparams, m); - expr_ref_vector lemmas(m); - obj_hashtable bs; - for (unsigned i = 0; i < safe.assumptions_size(); ++i) { - bs.insert(safe.assumptions(i)); - } - fl.get_lemmas(pr, bs, lemmas); - safe.elim_proxies(lemmas); - fl.simplify_lemmas(lemmas); // redundant? - - bool outside_of_logic = - (m_fparams.m_arith_mode == AS_DIFF_LOGIC && - !is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) || - (m_fparams.m_arith_mode == AS_UTVPI && - !is_utvpi_logic(m, lemmas.size(), lemmas.c_ptr())); - - if (outside_of_logic) { - IF_VERBOSE(2, - verbose_stream() << "not diff\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; - }); - extract_subset_core(safe); - } - else { - - IF_VERBOSE(2, - verbose_stream() << "Lemmas\n"; - for (unsigned i = 0; i < lemmas.size(); ++i) { - verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n"; - }); - - m_core->reset(); - m_core->append(lemmas); - - if (m_consequences) { - fl.get_consequences(pr, bs, *m_consequences); - } - } - } - - lbool prop_solver::check_assumptions(const expr_ref_vector & atoms) { - return check_assumptions_and_formula(atoms, m.mk_true()); - } - - lbool prop_solver::check_conjunction_as_assumptions(expr * conj) { - expr_ref_vector asmp(m); - asmp.push_back(conj); - return check_assumptions(asmp); - } - - lbool prop_solver::check_assumptions_and_formula(const expr_ref_vector & atoms, expr * form) - { - pdr::smt_context::scoped _scoped(*m_ctx); - safe_assumptions safe(*this, atoms); - m_ctx->assert_expr(form); - CTRACE("pdr", !m.is_true(form), tout << "check with formula: " << mk_pp(form, m) << "\n";); - lbool res = check_safe_assumptions(safe, safe.atoms()); - - // - // we don't have to undo model naming, as from the model - // we extract the values for state variables directly - // - return res; - } - - void prop_solver::collect_statistics(statistics& st) const { - } - - void prop_solver::reset_statistics() { - } - - - - -} diff --git a/src/muz/pdr/pdr_prop_solver.h b/src/muz/pdr/pdr_prop_solver.h deleted file mode 100644 index 463163fbd..000000000 --- a/src/muz/pdr/pdr_prop_solver.h +++ /dev/null @@ -1,139 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - prop_solver.h - -Abstract: - - SAT solver abstraction for PDR. - -Author: - - Krystof Hoder (t-khoder) 2011-8-17. - -Revision History: - ---*/ - -#ifndef PROP_SOLVER_H_ -#define PROP_SOLVER_H_ - -#include -#include -#include -#include "ast/ast.h" -#include "util/obj_hashtable.h" -#include "smt/smt_kernel.h" -#include "util/util.h" -#include "util/vector.h" -#include "muz/pdr/pdr_manager.h" -#include "muz/pdr/pdr_smt_context_manager.h" - - -namespace pdr { - class prop_solver { - - private: - smt_params& m_fparams; - ast_manager& m; - manager& m_pm; - symbol m_name; - scoped_ptr m_ctx; - decl_vector m_level_preds; - app_ref_vector m_pos_level_atoms; // atoms used to identify level - app_ref_vector m_neg_level_atoms; // - obj_hashtable m_level_atoms_set; - app_ref_vector m_proxies; // predicates for assumptions - expr_ref_vector* m_core; - model_ref* m_model; - expr_ref_vector* m_consequences; - bool m_subset_based_core; - bool m_assumes_level; - bool m_use_farkas; - func_decl_set m_aux_symbols; - bool m_in_level; - unsigned m_current_level; // set when m_in_level - - /** Add level atoms activating certain level into a vector */ - void push_level_atoms(unsigned level, expr_ref_vector & tgt) const; - - void ensure_level(unsigned lvl); - - class safe_assumptions; - - void extract_theory_core(safe_assumptions& assumptions); - - void extract_subset_core(safe_assumptions& assumptions); - - lbool check_safe_assumptions( - safe_assumptions& assumptions, - expr_ref_vector const& atoms); - - - public: - prop_solver(pdr::manager& pm, symbol const& name); - - /** return true is s is a symbol introduced by prop_solver */ - bool is_aux_symbol(func_decl * s) const { - return - m_aux_symbols.contains(s) || - m_ctx->is_aux_predicate(s); - } - - void set_core(expr_ref_vector* core) { m_core = core; } - void set_model(model_ref* mdl) { m_model = mdl; } - void set_subset_based_core(bool f) { m_subset_based_core = f; } - void set_consequences(expr_ref_vector* consequences) { m_consequences = consequences; } - - bool assumes_level() const { return m_assumes_level; } - - void add_level(); - unsigned level_cnt() const; - - class scoped_level { - bool& m_lev; - public: - scoped_level(prop_solver& ps, unsigned lvl):m_lev(ps.m_in_level) { - SASSERT(!m_lev); m_lev = true; ps.m_current_level = lvl; - } - ~scoped_level() { m_lev = false; } - }; - - void set_use_farkas(bool f) { m_use_farkas = f; } - bool get_use_farkas() const { return m_use_farkas; } - - void add_formula(expr * form); - void add_level_formula(expr * form, unsigned level); - - /** - * Return true iff conjunction of atoms is consistent with the current state of - * the solver. - * - * If the conjunction of atoms is inconsistent with the solver state and core is non-zero, - * core will contain an unsatisfiable core of atoms. - * - * If the conjunction of atoms is consistent with the solver state and o_model is non-zero, - * o_model will contain the "o" literals true in the assignment. - */ - lbool check_assumptions(const expr_ref_vector & atoms); - - lbool check_conjunction_as_assumptions(expr * conj); - - /** - * Like check_assumptions, except it also asserts an extra formula - */ - lbool check_assumptions_and_formula( - const expr_ref_vector & atoms, - expr * form); - - void collect_statistics(statistics& st) const; - - void reset_statistics(); - - }; -} - - -#endif diff --git a/src/muz/pdr/pdr_reachable_cache.cpp b/src/muz/pdr/pdr_reachable_cache.cpp deleted file mode 100644 index d28c1415b..000000000 --- a/src/muz/pdr/pdr_reachable_cache.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - reachable_cache.cpp - -Abstract: - - Object for caching of reachable states. - -Author: - - Krystof Hoder (t-khoder) 2011-9-14. - -Revision History: - ---*/ - -#include "muz/pdr/pdr_reachable_cache.h" - -namespace pdr { - - reachable_cache::reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm) - : m(pm.get_manager()), - m_pm(pm), - m_ctx(0), - m_ref_holder(m), - m_disj_connector(m), - m_cache_mode(cm) { - if (m_cache_mode == datalog::CONSTRAINT_CACHE) { - m_ctx = pm.mk_fresh(); - m_ctx->assert_expr(m_pm.get_background()); - } - } - - - void reachable_cache::add_disjuncted_formula(expr * f) { - app_ref new_connector(m.mk_fresh_const("disj_conn", m.mk_bool_sort()), m); - app_ref neg_new_connector(m.mk_not(new_connector), m); - app_ref extended_form(m); - - if(m_disj_connector) { - extended_form = m.mk_or(m_disj_connector, neg_new_connector, f); - } - else { - extended_form = m.mk_or(neg_new_connector, f); - } - if (m_ctx) { - m_ctx->assert_expr(extended_form); - } - - m_disj_connector = new_connector; - } - - void reachable_cache::add_reachable(expr * cube) { - - switch (m_cache_mode) { - case datalog::NO_CACHE: - break; - - case datalog::HASH_CACHE: - m_stats.m_inserts++; - m_cache.insert(cube); - m_ref_holder.push_back(cube); - break; - - case datalog::CONSTRAINT_CACHE: - m_stats.m_inserts++; - TRACE("pdr", tout << mk_pp(cube, m) << "\n";); - add_disjuncted_formula(cube); - break; - - default: - UNREACHABLE(); - } - } - - bool reachable_cache::is_reachable(expr * cube) { - bool found = false; - switch (m_cache_mode) { - case datalog::NO_CACHE: - return false; - - case datalog::HASH_CACHE: - found = m_cache.contains(cube); - break; - - case datalog::CONSTRAINT_CACHE: { - if(!m_disj_connector) { - found = false; - break; - } - expr * connector = m_disj_connector.get(); - expr_ref_vector assms(m); - assms.push_back(connector); - m_ctx->push(); - m_ctx->assert_expr(cube); - lbool res = m_ctx->check(assms); - m_ctx->pop(); - - TRACE("pdr", tout << "is_reachable: " << res << " " << mk_pp(cube, m) << "\n";); - - found = res == l_true; - break; - } - - default: - UNREACHABLE(); - break; - } - if (found) { - m_stats.m_hits++; - } - else { - m_stats.m_miss++; - } - return found; - } - - void reachable_cache::collect_statistics(statistics& st) const { - st.update("cache inserts", m_stats.m_inserts); - st.update("cache miss", m_stats.m_miss); - st.update("cache hits", m_stats.m_hits); - } - - void reachable_cache::reset_statistics() { - m_stats.reset(); - } - - -} diff --git a/src/muz/pdr/pdr_reachable_cache.h b/src/muz/pdr/pdr_reachable_cache.h deleted file mode 100644 index 0833541ba..000000000 --- a/src/muz/pdr/pdr_reachable_cache.h +++ /dev/null @@ -1,66 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - reachable_cache.h - -Abstract: - - Object for caching of reachable states. - -Author: - - Krystof Hoder (t-khoder) 2011-9-14. - -Revision History: - ---*/ - - -#ifndef REACHABLE_CACHE_H_ -#define REACHABLE_CACHE_H_ -#include "ast/ast.h" -#include "util/ref_vector.h" -#include "muz/pdr/pdr_manager.h" -#include "muz/pdr/pdr_smt_context_manager.h" - -namespace pdr { - class reachable_cache { - struct stats { - unsigned m_hits; - unsigned m_miss; - unsigned m_inserts; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - ast_manager & m; - manager & m_pm; - scoped_ptr m_ctx; - ast_ref_vector m_ref_holder; - app_ref m_disj_connector; - obj_hashtable m_cache; - stats m_stats; - datalog::PDR_CACHE_MODE m_cache_mode; - - void add_disjuncted_formula(expr * f); - - public: - reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm); - - void add_init(app * f) { add_disjuncted_formula(f); } - - /** add cube whose all models are reachable */ - void add_reachable(expr * cube); - - /** return true if there is a model of cube which is reachable */ - bool is_reachable(expr * cube); - - void collect_statistics(statistics& st) const; - - void reset_statistics(); - }; -} - -#endif diff --git a/src/muz/pdr/pdr_smt_context_manager.cpp b/src/muz/pdr/pdr_smt_context_manager.cpp deleted file mode 100644 index b87d1e451..000000000 --- a/src/muz/pdr/pdr_smt_context_manager.cpp +++ /dev/null @@ -1,167 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_smt_context_manager.cpp - -Abstract: - - Manager of smt contexts - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-26. - -Revision History: - ---*/ - -#include "muz/pdr/pdr_smt_context_manager.h" -#include "ast/has_free_vars.h" -#include "ast/ast_pp.h" -#include "ast/ast_smt_pp.h" -#include -#include "smt/params/smt_params.h" - -namespace pdr { - - smt_context::smt_context(smt_context_manager& p, ast_manager& m, app* pred): - m_pred(pred, m), - m_parent(p), - m_in_delay_scope(false), - m_pushed(false) - {} - - bool smt_context::is_aux_predicate(func_decl* p) { - return m_parent.is_aux_predicate(p); - } - - smt_context::scoped::scoped(smt_context& ctx): m_ctx(ctx) { - SASSERT(!m_ctx.m_in_delay_scope); - SASSERT(!m_ctx.m_pushed); - m_ctx.m_in_delay_scope = true; - } - - smt_context::scoped::~scoped() { - SASSERT(m_ctx.m_in_delay_scope); - if (m_ctx.m_pushed) { - m_ctx.pop(); - m_ctx.m_pushed = false; - } - m_ctx.m_in_delay_scope = false; - } - - - _smt_context::_smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred): - smt_context(p, ctx.m(), pred), - m_context(ctx) - {} - - void _smt_context::assert_expr(expr* e) { - ast_manager& m = m_context.m(); - if (m.is_true(e)) { - return; - } - CTRACE("pdr", has_free_vars(e), tout << mk_pp(e, m) << "\n";); - SASSERT(!has_free_vars(e)); - if (m_in_delay_scope && !m_pushed) { - m_context.push(); - m_pushed = true; - } - expr_ref fml(m); - fml = m_pushed?e:m.mk_implies(m_pred, e); - m_context.assert_expr(fml); - } - - lbool _smt_context::check(expr_ref_vector& assumptions) { - ast_manager& m = m_pred.get_manager(); - if (!m.is_true(m_pred)) { - assumptions.push_back(m_pred); - } - TRACE("pdr_check", - { - ast_smt_pp pp(m); - for (unsigned i = 0; i < m_context.size(); ++i) { - pp.add_assumption(m_context.get_formula(i)); - } - for (unsigned i = 0; i < assumptions.size(); ++i) { - pp.add_assumption(assumptions[i].get()); - } - - static unsigned lemma_id = 0; - std::ostringstream strm; - strm << "pdr-lemma-" << lemma_id << ".smt2"; - std::ofstream out(strm.str().c_str()); - pp.display_smt2(out, m.mk_true()); - out.close(); - lemma_id++; - tout << "pdr_check: " << strm.str() << "\n"; - }); - lbool result = m_context.check(assumptions.size(), assumptions.c_ptr()); - if (!m.is_true(m_pred)) { - assumptions.pop_back(); - } - return result; - } - - void _smt_context::get_model(model_ref& model) { - m_context.get_model(model); - } - - proof* _smt_context::get_proof() { - return m_context.get_proof(); - } - - smt_context_manager::smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m): - m_fparams(fp), - m(m), - m_max_num_contexts(max_num_contexts), - m_num_contexts(0), - m_predicate_list(m) { - } - - - smt_context_manager::~smt_context_manager() { - TRACE("pdr",tout << "\n";); - std::for_each(m_contexts.begin(), m_contexts.end(), delete_proc()); - } - - smt_context* smt_context_manager::mk_fresh() { - ++m_num_contexts; - app_ref pred(m); - smt::kernel * ctx = 0; - if (m_max_num_contexts == 0) { - m_contexts.push_back(alloc(smt::kernel, m, m_fparams)); - pred = m.mk_true(); - ctx = m_contexts[m_num_contexts-1]; - } - else { - if (m_contexts.size() < m_max_num_contexts) { - m_contexts.push_back(alloc(smt::kernel, m, m_fparams)); - } - std::stringstream name; - name << "#context" << m_num_contexts; - pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); - m_predicate_list.push_back(pred); - m_predicate_set.insert(pred->get_decl()); - ctx = m_contexts[(m_num_contexts-1)%m_max_num_contexts]; - } - return alloc(_smt_context, *ctx, *this, pred); - } - - void smt_context_manager::collect_statistics(statistics& st) const { - for (unsigned i = 0; i < m_contexts.size(); ++i) { - m_contexts[i]->collect_statistics(st); - } - } - - void smt_context_manager::reset_statistics() { - for (unsigned i = 0; i < m_contexts.size(); ++i) { - m_contexts[i]->reset_statistics(); - } - } - - -}; - diff --git a/src/muz/pdr/pdr_smt_context_manager.h b/src/muz/pdr/pdr_smt_context_manager.h deleted file mode 100644 index 735b2cd62..000000000 --- a/src/muz/pdr/pdr_smt_context_manager.h +++ /dev/null @@ -1,92 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_smt_context_manager.h - -Abstract: - - Manager of smt contexts - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-26. - -Revision History: - ---*/ - -#ifndef PDR_SMT_CONTEXT_MANAGER_H_ -#define PDR_SMT_CONTEXT_MANAGER_H_ - -#include "smt/smt_kernel.h" -#include "ast/func_decl_dependencies.h" -#include "muz/base/dl_util.h" - -namespace pdr { - - class smt_context_manager; - - class smt_context { - protected: - app_ref m_pred; - smt_context_manager& m_parent; - bool m_in_delay_scope; - bool m_pushed; - public: - smt_context(smt_context_manager& p, ast_manager& m, app* pred); - virtual ~smt_context() {} - virtual void assert_expr(expr* e) = 0; - virtual lbool check(expr_ref_vector& assumptions) = 0; - virtual void get_model(model_ref& model) = 0; - virtual proof* get_proof() = 0; - virtual unsigned get_unsat_core_size() = 0; - virtual expr* get_unsat_core_expr(unsigned i) = 0; - virtual void push() = 0; - virtual void pop() = 0; - bool is_aux_predicate(func_decl* p); - bool is_aux_predicate(expr* p) { return is_app(p) && is_aux_predicate(to_app(p)->get_decl()); } - class scoped { - smt_context& m_ctx; - public: - scoped(smt_context& ctx); - ~scoped(); - }; - }; - - class _smt_context : public smt_context { - smt::kernel & m_context; - public: - _smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred); - virtual ~_smt_context() {} - virtual void assert_expr(expr* e); - virtual lbool check(expr_ref_vector& assumptions); - virtual void get_model(model_ref& model); - virtual proof* get_proof(); - virtual void push() { m_context.push(); } - virtual void pop() { m_context.pop(1); } - virtual unsigned get_unsat_core_size() { return m_context.get_unsat_core_size(); } - virtual expr* get_unsat_core_expr(unsigned i) { return m_context.get_unsat_core_expr(i); } - }; - - class smt_context_manager { - smt_params& m_fparams; - ast_manager& m; - unsigned m_max_num_contexts; - ptr_vector m_contexts; - unsigned m_num_contexts; - app_ref_vector m_predicate_list; - func_decl_set m_predicate_set; - public: - smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m); - ~smt_context_manager(); - smt_context* mk_fresh(); - void collect_statistics(statistics& st) const; - void reset_statistics(); - bool is_aux_predicate(func_decl* p) const { return m_predicate_set.contains(p); } - }; - -}; - -#endif diff --git a/src/muz/pdr/pdr_sym_mux.cpp b/src/muz/pdr/pdr_sym_mux.cpp deleted file mode 100644 index 72549f2d6..000000000 --- a/src/muz/pdr/pdr_sym_mux.cpp +++ /dev/null @@ -1,601 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - sym_mux.cpp - -Abstract: - - A symbol multiplexer that helps with having multiple versions of each of a set of symbols. - -Author: - - Krystof Hoder (t-khoder) 2011-9-8. - -Revision History: - ---*/ - -#include -#include "ast/ast_pp.h" -#include "ast/for_each_expr.h" -#include "model/model.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/rewriter_def.h" -#include "muz/pdr/pdr_util.h" -#include "muz/pdr/pdr_sym_mux.h" - -using namespace pdr; - -sym_mux::sym_mux(ast_manager & m) - : m(m), m_ref_holder(m), - m_next_sym_suffix_idx(0) { - m_suffixes.push_back("_n"); - size_t suf_sz = m_suffixes.size(); - for(unsigned i = 0; i < suf_sz; ++i) { - symbol suff_sym = symbol(m_suffixes[i].c_str()); - m_used_suffixes.insert(suff_sym); - } -} - -std::string sym_mux::get_suffix(unsigned i) { - while(m_suffixes.size() <= i) { - std::string new_suffix; - symbol new_syffix_sym; - do { - std::stringstream stm; - stm<<'_'<0); - while(tuple.size()get_name().str(); - for(unsigned i=0; iget_arity()==arity); - SASSERT(tuple[i]->get_range()==range); - //domain should match as well, but we won't bother checking an array equality - } - else { - std::string name = pre+get_suffix(i); - tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, domain, range); - } - m_ref_holder.push_back(tuple[i]); - m_sym2idx.insert(tuple[i], i); - m_sym2prim.insert(tuple[i], tuple[0]); - } - - m_prim2all.insert(tuple[0], tuple); - m_prefix2prim.insert(prefix, tuple[0]); - m_prim2prefix.insert(tuple[0], prefix); - m_prim_preds.push_back(tuple[0]); - m_ref_holder.push_back(prefix); -} - -void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) { - SASSERT(m_prim2all.contains(prim)); - decl_vector& tuple = m_prim2all.find_core(prim)->get_data().m_value; - SASSERT(tuple[0]==prim); - - if(sz <= tuple.size()) { return; } - - func_decl * prefix; - TRUSTME(m_prim2prefix.find(prim, prefix)); - std::string prefix_name = prefix->get_name().bare_str(); - for(unsigned i = tuple.size(); i < sz; ++i) { - std::string name = prefix_name + get_suffix(i); - func_decl * new_sym = m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(), - prefix->get_domain(), prefix->get_range()); - - tuple.push_back(new_sym); - m_ref_holder.push_back(new_sym); - m_sym2idx.insert(new_sym, i); - m_sym2prim.insert(new_sym, prim); - } -} - -func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) -{ - if(src_idx==tgt_idx) { return sym; } - func_decl * prim = (src_idx==0) ? sym : get_primary(sym); - if(tgt_idx>src_idx) { - ensure_tuple_size(prim, tgt_idx+1); - } - decl_vector & sym_vect = m_prim2all.find_core(prim)->get_data().m_value; - SASSERT(sym_vect[src_idx]==sym); - return sym_vect[tgt_idx]; -} - - -func_decl * sym_mux::get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, - unsigned arity, sort * const * domain, sort * range) -{ - func_decl * prim = try_get_primary_by_prefix(prefix); - if(prim) { - SASSERT(prim->get_arity()==arity); - SASSERT(prim->get_range()==range); - //domain should match as well, but we won't bother checking an array equality - - return conv(prim, 0, idx); - } - - decl_vector syms; - create_tuple(prefix, arity, domain, range, idx+1, syms); - return syms[idx]; -} - -bool sym_mux::is_muxed_lit(expr * e, unsigned idx) const -{ - if(!is_app(e)) { return false; } - app * a = to_app(e); - if(m.is_not(a) && is_app(a->get_arg(0))) { - a = to_app(a->get_arg(0)); - } - return is_muxed(a->get_decl()); -} - - -struct sym_mux::formula_checker -{ - formula_checker(const sym_mux & parent, bool all, unsigned idx) : - m_parent(parent), m_all(all), m_idx(idx), - m_found_what_needed(false) - { - } - - void operator()(expr * e) - { - if(m_found_what_needed || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - unsigned sym_idx; - if(!m_parent.try_get_index(sym, sym_idx)) { return; } - - bool have_idx = sym_idx==m_idx; - - if( m_all ? (!have_idx) : have_idx ) { - m_found_what_needed = true; - } - - } - - bool all_have_idx() const - { - SASSERT(m_all); //we were looking for the queried property - return !m_found_what_needed; - } - - bool some_with_idx() const - { - SASSERT(!m_all); //we were looking for the queried property - return m_found_what_needed; - } - -private: - const sym_mux & m_parent; - bool m_all; - unsigned m_idx; - - /** - If we check whether all muxed symbols are of given index, we look for - counter-examples, checking whether form contains a muxed symbol of an index, - we look for symbol of index m_idx. - */ - bool m_found_what_needed; -}; - -bool sym_mux::contains(expr * e, unsigned idx) const -{ - formula_checker chck(*this, false, idx); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return chck.some_with_idx(); -} - -bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const -{ - formula_checker chck(*this, true, idx); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return chck.all_have_idx(); -} - -bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const -{ - expr * const * begin = vect.c_ptr(); - expr * const * end = begin + vect.size(); - for(expr * const * it = begin; it!=end; it++) { - if(!is_homogenous_formula(*it, idx)) { - return false; - } - } - return true; -} - -class sym_mux::index_collector { - sym_mux const& m_parent; - svector m_indices; -public: - index_collector(sym_mux const& s): - m_parent(s) {} - - void operator()(expr * e) { - if (is_app(e)) { - func_decl * sym = to_app(e)->get_decl(); - unsigned idx; - if (m_parent.try_get_index(sym, idx)) { - SASSERT(idx > 0); - --idx; - if (m_indices.size() <= idx) { - m_indices.resize(idx+1, false); - } - m_indices[idx] = true; - } - } - } - - void extract(unsigned_vector& indices) { - for (unsigned i = 0; i < m_indices.size(); ++i) { - if (m_indices[i]) { - indices.push_back(i); - } - } - } -}; - - - -void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const { - indices.reset(); - index_collector collector(*this); - for_each_expr(collector, m_visited, e); - m_visited.reset(); - collector.extract(indices); -} - -class sym_mux::variable_collector { - sym_mux const& m_parent; - vector >& m_vars; -public: - variable_collector(sym_mux const& s, vector >& vars): - m_parent(s), m_vars(vars) {} - - void operator()(expr * e) { - if (is_app(e)) { - func_decl * sym = to_app(e)->get_decl(); - unsigned idx; - if (m_parent.try_get_index(sym, idx)) { - SASSERT(idx > 0); - --idx; - if (m_vars.size() <= idx) { - m_vars.resize(idx+1, ptr_vector()); - } - m_vars[idx].push_back(to_app(e)); - } - } - } -}; - -void sym_mux::collect_variables(expr* e, vector >& vars) const { - vars.reset(); - variable_collector collector(*this, vars); - for_each_expr(collector, m_visited, e); - m_visited.reset(); -} - -class sym_mux::hmg_checker { - const sym_mux & m_parent; - - bool m_found_idx; - unsigned m_idx; - bool m_multiple_indexes; - -public: - hmg_checker(const sym_mux & parent) : - m_parent(parent), m_found_idx(false), m_multiple_indexes(false) - { - } - - void operator()(expr * e) - { - if(m_multiple_indexes || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - unsigned sym_idx; - if(!m_parent.try_get_index(sym, sym_idx)) { return; } - - if(!m_found_idx) { - m_found_idx = true; - m_idx = sym_idx; - return; - } - if(m_idx==sym_idx) { return; } - m_multiple_indexes = true; - } - - bool has_multiple_indexes() const - { - return m_multiple_indexes; - } -}; - -bool sym_mux::is_homogenous_formula(expr * e) const { - hmg_checker chck(*this); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return !chck.has_multiple_indexes(); -} - - -struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg -{ -private: - ast_manager & m; - sym_mux & m_parent; - unsigned m_from_idx; - unsigned m_to_idx; - bool m_homogenous; -public: - conv_rewriter_cfg(sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous) - : m(parent.get_manager()), - m_parent(parent), - m_from_idx(from_idx), - m_to_idx(to_idx), - m_homogenous(homogenous) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - if(!is_app(s)) { return false; } - app * a = to_app(s); - func_decl * sym = a->get_decl(); - if(!m_parent.has_index(sym, m_from_idx)) { - (void) m_homogenous; - SASSERT(!m_homogenous || !m_parent.is_muxed(sym)); - return false; - } - func_decl * tgt = m_parent.conv(sym, m_from_idx, m_to_idx); - - t = m.mk_app(tgt, a->get_args()); - return true; - } -}; - -void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous) -{ - if(src_idx==tgt_idx) { - res = f; - return; - } - conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous); - rewriter_tpl rwr(m, false, r_cfg); - rwr(f, res); -} - -struct sym_mux::shifting_rewriter_cfg : public default_rewriter_cfg -{ -private: - ast_manager & m; - sym_mux & m_parent; - int m_shift; -public: - shifting_rewriter_cfg(sym_mux & parent, int shift) - : m(parent.get_manager()), - m_parent(parent), - m_shift(shift) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - if(!is_app(s)) { return false; } - app * a = to_app(s); - func_decl * sym = a->get_decl(); - - unsigned idx; - if(!m_parent.try_get_index(sym, idx)) { - return false; - } - SASSERT(static_cast(idx)+m_shift>=0); - func_decl * tgt = m_parent.conv(sym, idx, idx+m_shift); - t = m.mk_app(tgt, a->get_args()); - return true; - } -}; - -void sym_mux::shift_formula(expr * f, int dist, expr_ref & res) -{ - if(dist==0) { - res = f; - return; - } - shifting_rewriter_cfg r_cfg(*this, dist); - rewriter_tpl rwr(m, false, r_cfg); - rwr(f, res); -} - -void sym_mux::conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, - expr_ref_vector & res) -{ - res.reset(); - expr * const * begin = vect.c_ptr(); - expr * const * end = begin + vect.size(); - for(expr * const * it = begin; it!=end; it++) { - expr_ref converted(m); - conv_formula(*it, src_idx, tgt_idx, converted); - res.push_back(converted); - } -} - -void sym_mux::filter_idx(expr_ref_vector & vect, unsigned idx) const { - unsigned i = 0; - while (i < vect.size()) { - expr* e = vect[i].get(); - if (contains(e, idx) && is_homogenous_formula(e, idx)) { - i++; - } - else { - // we don't allow mixing states inside vector elements - SASSERT(!contains(e, idx)); - vect[i] = vect.back(); - vect.pop_back(); - } - } -} - -void sym_mux::partition_o_idx( - expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, unsigned idx) const { - - for (unsigned i = 0; i < lits.size(); ++i) { - if (contains(lits[i], idx) && is_homogenous_formula(lits[i], idx)) { - o_lits.push_back(lits[i]); - } - else { - other.push_back(lits[i]); - } - } -} - - - -class sym_mux::nonmodel_sym_checker { - const sym_mux & m_parent; - - bool m_found; -public: - nonmodel_sym_checker(const sym_mux & parent) : - m_parent(parent), m_found(false) { - } - - void operator()(expr * e) { - if(m_found || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - - if(m_parent.is_non_model_sym(sym)) { - m_found = true; - } - } - - bool found() const { - return m_found; - } -}; - -bool sym_mux::has_nonmodel_symbol(expr * e) const { - nonmodel_sym_checker chck(*this); - for_each_expr(chck, e); - return chck.found(); -} - -void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const { - unsigned i = 0; - while (i < vect.size()) { - if (!has_nonmodel_symbol(vect[i].get())) { - i++; - } - else { - vect[i] = vect.back(); - vect.pop_back(); - } - } -} - -class sym_mux::decl_idx_comparator -{ - const sym_mux & m_parent; -public: - decl_idx_comparator(const sym_mux & parent) - : m_parent(parent) - { } - - bool operator()(func_decl * sym1, func_decl * sym2) - { - unsigned idx1, idx2; - if (!m_parent.try_get_index(sym1, idx1)) { idx1 = UINT_MAX; } - if (!m_parent.try_get_index(sym2, idx2)) { idx2 = UINT_MAX; } - - if (idx1 != idx2) { return idx1get_name(), sym2->get_name()); - } -}; - -std::string sym_mux::pp_model(const model_core & mdl) const { - decl_vector consts; - unsigned sz = mdl.get_num_constants(); - for (unsigned i = 0; i < sz; i++) { - func_decl * d = mdl.get_constant(i); - consts.push_back(d); - } - - std::sort(consts.begin(), consts.end(), decl_idx_comparator(*this)); - - std::stringstream res; - - decl_vector::iterator end = consts.end(); - for (decl_vector::iterator it = consts.begin(); it!=end; it++) { - func_decl * d = *it; - std::string name = d->get_name().str(); - const char * arrow = " -> "; - res << name << arrow; - unsigned indent = static_cast(name.length() + strlen(arrow)); - res << mk_pp(mdl.get_const_interp(d), m, indent) << "\n"; - - if (it+1 != end) { - unsigned idx1, idx2; - if (!try_get_index(*it, idx1)) { idx1 = UINT_MAX; } - if (!try_get_index(*(it+1), idx2)) { idx2 = UINT_MAX; } - if (idx1 != idx2) { res << "\n"; } - } - } - return res.str(); -} - - -#if 0 - -class sym_mux::index_renamer_cfg : public default_rewriter_cfg{ - const sym_mux & m_parent; - unsigned m_idx; - -public: - index_renamer_cfg(const sym_mux & p, unsigned idx) : m_parent(p), m_idx(idx) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) { - if (!is_app(s)) return false; - app * a = to_app(s); - if (a->get_family_id() != null_family_id) { - return false; - } - func_decl * sym = a->get_decl(); - unsigned idx; - if(!m_parent.try_get_index(sym, idx)) { - return false; - } - if (m_idx == idx) { - return false; - } - ast_manager& m = m_parent.get_manager(); - symbol name = symbol((sym->get_name().str() + "!").c_str()); - func_decl * tgt = m.mk_func_decl(name, sym->get_arity(), sym->get_domain(), sym->get_range()); - t = m.mk_app(tgt, a->get_num_args(), a->get_args()); - return true; - } -}; - -#endif diff --git a/src/muz/pdr/pdr_sym_mux.h b/src/muz/pdr/pdr_sym_mux.h deleted file mode 100644 index 981dc9615..000000000 --- a/src/muz/pdr/pdr_sym_mux.h +++ /dev/null @@ -1,247 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - sym_mux.h - -Abstract: - - A symbol multiplexer that helps with having multiple versions of each of a set of symbols. - -Author: - - Krystof Hoder (t-khoder) 2011-9-8. - -Revision History: - ---*/ - -#ifndef SYM_MUX_H_ -#define SYM_MUX_H_ - -#include "ast/ast.h" -#include "util/map.h" -#include "util/vector.h" -#include - -class model_core; - -namespace pdr { -class sym_mux -{ -public: - typedef ptr_vector app_vector; - typedef ptr_vector decl_vector; -private: - typedef obj_map sym2u; - typedef obj_map sym2dv; - typedef obj_map sym2sym; - typedef obj_map sym2pred; - typedef hashtable symbols; - - ast_manager & m; - mutable ast_ref_vector m_ref_holder; - mutable expr_mark m_visited; - - mutable unsigned m_next_sym_suffix_idx; - mutable symbols m_used_suffixes; - /** Here we have default suffixes for each of the variants */ - std::vector m_suffixes; - - - /** - Primary symbol is the 0-th variant. This member maps from primary symbol - to vector of all its variants (including the primary variant). - */ - sym2dv m_prim2all; - - /** - For each symbol contains its variant index - */ - mutable sym2u m_sym2idx; - /** - For each symbol contains its primary variant - */ - mutable sym2sym m_sym2prim; - - /** - Maps prefixes passed to the create_tuple to - the primary symbol created from it. - */ - sym2pred m_prefix2prim; - - /** - Maps pripary symbols to prefixes that were used to create them. - */ - sym2sym m_prim2prefix; - - decl_vector m_prim_preds; - - obj_hashtable m_non_model_syms; - - struct formula_checker; - struct conv_rewriter_cfg; - struct shifting_rewriter_cfg; - class decl_idx_comparator; - class hmg_checker; - class nonmodel_sym_checker; - class index_renamer_cfg; - class index_collector; - class variable_collector; - - std::string get_suffix(unsigned i); - void ensure_tuple_size(func_decl * prim, unsigned sz); - -public: - sym_mux(ast_manager & m); - - ast_manager & get_manager() const { return m; } - - bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); } - - bool try_get_index(func_decl * sym, unsigned & idx) const { - return m_sym2idx.find(sym,idx); - } - - bool has_index(func_decl * sym, unsigned idx) const { - unsigned actual_idx; - return try_get_index(sym, actual_idx) && idx==actual_idx; - } - - /** Return primary symbol. sym must be muxed. */ - func_decl * get_primary(func_decl * sym) const { - func_decl * prim; - TRUSTME(m_sym2prim.find(sym, prim)); - return prim; - } - - /** - Return primary symbol created from prefix, or 0 if the prefix was never used. - */ - func_decl * try_get_primary_by_prefix(func_decl* prefix) const { - func_decl * res; - if(!m_prefix2prim.find(prefix, res)) { - return 0; - } - return res; - } - - /** - Return symbol created from prefix, or 0 if the prefix was never used. - */ - func_decl * try_get_by_prefix(func_decl* prefix, unsigned idx) { - func_decl * prim = try_get_primary_by_prefix(prefix); - if(!prim) { - return 0; - } - return conv(prim, 0, idx); - } - - /** - Marks symbol as non-model which means it will not appear in models collected by - get_muxed_cube_from_model function. - This is to take care of auxiliary symbols introduced by the disjunction relations - to relativize lemmas coming from disjuncts. - */ - void mark_as_non_model(func_decl * sym) { - SASSERT(is_muxed(sym)); - m_non_model_syms.insert(get_primary(sym)); - } - - func_decl * get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, - unsigned arity, sort * const * domain, sort * range); - - - - bool is_muxed_lit(expr * e, unsigned idx) const; - - bool is_non_model_sym(func_decl * s) const { - return is_muxed(s) && m_non_model_syms.contains(get_primary(s)); - } - - /** - Create a multiplexed tuple of propositional constants. - Symbols may be suplied in the tuple vector, - those beyond the size of the array and those with corresponding positions - assigned to zero will be created using prefix. - Tuple length must be at least one. - */ - void create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range, - unsigned tuple_length, decl_vector & tuple); - - /** - Return true if the only multiplexed symbols which e contains are of index idx. - */ - bool is_homogenous_formula(expr * e, unsigned idx) const; - bool is_homogenous(const expr_ref_vector & vect, unsigned idx) const; - - /** - Return true if all multiplexed symbols which e contains are of one index. - */ - bool is_homogenous_formula(expr * e) const; - - /** - Return true if expression e contains a muxed symbol of index idx. - */ - bool contains(expr * e, unsigned idx) const; - - /** - Collect indices used in expression. - */ - void collect_indices(expr* e, unsigned_vector& indices) const; - - /** - Collect used variables of each index. - */ - void collect_variables(expr* e, vector >& vars) const; - - /** - Convert symbol sym which has to be of src_idx variant into variant tgt_idx. - */ - func_decl * conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx); - - - /** - Convert src_idx symbols in formula f variant into tgt_idx. - If homogenous is true, formula cannot contain symbols of other variants. - */ - void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous=true); - void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, - expr_ref_vector & res); - - /** - Shifts the muxed symbols in f by dist. Dist can be negative, but it should never shift - symbol index to a negative value. - */ - void shift_formula(expr * f, int dist, expr_ref & res); - - /** - Remove from vect literals (atoms or negations of atoms) of symbols - that contain multiplexed symbols with indexes other than idx. - - Each of the literals can contain only symbols multiplexed with one index - (this trivially holds if the literals are propositional). - - Order of elements in vect may be modified by this function - */ - void filter_idx(expr_ref_vector & vect, unsigned idx) const; - - /** - Partition literals into o_literals and others. - */ - void partition_o_idx(expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, unsigned idx) const; - - bool has_nonmodel_symbol(expr * e) const; - void filter_non_model_lits(expr_ref_vector & vect) const; - - func_decl * const * begin_prim_preds() const { return m_prim_preds.begin(); } - func_decl * const * end_prim_preds() const { return m_prim_preds.end(); } - - std::string pp_model(const model_core & mdl) const; -}; -} - -#endif diff --git a/src/muz/pdr/pdr_util.cpp b/src/muz/pdr/pdr_util.cpp deleted file mode 100644 index 19ffdeeec..000000000 --- a/src/muz/pdr/pdr_util.cpp +++ /dev/null @@ -1,508 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_util.cpp - -Abstract: - - Utility functions for PDR. - -Author: - - Krystof Hoder (t-khoder) 2011-8-19. - -Revision History: - - -Notes: - - ---*/ - -#include -#include "util/util.h" -#include "util/ref_vector.h" -#include "ast/array_decl_plugin.h" -#include "ast/ast_pp.h" -#include "ast/for_each_expr.h" -#include "ast/scoped_proof.h" -#include "ast/arith_decl_plugin.h" -#include "ast/rewriter/expr_replacer.h" -#include "ast/rewriter/bool_rewriter.h" -#include "ast/rewriter/poly_rewriter.h" -#include "ast/rewriter/poly_rewriter_def.h" -#include "ast/rewriter/arith_rewriter.h" -#include "ast/rewriter/rewriter.h" -#include "ast/rewriter/rewriter_def.h" -#include "smt/params/smt_params.h" -#include "model/model.h" -#include "muz/base/dl_util.h" -#include "muz/pdr/pdr_manager.h" -#include "muz/pdr/pdr_util.h" -#include "model/model_smt2_pp.h" - - - -namespace pdr { - - unsigned ceil_log2(unsigned u) { - if (u == 0) { return 0; } - unsigned pow2 = next_power_of_two(u); - return get_num_1bits(pow2-1); - } - - std::string pp_cube(const ptr_vector& model, ast_manager& m) { - return pp_cube(model.size(), model.c_ptr(), m); - } - - std::string pp_cube(const expr_ref_vector& model, ast_manager& m) { - return pp_cube(model.size(), model.c_ptr(), m); - } - - std::string pp_cube(const app_ref_vector& model, ast_manager& m) { - return pp_cube(model.size(), model.c_ptr(), m); - } - - std::string pp_cube(const app_vector& model, ast_manager& m) { - return pp_cube(model.size(), model.c_ptr(), m); - } - - std::string pp_cube(unsigned sz, app * const * lits, ast_manager& m) { - return pp_cube(sz, (expr * const *)(lits), m); - } - - std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& m) { - std::stringstream res; - res << "("; - expr * const * end = lits+sz; - for (expr * const * it = lits; it!=end; it++) { - res << mk_pp(*it, m); - if (it+1!=end) { - res << ", "; - } - } - res << ")"; - return res.str(); - } - - void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { - ast_manager& m = fml.get_manager(); - expr_ref_vector conjs(m); - flatten_and(fml, conjs); - obj_map diseqs; - expr* n, *lhs, *rhs; - for (unsigned i = 0; i < conjs.size(); ++i) { - if (m.is_not(conjs[i].get(), n) && - m.is_eq(n, lhs, rhs)) { - if (!m.is_value(rhs)) { - std::swap(lhs, rhs); - } - if (!m.is_value(rhs)) { - continue; - } - diseqs.insert_if_not_there2(lhs, 0)->get_data().m_value++; - } - } - expr_substitution sub(m); - - unsigned orig_size = conjs.size(); - unsigned num_deleted = 0; - expr_ref val(m), tmp(m); - proof_ref pr(m); - pr = m.mk_asserted(m.mk_true()); - obj_map::iterator it = diseqs.begin(); - obj_map::iterator end = diseqs.end(); - for (; it != end; ++it) { - if (it->m_value >= threshold) { - model.eval(it->m_key, val); - sub.insert(it->m_key, val, pr); - conjs.push_back(m.mk_eq(it->m_key, val)); - num_deleted += it->m_value; - } - } - if (orig_size < conjs.size()) { - scoped_ptr rep = mk_expr_simp_replacer(m); - rep->set_substitution(&sub); - for (unsigned i = 0; i < orig_size; ++i) { - tmp = conjs[i].get(); - (*rep)(tmp); - if (m.is_true(tmp)) { - conjs[i] = conjs.back(); - SASSERT(orig_size <= conjs.size()); - conjs.pop_back(); - SASSERT(orig_size <= 1 + conjs.size()); - if (i + 1 == orig_size) { - // no-op. - } - else if (orig_size <= conjs.size()) { - // no-op - } - else { - SASSERT(orig_size == 1 + conjs.size()); - --orig_size; - --i; - } - } - else { - conjs[i] = tmp; - } - } - IF_VERBOSE(2, verbose_stream() << "Deleted " << num_deleted << " disequalities " << conjs.size() << " conjuncts\n";); - } - fml = m.mk_and(conjs.size(), conjs.c_ptr()); - } - - class test_diff_logic { - ast_manager& m; - arith_util a; - bv_util bv; - bool m_is_dl; - bool m_test_for_utvpi; - - bool is_numeric(expr* e) const { - if (a.is_numeral(e)) { - return true; - } - expr* cond, *th, *el; - if (m.is_ite(e, cond, th, el)) { - return is_numeric(th) && is_numeric(el); - } - return false; - } - - bool is_arith_expr(expr *e) const { - return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); - } - - bool is_offset(expr* e) const { - if (a.is_numeral(e)) { - return true; - } - expr* cond, *th, *el, *e1, *e2; - if (m.is_ite(e, cond, th, el)) { - return is_offset(th) && is_offset(el); - } - // recognize offsets. - if (a.is_add(e, e1, e2)) { - if (is_numeric(e1)) { - return is_offset(e2); - } - if (is_numeric(e2)) { - return is_offset(e1); - } - return false; - } - if (m_test_for_utvpi) { - if (a.is_mul(e, e1, e2)) { - if (is_minus_one(e1)) { - return is_offset(e2); - } - if (is_minus_one(e2)) { - return is_offset(e1); - } - } - } - return !is_arith_expr(e); - } - - bool is_minus_one(expr const * e) const { - rational r; return a.is_numeral(e, r) && r.is_minus_one(); - } - - bool test_ineq(expr* e) const { - SASSERT(a.is_le(e) || a.is_ge(e) || m.is_eq(e)); - SASSERT(to_app(e)->get_num_args() == 2); - expr * lhs = to_app(e)->get_arg(0); - expr * rhs = to_app(e)->get_arg(1); - if (is_offset(lhs) && is_offset(rhs)) - return true; - if (!is_numeric(rhs)) - std::swap(lhs, rhs); - if (!is_numeric(rhs)) - return false; - // lhs can be 'x' or '(+ x (* -1 y))' - if (is_offset(lhs)) - return true; - expr* arg1, *arg2; - if (!a.is_add(lhs, arg1, arg2)) - return false; - // x - if (m_test_for_utvpi) { - return is_offset(arg1) && is_offset(arg2); - } - if (is_arith_expr(arg1)) - std::swap(arg1, arg2); - if (is_arith_expr(arg1)) - return false; - // arg2: (* -1 y) - expr* m1, *m2; - if (!a.is_mul(arg2, m1, m2)) - return false; - return is_minus_one(m1) && is_offset(m2); - } - - bool test_eq(expr* e) const { - expr* lhs = 0, *rhs = 0; - VERIFY(m.is_eq(e, lhs, rhs)); - if (!a.is_int_real(lhs)) { - return true; - } - if (a.is_numeral(lhs) || a.is_numeral(rhs)) { - return test_ineq(e); - } - return - test_term(lhs) && - test_term(rhs) && - !a.is_mul(lhs) && - !a.is_mul(rhs); - } - - bool test_term(expr* e) const { - if (m.is_bool(e)) { - return true; - } - if (a.is_numeral(e)) { - return true; - } - if (is_offset(e)) { - return true; - } - expr* lhs, *rhs; - if (a.is_add(e, lhs, rhs)) { - if (!a.is_numeral(lhs)) { - std::swap(lhs, rhs); - } - return a.is_numeral(lhs) && is_offset(rhs); - } - if (a.is_mul(e, lhs, rhs)) { - return is_minus_one(lhs) || is_minus_one(rhs); - } - return false; - } - - bool is_non_arith_or_basic(expr* e) { - if (!is_app(e)) { - return false; - } - family_id fid = to_app(e)->get_family_id(); - - if (fid == null_family_id && - !m.is_bool(e) && - to_app(e)->get_num_args() > 0) { - return true; - } - return - fid != m.get_basic_family_id() && - fid != null_family_id && - fid != a.get_family_id() && - fid != bv.get_family_id(); - } - - public: - test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - - void test_for_utvpi() { m_test_for_utvpi = true; } - - void operator()(expr* e) { - if (!m_is_dl) { - return; - } - if (a.is_le(e) || a.is_ge(e)) { - m_is_dl = test_ineq(e); - } - else if (m.is_eq(e)) { - m_is_dl = test_eq(e); - } - else if (is_non_arith_or_basic(e)) { - m_is_dl = false; - } - else if (is_app(e)) { - app* a = to_app(e); - for (unsigned i = 0; m_is_dl && i < a->get_num_args(); ++i) { - m_is_dl = test_term(a->get_arg(i)); - } - } - - if (!m_is_dl) { - char const* msg = "non-diff: "; - if (m_test_for_utvpi) { - msg = "non-utvpi: "; - } - IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); - } - } - - bool is_dl() const { return m_is_dl; } - }; - - bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { - test_diff_logic test(m); - expr_fast_mark1 mark; - for (unsigned i = 0; i < num_fmls; ++i) { - quick_for_each_expr(test, mark, fmls[i]); - } - return test.is_dl(); - } - - bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) { - test_diff_logic test(m); - test.test_for_utvpi(); - expr_fast_mark1 mark; - for (unsigned i = 0; i < num_fmls; ++i) { - quick_for_each_expr(test, mark, fmls[i]); - } - return test.is_dl(); - } - - class arith_normalizer : public poly_rewriter { - ast_manager& m; - arith_util m_util; - enum op_kind { LE, GE, EQ }; - public: - arith_normalizer(ast_manager& m, params_ref const& p = params_ref()): poly_rewriter(m, p), m(m), m_util(m) {} - - br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { - br_status st = BR_FAILED; - if (m.is_eq(f)) { - SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result); - } - - if (f->get_family_id() != get_fid()) { - return st; - } - switch (f->get_decl_kind()) { - case OP_NUM: st = BR_FAILED; break; - case OP_IRRATIONAL_ALGEBRAIC_NUM: st = BR_FAILED; break; - case OP_LE: SASSERT(num_args == 2); st = mk_le_core(args[0], args[1], result); break; - case OP_GE: SASSERT(num_args == 2); st = mk_ge_core(args[0], args[1], result); break; - case OP_LT: SASSERT(num_args == 2); st = mk_lt_core(args[0], args[1], result); break; - case OP_GT: SASSERT(num_args == 2); st = mk_gt_core(args[0], args[1], result); break; - default: st = BR_FAILED; break; - } - return st; - } - - private: - - br_status mk_eq_core(expr* arg1, expr* arg2, expr_ref& result) { - return mk_le_ge_eq_core(arg1, arg2, EQ, result); - } - br_status mk_le_core(expr* arg1, expr* arg2, expr_ref& result) { - return mk_le_ge_eq_core(arg1, arg2, LE, result); - } - br_status mk_ge_core(expr* arg1, expr* arg2, expr_ref& result) { - return mk_le_ge_eq_core(arg1, arg2, GE, result); - } - br_status mk_lt_core(expr* arg1, expr* arg2, expr_ref& result) { - result = m.mk_not(m_util.mk_ge(arg1, arg2)); - return BR_REWRITE2; - } - br_status mk_gt_core(expr* arg1, expr* arg2, expr_ref& result) { - result = m.mk_not(m_util.mk_le(arg1, arg2)); - return BR_REWRITE2; - } - - br_status mk_le_ge_eq_core(expr* arg1, expr* arg2, op_kind kind, expr_ref& result) { - if (m_util.is_real(arg1)) { - numeral g(0); - get_coeffs(arg1, g); - get_coeffs(arg2, g); - if (!g.is_one() && !g.is_zero()) { - SASSERT(g.is_pos()); - expr_ref new_arg1 = rdiv_polynomial(arg1, g); - expr_ref new_arg2 = rdiv_polynomial(arg2, g); - switch(kind) { - case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_DONE; - case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_DONE; - case EQ: result = m_util.mk_eq(new_arg1, new_arg2); return BR_DONE; - } - } - } - return BR_FAILED; - } - - void update_coeff(numeral const& r, numeral& g) { - if (g.is_zero() || abs(r) < g) { - g = abs(r); - } - } - - void get_coeffs(expr* e, numeral& g) { - rational r; - unsigned sz; - expr* const* args = get_monomials(e, sz); - for (unsigned i = 0; i < sz; ++i) { - expr* arg = args[i]; - if (!m_util.is_numeral(arg, r)) { - get_power_product(arg, r); - } - update_coeff(r, g); - } - } - - expr_ref rdiv_polynomial(expr* e, numeral const& g) { - rational r; - SASSERT(g.is_pos()); - SASSERT(!g.is_one()); - expr_ref_vector monomes(m); - unsigned sz; - expr* const* args = get_monomials(e, sz); - for (unsigned i = 0; i < sz; ++i) { - expr* arg = args[i]; - if (m_util.is_numeral(arg, r)) { - monomes.push_back(m_util.mk_numeral(r/g, false)); - } - else { - expr* p = get_power_product(arg, r); - r /= g; - if (r.is_one()) { - monomes.push_back(p); - } - else { - monomes.push_back(m_util.mk_mul(m_util.mk_numeral(r, false), p)); - } - } - } - expr_ref result(m); - mk_add(monomes.size(), monomes.c_ptr(), result); - return result; - } - - }; - - - struct arith_normalizer_cfg: public default_rewriter_cfg { - arith_normalizer m_r; - bool rewrite_patterns() const { return false; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - return m_r.mk_app_core(f, num, args, result); - } - arith_normalizer_cfg(ast_manager & m, params_ref const & p):m_r(m,p) {} - }; - - class arith_normalizer_star : public rewriter_tpl { - arith_normalizer_cfg m_cfg; - public: - arith_normalizer_star(ast_manager & m, params_ref const & p): - rewriter_tpl(m, false, m_cfg), - m_cfg(m, p) {} - }; - - - void normalize_arithmetic(expr_ref& t) { - ast_manager& m = t.get_manager(); - scoped_no_proof _sp(m); - params_ref p; - arith_normalizer_star rw(m, p); - expr_ref tmp(m); - rw(t, tmp); - t = tmp; - } - -} - -template class rewriter_tpl; - - diff --git a/src/muz/pdr/pdr_util.h b/src/muz/pdr/pdr_util.h deleted file mode 100644 index 51f6978ec..000000000 --- a/src/muz/pdr/pdr_util.h +++ /dev/null @@ -1,81 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - pdr_util.h - -Abstract: - - Utility functions for PDR. - -Author: - - Krystof Hoder (t-khoder) 2011-8-19. - -Revision History: - ---*/ - -#ifndef PDR_UTIL_H_ -#define PDR_UTIL_H_ - -#include "ast/ast.h" -#include "ast/ast_pp.h" -#include "ast/ast_util.h" -#include "util/obj_hashtable.h" -#include "util/ref_vector.h" -#include "util/trace.h" -#include "util/vector.h" -#include "ast/arith_decl_plugin.h" -#include "ast/array_decl_plugin.h" -#include "ast/bv_decl_plugin.h" - - -class model; -class model_core; - -namespace pdr { - - /** - * Return the ceiling of base 2 logarithm of a number, - * or zero if the nmber is zero. - */ - unsigned ceil_log2(unsigned u); - - typedef ptr_vector app_vector; - typedef ptr_vector decl_vector; - typedef obj_hashtable func_decl_set; - - std::string pp_cube(const ptr_vector& model, ast_manager& manager); - std::string pp_cube(const expr_ref_vector& model, ast_manager& manager); - std::string pp_cube(const ptr_vector& model, ast_manager& manager); - std::string pp_cube(const app_ref_vector& model, ast_manager& manager); - std::string pp_cube(unsigned sz, app * const * lits, ast_manager& manager); - std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& manager); - - - /** - \brief replace variables that are used in many disequalities by - an equality using the model. - - Assumption: the model satisfies the conjunctions. - */ - void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); - - /** - \brief normalize coefficients in polynomials so that least coefficient is 1. - */ - void normalize_arithmetic(expr_ref& t); - - - /** - \brief determine if formulas belong to difference logic or UTVPI fragment. - */ - bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); - - bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); - -} - -#endif diff --git a/src/muz/rel/aig_exporter.cpp b/src/muz/rel/aig_exporter.cpp index bc98ab1f8..c38964be0 100644 --- a/src/muz/rel/aig_exporter.cpp +++ b/src/muz/rel/aig_exporter.cpp @@ -32,7 +32,7 @@ namespace datalog { predicates.insert(I->first); } - // reserve pred id = 0 for initalization purposes + // reserve pred id = 0 for initialization purposes unsigned num_preds = (unsigned)predicates.size() + 1; // poor's man round-up log2 @@ -118,7 +118,7 @@ namespace datalog { } exprs.reset(); - assert_pred_id(numqs ? r->get_tail(0)->get_decl() : 0, m_ruleid_var_set, exprs); + assert_pred_id(numqs ? r->get_tail(0)->get_decl() : nullptr, m_ruleid_var_set, exprs); assert_pred_id(r->get_head()->get_decl(), m_ruleid_varp_set, exprs); subst.reset(); @@ -141,7 +141,7 @@ namespace datalog { if (m_facts) { for (fact_vector::const_iterator I = m_facts->begin(), E = m_facts->end(); I != E; ++I) { exprs.reset(); - assert_pred_id(0, m_ruleid_var_set, exprs); + assert_pred_id(nullptr, m_ruleid_var_set, exprs); assert_pred_id(I->first, m_ruleid_varp_set, exprs); for (unsigned i = 0; i < I->second.size(); ++i) { diff --git a/src/muz/rel/aig_exporter.h b/src/muz/rel/aig_exporter.h index ea241fe2b..aac139adb 100644 --- a/src/muz/rel/aig_exporter.h +++ b/src/muz/rel/aig_exporter.h @@ -23,7 +23,7 @@ Abstract: namespace datalog { class aig_exporter { public: - aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts = 0); + aig_exporter(const rule_set& rules, context& ctx, const fact_vector *facts = nullptr); void operator()(std::ostream& out); private: @@ -60,7 +60,7 @@ namespace datalog { unsigned mk_or(unsigned id1, unsigned id2); unsigned get_var(const expr *e); unsigned mk_var(const expr *e); - unsigned mk_input_var(const expr *e = 0); + unsigned mk_input_var(const expr *e = nullptr); unsigned mk_expr_id(); }; } diff --git a/src/muz/rel/check_relation.cpp b/src/muz/rel/check_relation.cpp index ddda295cd..bd8fb4778 100644 --- a/src/muz/rel/check_relation.cpp +++ b/src/muz/rel/check_relation.cpp @@ -150,7 +150,7 @@ namespace datalog { check_relation_plugin::check_relation_plugin(relation_manager& rm): relation_plugin(check_relation_plugin::get_name(), rm), m(rm.get_context().get_manager()), - m_base(0) { + m_base(nullptr) { } check_relation_plugin::~check_relation_plugin() { } @@ -158,7 +158,7 @@ namespace datalog { return dynamic_cast(r); } check_relation* check_relation_plugin::get(relation_base* r) { - return r?dynamic_cast(r):0; + return r?dynamic_cast(r):nullptr; } check_relation const & check_relation_plugin::get(relation_base const& r) { return dynamic_cast(r); @@ -192,8 +192,8 @@ namespace datalog { const unsigned * cols1, const unsigned * cols2) : convenient_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2), m_join(j) {} - virtual ~join_fn() {} - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + ~join_fn() override {} + relation_base * operator()(const relation_base & r1, const relation_base & r2) override { check_relation const& t1 = get(r1); check_relation const& t2 = get(r2); check_relation_plugin& p = t1.get_plugin(); @@ -207,7 +207,7 @@ namespace datalog { const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { relation_join_fn* j = m_base->mk_join_fn(get(t1).rb(), get(t2).rb(), col_cnt, cols1, cols2); - return j?alloc(join_fn, j, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2):0; + return j?alloc(join_fn, j, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2):nullptr; } class check_relation_plugin::join_project_fn : public convenient_relation_join_project_fn { @@ -221,8 +221,8 @@ namespace datalog { : convenient_join_project_fn(o1_sig, o2_sig, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), m_join(j) {} - virtual ~join_project_fn() {} - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + ~join_project_fn() override {} + relation_base * operator()(const relation_base & r1, const relation_base & r2) override { check_relation const& t1 = get(r1); check_relation const& t2 = get(r2); check_relation_plugin& p = t1.get_plugin(); @@ -239,7 +239,7 @@ namespace datalog { relation_join_fn* j = m_base->mk_join_project_fn(get(t1).rb(), get(t2).rb(), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); return j?alloc(join_project_fn, j, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, - removed_col_cnt, removed_cols):0; + removed_col_cnt, removed_cols):nullptr; } void check_relation_plugin::verify_filter_project( @@ -491,7 +491,7 @@ namespace datalog { public: union_fn(relation_union_fn* m): m_union(m) {} - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("doc", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); check_relation& r = get(_r); check_relation const& src = get(_src); @@ -499,8 +499,8 @@ namespace datalog { expr_ref fml0 = r.m_fml; expr_ref delta0(r.m_fml.get_manager()); if (d) d->to_formula(delta0); - (*m_union)(r.rb(), src.rb(), d?(&d->rb()):0); - r.get_plugin().verify_union(fml0, src.rb(), r.rb(), delta0, d?(&d->rb()):0); + (*m_union)(r.rb(), src.rb(), d?(&d->rb()):nullptr); + r.get_plugin().verify_union(fml0, src.rb(), r.rb(), delta0, d?(&d->rb()):nullptr); r.rb().to_formula(r.m_fml); if (d) d->rb().to_formula(d->m_fml); } @@ -508,16 +508,16 @@ namespace datalog { relation_union_fn * check_relation_plugin::mk_union_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { - relation_base const* d1 = delta?(&(get(*delta).rb())):0; + relation_base const* d1 = delta?(&(get(*delta).rb())):nullptr; relation_union_fn* u = m_base->mk_union_fn(get(tgt).rb(), get(src).rb(), d1); - return u?alloc(union_fn, u):0; + return u?alloc(union_fn, u):nullptr; } relation_union_fn * check_relation_plugin::mk_widen_fn( const relation_base & tgt, const relation_base & src, const relation_base * delta) { - relation_base const* d1 = delta?(&(get(*delta).rb())):0; + relation_base const* d1 = delta?(&(get(*delta).rb())):nullptr; relation_union_fn* u = m_base->mk_widen_fn(get(tgt).rb(), get(src).rb(), d1); - return u?alloc(union_fn, u):0; + return u?alloc(union_fn, u):nullptr; } class check_relation_plugin::filter_identical_fn : public relation_mutator_fn { @@ -529,9 +529,9 @@ namespace datalog { m_filter(f) { } - virtual ~filter_identical_fn() {} + ~filter_identical_fn() override {} - virtual void operator()(relation_base & _r) { + void operator()(relation_base & _r) override { check_relation& r = get(_r); check_relation_plugin& p = r.get_plugin(); ast_manager& m = p.m; @@ -553,7 +553,7 @@ namespace datalog { relation_mutator_fn * check_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { relation_mutator_fn* r = m_base->mk_filter_identical_fn(get(t).rb(), col_cnt, identical_cols); - return r?alloc(filter_identical_fn, r, col_cnt, identical_cols):0; + return r?alloc(filter_identical_fn, r, col_cnt, identical_cols):nullptr; } class check_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { @@ -565,9 +565,9 @@ namespace datalog { m_condition(condition) { } - virtual ~filter_interpreted_fn() {} + ~filter_interpreted_fn() override {} - virtual void operator()(relation_base & tb) { + void operator()(relation_base & tb) override { check_relation& r = get(tb); check_relation_plugin& p = r.get_plugin(); expr_ref fml = r.m_fml; @@ -579,7 +579,7 @@ namespace datalog { relation_mutator_fn * check_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { relation_mutator_fn* r = m_base->mk_filter_interpreted_fn(get(t).rb(), condition); app_ref cond(condition, m); - return r?alloc(filter_interpreted_fn, r, cond):0; + return r?alloc(filter_interpreted_fn, r, cond):nullptr; } class check_relation_plugin::project_fn : public convenient_relation_project_fn { @@ -592,9 +592,9 @@ namespace datalog { m_project(p) { } - virtual ~project_fn() {} + ~project_fn() override {} - virtual relation_base * operator()(const relation_base & tb) { + relation_base * operator()(const relation_base & tb) override { check_relation const& t = get(tb); check_relation_plugin& p = t.get_plugin(); relation_base* r = (*m_project)(t.rb()); @@ -608,7 +608,7 @@ namespace datalog { const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { relation_transformer_fn* p = m_base->mk_project_fn(get(t).rb(), col_cnt, removed_cols); - return p?alloc(project_fn, p, t, col_cnt, removed_cols):0; + return p?alloc(project_fn, p, t, col_cnt, removed_cols):nullptr; } class check_relation_plugin::rename_fn : public convenient_relation_rename_fn { @@ -620,9 +620,9 @@ namespace datalog { m_permute(permute) { } - virtual ~rename_fn() {} + ~rename_fn() override {} - virtual relation_base * operator()(const relation_base & _t) { + relation_base * operator()(const relation_base & _t) override { check_relation const& t = get(_t); check_relation_plugin& p = t.get_plugin(); relation_signature const& sig = get_result_signature(); @@ -635,7 +635,7 @@ namespace datalog { const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { relation_transformer_fn* p = m_base->mk_rename_fn(get(r).rb(), cycle_len, permutation_cycle); - return p?alloc(rename_fn, p, r, cycle_len, permutation_cycle):0; + return p?alloc(rename_fn, p, r, cycle_len, permutation_cycle):nullptr; } class check_relation_plugin::filter_equal_fn : public relation_mutator_fn { @@ -649,8 +649,8 @@ namespace datalog { m_val(val), m_col(col) {} - virtual ~filter_equal_fn() { } - virtual void operator()(relation_base & tb) { + ~filter_equal_fn() override { } + void operator()(relation_base & tb) override { check_relation & t = get(tb); check_relation_plugin& p = t.get_plugin(); (*m_filter)(t.rb()); @@ -663,7 +663,7 @@ namespace datalog { relation_mutator_fn * check_relation_plugin::mk_filter_equal_fn( const relation_base & t, const relation_element & value, unsigned col) { relation_mutator_fn* r = m_base->mk_filter_equal_fn(get(t).rb(), value, col); - return r?alloc(filter_equal_fn, r, t, value, col):0; + return r?alloc(filter_equal_fn, r, t, value, col):nullptr; } @@ -682,7 +682,7 @@ namespace datalog { SASSERT(joined_col_cnt > 0); } - virtual void operator()(relation_base& tb, const relation_base& negb) { + void operator()(relation_base& tb, const relation_base& negb) override { check_relation& t = get(tb); check_relation const& n = get(negb); check_relation_plugin& p = t.get_plugin(); @@ -700,7 +700,7 @@ namespace datalog { const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols) { relation_intersection_filter_fn* f = m_base->mk_filter_by_negation_fn(get(t).rb(), get(neg).rb(), joined_col_cnt, t_cols, negated_cols); - return f?alloc(negation_filter_fn, f, joined_col_cnt, t_cols, negated_cols):0; + return f?alloc(negation_filter_fn, f, joined_col_cnt, t_cols, negated_cols):nullptr; } /* @@ -763,9 +763,9 @@ namespace datalog { m_xform(xform) {} - virtual ~filter_proj_fn() {} + ~filter_proj_fn() override {} - virtual relation_base* operator()(const relation_base & tb) { + relation_base* operator()(const relation_base & tb) override { check_relation const & t = get(tb); check_relation_plugin& p = t.get_plugin(); relation_base* r = (*m_xform)(t.rb()); @@ -779,7 +779,7 @@ namespace datalog { unsigned removed_col_cnt, const unsigned * removed_cols) { relation_transformer_fn* r = m_base->mk_filter_interpreted_and_project_fn(get(t).rb(), condition, removed_col_cnt, removed_cols); app_ref cond(condition, m); - return r?alloc(filter_proj_fn, r, t, cond, removed_col_cnt, removed_cols):0; + return r?alloc(filter_proj_fn, r, t, cond, removed_col_cnt, removed_cols):nullptr; } diff --git a/src/muz/rel/check_relation.h b/src/muz/rel/check_relation.h index 0fbb2a269..8fbce4184 100644 --- a/src/muz/rel/check_relation.h +++ b/src/muz/rel/check_relation.h @@ -38,20 +38,20 @@ namespace datalog { expr_ref mk_eq(relation_fact const& f) const; public: check_relation(check_relation_plugin& p, relation_signature const& s, relation_base* r); - virtual ~check_relation(); - virtual void reset(); - virtual void add_fact(const relation_fact & f); - virtual void add_new_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual check_relation * clone() const; - virtual check_relation * complement(func_decl*) const; - virtual void to_formula(expr_ref& fml) const; + ~check_relation() override; + void reset() override; + void add_fact(const relation_fact & f) override; + void add_new_fact(const relation_fact & f) override; + bool contains_fact(const relation_fact & f) const override; + check_relation * clone() const override; + check_relation * complement(func_decl*) const override; + void to_formula(expr_ref& fml) const override; check_relation_plugin& get_plugin() const; - virtual bool fast_empty() const; - virtual bool empty() const; - virtual void display(std::ostream& out) const; - virtual bool is_precise() const { return m_relation->is_precise(); } - virtual unsigned get_size_estimate_rows() const { return m_relation->get_size_estimate_rows(); } + bool fast_empty() const override; + bool empty() const override; + void display(std::ostream& out) const override; + bool is_precise() const override { return m_relation->is_precise(); } + unsigned get_size_estimate_rows() const override { return m_relation->get_size_estimate_rows(); } relation_base& rb() { return *m_relation; } relation_base const& rb() const { return *m_relation; } expr_ref ground(expr* fml) const; @@ -90,39 +90,39 @@ namespace datalog { unsigned_vector const& cols1, unsigned_vector const& cols2); public: check_relation_plugin(relation_manager& rm); - ~check_relation_plugin(); + ~check_relation_plugin() override; void set_plugin(relation_plugin* p) { m_base = p; } - virtual bool can_handle_signature(const relation_signature & s); + bool can_handle_signature(const relation_signature & s) override; static symbol get_name() { return symbol("check_relation"); } - virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_join_fn * mk_join_project_fn( + relation_base * mk_empty(const relation_signature & s) override; + relation_base * mk_full(func_decl* p, const relation_signature & s) override; + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_join_fn * mk_join_project_fn( const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn( + unsigned removed_col_cnt, const unsigned * removed_cols) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; + relation_intersection_filter_fn * mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, - const unsigned *negated_cols); - virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn( + const unsigned *negated_cols) override; + relation_transformer_fn * mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, - unsigned removed_col_cnt, const unsigned * removed_cols); + unsigned removed_col_cnt, const unsigned * removed_cols) override; void verify_join(relation_base const& t1, relation_base const& t2, relation_base const& t, unsigned_vector const& cols1, unsigned_vector const& cols2); diff --git a/src/muz/rel/dl_base.cpp b/src/muz/rel/dl_base.cpp index 904faf71f..5dd21a9f0 100644 --- a/src/muz/rel/dl_base.cpp +++ b/src/muz/rel/dl_base.cpp @@ -51,7 +51,7 @@ namespace datalog { ast_manager & m = renaming_arg.get_manager(); unsigned sz = map.size(); unsigned ofs = sz-1; - renaming_arg.resize(sz, static_cast(0)); + renaming_arg.resize(sz, static_cast(nullptr)); for(unsigned i=0; i (1 << 18)) { @@ -423,17 +423,17 @@ namespace datalog { const row_interface & m_parent; unsigned m_index; protected: - virtual bool is_finished() const { return m_index==m_parent.size(); } + bool is_finished() const override { return m_index==m_parent.size(); } public: fact_row_iterator(const row_interface & row, bool finished) : m_parent(row), m_index(finished ? row.size() : 0) {} - virtual table_element operator*() { + table_element operator*() override { SASSERT(!is_finished()); return m_parent[m_index]; } - virtual void operator++() { + void operator++() override { m_index++; SASSERT(m_index<=m_parent.size()); } diff --git a/src/muz/rel/dl_base.h b/src/muz/rel/dl_base.h index 4e59e9258..4d202f2a2 100644 --- a/src/muz/rel/dl_base.h +++ b/src/muz/rel/dl_base.h @@ -41,20 +41,20 @@ namespace datalog { public: scoped_rel(T* t) : m_t(t) {} ~scoped_rel() { if (m_t) { universal_delete(m_t); } } - scoped_rel() : m_t(0) {} + scoped_rel() : m_t(nullptr) {} scoped_rel& operator=(T* t) { if (m_t && t != m_t) { universal_delete(m_t); } m_t = t; return *this; } T* operator->() { return m_t; } const T* operator->() const { return m_t; } T& operator*() { return *m_t; } const T& operator*() const { return *m_t; } - operator bool() const { return m_t!=0; } + operator bool() const { return m_t!=nullptr; } T* get() const { return m_t; } /** \brief Remove object from \c scoped_rel without deleting it. */ T* release() { T* res = m_t; - m_t = 0; + m_t = nullptr; return res; } }; @@ -202,7 +202,7 @@ namespace datalog { virtual void operator()(base_object & tgt, const base_object & src, base_object * delta) = 0; void operator()(base_object & tgt, const base_object & src) { - (*this)(tgt, src, static_cast(0)); + (*this)(tgt, src, static_cast(nullptr)); } }; @@ -220,7 +220,7 @@ namespace datalog { */ class mutator_fn : public base_fn { public: - virtual ~mutator_fn() {} + ~mutator_fn() override {} virtual void operator()(base_object & t) = 0; @@ -335,55 +335,55 @@ namespace datalog { virtual join_fn * mk_join_fn(const base_object & t1, const base_object & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return 0; } + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { return nullptr; } virtual transformer_fn * mk_project_fn(const base_object & t, unsigned col_cnt, - const unsigned * removed_cols) { return 0; } + const unsigned * removed_cols) { return nullptr; } virtual join_fn * mk_join_project_fn(const base_object & t1, const base_object & t2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } + unsigned removed_col_cnt, const unsigned * removed_cols) { return nullptr; } virtual transformer_fn * mk_rename_fn(const base_object & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle) { return 0; } + const unsigned * permutation_cycle) { return nullptr; } virtual transformer_fn * mk_permutation_rename_fn(const base_object & t, - const unsigned * permutation) { return 0; } + const unsigned * permutation) { return nullptr; } public: virtual union_fn * mk_union_fn(const base_object & tgt, const base_object & src, - const base_object * delta) { return 0; } + const base_object * delta) { return nullptr; } protected: virtual union_fn * mk_widen_fn(const base_object & tgt, const base_object & src, - const base_object * delta) { return 0; } + const base_object * delta) { return nullptr; } virtual mutator_fn * mk_filter_identical_fn(const base_object & t, unsigned col_cnt, - const unsigned * identical_cols) { return 0; } + const unsigned * identical_cols) { return nullptr; } virtual mutator_fn * mk_filter_equal_fn(const base_object & t, const element & value, - unsigned col) { return 0; } + unsigned col) { return nullptr; } virtual mutator_fn * mk_filter_interpreted_fn(const base_object & t, app * condition) - { return 0; } + { return nullptr; } virtual transformer_fn * mk_filter_interpreted_and_project_fn(const base_object & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) - { return 0; } + { return nullptr; } virtual transformer_fn * mk_select_equal_and_project_fn(const base_object & t, - const element & value, unsigned col) { return 0; } + const element & value, unsigned col) { return nullptr; } virtual intersection_filter_fn * mk_filter_by_intersection_fn(const base_object & t, const base_object & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) - { return 0; } + { return nullptr; } virtual intersection_filter_fn * mk_filter_by_negation_fn(const base_object & t, const base_object & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) - { return 0; } + { return nullptr; } virtual intersection_join_filter_fn * mk_filter_by_negated_join_fn( const base_object & t, @@ -393,7 +393,7 @@ namespace datalog { unsigned_vector const& src_cols, unsigned_vector const& src1_cols, unsigned_vector const& src2_cols) - { return 0; } + { return nullptr; } }; @@ -629,19 +629,19 @@ namespace datalog { class identity_transformer_fn : public transformer_fn { public: - virtual base_object * operator()(const base_object & t) { + base_object * operator()(const base_object & t) override { return t.clone(); } }; class identity_mutator_fn : public mutator_fn { public: - virtual void operator()(base_object & t) {}; + void operator()(base_object & t) override {}; }; class identity_intersection_filter_fn : public intersection_filter_fn { public: - virtual void operator()(base_object & t, const base_object & neg) {}; + void operator()(base_object & t, const base_object & neg) override {}; }; class default_permutation_rename_fn : public transformer_fn { @@ -655,11 +655,11 @@ namespace datalog { : m_permutation(o.get_signature().size(), permutation), m_renamers_initialized(false) {} - ~default_permutation_rename_fn() { + ~default_permutation_rename_fn() override { dealloc_ptr_vector_content(m_renamers); } - base_object * operator()(const base_object & o) { + base_object * operator()(const base_object & o) override { const base_object * res = &o; scoped_rel res_scoped; if(m_renamers_initialized) { @@ -803,11 +803,11 @@ namespace datalog { protected: relation_base(relation_plugin & plugin, const relation_signature & s) : base_ancestor(plugin, s) {} - virtual ~relation_base() {} + ~relation_base() override {} public: virtual relation_base * complement(func_decl* p) const = 0; - virtual void reset(); + void reset() override; virtual void display_tuples(func_decl & pred, std::ostream & out) const { out << "Tuples in " << pred.get_name() << ": \n"; @@ -832,7 +832,7 @@ namespace datalog { class table_plugin; class table_base; - typedef uint64 table_sort; + typedef uint64_t table_sort; typedef svector table_signature_base0; typedef uint64_hash table_sort_hash; @@ -1022,21 +1022,21 @@ namespace datalog { table_plugin(symbol const& n, relation_manager & manager) : plugin_object(n, manager) {} public: - virtual bool can_handle_signature(const table_signature & s) { return s.functional_columns()==0; } + bool can_handle_signature(const table_signature & s) override { return s.functional_columns()==0; } protected: /** If the returned value is non-zero, the returned object must take ownership of \c mapper. Otherwise \c mapper must remain unmodified. */ - virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { return 0; } + virtual table_mutator_fn * mk_map_fn(const table_base & t, table_row_mutator_fn * mapper) { return nullptr; } /** If the returned value is non-zero, the returned object must take ownership of \c reducer. Otherwise \c reducer must remain unmodified. */ virtual table_transformer_fn * mk_project_with_reduce_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { return 0; } + const unsigned * removed_cols, table_row_pair_reduce_fn * reducer) { return nullptr; } }; @@ -1044,17 +1044,17 @@ namespace datalog { protected: table_base(table_plugin & plugin, const table_signature & s) : base_ancestor(plugin, s) {} - virtual ~table_base() {} + ~table_base() override {} public: - virtual table_base * clone() const; - virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; - virtual bool empty() const; + table_base * clone() const override; + virtual table_base * complement(func_decl* p, const table_element * func_columns = nullptr) const; + bool empty() const override; /** \brief Return true if table contains fact that corresponds to \c f in all non-functional columns. */ - virtual bool contains_fact(const table_fact & f) const; + bool contains_fact(const table_fact & f) const override; /** \brief If \c f (i.e. its non-functional part) is not present in the table, @@ -1082,11 +1082,11 @@ namespace datalog { virtual void remove_fact(table_element const* fact) = 0; virtual void remove_facts(unsigned fact_cnt, const table_fact * facts); virtual void remove_facts(unsigned fact_cnt, const table_element * facts); - virtual void reset(); + void reset() override; class row_interface; - virtual void display(std::ostream & out) const; + void display(std::ostream & out) const override; /** \brief Convert table to a formula that encodes the table. @@ -1245,9 +1245,9 @@ namespace datalog { public: caching_row_interface(const table_base & parent) : row_interface(parent) {} - virtual void get_fact(table_fact & result) const = 0; + void get_fact(table_fact & result) const override = 0; - virtual table_element operator[](unsigned col) const { + table_element operator[](unsigned col) const override { ensure_populated(); return m_current[col]; } diff --git a/src/muz/rel/dl_bound_relation.cpp b/src/muz/rel/dl_bound_relation.cpp index 9dc0eb8d5..60358db48 100644 --- a/src/muz/rel/dl_bound_relation.cpp +++ b/src/muz/rel/dl_bound_relation.cpp @@ -79,11 +79,11 @@ namespace datalog { : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2) { } - virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + relation_base * operator()(const relation_base & _r1, const relation_base & _r2) override { bound_relation const& r1 = get(_r1); bound_relation const& r2 = get(_r2); bound_relation_plugin& p = r1.get_plugin(); - bound_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + bound_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); return result; } @@ -92,7 +92,7 @@ namespace datalog { relation_join_fn * bound_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(r1) || !check_kind(r2)) { - return 0; + return nullptr; } return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); } @@ -104,10 +104,10 @@ namespace datalog { : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { } - virtual relation_base * operator()(const relation_base & _r) { + relation_base * operator()(const relation_base & _r) override { bound_relation const& r = get(_r); bound_relation_plugin& p = r.get_plugin(); - bound_relation* result = get(p.mk_full(0, get_result_signature())); + bound_relation* result = get(p.mk_full(nullptr, get_result_signature())); result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); return result; } @@ -124,10 +124,10 @@ namespace datalog { : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { } - virtual relation_base * operator()(const relation_base & _r) { + relation_base * operator()(const relation_base & _r) override { bound_relation const& r = get(_r); bound_relation_plugin& p = r.get_plugin(); - bound_relation* result = get(p.mk_full(0, get_result_signature())); + bound_relation* result = get(p.mk_full(nullptr, get_result_signature())); result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); return result; } @@ -138,7 +138,7 @@ namespace datalog { if(check_kind(r)) { return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); } - return 0; + return nullptr; } @@ -148,7 +148,7 @@ namespace datalog { union_fn(bool is_widen) : m_is_widen(is_widen) { } - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); get(_r).mk_union(get(_src), get(_delta), m_is_widen); } @@ -160,7 +160,7 @@ namespace datalog { union_fn_i(bool is_widen) : m_is_widen(is_widen) { } - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("bound_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); get(_r).mk_union_i(get_interval_relation(_src), get(_delta), m_is_widen); TRACE("bound_relation", _r.display(tout << "dst':\n");); @@ -176,7 +176,7 @@ namespace datalog { if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { return alloc(union_fn, false); } - return 0; + return nullptr; } relation_union_fn * bound_relation_plugin::mk_widen_fn( @@ -188,7 +188,7 @@ namespace datalog { if (check_kind(tgt) && check_kind(src) && (!delta || check_kind(*delta))) { return alloc(union_fn, true); } - return 0; + return nullptr; } class bound_relation_plugin::filter_identical_fn : public relation_mutator_fn { @@ -197,7 +197,7 @@ namespace datalog { filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_cols(col_cnt, identical_cols) {} - virtual void operator()(relation_base & r) { + void operator()(relation_base & r) override { for (unsigned i = 1; i < m_cols.size(); ++i) { get(r).equate(m_cols[0], m_cols[i]); } @@ -209,14 +209,14 @@ namespace datalog { if(check_kind(t)) { return alloc(filter_identical_fn, col_cnt, identical_cols); } - return 0; + return nullptr; } class bound_relation_plugin::filter_equal_fn : public relation_mutator_fn { public: filter_equal_fn(relation_element const& value, unsigned col) {} - virtual void operator()(relation_base & r) { } + void operator()(relation_base & r) override { } }; relation_mutator_fn * bound_relation_plugin::mk_filter_equal_fn(const relation_base & r, @@ -224,7 +224,7 @@ namespace datalog { if (check_kind(r)) { return alloc(filter_equal_fn, value, col); } - return 0; + return nullptr; } class bound_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { @@ -280,7 +280,7 @@ namespace datalog { filter_interpreted_fn(ast_manager& m, app* cond) : m_cond(cond, m), - m_lt(m), m_arith(m), m_interval(0), m_kind(NOT_APPLICABLE) { + m_lt(m), m_arith(m), m_interval(nullptr), m_kind(NOT_APPLICABLE) { expr* l, *r, *r1, *r2, *c2; rational n1; if ((m_arith.is_lt(cond, l, r) || m_arith.is_gt(cond, r, l)) && @@ -342,7 +342,7 @@ namespace datalog { // x < y + z // - void operator()(relation_base& t) { + void operator()(relation_base& t) override { TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); bound_relation& r = get(t); switch(m_kind) { @@ -370,11 +370,11 @@ namespace datalog { TRACE("dl", t.display(tout << "result\n");); } - bool supports_attachment(relation_base& t) { + bool supports_attachment(relation_base& t) override { return is_interval_relation(t); } - void attach(relation_base& t) { + void attach(relation_base& t) override { SASSERT(is_interval_relation(t)); interval_relation& r = get_interval_relation(t); m_interval = &r; @@ -592,7 +592,7 @@ namespace datalog { scoped_ptr fe = get_plugin().mk_filter_equal_fn(r, f[i], i); (*fe)(r); } - mk_union(r, 0, false); + mk_union(r, nullptr, false); } bool bound_relation::contains_fact(const relation_fact & f) const { @@ -604,12 +604,12 @@ namespace datalog { } bound_relation * bound_relation::clone() const { - bound_relation* result = 0; + bound_relation* result = nullptr; if (empty()) { result = bound_relation_plugin::get(get_plugin().mk_empty(get_signature())); } else { - result = bound_relation_plugin::get(get_plugin().mk_full(0, get_signature())); + result = bound_relation_plugin::get(get_plugin().mk_full(nullptr, get_signature())); result->copy(*this); } return result; @@ -647,7 +647,7 @@ namespace datalog { bound_relation * bound_relation::complement(func_decl* p) const { UNREACHABLE(); - return 0; + return nullptr; } void bound_relation::to_formula(expr_ref& fml) const { diff --git a/src/muz/rel/dl_bound_relation.h b/src/muz/rel/dl_bound_relation.h index 3dec9d313..6c613a472 100644 --- a/src/muz/rel/dl_bound_relation.h +++ b/src/muz/rel/dl_bound_relation.h @@ -47,29 +47,29 @@ namespace datalog { bool_rewriter m_bsimp; public: bound_relation_plugin(relation_manager& m); - virtual bool can_handle_signature(const relation_signature & s); + bool can_handle_signature(const relation_signature & s) override; static symbol get_name() { return symbol("bound_relation"); } - virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - - virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols) { return 0; } + relation_base * mk_empty(const relation_signature & s) override; + relation_base * mk_full(func_decl* p, const relation_signature & s) override; + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; + + relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) override { return nullptr; } #if 0 @@ -123,12 +123,12 @@ namespace datalog { bound_relation(bound_relation_plugin& p, relation_signature const& s, bool is_empty); bound_relation& operator=(bound_relation const& other); - virtual bool empty() const { return m_empty; } - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual bound_relation * clone() const; - virtual bound_relation * complement(func_decl* p) const; - virtual void to_formula(expr_ref& fml) const; + bool empty() const override { return m_empty; } + void add_fact(const relation_fact & f) override; + bool contains_fact(const relation_fact & f) const override; + bound_relation * clone() const override; + bound_relation * complement(func_decl* p) const override; + void to_formula(expr_ref& fml) const override; bound_relation_plugin& get_plugin() const; void mk_union_i(interval_relation const& src, bound_relation* delta, bool is_widen); @@ -141,28 +141,28 @@ namespace datalog { bool is_lt(unsigned i, unsigned j) const; - virtual bool is_precise() const { return false; } + bool is_precise() const override { return false; } private: typedef uint_set2 T; - virtual T mk_intersect(T const& t1, T const& t2, bool& is_empty) const; + T mk_intersect(T const& t1, T const& t2, bool& is_empty) const override; - virtual T mk_widen(T const& t1, T const& t2) const; + T mk_widen(T const& t1, T const& t2) const override; - virtual T mk_unite(T const& t1, T const& t2) const; + T mk_unite(T const& t1, T const& t2) const override; - virtual T mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, T const& t) const; + T mk_eq(union_find<> const& old_eqs, union_find<> const& new_eqs, T const& t) const override; - virtual void mk_rename_elem(T& i, unsigned col_cnt, unsigned const* cycle); + void mk_rename_elem(T& i, unsigned col_cnt, unsigned const* cycle) override; - virtual bool is_subset_of(T const& t1, T const& t2) const; + bool is_subset_of(T const& t1, T const& t2) const override; - virtual bool is_full(T const& t) const; + bool is_full(T const& t) const override; - virtual bool is_empty(unsigned idx, T const& t) const; + bool is_empty(unsigned idx, T const& t) const override; - virtual void display_index(unsigned idx, T const& t, std::ostream& out) const; + void display_index(unsigned idx, T const& t, std::ostream& out) const override; void normalize(T const& src, T& dst) const; diff --git a/src/muz/rel/dl_check_table.cpp b/src/muz/rel/dl_check_table.cpp index 0c4be18f1..0a6d90027 100644 --- a/src/muz/rel/dl_check_table.cpp +++ b/src/muz/rel/dl_check_table.cpp @@ -40,12 +40,12 @@ namespace datalog { table_base& check_table_plugin::checker(table_base& r) { return *get(r).m_checker; } table_base const& check_table_plugin::checker(table_base const& r) { return *get(r).m_checker; } - table_base* check_table_plugin::checker(table_base* r) { return r?(get(*r).m_checker):0; } - table_base const* check_table_plugin::checker(table_base const* r) { return r?(get(*r).m_checker):0; } + table_base* check_table_plugin::checker(table_base* r) { return r?(get(*r).m_checker):nullptr; } + table_base const* check_table_plugin::checker(table_base const* r) { return r?(get(*r).m_checker):nullptr; } table_base& check_table_plugin::tocheck(table_base& r) { return *get(r).m_tocheck; } table_base const& check_table_plugin::tocheck(table_base const& r) { return *get(r).m_tocheck; } - table_base* check_table_plugin::tocheck(table_base* r) { return r?(get(*r).m_tocheck):0; } - table_base const* check_table_plugin::tocheck(table_base const* r) { return r?(get(*r).m_tocheck):0; } + table_base* check_table_plugin::tocheck(table_base* r) { return r?(get(*r).m_tocheck):nullptr; } + table_base const* check_table_plugin::tocheck(table_base const* r) { return r?(get(*r).m_tocheck):nullptr; } table_base * check_table_plugin::mk_empty(const table_signature & s) { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); @@ -65,7 +65,7 @@ namespace datalog { m_checker = p.get_manager().mk_join_fn(checker(t1), checker(t2), col_cnt, cols1, cols2); } - virtual table_base* operator()(const table_base & t1, const table_base & t2) { + table_base* operator()(const table_base & t1, const table_base & t2) override { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); @@ -77,7 +77,7 @@ namespace datalog { table_join_fn * check_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(t1) || !check_kind(t2)) { - return 0; + return nullptr; } return alloc(join_fn, *this, t1, t2, col_cnt, cols1, cols2); } @@ -93,7 +93,7 @@ namespace datalog { m_checker = p.get_manager().mk_join_project_fn(checker(t1), checker(t2), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } - virtual table_base* operator()(const table_base & t1, const table_base & t2) { + table_base* operator()(const table_base & t1, const table_base & t2) override { table_base* ttocheck = (*m_tocheck)(tocheck(t1), tocheck(t2)); table_base* tchecker = (*m_checker)(checker(t1), checker(t2)); check_table* result = alloc(check_table, get(t1).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); @@ -105,7 +105,7 @@ namespace datalog { unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if (!check_kind(t1) || !check_kind(t2)) { - return 0; + return nullptr; } return alloc(join_project_fn, *this, t1, t2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols); } @@ -119,7 +119,7 @@ namespace datalog { m_checker = p.get_manager().mk_union_fn(checker(tgt), checker(src), checker(delta)); } - virtual void operator()(table_base& tgt, const table_base& src, table_base* delta) { + void operator()(table_base& tgt, const table_base& src, table_base* delta) override { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); (*m_tocheck)(tocheck(tgt), tocheck(src), tocheck(delta)); (*m_checker)(checker(tgt), checker(src), checker(delta)); @@ -132,7 +132,7 @@ namespace datalog { table_union_fn * check_table_plugin::mk_union_fn(const table_base & tgt, const table_base & src, const table_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; + return nullptr; } return alloc(union_fn, *this, tgt, src, delta); @@ -147,7 +147,7 @@ namespace datalog { m_tocheck = p.get_manager().mk_project_fn(tocheck(t), col_cnt, removed_cols); } - table_base* operator()(table_base const& src) { + table_base* operator()(table_base const& src) override { table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); @@ -157,7 +157,7 @@ namespace datalog { table_transformer_fn * check_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (!check_kind(t)) { - return 0; + return nullptr; } return alloc(project_fn, *this, t, col_cnt, removed_cols); } @@ -171,7 +171,7 @@ namespace datalog { m_tocheck = p.get_manager().mk_select_equal_and_project_fn(tocheck(t), value, col); } - table_base* operator()(table_base const& src) { + table_base* operator()(table_base const& src) override { table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), tchecker->get_signature(), ttocheck, tchecker); @@ -182,7 +182,7 @@ namespace datalog { table_transformer_fn * check_table_plugin::mk_select_equal_and_project_fn(const table_base & t, const table_element & value, unsigned col) { if (!check_kind(t)) { - return 0; + return nullptr; } return alloc(select_equal_and_project_fn, *this, t, value, col); } @@ -196,7 +196,7 @@ namespace datalog { m_tocheck = p.get_manager().mk_rename_fn(tocheck(t), cycle_len, cycle); } - table_base* operator()(table_base const& src) { + table_base* operator()(table_base const& src) override { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); @@ -207,7 +207,7 @@ namespace datalog { table_transformer_fn * check_table_plugin::mk_rename_fn(const table_base & t, unsigned len, const unsigned * cycle) { if (!check_kind(t)) { - return 0; + return nullptr; } return alloc(rename_fn, *this, t, len, cycle); } @@ -222,7 +222,7 @@ namespace datalog { m_tocheck = p.get_manager().mk_filter_identical_fn(tocheck(t), cnt, cols); } - void operator()(table_base & t) { + void operator()(table_base & t) override { (*m_checker)(checker(t)); (*m_tocheck)(tocheck(t)); get(t).well_formed(); @@ -234,7 +234,7 @@ namespace datalog { if (check_kind(t)) { return alloc(filter_identical_fn, *this, t, col_cnt, identical_cols); } - return 0; + return nullptr; } class check_table_plugin::filter_equal_fn : public table_mutator_fn { @@ -247,7 +247,7 @@ namespace datalog { m_tocheck = p.get_manager().mk_filter_equal_fn(tocheck(t), v, col); } - virtual void operator()(table_base& src) { + void operator()(table_base& src) override { (*m_checker)(checker(src)); (*m_tocheck)(tocheck(src)); get(src).well_formed(); @@ -258,7 +258,7 @@ namespace datalog { if (check_kind(t)) { return alloc(filter_equal_fn, *this, t, value, col); } - return 0; + return nullptr; } class check_table_plugin::filter_interpreted_fn : public table_mutator_fn { @@ -271,7 +271,7 @@ namespace datalog { m_tocheck = p.get_manager().mk_filter_interpreted_fn(tocheck(t), condition); } - virtual void operator()(table_base& src) { + void operator()(table_base& src) override { (*m_checker)(checker(src)); (*m_tocheck)(tocheck(src)); get(src).well_formed(); @@ -282,7 +282,7 @@ namespace datalog { if (check_kind(t)) { return alloc(filter_interpreted_fn, *this, t, condition); } - return 0; + return nullptr; } class check_table_plugin::filter_interpreted_and_project_fn : public table_transformer_fn { @@ -296,7 +296,7 @@ namespace datalog { m_tocheck = p.get_manager().mk_filter_interpreted_and_project_fn(tocheck(t), condition, removed_col_cnt, removed_cols); } - table_base* operator()(table_base const& src) { + table_base* operator()(table_base const& src) override { table_base* tchecker = (*m_checker)(checker(src)); table_base* ttocheck = (*m_tocheck)(tocheck(src)); check_table* result = alloc(check_table, get(src).get_plugin(), ttocheck->get_signature(), ttocheck, tchecker); @@ -309,7 +309,7 @@ namespace datalog { if (check_kind(t)) { return alloc(filter_interpreted_and_project_fn, *this, t, condition, removed_col_cnt, removed_cols); } - return 0; + return nullptr; } class check_table_plugin::filter_by_negation_fn : public table_intersection_filter_fn { @@ -325,7 +325,7 @@ namespace datalog { m_tocheck = p.get_manager().mk_filter_by_negation_fn(tocheck(t), tocheck(negated_obj), joined_col_cnt, t_cols, negated_cols); } - virtual void operator()(table_base& src, table_base const& negated_obj) { + void operator()(table_base& src, table_base const& negated_obj) override { IF_VERBOSE(1, verbose_stream() << __FUNCTION__ << "\n";); (*m_checker)(checker(src), checker(negated_obj)); (*m_tocheck)(tocheck(src), tocheck(negated_obj)); @@ -340,7 +340,7 @@ namespace datalog { if (check_kind(t) && check_kind(negated_obj)) { return alloc(filter_by_negation_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } - return 0; + return nullptr; } // ------------------ diff --git a/src/muz/rel/dl_check_table.h b/src/muz/rel/dl_check_table.h index 412dd2dbc..7bd4cf12d 100644 --- a/src/muz/rel/dl_check_table.h +++ b/src/muz/rel/dl_check_table.h @@ -53,34 +53,34 @@ namespace datalog { m_checker(*manager.get_table_plugin(checker)), m_tocheck(*manager.get_table_plugin(tocheck)), m_count(0) {} - virtual table_base * mk_empty(const table_signature & s); + table_base * mk_empty(const table_signature & s) override; - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols); - virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, - const table_element & value, unsigned col); - virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, - unsigned col); - virtual table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition); - virtual table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, - app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); - virtual table_intersection_filter_fn * mk_filter_by_negation_fn( - const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); + table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) override; + table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta) override; + table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) override; + table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + table_mutator_fn * mk_filter_equal_fn(const table_base & t, const table_element & value, + unsigned col) override; + table_mutator_fn * mk_filter_interpreted_fn(const table_base & t, app * condition) override; + table_transformer_fn * mk_filter_interpreted_and_project_fn(const table_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) override; + table_intersection_filter_fn * mk_filter_by_negation_fn( + const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) override; - virtual bool can_handle_signature(table_signature const& s); + bool can_handle_signature(table_signature const& s) override; private: static check_table& get(table_base& r); @@ -106,7 +106,7 @@ namespace datalog { check_table(check_table_plugin & p, const table_signature & sig); check_table(check_table_plugin & p, const table_signature & sig, table_base* tocheck, table_base* checker); - virtual ~check_table(); + ~check_table() override; bool well_formed() const; @@ -116,18 +116,18 @@ namespace datalog { return static_cast(table_base::get_plugin()); } - virtual bool empty() const; - virtual void add_fact(const table_fact & f); - virtual void remove_fact(const table_element* fact); - virtual bool contains_fact(const table_fact & f) const; - virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; - virtual table_base * clone() const; + bool empty() const override; + void add_fact(const table_fact & f) override; + void remove_fact(const table_element* fact) override; + bool contains_fact(const table_fact & f) const override; + table_base * complement(func_decl* p, const table_element * func_columns = nullptr) const override; + table_base * clone() const override; - virtual iterator begin() const { SASSERT(well_formed()); return m_tocheck->begin(); } - virtual iterator end() const { return m_tocheck->end(); } + iterator begin() const override { SASSERT(well_formed()); return m_tocheck->begin(); } + iterator end() const override { return m_tocheck->end(); } - virtual unsigned get_size_estimate_rows() const { return m_tocheck->get_size_estimate_rows(); } - virtual unsigned get_size_estimate_bytes() const { return m_tocheck->get_size_estimate_bytes(); } + unsigned get_size_estimate_rows() const override { return m_tocheck->get_size_estimate_rows(); } + unsigned get_size_estimate_bytes() const override { return m_tocheck->get_size_estimate_bytes(); } }; }; diff --git a/src/muz/rel/dl_compiler.cpp b/src/muz/rel/dl_compiler.cpp index 32bcfed50..2de0679a5 100644 --- a/src/muz/rel/dl_compiler.cpp +++ b/src/muz/rel/dl_compiler.cpp @@ -1120,7 +1120,7 @@ namespace datalog { //and clear local deltas make_inloop_delta_transition(global_head_deltas, global_tail_deltas, local_deltas, *loop_body); - loop_body->set_observer(0); + loop_body->set_observer(nullptr); acc.push_back(instruction::mk_while_loop(loop_control_regs.size(), loop_control_regs.c_ptr(), loop_body)); } @@ -1316,7 +1316,7 @@ namespace datalog { pred2idx empty_pred2idx_map; - compile_strats(m_rule_set.get_stratifier(), static_cast(0), + compile_strats(m_rule_set.get_stratifier(), static_cast(nullptr), empty_pred2idx_map, true, execution_code); @@ -1331,7 +1331,7 @@ namespace datalog { termination_code.push_back(instruction::mk_store(m_context.get_manager(), pred, reg)); } - acc.set_observer(0); + acc.set_observer(nullptr); TRACE("dl", execution_code.display(execution_context(m_context), tout);); } diff --git a/src/muz/rel/dl_compiler.h b/src/muz/rel/dl_compiler.h index 9aedf141a..d7bdcad34 100644 --- a/src/muz/rel/dl_compiler.h +++ b/src/muz/rel/dl_compiler.h @@ -60,7 +60,7 @@ namespace datalog { ACK_UNBOUND_VAR(var_index) - encodes that the column contains a variable that is unbound (by the corresponding rule body), - var_index is the de-Brujin index (var->get_idx()) + var_index is the de-Bruijn index (var->get_idx()) of the variable associated with the column. ACK_CONSTANT(constant) - encodes that the column contains the constant. @@ -93,11 +93,11 @@ namespace datalog { compiler & m_parent; rule * m_current; public: - instruction_observer(compiler & parent) : m_parent(parent), m_current(0) {} + instruction_observer(compiler & parent) : m_parent(parent), m_current(nullptr) {} void start_rule(rule * r) { SASSERT(!m_current); m_current=r; } - void finish_rule() { m_current = 0; } - virtual void notify(instruction * i) { + void finish_rule() { m_current = nullptr; } + void notify(instruction * i) override { if(m_current) { i->set_accounting_parent_object(m_parent.m_context, m_current); } diff --git a/src/muz/rel/dl_external_relation.cpp b/src/muz/rel/dl_external_relation.cpp index ff9bef40e..88511530e 100644 --- a/src/muz/rel/dl_external_relation.cpp +++ b/src/muz/rel/dl_external_relation.cpp @@ -46,7 +46,7 @@ namespace datalog { args.push_back(f[i]); } if (!fn.get()) { - fn = m.mk_func_decl(fid, k, 0, 0, args.size(), args.c_ptr()); + fn = m.mk_func_decl(fid, k, 0, nullptr, args.size(), args.c_ptr()); } if (destructive) { get_plugin().reduce_assign(fn, args.size(), args.c_ptr(), 1, args.c_ptr()); @@ -63,7 +63,7 @@ namespace datalog { expr_ref res(m); if (!m_is_empty_fn.get()) { family_id fid = get_plugin().get_family_id(); - const_cast(m_is_empty_fn) = m.mk_func_decl(fid, OP_RA_IS_EMPTY, 0, 0, 1, &r); + const_cast(m_is_empty_fn) = m.mk_func_decl(fid, OP_RA_IS_EMPTY, 0, nullptr, 1, &r); } get_plugin().reduce(m_is_empty_fn, 1, &r, res); return m.is_true(res); @@ -86,7 +86,7 @@ namespace datalog { expr* rel = m_rel.get(); expr_ref res(m.mk_fresh_const("T", m.get_sort(rel)), m); expr* rel_out = res.get(); - func_decl_ref fn(m.mk_func_decl(fid, OP_RA_CLONE,0,0, 1, &rel), m); + func_decl_ref fn(m.mk_func_decl(fid, OP_RA_CLONE,0,nullptr, 1, &rel), m); get_plugin().reduce_assign(fn, 1, &rel, 1, &rel_out); return alloc(external_relation, get_plugin(), get_signature(), res); } @@ -96,7 +96,7 @@ namespace datalog { family_id fid = get_plugin().get_family_id(); expr_ref res(m); expr* rel = m_rel; - func_decl_ref fn(m.mk_func_decl(fid, OP_RA_COMPLEMENT,0,0, 1, &rel), m); + func_decl_ref fn(m.mk_func_decl(fid, OP_RA_COMPLEMENT,0,nullptr, 1, &rel), m); get_plugin().reduce(fn, 1, &rel, res); return alloc(external_relation, get_plugin(), get_signature(), res); } @@ -140,8 +140,8 @@ namespace datalog { family_id fid = get_family_id(); expr_ref e(m.mk_fresh_const("T", r_sort), m); expr* args[1] = { e.get() }; - func_decl_ref empty_decl(m.mk_func_decl(fid, OP_RA_EMPTY, 1, ¶m, 0, (sort*const*)0), m); - reduce_assign(empty_decl, 0, 0, 1, args); + func_decl_ref empty_decl(m.mk_func_decl(fid, OP_RA_EMPTY, 1, ¶m, 0, (sort*const*)nullptr), m); + reduce_assign(empty_decl, 0, nullptr, 1, args); return alloc(external_relation, *this, s, e); } @@ -195,7 +195,7 @@ namespace datalog { m_join_fn = m.mk_func_decl(fid, OP_RA_JOIN, params.size(), params.c_ptr(), 2, domain); } - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + relation_base * operator()(const relation_base & r1, const relation_base & r2) override { expr_ref res(m_plugin.get_ast_manager()); m_args[0] = get(r1).get_relation(); m_args[1] = get(r2).get_relation(); @@ -207,7 +207,7 @@ namespace datalog { relation_join_fn * external_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(r1) || !check_kind(r2)) { - return 0; + return nullptr; } return alloc(join_fn, *this, r1.get_signature(), r2.get_signature() , col_cnt, cols1, cols2); } @@ -231,7 +231,7 @@ namespace datalog { m_project_fn = m.mk_func_decl(fid, OP_RA_PROJECT, params.size(), params.c_ptr(), 1, &relation_sort); } - virtual relation_base * operator()(const relation_base & r) { + relation_base * operator()(const relation_base & r) override { expr_ref res(m_plugin.get_ast_manager()); expr* rel = get(r).get_relation(); m_plugin.reduce(m_project_fn, 1, &rel, res); @@ -265,7 +265,7 @@ namespace datalog { m_rename_fn = m.mk_func_decl(fid, OP_RA_RENAME, params.size(), params.c_ptr(), 1, &relation_sort); } - virtual relation_base * operator()(const relation_base & r) { + relation_base * operator()(const relation_base & r) override { expr* rel = get(r).get_relation(); expr_ref res(m_plugin.get_ast_manager()); m_args[0] = rel; @@ -277,7 +277,7 @@ namespace datalog { relation_transformer_fn * external_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if(!check_kind(r)) { - return 0; + return nullptr; } return alloc(rename_fn, *this, get(r).get_sort(), r.get_signature(), cycle_len, permutation_cycle); } @@ -295,10 +295,10 @@ namespace datalog { m_union_fn(p.get_ast_manager()) { ast_manager& m = p.get_ast_manager(); sort* domain[2] = { relation_sort, relation_sort }; - m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, 0, 2, domain); + m_union_fn = m.mk_func_decl(p.get_family_id(), k, 0, nullptr, 2, domain); } - virtual void operator()(relation_base & r, const relation_base & src, relation_base * delta) { + void operator()(relation_base & r, const relation_base & src, relation_base * delta) override { ast_manager& m = m_plugin.get_ast_manager(); expr_ref_vector res(m); m_args[0] = get(r).get_relation(); @@ -316,7 +316,7 @@ namespace datalog { relation_union_fn * external_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; + return nullptr; } return alloc(union_fn, *this, OP_RA_UNION, get(src).get_sort()); } @@ -324,7 +324,7 @@ namespace datalog { relation_union_fn * external_relation_plugin::mk_widen_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; + return nullptr; } return alloc(union_fn, *this, OP_RA_WIDEN, get(src).get_sort()); } @@ -342,7 +342,7 @@ namespace datalog { SASSERT(p.get_ast_manager().is_bool(condition)); } - virtual void operator()(relation_base & r) { + void operator()(relation_base & r) override { SASSERT(m_plugin.check_kind(r)); expr* arg = get(r).get_relation(); m_plugin.reduce_assign(m_filter_fn, 1, &arg, 1, &arg); @@ -351,7 +351,7 @@ namespace datalog { relation_mutator_fn * external_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * condition) { if(!check_kind(r)) { - return 0; + return nullptr; } return alloc(filter_interpreted_fn, *this, get(r).get_sort(), condition); } @@ -359,7 +359,7 @@ namespace datalog { relation_mutator_fn * external_relation_plugin::mk_filter_equal_fn(const relation_base & r, const relation_element & value, unsigned col) { if(!check_kind(r)) { - return 0; + return nullptr; } ast_manager& m = get_ast_manager(); app_ref condition(m); @@ -396,7 +396,7 @@ namespace datalog { } } - virtual void operator()(relation_base & r) { + void operator()(relation_base & r) override { expr* r0 = get(r).get_relation(); for (unsigned i = 0; i < m_filter_fn.size(); ++i) { m_plugin.reduce_assign(m_filter_fn[i].get(), 1, &r0, 1, &r0); @@ -407,7 +407,7 @@ namespace datalog { relation_mutator_fn * external_relation_plugin::mk_filter_identical_fn(const relation_base & r, unsigned col_cnt, const unsigned * identical_cols) { if (!check_kind(r)) { - return 0; + return nullptr; } return alloc(filter_identical_fn, *this, get(r).get_sort(), col_cnt, identical_cols); } @@ -436,7 +436,7 @@ namespace datalog { m_negated_filter_fn = m.mk_func_decl(fid, OP_RA_NEGATION_FILTER, params.size(), params.c_ptr(), 2, domain); } - void operator()(relation_base & t, const relation_base & negated_obj) { + void operator()(relation_base & t, const relation_base & negated_obj) override { m_args[0] = get(t).get_relation(); m_args[1] = get(negated_obj).get_relation(); m_plugin.reduce_assign(m_negated_filter_fn.get(), 2, m_args, 1, m_args); @@ -447,7 +447,7 @@ namespace datalog { const relation_base & negated_obj, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (!check_kind(t) || !check_kind(negated_obj)) { - return 0; + return nullptr; } return alloc(negation_filter_fn, *this, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } diff --git a/src/muz/rel/dl_external_relation.h b/src/muz/rel/dl_external_relation.h index 0dfb4a7f2..485e03618 100644 --- a/src/muz/rel/dl_external_relation.h +++ b/src/muz/rel/dl_external_relation.h @@ -54,30 +54,30 @@ namespace datalog { public: external_relation_plugin(external_relation_context& ctx, relation_manager & m); - virtual bool can_handle_signature(const relation_signature & s) { return true; } + bool can_handle_signature(const relation_signature & s) override { return true; } static symbol get_name() { return symbol("external_relation"); } - virtual relation_base * mk_empty(const relation_signature & s); + relation_base * mk_empty(const relation_signature & s) override; - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; + relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) override; private: @@ -123,28 +123,28 @@ namespace datalog { void mk_accessor(decl_kind k, func_decl_ref& fn, const relation_fact& f, bool destructive, expr_ref& res) const; external_relation(external_relation_plugin & p, const relation_signature & s, expr* r); - virtual ~external_relation(); + ~external_relation() override; public: external_relation_plugin & get_plugin() const; - virtual bool empty() const; + bool empty() const override; - virtual void add_fact(const relation_fact & f); + void add_fact(const relation_fact & f) override; - virtual bool contains_fact(const relation_fact & f) const; + bool contains_fact(const relation_fact & f) const override; - virtual external_relation * clone() const; + external_relation * clone() const override; - virtual external_relation * complement(func_decl*) const; + external_relation * complement(func_decl*) const override; - virtual void display(std::ostream & out) const; + void display(std::ostream & out) const override; - virtual void display_tuples(func_decl & pred, std::ostream & out) const; + void display_tuples(func_decl & pred, std::ostream & out) const override; expr* get_relation() const { return m_rel.get(); } - virtual void to_formula(expr_ref& fml) const { fml = get_relation(); } + void to_formula(expr_ref& fml) const override { fml = get_relation(); } }; diff --git a/src/muz/rel/dl_finite_product_relation.cpp b/src/muz/rel/dl_finite_product_relation.cpp index c036d3cfd..030fc1327 100644 --- a/src/muz/rel/dl_finite_product_relation.cpp +++ b/src/muz/rel/dl_finite_product_relation.cpp @@ -245,14 +245,14 @@ namespace datalog { } finite_product_relation * finite_product_relation_plugin::mk_from_table_relation(const table_relation & r) { - func_decl* pred = 0; + func_decl* pred = nullptr; const relation_signature & sig = r.get_signature(); const table_base & t = r.get_table(); table_plugin & tplugin = r.get_table().get_plugin(); relation_signature inner_sig; //empty signature for the inner relation if(!get_inner_plugin().can_handle_signature(inner_sig)) { - return 0; + return nullptr; } table_signature idx_singleton_sig; @@ -270,7 +270,7 @@ namespace datalog { idx_singleton_fact.push_back(0); idx_singleton->add_fact(idx_singleton_fact); - scoped_ptr join_fun = get_manager().mk_join_fn(t, *idx_singleton, 0, 0, 0); + scoped_ptr join_fun = get_manager().mk_join_fn(t, *idx_singleton, 0, nullptr, nullptr); SASSERT(join_fun); scoped_rel res_table = (*join_fun)(t, *idx_singleton); @@ -333,7 +333,7 @@ namespace datalog { : convenient_relation_join_fn(sig1, sig2, col_cnt, cols1, cols2), m_plugin(plugin) {} - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + relation_base * operator()(const relation_base & r1, const relation_base & r2) override { scoped_rel r1_conv; if(&r1.get_plugin()!=&m_plugin) { r1_conv = convert(r1); @@ -390,7 +390,7 @@ namespace datalog { relation_vector & rjoins) : m_parent(parent), m_r1(r1), m_r2(r2), m_rjoins(rjoins) {} - virtual bool operator()(table_element * func_columns) { + bool operator()(table_element * func_columns) override { const relation_base & or1 = m_r1.get_inner_rel(func_columns[0]); const relation_base & or2 = m_r2.get_inner_rel(func_columns[1]); SASSERT(&or1); @@ -450,7 +450,7 @@ namespace datalog { return (*m_rjoin_fn)(r1, r2); } - virtual relation_base * operator()(const relation_base & rb1, const relation_base & rb2) { + relation_base * operator()(const relation_base & rb1, const relation_base & rb2) override { finite_product_relation_plugin & plugin = get(rb1).get_plugin(); relation_manager & rmgr = plugin.get_manager(); @@ -513,7 +513,7 @@ namespace datalog { return alloc(converting_join_fn, *this, rb1.get_signature(), rb2.get_signature(), col_cnt, cols1, cols2); } - return 0; + return nullptr; } const finite_product_relation & r1 = get(rb1); const finite_product_relation & r2 = get(rb2); @@ -565,7 +565,7 @@ namespace datalog { project_reducer(project_fn & parent, relation_vector & relations) : m_parent(parent), m_relations(relations) {} - virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { + void operator()(table_element * func_columns, const table_element * merged_func_columns) override { relation_base * tgt = m_relations[static_cast(func_columns[0])]->clone(); relation_base & src = *m_relations[static_cast(merged_func_columns[0])]; if(!m_parent.m_inner_rel_union) { @@ -579,7 +579,7 @@ namespace datalog { } }; - virtual relation_base * operator()(const relation_base & rb) { + relation_base * operator()(const relation_base & rb) override { const finite_product_relation & r = get(rb); finite_product_relation_plugin & plugin = r.get_plugin(); const table_base & rtable = r.get_table(); @@ -590,7 +590,7 @@ namespace datalog { unsigned orig_rel_cnt = r.m_others.size(); for(unsigned i=0; iclone() : 0); + res_relations.push_back(orig_rel ? orig_rel->clone() : nullptr); } SASSERT(res_relations.size()==orig_rel_cnt); @@ -608,7 +608,7 @@ namespace datalog { res_table = (*tproject)(rtable); } - relation_plugin * res_oplugin = 0; + relation_plugin * res_oplugin = nullptr; if(!m_removed_rel_cols.empty()) { unsigned res_rel_cnt = res_relations.size(); @@ -651,7 +651,7 @@ namespace datalog { relation_transformer_fn * finite_product_relation_plugin::mk_project_fn(const relation_base & rb, unsigned col_cnt, const unsigned * removed_cols) { if(&rb.get_plugin()!=this) { - return 0; + return nullptr; } return alloc(project_fn, get(rb), col_cnt, removed_cols); } @@ -696,7 +696,7 @@ namespace datalog { } - virtual relation_base * operator()(const relation_base & rb) { + relation_base * operator()(const relation_base & rb) override { const finite_product_relation & r = get(rb); const table_base & rtable = r.get_table(); @@ -705,7 +705,7 @@ namespace datalog { unsigned orig_rel_cnt = r.m_others.size(); for(unsigned i=0; iclone() : 0); + res_relations.push_back(orig_rel ? orig_rel->clone() : nullptr); } if(!m_rel_identity) { @@ -746,7 +746,7 @@ namespace datalog { unsigned permutation_cycle_len, const unsigned * permutation_cycle) { if(&rb.get_plugin()!=this) { - return 0; + return nullptr; } const finite_product_relation & r = get(rb); return alloc(rename_fn, r, permutation_cycle_len, permutation_cycle); @@ -771,7 +771,7 @@ namespace datalog { relation_union_fn & get_inner_rel_union_op(relation_base & r) { if(!m_rel_union) { - m_rel_union = r.get_manager().mk_union_fn(r, r, m_use_delta ? &r : 0); + m_rel_union = r.get_manager().mk_union_fn(r, r, m_use_delta ? &r : nullptr); } return *m_rel_union; } @@ -795,9 +795,9 @@ namespace datalog { m_delta_indexes(delta_indexes), m_delta_rels(delta_rels) {} - virtual ~union_mapper() {} + ~union_mapper() override {} - virtual bool operator()(table_element * func_columns) { + bool operator()(table_element * func_columns) override { relation_base & otgt_orig = m_tgt.get_inner_rel(func_columns[0]); const relation_base & osrc = m_src.get_inner_rel(func_columns[1]); @@ -838,7 +838,7 @@ namespace datalog { src_copying_mapper(finite_product_relation & tgt, const finite_product_relation & src) : m_tgt(tgt), m_src(src) {} - virtual bool operator()(table_element * func_columns) { + bool operator()(table_element * func_columns) override { const relation_base & osrc = m_src.get_inner_rel(func_columns[0]); unsigned new_tgt_idx = m_tgt.get_next_rel_idx(); m_tgt.set_inner_rel(new_tgt_idx, osrc.clone()); @@ -847,7 +847,7 @@ namespace datalog { } }; - virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { + void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) override { finite_product_relation & tgt = get(tgtb); const finite_product_relation & src0 = get(srcb); finite_product_relation * delta = get(deltab); @@ -1088,7 +1088,7 @@ namespace datalog { class finite_product_relation_plugin::converting_union_fn : public relation_union_fn { scoped_ptr m_tr_union_fun; public: - virtual void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) { + void operator()(relation_base & tgtb, const relation_base & srcb, relation_base * deltab) override { SASSERT(srcb.get_plugin().is_finite_product_relation()); const finite_product_relation & src = get(srcb); finite_product_relation_plugin & plugin = src.get_plugin(); @@ -1104,19 +1104,19 @@ namespace datalog { relation_union_fn * finite_product_relation_plugin::mk_union_fn(const relation_base & tgtb, const relation_base & srcb, const relation_base * deltab) { if(&srcb.get_plugin()!=this) { - return 0; + return nullptr; } const finite_product_relation & src = get(srcb); if(&tgtb.get_plugin()!=this || (deltab && &deltab->get_plugin()!=this) ) { if(can_convert_to_table_relation(src)) { return alloc(converting_union_fn); } - return 0; + return nullptr; } const finite_product_relation * delta = get(deltab); - return alloc(union_fn, get(tgtb), delta!=0); + return alloc(union_fn, get(tgtb), delta!=nullptr); } @@ -1131,7 +1131,7 @@ namespace datalog { scoped_ptr m_tr_filter; public: filter_identical_fn(const finite_product_relation & r, unsigned col_cnt, const unsigned * identical_cols) - : m_table_filter(0), m_rel_filter(0), m_tr_filter(0) { + : m_table_filter(nullptr), m_rel_filter(nullptr), m_tr_filter(nullptr) { finite_product_relation_plugin & plugin = r.get_plugin(); for(unsigned i=0; i1) { @@ -1192,7 +1192,7 @@ namespace datalog { relation_mutator_fn * finite_product_relation_plugin::mk_filter_identical_fn(const relation_base & rb, unsigned col_cnt, const unsigned * identical_cols) { if(&rb.get_plugin()!=this) { - return 0; + return nullptr; } return alloc(filter_identical_fn, get(rb), col_cnt, identical_cols); } @@ -1212,7 +1212,7 @@ namespace datalog { } } - virtual void operator()(relation_base & rb) { + void operator()(relation_base & rb) override { finite_product_relation & r = get(rb); if(m_table_filter) { @@ -1238,7 +1238,7 @@ namespace datalog { relation_mutator_fn * finite_product_relation_plugin::mk_filter_equal_fn(const relation_base & rb, const relation_element & value, unsigned col) { if(&rb.get_plugin()!=this) { - return 0; + return nullptr; } return alloc(filter_equal_fn, get(rb), value, col); } @@ -1339,7 +1339,7 @@ namespace datalog { } } - virtual void operator()(relation_base & rb) { + void operator()(relation_base & rb) override { finite_product_relation & r = get(rb); table_base & rtable = r.get_table(); table_plugin & tplugin = r.get_table_plugin(); @@ -1357,7 +1357,7 @@ namespace datalog { unsigned rel_cnt = r.m_others.size(); for(unsigned i=0; i complement_table = m_table->complement(p, &full_rel_idx); - scoped_ptr u_fn = get_manager().mk_union_fn(*m_table, *complement_table, 0); + scoped_ptr u_fn = get_manager().mk_union_fn(*m_table, *complement_table, nullptr); SASSERT(u_fn); - (*u_fn)(*m_table, *complement_table, 0); + (*u_fn)(*m_table, *complement_table, nullptr); } finite_product_relation * finite_product_relation::complement(func_decl* p) const { @@ -2043,7 +2043,7 @@ namespace datalog { public: live_rel_collection_reducer(idx_set & accumulator) : m_accumulator(accumulator) {} - virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { + void operator()(table_element * func_columns, const table_element * merged_func_columns) override { m_accumulator.insert(static_cast(merged_func_columns[0])); } }; diff --git a/src/muz/rel/dl_finite_product_relation.h b/src/muz/rel/dl_finite_product_relation.h index 946296df1..750ac325f 100644 --- a/src/muz/rel/dl_finite_product_relation.h +++ b/src/muz/rel/dl_finite_product_relation.h @@ -93,23 +93,23 @@ namespace datalog { finite_product_relation_plugin(relation_plugin & inner_plugin, relation_manager & manager); - virtual void initialize(family_id fid); + void initialize(family_id fid) override; relation_plugin & get_inner_plugin() const { return m_inner_plugin; } - virtual bool can_handle_signature(const relation_signature & s); + bool can_handle_signature(const relation_signature & s) override; - virtual relation_base * mk_empty(const relation_signature & s); + relation_base * mk_empty(const relation_signature & s) override; /** \c inner_kind==null_family_id means we don't care about the kind of the inner relation */ finite_product_relation * mk_empty(const relation_signature & s, const bool * table_columns, family_id inner_kind=null_family_id); finite_product_relation * mk_empty(const finite_product_relation & original); - virtual relation_base * mk_empty(const relation_base & original); - virtual relation_base * mk_empty(const relation_signature & s, family_id kind); + relation_base * mk_empty(const relation_base & original) override; + relation_base * mk_empty(const relation_signature & s, family_id kind) override; - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + relation_base * mk_full(func_decl* p, const relation_signature & s) override; /** \brief Return true if \c r can be converted to \c finite_product_relation_plugin either @@ -127,22 +127,22 @@ namespace datalog { table_relation * to_table_relation(const finite_product_relation & r); protected: - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; + relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) override; private: /** @@ -281,11 +281,11 @@ namespace datalog { \brief Extract the values of table non-functional columns from the relation fact. The value of the functional column which determines index of the inner relation is undefined. */ - void extract_table_fact(const relation_fact rf, table_fact & tf) const; + void extract_table_fact(const relation_fact & rf, table_fact & tf) const; /** \brief Extract the values of the inner relation columns from the relation fact. */ - void extract_other_fact(const relation_fact rf, relation_fact & of) const; + void extract_other_fact(const relation_fact & rf, relation_fact & of) const; relation_base * mk_empty_inner(); relation_base * mk_full_inner(func_decl* pred); @@ -309,7 +309,7 @@ namespace datalog { bool try_modify_specification(const bool * table_cols); - virtual bool can_swap(const relation_base & r) const + bool can_swap(const relation_base & r) const override { return &get_plugin()==&r.get_plugin(); } /** @@ -317,7 +317,7 @@ namespace datalog { Both relations must come from the same plugin and be of the same signature. */ - virtual void swap(relation_base & r); + void swap(relation_base & r) override; /** \brief Create a \c finite_product_relation object. @@ -325,7 +325,7 @@ namespace datalog { finite_product_relation(finite_product_relation_plugin & p, const relation_signature & s, const bool * table_columns, table_plugin & tplugin, relation_plugin & oplugin, family_id other_kind); finite_product_relation(const finite_product_relation & r); - virtual ~finite_product_relation(); + ~finite_product_relation() override; public: context & get_context() const; finite_product_relation_plugin & get_plugin() const { @@ -342,22 +342,22 @@ namespace datalog { /** The function calls garbage_collect, so the internal state may change when it is called. */ - virtual bool empty() const; - void reset() { m_table->reset(); garbage_collect(false); } + bool empty() const override; + void reset() override { m_table->reset(); garbage_collect(false); } - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; + void add_fact(const relation_fact & f) override; + bool contains_fact(const relation_fact & f) const override; - virtual finite_product_relation * clone() const; - virtual finite_product_relation * complement(func_decl* p) const; + finite_product_relation * clone() const override; + finite_product_relation * complement(func_decl* p) const override; - virtual void display(std::ostream & out) const; - virtual void display_tuples(func_decl & pred, std::ostream & out) const; + void display(std::ostream & out) const override; + void display_tuples(func_decl & pred, std::ostream & out) const override; - virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } - virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } + unsigned get_size_estimate_rows() const override { return m_table->get_size_estimate_rows(); } + unsigned get_size_estimate_bytes() const override { return m_table->get_size_estimate_bytes(); } - virtual void to_formula(expr_ref& fml) const; + void to_formula(expr_ref& fml) const override; }; }; diff --git a/src/muz/rel/dl_instruction.cpp b/src/muz/rel/dl_instruction.cpp index fb718d8b8..f7d1665d2 100644 --- a/src/muz/rel/dl_instruction.cpp +++ b/src/muz/rel/dl_instruction.cpp @@ -36,7 +36,7 @@ namespace datalog { execution_context::execution_context(context & context) : m_context(context), - m_stopwatch(0), + m_stopwatch(nullptr), m_timelimit_ms(0) {} execution_context::~execution_context() { @@ -168,7 +168,7 @@ namespace datalog { } - void instruction::display_indented(execution_context const & _ctx, std::ostream & out, std::string indentation) const { + void instruction::display_indented(execution_context const & _ctx, std::ostream & out, const std::string & indentation) const { out << indentation; rel_context const& ctx = _ctx.get_rel_context(); display_head_impl(_ctx, out); @@ -190,9 +190,9 @@ namespace datalog { func_decl_ref m_pred; reg_idx m_reg; public: - instr_io(bool store, func_decl_ref pred, reg_idx reg) + instr_io(bool store, func_decl_ref const& pred, reg_idx reg) : m_store(store), m_pred(pred), m_reg(reg) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); if (m_store) { if (ctx.reg(m_reg)) { @@ -218,10 +218,10 @@ namespace datalog { } return true; } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { ctx.set_register_annotation(m_reg, m_pred->get_name().bare_str()); } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { const char * rel_name = m_pred->get_name().bare_str(); if (m_store) { out << "store " << m_reg << " into " << rel_name; @@ -245,14 +245,14 @@ namespace datalog { reg_idx m_reg; public: instr_dealloc(reg_idx reg) : m_reg(reg) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { ctx.make_empty(m_reg); return true; } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { ctx.set_register_annotation(m_reg, "alloc"); } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "dealloc " << m_reg; } }; @@ -268,17 +268,17 @@ namespace datalog { public: instr_clone_move(bool clone, reg_idx src, reg_idx tgt) : m_clone(clone), m_src(src), m_tgt(tgt) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { if (ctx.reg(m_src)) log_verbose(ctx); if (m_clone) { - ctx.set_reg(m_tgt, ctx.reg(m_src) ? ctx.reg(m_src)->clone() : 0); + ctx.set_reg(m_tgt, ctx.reg(m_src) ? ctx.reg(m_src)->clone() : nullptr); } else { - ctx.set_reg(m_tgt, ctx.reg(m_src) ? ctx.release_reg(m_src) : 0); + ctx.set_reg(m_tgt, ctx.reg(m_src) ? ctx.release_reg(m_src) : nullptr); } return true; } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::string str; if (ctx.get_register_annotation(m_src, str)) { ctx.set_register_annotation(m_tgt, str); @@ -287,7 +287,7 @@ namespace datalog { ctx.set_register_annotation(m_src, str); } } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << (m_clone ? "clone " : "move ") << m_src << " into " << m_tgt; } }; @@ -317,17 +317,17 @@ namespace datalog { return true; } protected: - virtual void process_all_costs() { + void process_all_costs() override { instruction::process_all_costs(); m_body->process_all_costs(); } public: instr_while_loop(unsigned control_reg_cnt, const reg_idx * control_regs, instruction_block * body) : m_controls(control_reg_cnt, control_regs), m_body(body) {} - virtual ~instr_while_loop() { + ~instr_while_loop() override { dealloc(m_body); } - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); TRACE("dl", tout << "loop entered\n";); unsigned count = 0; @@ -341,14 +341,14 @@ namespace datalog { TRACE("dl", tout << "while loop exited\n";); return true; } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { m_body->make_annotations(ctx); } - virtual void display_head_impl(execution_context const & ctx, std::ostream & out) const { + void display_head_impl(execution_context const & ctx, std::ostream & out) const override { out << "while"; print_container(m_controls, out); } - virtual void display_body_impl(execution_context const & ctx, std::ostream & out, std::string indentation) const { + void display_body_impl(execution_context const & ctx, std::ostream & out, const std::string & indentation) const override { m_body->display_indented(ctx, out, indentation+" "); } }; @@ -371,7 +371,7 @@ namespace datalog { const unsigned * cols2, reg_idx result) : m_rel1(rel1), m_rel2(rel2), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2), m_res(result) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_join; if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { @@ -408,13 +408,13 @@ namespace datalog { } return true; } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::string a1 = "rel1", a2 = "rel2"; ctx.get_register_annotation(m_rel1, a1); ctx.get_register_annotation(m_rel1, a1); ctx.set_register_annotation(m_res, "join " + a1 + " " + a2); } - virtual void display_head_impl(execution_context const & ctx, std::ostream & out) const { + void display_head_impl(execution_context const & ctx, std::ostream & out) const override { out << "join " << m_rel1; print_container(m_cols1, out); out << " and " << m_rel2; @@ -435,7 +435,7 @@ namespace datalog { public: instr_filter_equal(ast_manager & m, reg_idx reg, const relation_element & value, unsigned col) : m_reg(reg), m_value(value, m), m_col(col) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_filter_eq; if (!ctx.reg(m_reg)) { @@ -460,12 +460,12 @@ namespace datalog { } return true; } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::stringstream a; a << "filter_equal " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); ctx.set_register_annotation(m_reg, a.str()); } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "filter_equal " << m_reg << " col: " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); } @@ -484,7 +484,7 @@ namespace datalog { public: instr_filter_identical(reg_idx reg, unsigned col_cnt, const unsigned * identical_cols) : m_reg(reg), m_cols(col_cnt, identical_cols) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_filter_id; if (!ctx.reg(m_reg)) { @@ -509,11 +509,11 @@ namespace datalog { } return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "filter_identical " << m_reg << " "; print_container(m_cols, out); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { ctx.set_register_annotation(m_reg, "filter_identical"); } }; @@ -529,7 +529,7 @@ namespace datalog { public: instr_filter_interpreted(reg_idx reg, app_ref & condition) : m_reg(reg), m_cond(condition) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { if (!ctx.reg(m_reg)) { return true; } @@ -557,11 +557,11 @@ namespace datalog { return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "filter_interpreted " << m_reg << " using " << mk_pp(m_cond, m_cond.get_manager()); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::stringstream a; a << "filter_interpreted " << mk_pp(m_cond, m_cond.get_manager()); ctx.set_register_annotation(m_reg, a.str()); @@ -584,7 +584,7 @@ namespace datalog { : m_src(src), m_cond(condition), m_cols(col_cnt, removed_cols), m_res(result) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); if (!ctx.reg(m_src)) { ctx.make_empty(m_res); @@ -614,14 +614,14 @@ namespace datalog { return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "filter_interpreted_and_project " << m_src << " into " << m_res; out << " using " << mk_pp(m_cond, m_cond.get_manager()); out << " deleting columns "; print_container(m_cols, out); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::stringstream s; std::string a = "rel_src"; ctx.get_register_annotation(m_src, a); @@ -644,7 +644,7 @@ namespace datalog { public: instr_union(reg_idx src, reg_idx tgt, reg_idx delta, bool widen) : m_src(src), m_tgt(tgt), m_delta(delta), m_widen(widen) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { TRACE("dl", tout << "union " << m_src << " into " << m_tgt << " " << ctx.reg(m_src) << " " << ctx.reg(m_tgt) << "\n";); if (!ctx.reg(m_src)) { @@ -662,7 +662,7 @@ namespace datalog { relation_base * new_delta = r_tgt.get_plugin().mk_empty(r_tgt); ctx.set_reg(m_delta, new_delta); } - relation_base * r_delta = (m_delta!=execution_context::void_register) ? ctx.reg(m_delta) : 0; + relation_base * r_delta = (m_delta!=execution_context::void_register) ? ctx.reg(m_delta) : nullptr; relation_union_fn * fn; @@ -687,10 +687,10 @@ namespace datalog { else { if (!find_fn(r_tgt, r_src, fn)) { if (m_widen) { - fn = r_src.get_manager().mk_widen_fn(r_tgt, r_src, 0); + fn = r_src.get_manager().mk_widen_fn(r_tgt, r_src, nullptr); } else { - fn = r_src.get_manager().mk_union_fn(r_tgt, r_src, 0); + fn = r_src.get_manager().mk_union_fn(r_tgt, r_src, nullptr); } if (!fn) { std::stringstream sstm; @@ -721,7 +721,7 @@ namespace datalog { return true; } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::string str = "union"; if (!ctx.get_register_annotation(m_tgt, str)) { ctx.set_register_annotation(m_tgt, "union"); @@ -731,7 +731,7 @@ namespace datalog { } ctx.set_register_annotation(m_delta, str); } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << (m_widen ? "widen " : "union ") << m_src << " into " << m_tgt; if (m_delta!=execution_context::void_register) { out << " with delta " << m_delta; @@ -758,7 +758,7 @@ namespace datalog { instr_project_rename(bool projection, reg_idx src, unsigned col_cnt, const unsigned * cols, reg_idx tgt) : m_projection(projection), m_src(src), m_cols(col_cnt, cols), m_tgt(tgt) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { if (!ctx.reg(m_src)) { ctx.make_empty(m_tgt); return true; @@ -787,12 +787,12 @@ namespace datalog { return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << (m_projection ? "project " : "rename ") << m_src << " into " << m_tgt; out << (m_projection ? " deleting columns " : " with cycle "); print_container(m_cols, out); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::stringstream s; std::string a = "rel_src"; ctx.get_register_annotation(m_src, a); @@ -825,7 +825,7 @@ namespace datalog { : m_rel1(rel1), m_rel2(rel2), m_cols1(joined_col_cnt, cols1), m_cols2(joined_col_cnt, cols2), m_removed_cols(removed_col_cnt, removed_cols), m_res(result) { } - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); if (!ctx.reg(m_rel1) || !ctx.reg(m_rel2)) { ctx.make_empty(m_res); @@ -852,7 +852,7 @@ namespace datalog { } return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { relation_base const* r1 = ctx.reg(m_rel1); relation_base const* r2 = ctx.reg(m_rel2); out << "join_project " << m_rel1; @@ -870,7 +870,7 @@ namespace datalog { out << " into " << m_res << " removing columns "; print_container(m_removed_cols, out); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::string s1 = "rel1", s2 = "rel2"; ctx.get_register_annotation(m_rel1, s1); ctx.get_register_annotation(m_rel2, s2); @@ -898,7 +898,7 @@ namespace datalog { // TRACE("dl", tout << "src:" << m_src << " result: " << m_result << " value:" << m_value << " column:" << m_col << "\n";); } - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { if (!ctx.reg(m_src)) { ctx.make_empty(m_result); return true; @@ -923,11 +923,11 @@ namespace datalog { } return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "select_equal_and_project " << m_src <<" into " << m_result << " col: " << m_col << " val: " << ctx.get_rel_context().get_rmanager().to_nice_string(m_value); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::stringstream s; std::string s1 = "src"; ctx.get_register_annotation(m_src, s1); @@ -953,7 +953,7 @@ namespace datalog { instr_filter_by_negation(reg_idx tgt, reg_idx neg_rel, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) : m_tgt(tgt), m_neg_rel(neg_rel), m_cols1(col_cnt, cols1), m_cols2(col_cnt, cols2) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); if (!ctx.reg(m_tgt) || !ctx.reg(m_neg_rel)) { return true; @@ -980,14 +980,14 @@ namespace datalog { } return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "filter_by_negation on " << m_tgt; print_container(m_cols1, out); out << " with " << m_neg_rel; print_container(m_cols2, out); out << " as the negated table"; } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::string s = "negated relation"; ctx.get_register_annotation(m_neg_rel, s); ctx.set_register_annotation(m_tgt, "filter by negation " + s); @@ -1011,7 +1011,7 @@ namespace datalog { m_sig.push_back(s); m_fact.push_back(val); } - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_unary_singleton; relation_base * rel = ctx.get_rel_context().get_rmanager().mk_empty_relation(m_sig, m_pred); @@ -1019,12 +1019,12 @@ namespace datalog { ctx.set_reg(m_tgt, rel); return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "mk_unary_singleton into " << m_tgt << " sort:" << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig[0]) << " val:" << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig[0], m_fact[0]); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::string s; if (!ctx.get_register_annotation(m_tgt, s)) { ctx.set_register_annotation(m_tgt, "mk unary singleton"); @@ -1044,18 +1044,18 @@ namespace datalog { reg_idx m_tgt; public: instr_mk_total(const relation_signature & sig, func_decl* p, reg_idx tgt) : m_sig(sig), m_pred(p), m_tgt(tgt) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); ++ctx.m_stats.m_total; ctx.set_reg(m_tgt, ctx.get_rel_context().get_rmanager().mk_full_relation(m_sig, m_pred)); return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "mk_total into " << m_tgt << " sort:" << ctx.get_rel_context().get_rmanager().to_nice_string(m_sig) << " " << m_pred->get_name(); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::string s; if (!ctx.get_register_annotation(m_tgt, s)) { ctx.set_register_annotation(m_tgt, "mk_total"); @@ -1072,15 +1072,15 @@ namespace datalog { public: instr_mark_saturated(ast_manager & m, func_decl * pred) : m_pred(pred, m) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); ctx.get_rel_context().get_rmanager().mark_saturated(m_pred); return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "mark_saturated " << m_pred->get_name().bare_str(); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { } }; @@ -1094,18 +1094,18 @@ namespace datalog { public: instr_assert_signature(const relation_signature & s, reg_idx tgt) : m_sig(s), m_tgt(tgt) {} - virtual bool perform(execution_context & ctx) { + bool perform(execution_context & ctx) override { log_verbose(ctx); if (ctx.reg(m_tgt)) { SASSERT(ctx.reg(m_tgt)->get_signature()==m_sig); } return true; } - virtual void display_head_impl(execution_context const& ctx, std::ostream & out) const { + void display_head_impl(execution_context const& ctx, std::ostream & out) const override { out << "instr_assert_signature of " << m_tgt << " signature:"; print_container(m_sig, out); } - virtual void make_annotations(execution_context & ctx) { + void make_annotations(execution_context & ctx) override { std::string s; if (!ctx.get_register_annotation(m_tgt, s)) { ctx.set_register_annotation(m_tgt, "assert signature"); @@ -1135,7 +1135,7 @@ namespace datalog { dealloc(*it); } m_data.reset(); - m_observer = 0; + m_observer = nullptr; } bool instruction_block::perform(execution_context & ctx) const { @@ -1182,7 +1182,7 @@ namespace datalog { } } - void instruction_block::display_indented(execution_context const& _ctx, std::ostream & out, std::string indentation) const { + void instruction_block::display_indented(execution_context const& _ctx, std::ostream & out, const std::string & indentation) const { rel_context const& ctx = _ctx.get_rel_context(); instr_seq_type::const_iterator it = m_data.begin(); instr_seq_type::const_iterator end = m_data.end(); diff --git a/src/muz/rel/dl_instruction.h b/src/muz/rel/dl_instruction.h index 56dd249a5..6e0b7bfe5 100644 --- a/src/muz/rel/dl_instruction.h +++ b/src/muz/rel/dl_instruction.h @@ -107,7 +107,7 @@ namespace datalog { */ reg_type reg(reg_idx i) const { if (i >= m_registers.size()) { - return 0; + return nullptr; } return m_registers[i]; } @@ -138,7 +138,7 @@ namespace datalog { void make_empty(reg_idx i) { if (reg(i)) { - set_reg(i, 0); + set_reg(i, nullptr); } } @@ -150,7 +150,7 @@ namespace datalog { return m_reg_annotation.find(reg, res); } - void set_register_annotation(reg_idx reg, std::string str) { + void set_register_annotation(reg_idx reg, const std::string & str) { m_reg_annotation.insert(reg, str); } @@ -233,7 +233,7 @@ namespace datalog { Each line must be prepended by \c indentation and ended by a newline character. */ - virtual void display_body_impl(execution_context const & ctx, std::ostream & out, std::string indentation) const {} + virtual void display_body_impl(execution_context const & ctx, std::ostream & out, const std::string & indentation) const {} void log_verbose(execution_context& ctx); public: @@ -249,7 +249,7 @@ namespace datalog { void display(execution_context const& ctx, std::ostream & out) const { display_indented(ctx, out, ""); } - void display_indented(execution_context const & ctx, std::ostream & out, std::string indentation) const; + void display_indented(execution_context const & ctx, std::ostream & out, const std::string & indentation) const; static instruction * mk_load(ast_manager & m, func_decl * pred, reg_idx tgt); /** @@ -326,7 +326,7 @@ namespace datalog { instr_seq_type m_data; instruction_observer* m_observer; public: - instruction_block() : m_observer(0) {} + instruction_block() : m_observer(nullptr) {} ~instruction_block(); void reset(); @@ -359,7 +359,7 @@ namespace datalog { void display(execution_context const & ctx, std::ostream & out) const { display_indented(ctx, out, ""); } - void display_indented(execution_context const & ctx, std::ostream & out, std::string indentation) const; + void display_indented(execution_context const & ctx, std::ostream & out, const std::string & indentation) const; unsigned num_instructions() const { return m_data.size(); } }; diff --git a/src/muz/rel/dl_interval_relation.cpp b/src/muz/rel/dl_interval_relation.cpp index e14d242bb..75f9e7e42 100644 --- a/src/muz/rel/dl_interval_relation.cpp +++ b/src/muz/rel/dl_interval_relation.cpp @@ -59,11 +59,11 @@ namespace datalog { : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ } - virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + relation_base * operator()(const relation_base & _r1, const relation_base & _r2) override { interval_relation const& r1 = get(_r1); interval_relation const& r2 = get(_r2); interval_relation_plugin& p = r1.get_plugin(); - interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + interval_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); return result; } @@ -72,7 +72,7 @@ namespace datalog { relation_join_fn * interval_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(r1) || !check_kind(r2)) { - return 0; + return nullptr; } return alloc(join_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, cols2); } @@ -84,10 +84,10 @@ namespace datalog { : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { } - virtual relation_base * operator()(const relation_base & _r) { + relation_base * operator()(const relation_base & _r) override { interval_relation const& r = get(_r); interval_relation_plugin& p = r.get_plugin(); - interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + interval_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); return result; } @@ -104,10 +104,10 @@ namespace datalog { : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) { } - virtual relation_base * operator()(const relation_base & _r) { + relation_base * operator()(const relation_base & _r) override { interval_relation const& r = get(_r); interval_relation_plugin& p = r.get_plugin(); - interval_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + interval_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); return result; } @@ -116,7 +116,7 @@ namespace datalog { relation_transformer_fn * interval_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if(!check_kind(r)) { - return 0; + return nullptr; } return alloc(rename_fn, r.get_signature(), cycle_len, permutation_cycle); } @@ -134,7 +134,7 @@ namespace datalog { high = src2.sup(); r_open = src2.is_upper_open(); } - return interval(dep(), low, l_open, 0, high, r_open, 0); + return interval(dep(), low, l_open, nullptr, high, r_open, nullptr); } interval interval_relation_plugin::widen(interval const& src1, interval const& src2) { @@ -151,7 +151,7 @@ namespace datalog { high = ext_numeral(true); r_open = true; } - return interval(dep(), low, l_open, 0, high, r_open, 0); + return interval(dep(), low, l_open, nullptr, high, r_open, nullptr); } interval interval_relation_plugin::meet(interval const& src1, interval const& src2, bool& isempty) { @@ -179,7 +179,7 @@ namespace datalog { return interval(dep()); } else { - return interval(dep(), low, l_open, 0, high, r_open, 0); + return interval(dep(), low, l_open, nullptr, high, r_open, nullptr); } } @@ -198,7 +198,7 @@ namespace datalog { m_is_widen(is_widen) { } - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("interval_relation", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); @@ -209,7 +209,7 @@ namespace datalog { r.mk_union(src, &d, m_is_widen); } else { - r.mk_union(src, 0, m_is_widen); + r.mk_union(src, nullptr, m_is_widen); } } }; @@ -217,7 +217,7 @@ namespace datalog { relation_union_fn * interval_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; + return nullptr; } return alloc(union_fn, false); } @@ -226,7 +226,7 @@ namespace datalog { const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; + return nullptr; } return alloc(union_fn, true); } @@ -237,7 +237,7 @@ namespace datalog { filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_identical_cols(col_cnt, identical_cols) {} - virtual void operator()(relation_base & r) { + void operator()(relation_base & r) override { interval_relation & pr = get(r); for (unsigned i = 1; i < m_identical_cols.size(); ++i) { unsigned c1 = m_identical_cols[0]; @@ -250,7 +250,7 @@ namespace datalog { relation_mutator_fn * interval_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(!check_kind(t)) { - return 0; + return nullptr; } return alloc(filter_identical_fn, col_cnt, identical_cols); } @@ -266,7 +266,7 @@ namespace datalog { VERIFY(arith.is_numeral(value, m_value)); } - virtual void operator()(relation_base & _r) { + void operator()(relation_base & _r) override { interval_relation & r = get(_r); interval_relation_plugin & p = r.get_plugin(); r.mk_intersect(m_col, interval(p.dep(), m_value)); @@ -279,7 +279,7 @@ namespace datalog { if(check_kind(r)) { return alloc(filter_equal_fn, get_manager(), value, col); } - return 0; + return nullptr; } @@ -290,7 +290,7 @@ namespace datalog { m_cond(cond, t.get_plugin().get_ast_manager()) { } - void operator()(relation_base& t) { + void operator()(relation_base& t) override { get(t).filter_interpreted(m_cond); TRACE("interval_relation", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); } @@ -300,7 +300,7 @@ namespace datalog { if (check_kind(t)) { return alloc(filter_interpreted_fn, get(t), condition); } - return 0; + return nullptr; } interval_relation& interval_relation_plugin::get(relation_base& r) { @@ -329,7 +329,7 @@ namespace datalog { eq = m.mk_eq(m.mk_var(i, m.get_sort(e)), e); r.filter_interpreted(eq.get()); } - mk_union(r, 0, false); + mk_union(r, nullptr, false); } bool interval_relation::contains_fact(const relation_fact & f) const { @@ -365,7 +365,7 @@ namespace datalog { interval_relation * interval_relation::complement(func_decl*) const { UNREACHABLE(); - return 0; + return nullptr; } void interval_relation::to_formula(expr_ref& fml) const { @@ -434,22 +434,22 @@ namespace datalog { // 0 < x - y + k if (x == UINT_MAX) { // y < k - mk_intersect(y, interval(p.dep(), k, true, false, 0)); + mk_intersect(y, interval(p.dep(), k, true, false, nullptr)); return; } if (y == UINT_MAX) { // -k < x - mk_intersect(x, interval(p.dep(), -k, true, true, 0)); + mk_intersect(x, interval(p.dep(), -k, true, true, nullptr)); return; } // y < x + k ext_numeral x_hi = (*this)[x].sup(); ext_numeral y_lo = (*this)[y].inf(); if (!x_hi.is_infinite()) { - mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), true, false, 0)); + mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), true, false, nullptr)); } if (!y_lo.is_infinite()) { - mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, true, true, 0)); + mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, true, true, nullptr)); } return; } @@ -458,21 +458,21 @@ namespace datalog { // 0 <= x - y + k if (x == UINT_MAX) { // y <= k - mk_intersect(y, interval(p.dep(), k, false, false, 0)); + mk_intersect(y, interval(p.dep(), k, false, false, nullptr)); return; } if (y == UINT_MAX) { // -k <= x - mk_intersect(x, interval(p.dep(), -k, false, true, 0)); + mk_intersect(x, interval(p.dep(), -k, false, true, nullptr)); return; } ext_numeral x_hi = (*this)[x].sup(); ext_numeral y_lo = (*this)[y].inf(); if (!x_hi.is_infinite()) { - mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), false, false, 0)); + mk_intersect(y, interval(p.dep(), k + x_hi.to_rational(), false, false, nullptr)); } if (!y_lo.is_infinite()) { - mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, false, true, 0)); + mk_intersect(x, interval(p.dep(), y_lo.to_rational() - k, false, true, nullptr)); } return; } diff --git a/src/muz/rel/dl_interval_relation.h b/src/muz/rel/dl_interval_relation.h index a9cce9802..896bb30ab 100644 --- a/src/muz/rel/dl_interval_relation.h +++ b/src/muz/rel/dl_interval_relation.h @@ -53,25 +53,25 @@ namespace datalog { public: interval_relation_plugin(relation_manager& m); - virtual bool can_handle_signature(const relation_signature & s); + bool can_handle_signature(const relation_signature & s) override; static symbol get_name() { return symbol("interval_relation"); } - virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + relation_base * mk_empty(const relation_signature & s) override; + relation_base * mk_full(func_decl* p, const relation_signature & s) override; + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; static bool is_empty(unsigned idx, interval const& i); static bool is_infinite(interval const& i); @@ -97,39 +97,39 @@ namespace datalog { public: interval_relation(interval_relation_plugin& p, relation_signature const& s, bool is_empty); - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual interval_relation * clone() const; - virtual interval_relation * complement(func_decl*) const; - virtual void to_formula(expr_ref& fml) const; + void add_fact(const relation_fact & f) override; + bool contains_fact(const relation_fact & f) const override; + interval_relation * clone() const override; + interval_relation * complement(func_decl*) const override; + void to_formula(expr_ref& fml) const override; interval_relation_plugin& get_plugin() const; void filter_interpreted(app* cond); - virtual bool is_precise() const { return false; } + bool is_precise() const override { return false; } private: - virtual interval mk_intersect(interval const& t1, interval const& t2, bool& is_empty) const { + interval mk_intersect(interval const& t1, interval const& t2, bool& is_empty) const override { return get_plugin().meet(t1, t2, is_empty); } - virtual interval mk_unite(interval const& t1, interval const& t2) const { return get_plugin().unite(t1,t2); } + interval mk_unite(interval const& t1, interval const& t2) const override { return get_plugin().unite(t1,t2); } - virtual interval mk_widen(interval const& t1, interval const& t2) const { return get_plugin().widen(t1,t2); } + interval mk_widen(interval const& t1, interval const& t2) const override { return get_plugin().widen(t1,t2); } - virtual bool is_subset_of(interval const& t1, interval const& t2) const { NOT_IMPLEMENTED_YET(); return false; } + bool is_subset_of(interval const& t1, interval const& t2) const override { NOT_IMPLEMENTED_YET(); return false; } - virtual bool is_full(interval const& t) const { + bool is_full(interval const& t) const override { return interval_relation_plugin::is_infinite(t); } - virtual bool is_empty(unsigned idx, interval const& t) const { + bool is_empty(unsigned idx, interval const& t) const override { return interval_relation_plugin::is_empty(idx, t); } - virtual void mk_rename_elem(interval& i, unsigned col_cnt, unsigned const* cycle); + void mk_rename_elem(interval& i, unsigned col_cnt, unsigned const* cycle) override; - virtual void display_index(unsigned idx, interval const & i, std::ostream& out) const; + void display_index(unsigned idx, interval const & i, std::ostream& out) const override; void mk_intersect(unsigned idx, interval const& i); diff --git a/src/muz/rel/dl_lazy_table.cpp b/src/muz/rel/dl_lazy_table.cpp index 54105891a..53d099532 100644 --- a/src/muz/rel/dl_lazy_table.cpp +++ b/src/muz/rel/dl_lazy_table.cpp @@ -50,7 +50,7 @@ namespace datalog { unsigned const* cols1, unsigned const* cols2): convenient_table_join_fn(s1, s2, col_cnt, cols1, cols2) {} - virtual table_base* operator()(const table_base& _t1, const table_base& _t2) { + table_base* operator()(const table_base& _t1, const table_base& _t2) override { lazy_table const& t1 = get(_t1); lazy_table const& t2 = get(_t2); lazy_table_ref* tr = alloc(lazy_table_join, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr(), t1, t2, get_result_signature()); @@ -65,7 +65,7 @@ namespace datalog { return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } else { - return 0; + return nullptr; } } @@ -75,13 +75,13 @@ namespace datalog { class lazy_table_plugin::union_fn : public table_union_fn { public: void operator()(table_base & _tgt, const table_base & _src, - table_base * _delta) { + table_base * _delta) override { lazy_table& tgt = get(_tgt); lazy_table const& src = get(_src); lazy_table* delta = get(_delta); table_base const* t_src = src.eval(); table_base * t_tgt = tgt.eval(); - table_base * t_delta = delta?delta->eval():0; + table_base * t_delta = delta?delta->eval():nullptr; verbose_action _t("union"); table_union_fn* m = tgt.get_lplugin().get_manager().mk_union_fn(*t_tgt, *t_src, t_delta); SASSERT(m); @@ -98,7 +98,7 @@ namespace datalog { return alloc(union_fn); } else { - return 0; + return nullptr; } } @@ -111,7 +111,7 @@ namespace datalog { convenient_table_project_fn(orig_sig, cnt, cols) {} - virtual table_base* operator()(table_base const& _t) { + table_base* operator()(table_base const& _t) override { lazy_table const& t = get(_t); return alloc(lazy_table, alloc(lazy_table_project, m_removed_cols.size(), m_removed_cols.c_ptr(), t, get_result_signature())); } @@ -124,7 +124,7 @@ namespace datalog { return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); } else { - return 0; + return nullptr; } } @@ -137,7 +137,7 @@ namespace datalog { convenient_table_rename_fn(orig_sig, cnt, cols) {} - virtual table_base* operator()(table_base const& _t) { + table_base* operator()(table_base const& _t) override { lazy_table const& t = get(_t); return alloc(lazy_table, alloc(lazy_table_rename, m_cycle.size(), m_cycle.c_ptr(), t, get_result_signature())); } @@ -150,7 +150,7 @@ namespace datalog { return alloc(rename_fn, t.get_signature(), col_cnt, removed_cols); } else { - return 0; + return nullptr; } } @@ -163,7 +163,7 @@ namespace datalog { public: filter_identical_fn(unsigned cnt, unsigned const* cols): m_cols(cnt, cols) {} - virtual void operator()(table_base& _t) { + void operator()(table_base& _t) override { lazy_table& t = get(_t); t.set(alloc(lazy_table_filter_identical, m_cols.size(), m_cols.c_ptr(), t)); } @@ -175,7 +175,7 @@ namespace datalog { return alloc(filter_identical_fn, col_cnt, identical_cols); } else { - return 0; + return nullptr; } } @@ -188,7 +188,7 @@ namespace datalog { public: filter_interpreted_fn(app_ref& p): m_condition(p) {} - virtual void operator()(table_base& _t) { + void operator()(table_base& _t) override { lazy_table& t = get(_t); t.set(alloc(lazy_table_filter_interpreted, t, m_condition)); } @@ -201,7 +201,7 @@ namespace datalog { return alloc(filter_interpreted_fn, cond); } else { - return 0; + return nullptr; } } @@ -214,7 +214,7 @@ namespace datalog { public: filter_by_negation_fn(unsigned cnt, unsigned const* cols1, unsigned const* cols2): m_cols1(cnt, cols1), m_cols2(cnt, cols2) {} - virtual void operator()(table_base & _t, const table_base & _intersected_obj) { + void operator()(table_base & _t, const table_base & _intersected_obj) override { lazy_table& t = get(_t); lazy_table const& it = get(_intersected_obj); t.set(alloc(lazy_table_filter_by_negation, t, it, m_cols1, m_cols2)); @@ -229,7 +229,7 @@ namespace datalog { return alloc(filter_by_negation_fn, joined_col_cnt, t_cols, negated_cols); } else { - return 0; + return nullptr; } } @@ -246,7 +246,7 @@ namespace datalog { m_col(col) { } - virtual void operator()(table_base& _t) { + void operator()(table_base& _t) override { lazy_table& t = get(_t); t.set(alloc(lazy_table_filter_equal, m_col, m_value, t)); } @@ -258,7 +258,7 @@ namespace datalog { return alloc(filter_equal_fn, value, col); } else { - return 0; + return nullptr; } } @@ -269,7 +269,7 @@ namespace datalog { return alloc(lazy_table_plugin, *sp); } else { - return 0; + return nullptr; } } @@ -397,7 +397,7 @@ namespace datalog { SASSERT(!m_table); m_table = m_src->eval(); m_src->release_table(); - m_src = 0; + m_src = nullptr; verbose_action _t("filter_identical"); table_mutator_fn* m = rm().mk_filter_identical_fn(*m_table, m_cols.size(), m_cols.c_ptr()); SASSERT(m); @@ -410,7 +410,7 @@ namespace datalog { SASSERT(!m_table); m_table = m_src->eval(); m_src->release_table(); - m_src = 0; + m_src = nullptr; verbose_action _t("filter_equal"); table_mutator_fn* m = rm().mk_filter_equal_fn(*m_table, m_value, m_col); SASSERT(m); @@ -423,7 +423,7 @@ namespace datalog { SASSERT(!m_table); m_table = m_src->eval(); m_src->release_table(); - m_src = 0; + m_src = nullptr; verbose_action _t("filter_interpreted"); table_mutator_fn* m = rm().mk_filter_interpreted_fn(*m_table, m_condition); SASSERT(m); @@ -436,7 +436,7 @@ namespace datalog { SASSERT(!m_table); m_table = m_tgt->eval(); m_tgt->release_table(); - m_tgt = 0; + m_tgt = nullptr; switch(m_src->kind()) { diff --git a/src/muz/rel/dl_lazy_table.h b/src/muz/rel/dl_lazy_table.h index 6752e5a5a..7e5991a7a 100644 --- a/src/muz/rel/dl_lazy_table.h +++ b/src/muz/rel/dl_lazy_table.h @@ -48,37 +48,37 @@ namespace datalog { table_plugin(mk_name(p), p.get_manager()), m_plugin(p) {} - virtual bool can_handle_signature(const table_signature & s) { + bool can_handle_signature(const table_signature & s) override { return m_plugin.can_handle_signature(s); } - virtual table_base * mk_empty(const table_signature & s); + table_base * mk_empty(const table_signature & s) override; static table_plugin* mk_sparse(relation_manager& rm); protected: - virtual table_join_fn * mk_join_fn( + table_join_fn * mk_join_fn( const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual table_union_fn * mk_union_fn( - const table_base & tgt, const table_base & src, - const table_base * delta); - virtual table_transformer_fn * mk_project_fn( - const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual table_transformer_fn * mk_rename_fn( + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + table_union_fn * mk_union_fn( + const table_base & tgt, const table_base & src, + const table_base * delta) override; + table_transformer_fn * mk_project_fn( + const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + table_transformer_fn * mk_rename_fn( const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual table_mutator_fn * mk_filter_identical_fn( - const table_base & t, unsigned col_cnt, const unsigned * identical_cols); - virtual table_mutator_fn * mk_filter_equal_fn( - const table_base & t, const table_element & value, unsigned col); - virtual table_mutator_fn * mk_filter_interpreted_fn( - const table_base & t, app * condition); - virtual table_intersection_filter_fn * mk_filter_by_negation_fn( - const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); + const unsigned * permutation_cycle) override; + table_mutator_fn * mk_filter_identical_fn( + const table_base & t, unsigned col_cnt, const unsigned * identical_cols) override; + table_mutator_fn * mk_filter_equal_fn( + const table_base & t, const table_element & value, unsigned col) override; + table_mutator_fn * mk_filter_interpreted_fn( + const table_base & t, app * condition) override; + table_intersection_filter_fn * mk_filter_by_negation_fn( + const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) override; static lazy_table const& get(table_base const& tb); static lazy_table& get(table_base& tb); @@ -129,30 +129,30 @@ namespace datalog { m_ref(t) {} - virtual ~lazy_table() {} + ~lazy_table() override {} lazy_table_plugin& get_lplugin() const { return dynamic_cast(table_base::get_plugin()); } - virtual table_base * clone() const; - virtual table_base * complement(func_decl* p, const table_element * func_columns = 0) const; - virtual bool empty() const; - virtual bool contains_fact(const table_fact & f) const; - virtual void remove_fact(table_element const* fact); - virtual void remove_facts(unsigned fact_cnt, const table_fact * facts); - virtual void remove_facts(unsigned fact_cnt, const table_element * facts); - virtual void reset(); - virtual void add_fact(table_fact const& f); + table_base * clone() const override; + table_base * complement(func_decl* p, const table_element * func_columns = nullptr) const override; + bool empty() const override; + bool contains_fact(const table_fact & f) const override; + void remove_fact(table_element const* fact) override; + void remove_facts(unsigned fact_cnt, const table_fact * facts) override; + void remove_facts(unsigned fact_cnt, const table_element * facts) override; + void reset() override; + void add_fact(table_fact const& f) override; - virtual unsigned get_size_estimate_rows() const { return 1; } - virtual unsigned get_size_estimate_bytes() const { return 1; } - virtual bool knows_exact_size() const { return false; } + unsigned get_size_estimate_rows() const override { return 1; } + unsigned get_size_estimate_bytes() const override { return 1; } + bool knows_exact_size() const override { return false; } table_base* eval() const; - virtual table_base::iterator begin() const; - virtual table_base::iterator end() const; + table_base::iterator begin() const override; + table_base::iterator end() const override; lazy_table_ref* get_ref() const { return m_ref.get(); } void set(lazy_table_ref* r) { m_ref = r; } @@ -165,9 +165,9 @@ namespace datalog { m_table = table; // SASSERT(&p.m_plugin == &table->get_lplugin()); } - virtual ~lazy_table_base() {} - virtual lazy_table_kind kind() const { return LAZY_TABLE_BASE; } - virtual table_base* force() { return m_table.get(); } + ~lazy_table_base() override {} + lazy_table_kind kind() const override { return LAZY_TABLE_BASE; } + table_base* force() override { return m_table.get(); } }; class lazy_table_join : public lazy_table_ref { @@ -184,13 +184,13 @@ namespace datalog { m_cols2(col_cnt, cols2), m_t1(t1.get_ref()), m_t2(t2.get_ref()) { } - virtual ~lazy_table_join() {} - virtual lazy_table_kind kind() const { return LAZY_TABLE_JOIN; } + ~lazy_table_join() override {} + lazy_table_kind kind() const override { return LAZY_TABLE_JOIN; } unsigned_vector const& cols1() const { return m_cols1; } unsigned_vector const& cols2() const { return m_cols2; } lazy_table_ref* t1() const { return m_t1.get(); } lazy_table_ref* t2() const { return m_t2.get(); } - virtual table_base* force(); + table_base* force() override; }; @@ -202,12 +202,12 @@ namespace datalog { : lazy_table_ref(src.get_lplugin(), sig), m_cols(col_cnt, cols), m_src(src.get_ref()) {} - virtual ~lazy_table_project() {} + ~lazy_table_project() override {} - virtual lazy_table_kind kind() const { return LAZY_TABLE_PROJECT; } + lazy_table_kind kind() const override { return LAZY_TABLE_PROJECT; } unsigned_vector const& cols() const { return m_cols; } lazy_table_ref* src() const { return m_src.get(); } - virtual table_base* force(); + table_base* force() override; }; class lazy_table_rename : public lazy_table_ref { @@ -218,12 +218,12 @@ namespace datalog { : lazy_table_ref(src.get_lplugin(), sig), m_cols(col_cnt, cols), m_src(src.get_ref()) {} - virtual ~lazy_table_rename() {} + ~lazy_table_rename() override {} - virtual lazy_table_kind kind() const { return LAZY_TABLE_RENAME; } + lazy_table_kind kind() const override { return LAZY_TABLE_RENAME; } unsigned_vector const& cols() const { return m_cols; } lazy_table_ref* src() const { return m_src.get(); } - virtual table_base* force(); + table_base* force() override; }; class lazy_table_filter_identical : public lazy_table_ref { @@ -232,12 +232,12 @@ namespace datalog { public: lazy_table_filter_identical(unsigned col_cnt, const unsigned * cols, lazy_table const& src) : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_cols(col_cnt, cols), m_src(src.get_ref()) {} - virtual ~lazy_table_filter_identical() {} + ~lazy_table_filter_identical() override {} - virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_IDENTICAL; } + lazy_table_kind kind() const override { return LAZY_TABLE_FILTER_IDENTICAL; } unsigned_vector const& cols() const { return m_cols; } lazy_table_ref* src() const { return m_src.get(); } - virtual table_base* force(); + table_base* force() override; }; class lazy_table_filter_equal : public lazy_table_ref { @@ -250,13 +250,13 @@ namespace datalog { m_col(col), m_value(value), m_src(src.get_ref()) {} - virtual ~lazy_table_filter_equal() {} + ~lazy_table_filter_equal() override {} - virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_EQUAL; } + lazy_table_kind kind() const override { return LAZY_TABLE_FILTER_EQUAL; } unsigned col() const { return m_col; } table_element value() const { return m_value; } lazy_table_ref* src() const { return m_src.get(); } - virtual table_base* force(); + table_base* force() override; }; class lazy_table_filter_interpreted : public lazy_table_ref { @@ -266,12 +266,12 @@ namespace datalog { lazy_table_filter_interpreted(lazy_table const& src, app* condition) : lazy_table_ref(src.get_lplugin(), src.get_signature()), m_condition(condition, src.get_lplugin().get_ast_manager()), m_src(src.get_ref()) {} - virtual ~lazy_table_filter_interpreted() {} + ~lazy_table_filter_interpreted() override {} - virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_INTERPRETED; } + lazy_table_kind kind() const override { return LAZY_TABLE_FILTER_INTERPRETED; } app* condition() const { return m_condition; } lazy_table_ref* src() const { return m_src.get(); } - virtual table_base* force(); + table_base* force() override; }; @@ -288,13 +288,13 @@ namespace datalog { m_src(src.get_ref()), m_cols1(c1), m_cols2(c2) {} - virtual ~lazy_table_filter_by_negation() {} - virtual lazy_table_kind kind() const { return LAZY_TABLE_FILTER_BY_NEGATION; } + ~lazy_table_filter_by_negation() override {} + lazy_table_kind kind() const override { return LAZY_TABLE_FILTER_BY_NEGATION; } lazy_table_ref* tgt() const { return m_tgt.get(); } lazy_table_ref* src() const { return m_src.get(); } unsigned_vector const& cols1() const { return m_cols1; } unsigned_vector const& cols2() const { return m_cols2; } - virtual table_base* force(); + table_base* force() override; }; diff --git a/src/muz/rel/dl_mk_explanations.cpp b/src/muz/rel/dl_mk_explanations.cpp index c4fb57eeb..1a1a47f74 100644 --- a/src/muz/rel/dl_mk_explanations.cpp +++ b/src/muz/rel/dl_mk_explanations.cpp @@ -69,7 +69,7 @@ namespace datalog { m_relation_level_explanations(relation_level), m_union_decl(mk_explanations::get_union_decl(get_context()), get_ast_manager()) {} - ~explanation_relation_plugin() { + ~explanation_relation_plugin() override { for (unsigned i = 0; i < m_pool.size(); ++i) { for (unsigned j = 0; j < m_pool[i].size(); ++j) { dealloc(m_pool[i][j]); @@ -77,7 +77,7 @@ namespace datalog { } } - virtual bool can_handle_signature(const relation_signature & s) { + bool can_handle_signature(const relation_signature & s) override { unsigned n=s.size(); for (unsigned i=0; i(relation_base::get_plugin()); } - virtual void to_formula(expr_ref& fml) const { + void to_formula(expr_ref& fml) const override { ast_manager& m = fml.get_manager(); fml = m.mk_eq(m.mk_var(0, m.get_sort(m_data[0])), m_data[0]); } bool is_undefined(unsigned col_idx) const { - return m_data[col_idx]==0; + return m_data[col_idx]==nullptr; } bool no_undefined() const { if (empty()) { @@ -204,23 +204,23 @@ namespace datalog { return true; } - virtual bool empty() const { return m_empty; } + bool empty() const override { return m_empty; } - virtual void reset() { + void reset() override { m_empty = true; } - virtual void add_fact(const relation_fact & f) { + void add_fact(const relation_fact & f) override { SASSERT(empty()); assign_data(f); } - virtual bool contains_fact(const relation_fact & f) const { + bool contains_fact(const relation_fact & f) const override { UNREACHABLE(); throw 0; } - virtual explanation_relation * clone() const { + explanation_relation * clone() const override { explanation_relation * res = static_cast(get_plugin().mk_empty(get_signature())); res->m_empty = m_empty; SASSERT(res->m_data.empty()); @@ -228,7 +228,7 @@ namespace datalog { return res; } - virtual relation_base * complement(func_decl* pred) const { + relation_base * complement(func_decl* pred) const override { explanation_relation * res = static_cast(get_plugin().mk_empty(get_signature())); if (empty()) { res->set_undefined(); @@ -247,7 +247,7 @@ namespace datalog { } } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { if (empty()) { out << "\n"; return; @@ -296,9 +296,9 @@ namespace datalog { class explanation_relation_plugin::join_fn : public convenient_relation_join_fn { public: join_fn(const relation_signature & sig1, const relation_signature & sig2) - : convenient_relation_join_fn(sig1, sig2, 0, 0, 0) {} + : convenient_relation_join_fn(sig1, sig2, 0, nullptr, nullptr) {} - virtual relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) { + relation_base * operator()(const relation_base & r1_0, const relation_base & r2_0) override { const explanation_relation & r1 = static_cast(r1_0); const explanation_relation & r2 = static_cast(r2_0); explanation_relation_plugin & plugin = r1.get_plugin(); @@ -317,10 +317,10 @@ namespace datalog { relation_join_fn * explanation_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (&r1.get_plugin()!=this || &r2.get_plugin()!=this) { - return 0; + return nullptr; } if (col_cnt!=0) { - return 0; + return nullptr; } return alloc(join_fn, r1.get_signature(), r2.get_signature()); } @@ -331,7 +331,7 @@ namespace datalog { project_fn(const relation_signature & sig, unsigned col_cnt, const unsigned * removed_cols) : convenient_relation_project_fn(sig, col_cnt, removed_cols) {} - virtual relation_base * operator()(const relation_base & r0) { + relation_base * operator()(const relation_base & r0) override { const explanation_relation & r = static_cast(r0); explanation_relation_plugin & plugin = r.get_plugin(); @@ -348,7 +348,7 @@ namespace datalog { relation_transformer_fn * explanation_relation_plugin::mk_project_fn(const relation_base & r, unsigned col_cnt, const unsigned * removed_cols) { if (&r.get_plugin()!=this) { - return 0; + return nullptr; } return alloc(project_fn, r.get_signature(), col_cnt, removed_cols); } @@ -359,7 +359,7 @@ namespace datalog { rename_fn(const relation_signature & sig, unsigned permutation_cycle_len, const unsigned * permutation_cycle) : convenient_relation_rename_fn(sig, permutation_cycle_len, permutation_cycle) {} - virtual relation_base * operator()(const relation_base & r0) { + relation_base * operator()(const relation_base & r0) override { const explanation_relation & r = static_cast(r0); explanation_relation_plugin & plugin = r.get_plugin(); @@ -382,10 +382,10 @@ namespace datalog { class explanation_relation_plugin::union_fn : public relation_union_fn { scoped_ptr m_delta_union_fun; public: - virtual void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) { + void operator()(relation_base & tgt0, const relation_base & src0, relation_base * delta0) override { explanation_relation & tgt = static_cast(tgt0); const explanation_relation & src = static_cast(src0); - explanation_relation * delta = delta0 ? static_cast(delta0) : 0; + explanation_relation * delta = delta0 ? static_cast(delta0) : nullptr; explanation_relation_plugin & plugin = tgt.get_plugin(); if (!src.no_undefined() || !tgt.no_undefined() || (delta && !delta->no_undefined())) { @@ -418,9 +418,9 @@ namespace datalog { class explanation_relation_plugin::foreign_union_fn : public relation_union_fn { scoped_ptr m_delta_union_fun; public: - virtual void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) { + void operator()(relation_base & tgt0, const relation_base & src, relation_base * delta0) override { explanation_relation & tgt = static_cast(tgt0); - explanation_relation * delta = delta0 ? static_cast(delta0) : 0; + explanation_relation * delta = delta0 ? static_cast(delta0) : nullptr; if (src.empty()) { return; @@ -435,7 +435,7 @@ namespace datalog { relation_union_fn * explanation_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || (delta && !check_kind(*delta))) { - return 0; + return nullptr; } if (!check_kind(src)) { //this is to handle the product relation @@ -454,9 +454,9 @@ namespace datalog { : m_manager(ctx.get_manager()), m_subst(ctx.get_var_subst()), m_col_idx(col_idx), - m_new_rule(new_rule) {} + m_new_rule(std::move(new_rule)) {} - virtual void operator()(relation_base & r0) { + void operator()(relation_base & r0) override { explanation_relation & r = static_cast(r0); if (!r.is_undefined(m_col_idx)) { @@ -480,11 +480,11 @@ namespace datalog { relation_mutator_fn * explanation_relation_plugin::mk_filter_interpreted_fn(const relation_base & r, app * cond) { if (&r.get_plugin()!=this) { - return 0; + return nullptr; } ast_manager & m = get_ast_manager(); if (!m.is_eq(cond)) { - return 0; + return nullptr; } expr * arg1 = cond->get_arg(0); expr * arg2 = cond->get_arg(1); @@ -494,12 +494,12 @@ namespace datalog { } if (!is_var(arg1) || !is_app(arg2)) { - return 0; + return nullptr; } var * col_var = to_var(arg1); app * new_rule = to_app(arg2); if (!get_context().get_decl_util().is_rule_sort(col_var->get_sort())) { - return 0; + return nullptr; } unsigned col_idx = col_var->get_idx(); @@ -509,7 +509,7 @@ namespace datalog { class explanation_relation_plugin::negation_filter_fn : public relation_intersection_filter_fn { public: - virtual void operator()(relation_base & r, const relation_base & neg) { + void operator()(relation_base & r, const relation_base & neg) override { if (!neg.empty()) { r.reset(); } @@ -520,7 +520,7 @@ namespace datalog { const relation_base & neg, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) { if (&r.get_plugin()!=this || &neg.get_plugin()!=this) { - return 0; + return nullptr; } return alloc(negation_filter_fn); } @@ -531,7 +531,7 @@ namespace datalog { intersection_filter_fn(explanation_relation_plugin & plugin) : m_union_decl(plugin.m_union_decl) {} - virtual void operator()(relation_base & tgt0, const relation_base & src0) { + void operator()(relation_base & tgt0, const relation_base & src0) override { explanation_relation & tgt = static_cast(tgt0); const explanation_relation & src = static_cast(src0); @@ -569,18 +569,18 @@ namespace datalog { const relation_base & tgt, const relation_base & src, unsigned joined_col_cnt, const unsigned * tgt_cols, const unsigned * src_cols) { if (&tgt.get_plugin()!=this || &src.get_plugin()!=this) { - return 0; + return nullptr; } //this checks the join is one to one on all columns if (tgt.get_signature()!=src.get_signature() || joined_col_cnt!=tgt.get_signature().size() || !containers_equal(tgt_cols, tgt_cols+joined_col_cnt, src_cols, src_cols+joined_col_cnt)) { - return 0; + return nullptr; } counter ctr; ctr.count(joined_col_cnt, tgt_cols); if (ctr.get_max_counter_value()>1 || (joined_col_cnt && ctr.get_max_positive()!=joined_col_cnt-1)) { - return 0; + return nullptr; } return alloc(intersection_filter_fn, *this); } @@ -776,7 +776,7 @@ namespace datalog { app_ref orig_lit(m_manager.mk_app(orig_decl, lit_args.c_ptr()), m_manager); app_ref e_lit(get_e_lit(orig_lit, arity), m_manager); app * tail[] = { e_lit.get() }; - dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, 0)); + dst.add_rule(m_context.get_rule_manager().mk(orig_lit, 1, tail, nullptr)); } } @@ -852,7 +852,7 @@ namespace datalog { translate_rel_level_relation(rmgr, orig_rel, e_rel); } else { - scoped_ptr product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, 0, 0); + scoped_ptr product_fun = rmgr.mk_join_fn(orig_rel, *m_e_fact_relation, 0, nullptr, nullptr); SASSERT(product_fun); scoped_rel aux_extended_rel = (*product_fun)(orig_rel, *m_e_fact_relation); TRACE("dl", tout << aux_extended_rel << " " << aux_extended_rel->get_plugin().get_name() << "\n"; @@ -868,10 +868,10 @@ namespace datalog { rule_set * mk_explanations::operator()(rule_set const & source) { if (source.empty()) { - return 0; + return nullptr; } if (!m_context.generate_explanations()) { - return 0; + return nullptr; } rule_set * res = alloc(rule_set, m_context); transform_facts(m_context.get_rel_context()->get_rmanager(), source, *res); diff --git a/src/muz/rel/dl_mk_explanations.h b/src/muz/rel/dl_mk_explanations.h index 6142b61d6..a96d50cb3 100644 --- a/src/muz/rel/dl_mk_explanations.h +++ b/src/muz/rel/dl_mk_explanations.h @@ -76,7 +76,7 @@ namespace datalog { return get_union_decl(m_context); } - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; static expr* get_explanation(relation_base const& r); }; diff --git a/src/muz/rel/dl_mk_similarity_compressor.cpp b/src/muz/rel/dl_mk_similarity_compressor.cpp index c4abf326a..c6063772a 100644 --- a/src/muz/rel/dl_mk_similarity_compressor.cpp +++ b/src/muz/rel/dl_mk_similarity_compressor.cpp @@ -531,7 +531,7 @@ namespace datalog { } } - rule_set * result = static_cast(0); + rule_set * result = static_cast(nullptr); if (m_modified) { result = alloc(rule_set, m_context); unsigned fin_rule_cnt = m_result_rules.size(); diff --git a/src/muz/rel/dl_mk_similarity_compressor.h b/src/muz/rel/dl_mk_similarity_compressor.h index 096305c59..da56e9eca 100644 --- a/src/muz/rel/dl_mk_similarity_compressor.h +++ b/src/muz/rel/dl_mk_similarity_compressor.h @@ -69,7 +69,7 @@ namespace datalog { public: mk_similarity_compressor(context & ctx); - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/rel/dl_mk_simple_joins.cpp b/src/muz/rel/dl_mk_simple_joins.cpp index 60eea2e53..ac3d30e9e 100644 --- a/src/muz/rel/dl_mk_simple_joins.cpp +++ b/src/muz/rel/dl_mk_simple_joins.cpp @@ -165,7 +165,7 @@ namespace datalog { SASSERT(is_var(t->get_arg(i))); var * v = to_var(t->get_arg(i)); unsigned var_idx = v->get_idx(); - if (result[res_ofs-var_idx]==0) { + if (result[res_ofs-var_idx]==nullptr) { result[res_ofs-var_idx]=m.mk_var(next_var, v->get_sort()); next_var++; } @@ -235,7 +235,7 @@ namespace datalog { //so the order should not matter } - result.resize(max_var_idx+1, static_cast(0)); + result.resize(max_var_idx+1, static_cast(nullptr)); unsigned next_var = 0; get_normalizer(t1, next_var, result); get_normalizer(t2, next_var, result); @@ -268,9 +268,9 @@ namespace datalog { */ void register_pair(app * t1, app * t2, rule * r, const var_idx_set & non_local_vars) { SASSERT(t1!=t2); - cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), 0); + cost_map::entry * e = m_costs.insert_if_not_there2(get_key(t1, t2), nullptr); pair_info * & ptr_inf = e->get_data().m_value; - if (ptr_inf==0) { + if (ptr_inf==nullptr) { ptr_inf = alloc(pair_info); } pair_info & inf = *ptr_inf; @@ -297,7 +297,7 @@ namespace datalog { } void remove_rule_from_pair(app_pair key, rule * r, unsigned original_len) { - pair_info * ptr = 0; + pair_info * ptr = nullptr; if (m_costs.find(key, ptr) && ptr && ptr->remove_rule(r, original_len)) { SASSERT(ptr->m_rules.empty()); @@ -354,7 +354,7 @@ namespace datalog { void join_pair(app_pair pair_key) { app * t1 = pair_key.first; app * t2 = pair_key.second; - pair_info* infp = 0; + pair_info* infp = nullptr; if (!m_costs.find(pair_key, infp) || !infp) { UNREACHABLE(); return; @@ -402,7 +402,7 @@ namespace datalog { app * tail[] = {t1, t2}; - rule * new_rule = m_context.get_rule_manager().mk(head, 2, tail, 0); + rule * new_rule = m_context.get_rule_manager().mk(head, 2, tail, nullptr); //TODO: update accounting so that it can handle multiple parents new_rule->set_accounting_parent_object(m_context, one_parent); @@ -702,7 +702,7 @@ namespace datalog { } if (!m_modified_rules) { - return 0; + return nullptr; } rule_set * result = alloc(rule_set, m_context); rule_pred_map::iterator rcit = m_rules_content.begin(); diff --git a/src/muz/rel/dl_mk_simple_joins.h b/src/muz/rel/dl_mk_simple_joins.h index cf4522c22..8eb6bd0c2 100644 --- a/src/muz/rel/dl_mk_simple_joins.h +++ b/src/muz/rel/dl_mk_simple_joins.h @@ -54,7 +54,7 @@ namespace datalog { public: mk_simple_joins(context & ctx); - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/rel/dl_product_relation.cpp b/src/muz/rel/dl_product_relation.cpp index 8a2029748..c591b7186 100644 --- a/src/muz/rel/dl_product_relation.cpp +++ b/src/muz/rel/dl_product_relation.cpp @@ -261,7 +261,7 @@ namespace datalog { void init(relation_signature const& r1_sig, unsigned num_rels1, relation_base const* const* r1, relation_signature const& r2_sig, unsigned num_rels2, relation_base const* const* r2, unsigned col_cnt, unsigned const* cols1, unsigned const* cols2) { - func_decl* p = 0; + func_decl* p = nullptr; bit_vector bv; bv.resize(num_rels2); relation_manager& rmgr = m_plugin.get_manager(); @@ -444,16 +444,16 @@ namespace datalog { init(r1.get_signature(), 1, rels1, r2.get_signature(), 1, rels2, col_cnt, cols1, cols2); } - ~join_fn() { + ~join_fn() override { dealloc_ptr_vector_content(m_joins); dealloc_ptr_vector_content(m_full); } - virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + relation_base * operator()(const relation_base & _r1, const relation_base & _r2) override { TRACE("dl", _r1.display(tout); _r2.display(tout);); ptr_vector relations; unsigned sz = m_joins.size(); - relation_base* result = 0; + relation_base* result = nullptr; for (unsigned i = 0; i < sz; ++i) { relation_base const& r1 = (m_kind1[i] == T_FULL)?(*m_full[m_offset1[i]]):access(m_offset1[i], _r1); relation_base const& r2 = (m_kind2[i] == T_FULL)?(*m_full[m_offset2[i]]):access(m_offset2[i], _r2); @@ -479,7 +479,7 @@ namespace datalog { if (r1.get_kind() != r2.get_kind()) { return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2); } - return 0; + return nullptr; } @@ -488,12 +488,12 @@ namespace datalog { ptr_vector m_transforms; public: transform_fn(relation_signature s, unsigned num_trans, relation_transformer_fn** trans): - m_sig(s), + m_sig(std::move(s)), m_transforms(num_trans, trans) {} - ~transform_fn() { dealloc_ptr_vector_content(m_transforms); } + ~transform_fn() override { dealloc_ptr_vector_content(m_transforms); } - virtual relation_base * operator()(const relation_base & _r) { + relation_base * operator()(const relation_base & _r) override { product_relation const& r = get(_r); product_relation_plugin& p = r.get_plugin(); SASSERT(m_transforms.size() == r.size()); @@ -519,7 +519,7 @@ namespace datalog { relation_signature::from_project(r.get_signature(), col_cnt, removed_cols, s); return alloc(transform_fn, s, projs.size(), projs.c_ptr()); } - return 0; + return nullptr; } relation_transformer_fn * product_relation_plugin::mk_rename_fn(const relation_base & _r, @@ -534,7 +534,7 @@ namespace datalog { relation_signature::from_rename(r.get_signature(), cycle_len, permutation_cycle, s); return alloc(transform_fn, s, trans.size(), trans.c_ptr()); } - return 0; + return nullptr; } class product_relation_plugin::aligned_union_fn : public relation_union_fn { @@ -549,7 +549,7 @@ namespace datalog { void mk_union_fn(unsigned i, unsigned j, relation_base const& r1, relation_base const& r2, const relation_base* delta) { relation_manager& rmgr = r1.get_manager(); - relation_union_fn* u = 0; + relation_union_fn* u = nullptr; if (m_is_widen) { u = rmgr.mk_widen_fn(r1, r2, delta); } @@ -596,7 +596,7 @@ namespace datalog { return; } do_intersection(*tgt, *src); - src = 0; + src = nullptr; } void do_intersection(relation_base& tgt, relation_base& src) { @@ -625,17 +625,17 @@ namespace datalog { m_is_widen(is_widen) { SASSERT(vectors_equal(tgt.m_spec, src.m_spec)); SASSERT(!delta || vectors_equal(tgt.m_spec, delta->m_spec)); - init(tgt.m_relations, src.m_relations, delta ? &delta->m_relations : 0); + init(tgt.m_relations, src.m_relations, delta ? &delta->m_relations : nullptr); } - ~aligned_union_fn() { + ~aligned_union_fn() override { unsigned sz = m_unions.size(); for(unsigned i=0; i fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; + scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : nullptr; scoped_rel side_result; scoped_rel side_delta; @@ -665,7 +665,7 @@ namespace datalog { TRACE("dl", itgt.display(tout << "tgt:\n"); src[j].display(tout << "src:\n");); // union[i][j] scoped_rel one_side_union = itgt.clone(); - scoped_rel one_side_delta = fresh_delta ? fresh_delta->clone() : 0; + scoped_rel one_side_delta = fresh_delta ? fresh_delta->clone() : nullptr; TRACE("dl", one_side_union->display(tout << "union 1:\n"); src[j].display(tout);); do_inner_union(i, j, *one_side_union, src[j], one_side_delta.get()); TRACE("dl", one_side_union->display(tout << "union:\n");); @@ -679,7 +679,7 @@ namespace datalog { // union[j][i] one_side_union = src[i].clone(); - one_side_delta = fresh_delta ? fresh_delta->clone() : 0; + one_side_delta = fresh_delta ? fresh_delta->clone() : nullptr; TRACE("dl", one_side_union->display(tout << "union 2:\n"); tgt[j].display(tout);); do_inner_union(i, j, *one_side_union, tgt[j], one_side_delta.get()); TRACE("dl", one_side_union->display(tout << "union:\n");); @@ -697,8 +697,8 @@ namespace datalog { } for (unsigned i = 0; i < num; ++i) { relation_base& itgt = tgt[i]; - relation_base* idelta = delta ? &(*delta)[i] : 0; - scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : 0; + relation_base* idelta = delta ? &(*delta)[i] : nullptr; + scoped_rel fresh_delta = idelta ? idelta->get_plugin().mk_empty(*idelta) : nullptr; scoped_rel side_result(side_results[i]); scoped_rel side_delta(side_deltas[i]); @@ -748,11 +748,11 @@ namespace datalog { } - virtual void operator()(relation_base& _tgt, const relation_base& _src, relation_base* _delta) { + void operator()(relation_base& _tgt, const relation_base& _src, relation_base* _delta) override { TRACE("dl_verbose", _tgt.display(tout << "dst:\n"); _src.display(tout << "src:\n");); product_relation& tgt = get(_tgt); product_relation const& src0 = get(_src); - product_relation* delta = _delta ? get(_delta) : 0; + product_relation* delta = _delta ? get(_delta) : nullptr; tgt.convert_spec(m_common_spec); if(delta) { @@ -783,7 +783,7 @@ namespace datalog { : m_single_rel_idx(single_rel_idx), m_inner_union_fun(inner_union_fun) {} - virtual void operator()(relation_base& tgt, const relation_base& _src, relation_base* delta) { + void operator()(relation_base& tgt, const relation_base& _src, relation_base* delta) override { TRACE("dl", tgt.display(tout); _src.display(tout); ); product_relation const& src = get(_src); (*m_inner_union_fun)(tgt, src[m_single_rel_idx], delta); @@ -815,7 +815,7 @@ namespace datalog { } } } - return 0; + return nullptr; } relation_union_fn * product_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, @@ -834,9 +834,9 @@ namespace datalog { mutator_fn(unsigned sz, relation_mutator_fn** muts): m_mutators(sz, muts) {} - ~mutator_fn() { dealloc_ptr_vector_content(m_mutators); } + ~mutator_fn() override { dealloc_ptr_vector_content(m_mutators); } - virtual void operator()(relation_base & _r) { + void operator()(relation_base & _r) override { TRACE("dl", _r.display(tout);); product_relation& r = get(_r); SASSERT(m_mutators.size() == r.size()); @@ -867,7 +867,7 @@ namespace datalog { return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); } } - return 0; + return nullptr; } relation_mutator_fn * product_relation_plugin::mk_filter_equal_fn(const relation_base & _t, @@ -885,7 +885,7 @@ namespace datalog { return alloc(mutator_fn, mutators.size(), mutators.c_ptr()); } } - return 0; + return nullptr; } class product_relation_plugin::filter_interpreted_fn : public relation_mutator_fn { @@ -911,9 +911,9 @@ namespace datalog { } } - ~filter_interpreted_fn() { dealloc_ptr_vector_content(m_mutators); } + ~filter_interpreted_fn() override { dealloc_ptr_vector_content(m_mutators); } - void operator()(relation_base& _r) { + void operator()(relation_base& _r) override { TRACE("dl", _r.display(tout);); product_relation const& r = get(_r); for (unsigned i = 0; i < m_attach.size(); ++i) { @@ -983,7 +983,7 @@ namespace datalog { void product_relation::convert_spec(const rel_spec & spec) { - func_decl* p = 0; + func_decl* p = nullptr; const relation_signature & sig = get_signature(); family_id new_kind = get_plugin().get_relation_kind(sig, spec); if (new_kind == get_kind()) { @@ -1008,7 +1008,7 @@ namespace datalog { //the loop is quadratic with the number of relations, maybe we want to fix it for(unsigned i=0; iget_kind()==ikind) { @@ -1096,7 +1096,7 @@ namespace datalog { return res; } UNREACHABLE(); - return 0; + return nullptr; } bool product_relation::empty() const { diff --git a/src/muz/rel/dl_product_relation.h b/src/muz/rel/dl_product_relation.h index 002fe5289..a872bc969 100644 --- a/src/muz/rel/dl_product_relation.h +++ b/src/muz/rel/dl_product_relation.h @@ -59,37 +59,37 @@ namespace datalog { product_relation_plugin(relation_manager& m); - virtual void initialize(family_id fid); + void initialize(family_id fid) override; - virtual bool can_handle_signature(const relation_signature & s); - virtual bool can_handle_signature(const relation_signature & s, family_id kind); + bool can_handle_signature(const relation_signature & s) override; + bool can_handle_signature(const relation_signature & s, family_id kind) override; static symbol get_name() { return symbol("product_relation"); } family_id get_relation_kind(const relation_signature & sig, const rel_spec & spec); - virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_empty(const relation_signature & s, family_id kind); + relation_base * mk_empty(const relation_signature & s) override; + relation_base * mk_empty(const relation_signature & s, family_id kind) override; - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - virtual relation_base * mk_full(func_decl* p, const relation_signature & s, family_id kind); + relation_base * mk_full(func_decl* p, const relation_signature & s) override; + relation_base * mk_full(func_decl* p, const relation_signature & s, family_id kind) override; protected: - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; static bool is_product_relation(relation_base const& r); @@ -154,15 +154,15 @@ namespace datalog { product_relation(product_relation_plugin& p, relation_signature const& s); product_relation(product_relation_plugin& p, relation_signature const& s, unsigned num_relations, relation_base** relations); - ~product_relation(); + ~product_relation() override; - virtual bool empty() const; - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual product_relation * clone() const; - virtual product_relation * complement(func_decl* p) const; - virtual void display(std::ostream & out) const; - virtual void to_formula(expr_ref& fml) const; + bool empty() const override; + void add_fact(const relation_fact & f) override; + bool contains_fact(const relation_fact & f) const override; + product_relation * clone() const override; + product_relation * complement(func_decl* p) const override; + void display(std::ostream & out) const override; + void to_formula(expr_ref& fml) const override; product_relation_plugin& get_plugin() const; unsigned size() const { return m_relations.size(); } @@ -175,7 +175,7 @@ namespace datalog { */ bool try_get_single_non_transparent(unsigned & idx) const; - virtual bool is_precise() const { + bool is_precise() const override { for (unsigned i = 0; i < m_relations.size(); ++i) { if (!m_relations[i]->is_precise()) { return false; diff --git a/src/muz/rel/dl_relation_manager.cpp b/src/muz/rel/dl_relation_manager.cpp index 1eb973776..ff12e66c9 100644 --- a/src/muz/rel/dl_relation_manager.cpp +++ b/src/muz/rel/dl_relation_manager.cpp @@ -47,8 +47,8 @@ namespace datalog { void relation_manager::reset() { reset_relations(); - m_favourite_table_plugin = static_cast(0); - m_favourite_relation_plugin = static_cast(0); + m_favourite_table_plugin = static_cast(nullptr); + m_favourite_relation_plugin = static_cast(nullptr); dealloc_ptr_vector_content(m_table_plugins); m_table_plugins.reset(); dealloc_ptr_vector_content(m_relation_plugins); @@ -95,9 +95,9 @@ namespace datalog { } relation_base * relation_manager::try_get_relation(func_decl * pred) const { - relation_base * res = 0; + relation_base * res = nullptr; if(!m_relations.find(pred, res)) { - return 0; + return nullptr; } SASSERT(res); return res; @@ -210,14 +210,12 @@ namespace datalog { if(m_favourite_relation_plugin && m_favourite_relation_plugin->can_handle_signature(s)) { return m_favourite_relation_plugin; } - relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); - relation_plugin_vector::iterator rpend = m_relation_plugins.end(); - for(; rpit!=rpend; ++rpit) { - if((*rpit)->can_handle_signature(s)) { - return *rpit; + for (auto * r : m_relation_plugins) { + if (r->can_handle_signature(s)) { + return r; } } - return 0; + return nullptr; } relation_plugin & relation_manager::get_appropriate_plugin(const relation_signature & s) { @@ -232,14 +230,12 @@ namespace datalog { if (m_favourite_table_plugin && m_favourite_table_plugin->can_handle_signature(t)) { return m_favourite_table_plugin; } - table_plugin_vector::iterator tpit = m_table_plugins.begin(); - table_plugin_vector::iterator tpend = m_table_plugins.end(); - for(; tpit!=tpend; ++tpit) { - if((*tpit)->can_handle_signature(t)) { - return *tpit; + for (auto * a : m_table_plugins) { + if (a->can_handle_signature(t)) { + return a; } } - return 0; + return nullptr; } table_plugin & relation_manager::get_appropriate_plugin(const table_signature & t) { @@ -251,20 +247,18 @@ namespace datalog { } relation_plugin * relation_manager::get_relation_plugin(symbol const& s) { - relation_plugin_vector::iterator rpit = m_relation_plugins.begin(); - relation_plugin_vector::iterator rpend = m_relation_plugins.end(); - for(; rpit!=rpend; ++rpit) { - if((*rpit)->get_name()==s) { - return *rpit; + for (auto* r : m_relation_plugins) { + if(r->get_name() == s) { + return r; } } - return 0; + return nullptr; } relation_plugin & relation_manager::get_relation_plugin(family_id kind) { SASSERT(kind>=0); SASSERT(kindm_key->get_name() << "\n"; - it->m_value->display(out); + for (auto const& kv : m_relations) { + out << "Table " << kv.m_key->get_name() << "\n"; + kv.m_value->display(out); } } void relation_manager::display_relation_sizes(std::ostream & out) const { - relation_map::iterator it=m_relations.begin(); - relation_map::iterator end=m_relations.end(); - for(;it!=end;++it) { - out << "Relation " << it->m_key->get_name() << " has size " - << it->m_value->get_size_estimate_rows() << "\n"; + for (auto const& kv : m_relations) { + out << "Relation " << kv.m_key->get_name() << " has size " + << kv.m_value->get_size_estimate_rows() << "\n"; } } void relation_manager::display_output_tables(rule_set const& rules, std::ostream & out) const { const decl_set & output_preds = rules.get_output_predicates(); - decl_set::iterator it=output_preds.begin(); - decl_set::iterator end=output_preds.end(); - for(; it!=end; ++it) { - func_decl * pred = *it; + for (func_decl * pred : output_preds) { relation_base * rel = try_get_relation(pred); if (!rel) { out << "Tuples in " << pred->get_name() << ": \n"; @@ -538,7 +523,7 @@ namespace datalog { class relation_manager::empty_signature_relation_join_fn : public relation_join_fn { public: - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + relation_base * operator()(const relation_base & r1, const relation_base & r2) override { TRACE("dl", tout << r1.get_plugin().get_name() << " " << r2.get_plugin().get_name() << "\n";); if(r1.get_signature().empty()) { if(r1.empty()) { @@ -612,10 +597,10 @@ namespace datalog { unsigned removed_col_cnt, const unsigned * removed_cols) : m_filter(filter), - m_project(0), + m_project(nullptr), m_removed_cols(removed_col_cnt, removed_cols) {} - virtual relation_base * operator()(const relation_base & t) { + relation_base * operator()(const relation_base & t) override { scoped_rel t1 = t.clone(); (*m_filter)(*t1); if( !m_project) { @@ -658,11 +643,11 @@ namespace datalog { default_relation_apply_sequential_fn(unsigned n, relation_mutator_fn ** mutators): m_mutators(n, mutators) { } - virtual ~default_relation_apply_sequential_fn() { + ~default_relation_apply_sequential_fn() override { std::for_each(m_mutators.begin(), m_mutators.end(), delete_proc()); } - virtual void operator()(relation_base& t) { + void operator()(relation_base& t) override { for (unsigned i = 0; i < m_mutators.size(); ++i) { if (t.empty()) return; (*(m_mutators[i]))(t); @@ -686,9 +671,9 @@ namespace datalog { */ default_relation_join_project_fn(join_fn * join, unsigned removed_col_cnt, const unsigned * removed_cols) - : m_join(join), m_project(0), m_removed_cols(removed_col_cnt, removed_cols) {} + : m_join(join), m_project(nullptr), m_removed_cols(removed_col_cnt, removed_cols) {} - virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { + relation_base * operator()(const relation_base & t1, const relation_base & t2) override { scoped_rel aux = (*m_join)(t1, t2); if(!m_project) { relation_manager & rmgr = aux->get_plugin().get_manager(); @@ -787,7 +772,7 @@ namespace datalog { default_relation_select_equal_and_project_fn(relation_mutator_fn * filter, relation_transformer_fn * project) : m_filter(filter), m_project(project) {} - virtual relation_base * operator()(const relation_base & t1) { + relation_base * operator()(const relation_base & t1) override { TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); scoped_rel aux = t1.clone(); (*m_filter)(*aux); @@ -823,7 +808,7 @@ namespace datalog { default_relation_intersection_filter_fn(relation_join_fn * join_fun, relation_union_fn * union_fun) : m_join_fun(join_fun), m_union_fun(union_fun) {} - virtual void operator()(relation_base & tgt, const relation_base & intersected_obj) { + void operator()(relation_base & tgt, const relation_base & intersected_obj) override { scoped_rel filtered_rel = (*m_join_fun)(tgt, intersected_obj); TRACE("dl", tgt.display(tout << "tgt:\n"); @@ -851,21 +836,21 @@ namespace datalog { scoped_rel join_fun = mk_join_project_fn(tgt, src, joined_col_cnt, tgt_cols, src_cols, join_removed_cols.size(), join_removed_cols.c_ptr(), false); if(!join_fun) { - return 0; + return nullptr; } //we perform the join operation here to see what the result is scoped_rel join_res = (*join_fun)(tgt, src); if(tgt.can_swap(*join_res)) { - return alloc(default_relation_intersection_filter_fn, join_fun.release(), 0); + return alloc(default_relation_intersection_filter_fn, join_fun.release(), nullptr); } if(join_res->get_plugin().is_product_relation()) { //we cannot have the product relation here, since it uses the intersection operation //for unions and therefore we would get into an infinite recursion - return 0; + return nullptr; } scoped_rel union_fun = mk_union_fn(tgt, *join_res); if(!union_fun) { - return 0; + return nullptr; } return alloc(default_relation_intersection_filter_fn, join_fun.release(), union_fun.release()); } @@ -926,7 +911,7 @@ namespace datalog { const unsigned * cols1, const unsigned * cols2) : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), m_col_cnt(col_cnt) {} - virtual table_base * operator()(const table_base & t1, const table_base & t2) { + table_base * operator()(const table_base & t1, const table_base & t2) override { table_plugin * plugin = &t1.get_plugin(); const table_signature & res_sign = get_result_signature(); @@ -1016,11 +1001,8 @@ namespace datalog { SASSERT(plugin.can_handle_signature(res_sign)); table_base * res = plugin.mk_empty(res_sign); - table_base::iterator it = t.begin(); - table_base::iterator end = t.end(); - - for(; it!=end; ++it) { - it->get_fact(m_row); + for (table_base::row_interface& a : t) { + a.get_fact(m_row); modify_fact(m_row); res->add_fact(m_row); } @@ -1037,15 +1019,15 @@ namespace datalog { SASSERT(removed_col_cnt>0); } - virtual const table_signature & get_result_signature() const { + const table_signature & get_result_signature() const override { return convenient_table_project_fn::get_result_signature(); } - virtual void modify_fact(table_fact & f) const { + void modify_fact(table_fact & f) const override { project_out_vector_columns(f, m_removed_cols); } - virtual table_base * operator()(const table_base & t) { + table_base * operator()(const table_base & t) override { return auxiliary_table_transformer_fn::operator()(t); } }; @@ -1054,7 +1036,7 @@ namespace datalog { const table_signature m_empty_sig; public: null_signature_table_project_fn() : m_empty_sig() {} - virtual table_base * operator()(const table_base & t) { + table_base * operator()(const table_base & t) override { relation_manager & m = t.get_plugin().get_manager(); table_base * res = m.mk_empty_table(m_empty_sig); if(!t.empty()) { @@ -1096,14 +1078,14 @@ namespace datalog { m_removed_cols(removed_col_cnt, removed_cols) {} class unreachable_reducer : public table_row_pair_reduce_fn { - virtual void operator()(table_element * func_columns, const table_element * merged_func_columns) { + void operator()(table_element * func_columns, const table_element * merged_func_columns) override { //we do project_with_reduce only if we are sure there will be no reductions //(see code of the table_signature::from_join_project function) UNREACHABLE(); } }; - virtual table_base * operator()(const table_base & t1, const table_base & t2) { + table_base * operator()(const table_base & t1, const table_base & t2) override { table_base * aux = (*m_join)(t1, t2); if(m_project==0) { relation_manager & rmgr = aux->get_plugin().get_manager(); @@ -1154,15 +1136,15 @@ namespace datalog { SASSERT(permutation_cycle_len>=2); } - virtual const table_signature & get_result_signature() const { + const table_signature & get_result_signature() const override { return convenient_table_rename_fn::get_result_signature(); } - virtual void modify_fact(table_fact & f) const { + void modify_fact(table_fact & f) const override { permutate_by_cycle(f, m_cycle); } - virtual table_base * operator()(const table_base & t) { + table_base * operator()(const table_base & t) override { return auxiliary_table_transformer_fn::operator()(t); } @@ -1190,14 +1172,11 @@ namespace datalog { class relation_manager::default_table_union_fn : public table_union_fn { table_fact m_row; public: - virtual void operator()(table_base & tgt, const table_base & src, table_base * delta) { - table_base::iterator it = src.begin(); - table_base::iterator iend = src.end(); + void operator()(table_base & tgt, const table_base & src, table_base * delta) override { + for (table_base::row_interface& a : src) { + a.get_fact(m_row); - for(; it!=iend; ++it) { - it->get_fact(m_row); - - if(delta) { + if (delta) { if(!tgt.contains_fact(m_row)) { tgt.add_new_fact(m_row); delta->add_fact(m_row); @@ -1260,11 +1239,9 @@ namespace datalog { void operator()(table_base & r) { m_to_remove.reset(); unsigned sz = 0; - table_base::iterator it = r.begin(); - table_base::iterator iend = r.end(); - for(; it!=iend; ++it) { - it->get_fact(m_row); - if(should_remove(m_row)) { + for (table_base::row_interface& a : r) { + a.get_fact(m_row); + if (should_remove(m_row)) { m_to_remove.append(m_row.size(), m_row.c_ptr()); ++sz; } @@ -1283,7 +1260,7 @@ namespace datalog { SASSERT(col_cnt>=2); } - virtual bool should_remove(const table_fact & f) const { + bool should_remove(const table_fact & f) const override { table_element val=f[m_identical_cols[0]]; for(unsigned i=1; iget_arg(0); if (!m.is_eq(condition)) { - return 0; + return nullptr; } expr* x = to_app(condition)->get_arg(0); expr* y = to_app(condition)->get_arg(1); @@ -1369,12 +1346,12 @@ namespace datalog { std::swap(x, y); } if (!is_var(x)) { - return 0; + return nullptr; } dl_decl_util decl_util(m); - uint64 value = 0; + uint64_t value = 0; if (!decl_util.is_numeral_ext(y, value)) { - return 0; + return nullptr; } return alloc(default_table_filter_not_equal_fn, ctx, to_var(x)->get_idx(), value); } @@ -1402,7 +1379,7 @@ namespace datalog { m_free_vars(m_condition); } - virtual bool should_remove(const table_fact & f) const { + bool should_remove(const table_fact & f) const override { expr_ref_vector& args = const_cast(m_args); args.reset(); @@ -1410,7 +1387,7 @@ namespace datalog { unsigned col_cnt = f.size(); for(int i=col_cnt-1;i>=0;i--) { if(!m_free_vars.contains(i)) { - args.push_back(0); + args.push_back(nullptr); continue; //this variable does not occur in the condition; } @@ -1425,7 +1402,7 @@ namespace datalog { return m_ast_manager.is_false(ground); } - virtual void operator()(table_base & t) { + void operator()(table_base & t) override { auxiliary_table_filter_fn::operator()(t); } }; @@ -1455,8 +1432,8 @@ namespace datalog { : m_filter(filter), m_condition(condition, ctx.get_manager()), m_removed_cols(removed_col_cnt, removed_cols) {} - virtual table_base* operator()(const table_base & tb) { - table_base *t2 = tb.clone(); + table_base* operator()(const table_base & tb) override { + scoped_rel t2 = tb.clone(); (*m_filter)(*t2); if (!m_project) { relation_manager & rmgr = t2->get_plugin().get_manager(); @@ -1502,11 +1479,11 @@ namespace datalog { default_table_negation_filter_fn(const table_base & tgt, const table_base & neg_t, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * negated_cols) : convenient_table_negation_filter_fn(tgt, neg_t, joined_col_cnt, t_cols, negated_cols), - m_negated_table(0) { + m_negated_table(nullptr) { m_aux_fact.resize(neg_t.get_signature().size()); } - virtual bool should_remove(const table_fact & f) const { + bool should_remove(const table_fact & f) const override { if(!m_all_neg_bound || m_overlap) { table_base::iterator nit = m_negated_table->begin(); table_base::iterator nend = m_negated_table->end(); @@ -1524,7 +1501,7 @@ namespace datalog { } } - virtual void operator()(table_base & tgt, const table_base & negated_table) { + void operator()(table_base & tgt, const table_base & negated_table) override { SASSERT(m_negated_table==0); flet flet_neg_table(m_negated_table, &negated_table); auxiliary_table_filter_fn::operator()(tgt); @@ -1568,12 +1545,11 @@ namespace datalog { default_table_select_equal_and_project_fn(table_mutator_fn * filter, table_transformer_fn * project) : m_filter(filter), m_project(project) {} - virtual table_base * operator()(const table_base & t1) { + table_base * operator()(const table_base & t1) override { TRACE("dl", tout << t1.get_plugin().get_name() << "\n";); scoped_rel aux = t1.clone(); (*m_filter)(*aux); - table_base * res = (*m_project)(*aux); - return res; + return (*m_project)(*aux); } }; @@ -1603,29 +1579,26 @@ namespace datalog { SASSERT(t.get_signature().functional_columns()>0); table_plugin & plugin = t.get_plugin(); m_aux_table = plugin.mk_empty(t.get_signature()); - m_union_fn = plugin.mk_union_fn(t, *m_aux_table, static_cast(0)); + m_union_fn = plugin.mk_union_fn(t, *m_aux_table, static_cast(nullptr)); } - virtual ~default_table_map_fn() {} + ~default_table_map_fn() override {} - virtual void operator()(table_base & t) { + void operator()(table_base & t) override { SASSERT(t.get_signature()==m_aux_table->get_signature()); if(!m_aux_table->empty()) { m_aux_table->reset(); } - - table_base::iterator it = t.begin(); - table_base::iterator iend = t.end(); - for(; it!=iend; ++it) { - it->get_fact(m_curr_fact); + for (table_base::row_interface& a : t) { + a.get_fact(m_curr_fact); if((*m_mapper)(m_curr_fact.c_ptr()+m_first_functional)) { m_aux_table->add_fact(m_curr_fact); } } t.reset(); - (*m_union_fn)(t, *m_aux_table, static_cast(0)); + (*m_union_fn)(t, *m_aux_table, static_cast(nullptr)); } }; @@ -1664,7 +1637,7 @@ namespace datalog { m_former_row.resize(get_result_signature().size()); } - virtual ~default_table_project_with_reduce_fn() {} + ~default_table_project_with_reduce_fn() override {} virtual void modify_fact(table_fact & f) const { unsigned ofs=1; @@ -1693,19 +1666,16 @@ namespace datalog { } } - virtual table_base * operator()(const table_base & t) { + table_base * operator()(const table_base & t) override { table_plugin & plugin = t.get_plugin(); const table_signature & res_sign = get_result_signature(); SASSERT(plugin.can_handle_signature(res_sign)); table_base * res = plugin.mk_empty(res_sign); - table_base::iterator it = t.begin(); - table_base::iterator end = t.end(); - - - for(; it!=end; ++it) { + table_base::iterator it = t.begin(), end = t.end(); + for (; it != end; ++it) { mk_project(it); - if(!res->suggest_fact(m_former_row)) { + if (!res->suggest_fact(m_former_row)) { (*m_reducer)(m_former_row.c_ptr()+m_res_first_functional, m_row.c_ptr()+m_res_first_functional); res->ensure_fact(m_former_row); } diff --git a/src/muz/rel/dl_relation_manager.h b/src/muz/rel/dl_relation_manager.h index c77127eb7..bd7b9ae8c 100644 --- a/src/muz/rel/dl_relation_manager.h +++ b/src/muz/rel/dl_relation_manager.h @@ -109,8 +109,8 @@ namespace datalog { public: relation_manager(context & ctx) : m_context(ctx), - m_favourite_table_plugin(0), - m_favourite_relation_plugin(0), + m_favourite_table_plugin(nullptr), + m_favourite_relation_plugin(nullptr), m_next_table_fid(0), m_next_relation_fid(0) {} @@ -289,7 +289,7 @@ namespace datalog { relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation); relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, - const unsigned_vector permutation) { + const unsigned_vector & permutation) { SASSERT(t.get_signature().size()==permutation.size()); return mk_permutation_rename_fn(t, permutation.c_ptr()); } @@ -331,7 +331,7 @@ namespace datalog { const relation_base * delta); relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src) { - return mk_union_fn(tgt, src, static_cast(0)); + return mk_union_fn(tgt, src, static_cast(nullptr)); } /** @@ -343,7 +343,7 @@ namespace datalog { relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols); - relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, const unsigned_vector identical_cols) { + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, const unsigned_vector & identical_cols) { return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); } @@ -468,7 +468,7 @@ namespace datalog { of column number. */ table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned * permutation); - table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned_vector permutation) { + table_transformer_fn * mk_permutation_rename_fn(const table_base & t, const unsigned_vector & permutation) { SASSERT(t.get_signature().size()==permutation.size()); return mk_permutation_rename_fn(t, permutation.c_ptr()); } @@ -510,7 +510,7 @@ namespace datalog { const table_base * delta); table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src) { - return mk_union_fn(tgt, src, static_cast(0)); + return mk_union_fn(tgt, src, static_cast(nullptr)); } /** @@ -522,7 +522,7 @@ namespace datalog { table_mutator_fn * mk_filter_identical_fn(const table_base & t, unsigned col_cnt, const unsigned * identical_cols); - table_mutator_fn * mk_filter_identical_fn(const table_base & t, const unsigned_vector identical_cols) { + table_mutator_fn * mk_filter_identical_fn(const table_base & t, const unsigned_vector & identical_cols) { return mk_filter_identical_fn(t, identical_cols.size(), identical_cols.c_ptr()); } @@ -609,7 +609,7 @@ namespace datalog { std::string to_nice_string(const relation_element & el) const; /** This one may give a nicer representation of \c el than the - \c to_nice_string(const relation_element & el) function, by unsing the information about the sort + \c to_nice_string(const relation_element & el) function, by using the information about the sort of the element. */ std::string to_nice_string(const relation_sort & s, const relation_element & el) const; diff --git a/src/muz/rel/dl_sieve_relation.cpp b/src/muz/rel/dl_sieve_relation.cpp index 0d70212e2..a5ad20059 100644 --- a/src/muz/rel/dl_sieve_relation.cpp +++ b/src/muz/rel/dl_sieve_relation.cpp @@ -67,7 +67,7 @@ namespace datalog { } relation_base * sieve_relation::complement(func_decl* p) const { - //this is not precisely a complement, because we still treat the ignored collumns as + //this is not precisely a complement, because we still treat the ignored columns as //full, but it should give reasonable results inside the product relation relation_base * new_inner = get_inner().complement(p); return get_plugin().mk_from_inner(get_signature(), m_inner_cols.c_ptr(), new_inner); @@ -226,7 +226,7 @@ namespace datalog { relation_base * sieve_relation_plugin::mk_empty(const relation_signature & s) { UNREACHABLE(); - return 0; + return nullptr; #if 0 svector inner_cols(s.size()); extract_inner_columns(s, inner_cols.c_ptr()); @@ -278,8 +278,8 @@ namespace datalog { m_inner_join_fun(inner_join_fun) { bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); - const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; - const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; + const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : nullptr; + const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : nullptr; if(r1_sieved) { m_result_inner_cols.append(sr1->m_inner_cols); } @@ -294,12 +294,12 @@ namespace datalog { } } - virtual relation_base * operator()(const relation_base & r1, const relation_base & r2) { + relation_base * operator()(const relation_base & r1, const relation_base & r2) override { bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); SASSERT(r1_sieved || r2_sieved); - const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; - const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; + const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : nullptr; + const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : nullptr; const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; @@ -313,12 +313,12 @@ namespace datalog { unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if( &r1.get_plugin()!=this && &r2.get_plugin()!=this ) { //we create just operations that involve the current plugin - return 0; + return nullptr; } bool r1_sieved = r1.get_plugin().is_sieve_relation(); bool r2_sieved = r2.get_plugin().is_sieve_relation(); - const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : 0; - const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : 0; + const sieve_relation * sr1 = r1_sieved ? static_cast(&r1) : nullptr; + const sieve_relation * sr2 = r2_sieved ? static_cast(&r2) : nullptr; const relation_base & inner1 = r1_sieved ? sr1->get_inner() : r1; const relation_base & inner2 = r2_sieved ? sr2->get_inner() : r2; @@ -340,7 +340,7 @@ namespace datalog { relation_join_fn * inner_join_fun = get_manager().mk_join_fn(inner1, inner2, inner_cols1, inner_cols2, false); if(!inner_join_fun) { - return 0; + return nullptr; } return alloc(join_fn, *this, r1, r2, col_cnt, cols1, cols2, inner_join_fun); } @@ -357,7 +357,7 @@ namespace datalog { get_result_signature() = result_sig; } - virtual relation_base * operator()(const relation_base & r0) { + relation_base * operator()(const relation_base & r0) override { SASSERT(r0.get_plugin().is_sieve_relation()); const sieve_relation & r = static_cast(r0); sieve_relation_plugin & plugin = r.get_plugin(); @@ -371,7 +371,7 @@ namespace datalog { relation_transformer_fn * sieve_relation_plugin::mk_project_fn(const relation_base & r0, unsigned col_cnt, const unsigned * removed_cols) { if(&r0.get_plugin()!=this) { - return 0; + return nullptr; } const sieve_relation & r = static_cast(r0); unsigned_vector inner_removed_cols; @@ -398,7 +398,7 @@ namespace datalog { } if(!inner_fun) { - return 0; + return nullptr; } return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); } @@ -406,7 +406,7 @@ namespace datalog { relation_transformer_fn * sieve_relation_plugin::mk_rename_fn(const relation_base & r0, unsigned cycle_len, const unsigned * permutation_cycle) { if(&r0.get_plugin()!=this) { - return 0; + return nullptr; } const sieve_relation & r = static_cast(r0); @@ -428,7 +428,7 @@ namespace datalog { relation_transformer_fn * inner_fun = get_manager().mk_permutation_rename_fn(r.get_inner(), inner_permutation); if(!inner_fun) { - return 0; + return nullptr; } return alloc(transformer_fn, inner_fun, result_sig, result_inner_cols.c_ptr()); } @@ -439,13 +439,13 @@ namespace datalog { public: union_fn(relation_union_fn * union_fun) : m_union_fun(union_fun) {} - virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { + void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) override { bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); bool src_sieved = src.get_plugin().is_sieve_relation(); bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); - sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : 0; - const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; - sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; + sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : nullptr; + const sieve_relation * ssrc = src_sieved ? static_cast(&src) : nullptr; + sieve_relation * sdelta = delta_sieved ? static_cast(delta) : nullptr; relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; @@ -458,15 +458,15 @@ namespace datalog { const relation_base * delta) { if(&tgt.get_plugin()!=this && &src.get_plugin()!=this && (delta && &delta->get_plugin()!=this)) { //we create the operation only if it involves this plugin - return 0; + return nullptr; } bool tgt_sieved = tgt.get_plugin().is_sieve_relation(); bool src_sieved = src.get_plugin().is_sieve_relation(); bool delta_sieved = delta && delta->get_plugin().is_sieve_relation(); - const sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : 0; - const sieve_relation * ssrc = src_sieved ? static_cast(&src) : 0; - const sieve_relation * sdelta = delta_sieved ? static_cast(delta) : 0; + const sieve_relation * stgt = tgt_sieved ? static_cast(&tgt) : nullptr; + const sieve_relation * ssrc = src_sieved ? static_cast(&src) : nullptr; + const sieve_relation * sdelta = delta_sieved ? static_cast(delta) : nullptr; const relation_base & itgt = tgt_sieved ? stgt->get_inner() : tgt; const relation_base & isrc = src_sieved ? ssrc->get_inner() : src; const relation_base * idelta = delta_sieved ? &sdelta->get_inner() : delta; @@ -476,7 +476,7 @@ namespace datalog { if( tgt_sieved && src_sieved && (!delta || delta_sieved) ) { if( !vectors_equal(stgt->m_inner_cols, ssrc->m_inner_cols) || (delta && !vectors_equal(stgt->m_inner_cols, sdelta->m_inner_cols)) ) { - return 0; + return nullptr; } } else { @@ -485,13 +485,13 @@ namespace datalog { || (sdelta && !sdelta->no_sieved_columns()) ) { //We have an unsieved relation and then some relation with some sieved columns, //which means there is an misalignment. - return 0; + return nullptr; } } relation_union_fn * union_fun = get_manager().mk_union_fn(itgt, isrc, idelta); if(!union_fun) { - return 0; + return nullptr; } return alloc(union_fn, union_fun); @@ -504,7 +504,7 @@ namespace datalog { filter_fn(relation_mutator_fn * inner_fun) : m_inner_fun(inner_fun) {} - virtual void operator()(relation_base & r0) { + void operator()(relation_base & r0) override { SASSERT(r0.get_plugin().is_sieve_relation()); sieve_relation & r = static_cast(r0); @@ -515,7 +515,7 @@ namespace datalog { relation_mutator_fn * sieve_relation_plugin::mk_filter_identical_fn(const relation_base & r0, unsigned col_cnt, const unsigned * identical_cols) { if(&r0.get_plugin()!=this) { - return 0; + return nullptr; } const sieve_relation & r = static_cast(r0); unsigned_vector inner_icols; @@ -534,7 +534,7 @@ namespace datalog { relation_mutator_fn * inner_fun = get_manager().mk_filter_identical_fn(r.get_inner(), inner_icols); if(!inner_fun) { - return 0; + return nullptr; } return alloc(filter_fn, inner_fun); } @@ -542,7 +542,7 @@ namespace datalog { relation_mutator_fn * sieve_relation_plugin::mk_filter_equal_fn(const relation_base & r0, const relation_element & value, unsigned col) { if(&r0.get_plugin()!=this) { - return 0; + return nullptr; } const sieve_relation & r = static_cast(r0); if(!r.is_inner_col(col)) { @@ -553,7 +553,7 @@ namespace datalog { relation_mutator_fn * inner_fun = get_manager().mk_filter_equal_fn(r.get_inner(), value, inner_col); if(!inner_fun) { - return 0; + return nullptr; } return alloc(filter_fn, inner_fun); } @@ -561,7 +561,7 @@ namespace datalog { relation_mutator_fn * sieve_relation_plugin::mk_filter_interpreted_fn(const relation_base & rb, app * condition) { if(&rb.get_plugin()!=this) { - return 0; + return nullptr; } ast_manager & m = get_ast_manager(); const sieve_relation & r = static_cast(rb); @@ -589,7 +589,7 @@ namespace datalog { relation_mutator_fn * inner_fun = get_manager().mk_filter_interpreted_fn(r.get_inner(), to_app(inner_cond)); if(!inner_fun) { - return 0; + return nullptr; } return alloc(filter_fn, inner_fun); } @@ -600,12 +600,12 @@ namespace datalog { negation_filter_fn(relation_intersection_filter_fn * inner_fun) : m_inner_fun(inner_fun) {} - virtual void operator()(relation_base & r, const relation_base & neg) { + void operator()(relation_base & r, const relation_base & neg) override { bool r_sieved = r.get_plugin().is_sieve_relation(); bool neg_sieved = neg.get_plugin().is_sieve_relation(); SASSERT(r_sieved || neg_sieved); - sieve_relation * sr = r_sieved ? static_cast(&r) : 0; - const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; + sieve_relation * sr = r_sieved ? static_cast(&r) : nullptr; + const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : nullptr; relation_base & inner_r = r_sieved ? sr->get_inner() : r; const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; @@ -618,13 +618,13 @@ namespace datalog { const unsigned * neg_cols) { if(&r.get_plugin()!=this && &neg.get_plugin()!=this) { //we create just operations that involve the current plugin - return 0; + return nullptr; } bool r_sieved = r.get_plugin().is_sieve_relation(); bool neg_sieved = neg.get_plugin().is_sieve_relation(); SASSERT(r_sieved || neg_sieved); - const sieve_relation * sr = r_sieved ? static_cast(&r) : 0; - const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : 0; + const sieve_relation * sr = r_sieved ? static_cast(&r) : nullptr; + const sieve_relation * sneg = neg_sieved ? static_cast(&neg) : nullptr; const relation_base & inner_r = r_sieved ? sr->get_inner() : r; const relation_base & inner_neg = neg_sieved ? sneg->get_inner() : neg; @@ -657,7 +657,7 @@ namespace datalog { relation_intersection_filter_fn * inner_fun = get_manager().mk_filter_by_negation_fn(inner_r, inner_neg, ir_cols, ineg_cols); if(!inner_fun) { - return 0; + return nullptr; } return alloc(negation_filter_fn, inner_fun); } diff --git a/src/muz/rel/dl_sieve_relation.h b/src/muz/rel/dl_sieve_relation.h index 021c9d8df..5f20cecb4 100644 --- a/src/muz/rel/dl_sieve_relation.h +++ b/src/muz/rel/dl_sieve_relation.h @@ -85,7 +85,7 @@ namespace datalog { sieve_relation_plugin(relation_manager & manager); - virtual void initialize(family_id fid); + void initialize(family_id fid) override; family_id get_relation_kind(const relation_signature & sig, const bool * inner_columns, family_id inner_kind); @@ -95,20 +95,20 @@ namespace datalog { return get_relation_kind(sig, inner_columns.c_ptr(), inner_kind); } - virtual bool can_handle_signature(const relation_signature & s); + bool can_handle_signature(const relation_signature & s) override; - virtual relation_base * mk_empty(const relation_signature & s); + relation_base * mk_empty(const relation_signature & s) override; sieve_relation * mk_empty(const sieve_relation & original); - virtual relation_base * mk_empty(const relation_base & original); - virtual relation_base * mk_empty(const relation_signature & s, family_id kind); + relation_base * mk_empty(const relation_base & original) override; + relation_base * mk_empty(const relation_signature & s, family_id kind) override; sieve_relation * mk_empty(const relation_signature & s, relation_plugin & inner_plugin); - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + relation_base * mk_full(func_decl* p, const relation_signature & s) override; sieve_relation * full(func_decl* p, const relation_signature & s, relation_plugin & inner_plugin); sieve_relation * mk_from_inner(const relation_signature & s, const bool * inner_columns, relation_base * inner_rel); - sieve_relation * mk_from_inner(const relation_signature & s, const svector inner_columns, + sieve_relation * mk_from_inner(const relation_signature & s, const svector & inner_columns, relation_base * inner_rel) { SASSERT(inner_columns.size()==s.size()); return mk_from_inner(s, inner_columns.c_ptr(), inner_rel); @@ -116,22 +116,22 @@ namespace datalog { protected: - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; + relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) override; }; @@ -176,18 +176,18 @@ namespace datalog { relation_base & get_inner() { return *m_inner; } const relation_base & get_inner() const { return *m_inner; } - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual sieve_relation * clone() const; - virtual relation_base * complement(func_decl*p) const; - virtual void to_formula(expr_ref& fml) const; + void add_fact(const relation_fact & f) override; + bool contains_fact(const relation_fact & f) const override; + sieve_relation * clone() const override; + relation_base * complement(func_decl*p) const override; + void to_formula(expr_ref& fml) const override; - virtual bool empty() const { return get_inner().empty(); } - virtual void reset() { get_inner().reset(); } - virtual unsigned get_size_estimate_rows() const { return get_inner().get_size_estimate_rows(); } - virtual unsigned get_size_estimate_bytes() const { return get_inner().get_size_estimate_bytes(); } + bool empty() const override { return get_inner().empty(); } + void reset() override { get_inner().reset(); } + unsigned get_size_estimate_rows() const override { return get_inner().get_size_estimate_rows(); } + unsigned get_size_estimate_bytes() const override { return get_inner().get_size_estimate_bytes(); } - virtual void display(std::ostream & out) const; + void display(std::ostream & out) const override; }; diff --git a/src/muz/rel/dl_sparse_table.cpp b/src/muz/rel/dl_sparse_table.cpp index 6048f358b..bb48211c7 100644 --- a/src/muz/rel/dl_sparse_table.cpp +++ b/src/muz/rel/dl_sparse_table.cpp @@ -93,7 +93,7 @@ namespace datalog { // // ----------------------------------- - unsigned get_domain_length(uint64 dom_size) { + unsigned get_domain_length(uint64_t dom_size) { SASSERT(dom_size>0); unsigned length = 0; @@ -128,7 +128,7 @@ namespace datalog { unsigned sig_sz = sig.size(); unsigned first_functional = sig_sz-m_functional_col_cnt; for (unsigned i=0; i0); SASSERT(length<=64); @@ -198,7 +198,7 @@ namespace datalog { row_interface(t), m_parent(parent) {} - virtual table_element operator[](unsigned col) const { + table_element operator[](unsigned col) const override { return m_parent.m_layout.get(m_parent.m_ptr, col); } @@ -218,15 +218,15 @@ namespace datalog { m_row_obj(t, *this), m_layout(t.m_column_layout) {} - virtual bool is_finished() const { + bool is_finished() const override { return m_ptr == m_end; } - virtual row_interface & operator*() { + row_interface & operator*() override { SASSERT(!is_finished()); return m_row_obj; } - virtual void operator++() { + void operator++() override { SASSERT(!is_finished()); m_ptr+=m_fact_size; } @@ -257,8 +257,8 @@ namespace datalog { \brief Empty result. */ query_result() : m_singleton(false) { - m_many.begin = 0; - m_many.end = 0; + m_many.begin = nullptr; + m_many.end = nullptr; } query_result(offset_iterator begin, offset_iterator end) : m_singleton(false) { m_many.begin = begin; @@ -312,7 +312,7 @@ namespace datalog { m_keys(key_len*sizeof(table_element)), m_first_nonindexed(0) {} - virtual void update(const sparse_table & t) { + void update(const sparse_table & t) override { if (m_first_nonindexed == t.m_data.after_last_offset()) { return; } @@ -327,7 +327,7 @@ namespace datalog { key_value key; key.resize(key_len); - offset_vector * index_entry = 0; + offset_vector * index_entry = nullptr; bool key_modified = true; for (; ofs!=after_last; ofs+=t.m_fact_size) { @@ -351,7 +351,7 @@ namespace datalog { m_first_nonindexed = t.m_data.after_last_offset(); } - virtual query_result get_matching_offsets(const key_value & key) const { + query_result get_matching_offsets(const key_value & key) const override { key_to_reserve(key); store_offset ofs; if (!m_keys.find_reserve_content(ofs)) { @@ -406,9 +406,9 @@ namespace datalog { m_key_fact.resize(t.get_signature().size()); } - virtual ~full_signature_key_indexer() {} + ~full_signature_key_indexer() override {} - virtual query_result get_matching_offsets(const key_value & key) const { + query_result get_matching_offsets(const key_value & key) const override { unsigned key_len = m_key_cols.size(); for (unsigned i=0; iget_data().m_value) { if (full_signature_key_indexer::can_handle(key_len, key_cols, *this)) { key_map_entry->get_data().m_value = alloc(full_signature_key_indexer, key_len, key_cols, *this); @@ -777,9 +777,9 @@ namespace datalog { const table_signature & sig = t->get_signature(); t->reset(); - table_pool::entry * e = m_pool.insert_if_not_there2(sig, 0); + table_pool::entry * e = m_pool.insert_if_not_there2(sig, nullptr); sp_table_vector * & vect = e->get_data().m_value; - if (vect == 0) { + if (vect == nullptr) { vect = alloc(sp_table_vector); } IF_VERBOSE(12, verbose_stream() << "Recycle: " << t->get_size_estimate_bytes() << "\n";); @@ -826,7 +826,7 @@ namespace datalog { m_removed_cols.push_back(UINT_MAX); } - virtual table_base * operator()(const table_base & tb1, const table_base & tb2) { + table_base * operator()(const table_base & tb1, const table_base & tb2) override { const sparse_table & t1 = get(tb1); const sparse_table & t2 = get(tb2); @@ -859,9 +859,9 @@ namespace datalog { if (t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind() || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { //We also don't allow indexes on functional columns (and they are needed for joins) - return 0; + return nullptr; } - return mk_join_project_fn(t1, t2, col_cnt, cols1, cols2, 0, static_cast(0)); + return mk_join_project_fn(t1, t2, col_cnt, cols1, cols2, 0, static_cast(nullptr)); } table_join_fn * sparse_table_plugin::mk_join_project_fn(const table_base & t1, const table_base & t2, @@ -874,7 +874,7 @@ namespace datalog { || join_involves_functional(sig1, sig2, col_cnt, cols1, cols2)) { //We don't allow sparse tables with zero signatures (and project on all columns leads to such) //We also don't allow indexes on functional columns. - return 0; + return nullptr; } return alloc(join_project_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2, removed_col_cnt, removed_cols); @@ -882,7 +882,7 @@ namespace datalog { class sparse_table_plugin::union_fn : public table_union_fn { public: - virtual void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) { + void operator()(table_base & tgt0, const table_base & src0, table_base * delta0) override { verbose_action _va("union"); sparse_table & tgt = get(tgt0); const sparse_table & src = get(src0); @@ -905,7 +905,7 @@ namespace datalog { || (delta && delta->get_kind()!=get_kind()) || tgt.get_signature()!=src.get_signature() || (delta && delta->get_signature()!=tgt.get_signature())) { - return 0; + return nullptr; } return alloc(union_fn); } @@ -941,7 +941,7 @@ namespace datalog { SASSERT(r_idx == m_removed_col_cnt); } - virtual table_base * operator()(const table_base & tb) { + table_base * operator()(const table_base & tb) override { verbose_action _va("project"); const sparse_table & t = get(tb); @@ -969,7 +969,7 @@ namespace datalog { table_transformer_fn * sparse_table_plugin::mk_project_fn(const table_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (col_cnt == t.get_signature().size()) { - return 0; + return nullptr; } return alloc(project_fn, t.get_signature(), col_cnt, removed_cols); } @@ -985,7 +985,7 @@ namespace datalog { m_key.push_back(val); } - virtual table_base * operator()(const table_base & tb) { + table_base * operator()(const table_base & tb) override { verbose_action _va("select_equal_and_project"); const sparse_table & t = get(tb); @@ -1032,7 +1032,7 @@ namespace datalog { //column table produces one). //We also don't allow indexes on functional columns. And our implementation of //select_equal_and_project uses index on \c col. - return 0; + return nullptr; } return alloc(select_equal_and_project_fn, t.get_signature(), value, col); } @@ -1072,7 +1072,7 @@ namespace datalog { } } - virtual table_base * operator()(const table_base & tb) { + table_base * operator()(const table_base & tb) override { verbose_action _va("rename"); const sparse_table & t = get(tb); @@ -1113,7 +1113,7 @@ namespace datalog { table_transformer_fn * sparse_table_plugin::mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { if (t.get_kind()!=get_kind()) { - return 0; + return nullptr; } return alloc(rename_fn, t.get_signature(), permutation_cycle_len, permutation_cycle); } @@ -1210,7 +1210,7 @@ namespace datalog { } } - virtual void operator()(table_base & tgt0, const table_base & neg0) { + void operator()(table_base & tgt0, const table_base & neg0) override { sparse_table & tgt = get(tgt0); const sparse_table & neg = get(neg0); @@ -1252,7 +1252,7 @@ namespace datalog { if (!check_kind(t) || !check_kind(negated_obj) || join_involves_functional(t.get_signature(), negated_obj.get_signature(), joined_col_cnt, t_cols, negated_cols) ) { - return 0; + return nullptr; } return alloc(negation_filter_fn, t, negated_obj, joined_col_cnt, t_cols, negated_cols); } @@ -1310,7 +1310,7 @@ namespace datalog { m_s2_cols.append(src2_cols); } - virtual void operator()(table_base & _t, const table_base & _s1, const table_base& _s2) { + void operator()(table_base & _t, const table_base & _s1, const table_base& _s2) override { verbose_action _va("negated_join"); sparse_table& t = get(_t); @@ -1394,7 +1394,7 @@ namespace datalog { return alloc(negated_join_fn, src1, t_cols, src_cols, src1_cols, src2_cols); } else { - return 0; + return nullptr; } } diff --git a/src/muz/rel/dl_sparse_table.h b/src/muz/rel/dl_sparse_table.h index a699cf165..43a967729 100644 --- a/src/muz/rel/dl_sparse_table.h +++ b/src/muz/rel/dl_sparse_table.h @@ -69,39 +69,39 @@ namespace datalog { typedef sparse_table table; sparse_table_plugin(relation_manager & manager); - ~sparse_table_plugin(); + ~sparse_table_plugin() override; - virtual bool can_handle_signature(const table_signature & s) + bool can_handle_signature(const table_signature & s) override { return s.size()>0; } - virtual table_base * mk_empty(const table_signature & s); + table_base * mk_empty(const table_signature & s) override; sparse_table * mk_clone(const sparse_table & t); protected: - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols); - virtual table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, - const table_base * delta); - virtual table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, - const table_element & value, unsigned col); - virtual table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, - const table_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - virtual table_intersection_join_filter_fn* mk_filter_by_negated_join_fn( - const table_base & t, - const table_base & src1, - const table_base & src2, + table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + table_join_fn * mk_join_project_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) override; + table_union_fn * mk_union_fn(const table_base & tgt, const table_base & src, + const table_base * delta) override; + table_transformer_fn * mk_project_fn(const table_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + table_transformer_fn * mk_rename_fn(const table_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + table_transformer_fn * mk_select_equal_and_project_fn(const table_base & t, + const table_element & value, unsigned col) override; + table_intersection_filter_fn * mk_filter_by_negation_fn(const table_base & t, + const table_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) override; + table_intersection_join_filter_fn* mk_filter_by_negated_join_fn( + const table_base & t, + const table_base & src1, + const table_base & src2, unsigned_vector const& t_cols, unsigned_vector const& src_cols, unsigned_vector const& src1_cols, - unsigned_vector const& src2_cols); + unsigned_vector const& src2_cols) override; static sparse_table const& get(table_base const&); static sparse_table& get(table_base&); @@ -153,7 +153,7 @@ namespace datalog { variable. Otherwise \c m_reserve==NO_RESERVE. The size of m_data is actually 8 bytes larger than stated in m_data_size, so that we may - deref an uint64 pointer at the end of the array. + deref an uint64_t pointer at the end of the array. */ storage m_data; storage_indexer m_data_indexer; @@ -290,10 +290,10 @@ namespace datalog { //the following two operations allow breaking of the object invariant! void resize_data(size_t sz) { m_data_size = sz; - if (sz + sizeof(uint64) < sz) { + if (sz + sizeof(uint64_t) < sz) { throw default_exception("overflow resizing data section for sparse table"); } - m_data.resize(sz + sizeof(uint64)); + m_data.resize(sz + sizeof(uint64_t)); } bool insert_offset(store_offset ofs) { @@ -321,34 +321,34 @@ namespace datalog { class column_info { unsigned m_big_offset; unsigned m_small_offset; - uint64 m_mask; - uint64 m_write_mask; + uint64_t m_mask; + uint64_t m_write_mask; public: unsigned m_offset; //!< in bits unsigned m_length; //!< in bits - - column_info(unsigned offset, unsigned length) \ - : m_big_offset(offset/8), - m_small_offset(offset%8), - m_mask( length==64 ? ULLONG_MAX : (static_cast(1)<(1)<(rec+m_big_offset); - uint64 res = *ptr; - res>>=m_small_offset; - res&=m_mask; + const uint64_t * ptr = reinterpret_cast(rec + m_big_offset); + uint64_t res = *ptr; + res >>= m_small_offset; + res &= m_mask; return res; } void set(char * rec, table_element val) const { SASSERT( (val&~m_mask)==0 ); //the value fits into the column - uint64 * ptr = reinterpret_cast(rec+m_big_offset); - *ptr&=m_write_mask; - *ptr|=val<(rec + m_big_offset); + *ptr &= m_write_mask; + *ptr |= val << m_small_offset; } unsigned next_ofs() const { return m_offset+m_length; } }; @@ -424,7 +424,7 @@ namespace datalog { /** \c array \c removed_cols contains column indexes to be removed in ascending order and - is terminated by a number greated than the highest column index of a join the the two tables. + is terminated by a number greater than the highest column index of a join the two tables. This is to simplify the traversal of the array when building facts. */ static void concatenate_rows(const column_layout & layout1, const column_layout & layout2, @@ -436,7 +436,7 @@ namespace datalog { columns from t2 using indexing. \c array \c removed_cols contains column indexes to be removed in ascending order and - is terminated by a number greated than the highest column index of a join the the two tables. + is terminated by a number greater than the highest column index of a join the two tables. This is to simplify the traversal of the array when building facts. \c tables_swapped value means that the resulting facts should contain facts from t2 first, @@ -463,10 +463,10 @@ namespace datalog { sparse_table(sparse_table_plugin & p, const table_signature & sig, unsigned init_capacity=0); sparse_table(const sparse_table & t); - virtual ~sparse_table(); + ~sparse_table() override; public: - virtual void deallocate() { + void deallocate() override { get_plugin().recycle(this); } @@ -475,22 +475,22 @@ namespace datalog { sparse_table_plugin & get_plugin() const { return static_cast(table_base::get_plugin()); } - virtual bool empty() const { return row_count()==0; } - virtual void add_fact(const table_fact & f); - virtual bool contains_fact(const table_fact & f) const; - virtual bool fetch_fact(table_fact & f) const; - virtual void ensure_fact(const table_fact & f); - virtual void remove_fact(const table_element* fact); - virtual void reset(); + bool empty() const override { return row_count()==0; } + void add_fact(const table_fact & f) override; + bool contains_fact(const table_fact & f) const override; + bool fetch_fact(table_fact & f) const override; + void ensure_fact(const table_fact & f) override; + void remove_fact(const table_element* fact) override; + void reset() override; - virtual table_base * clone() const; + table_base * clone() const override; - virtual table_base::iterator begin() const; - virtual table_base::iterator end() const; + table_base::iterator begin() const override; + table_base::iterator end() const override; - virtual unsigned get_size_estimate_rows() const { return row_count(); } - virtual unsigned get_size_estimate_bytes() const; - virtual bool knows_exact_size() const { return true; } + unsigned get_size_estimate_rows() const override { return row_count(); } + unsigned get_size_estimate_bytes() const override; + bool knows_exact_size() const override { return true; } }; }; diff --git a/src/muz/rel/dl_table.cpp b/src/muz/rel/dl_table.cpp index 1c068a878..71ffd88f3 100644 --- a/src/muz/rel/dl_table.cpp +++ b/src/muz/rel/dl_table.cpp @@ -43,7 +43,7 @@ namespace datalog { : convenient_table_join_fn(t1_sig, t2_sig, col_cnt, cols1, cols2), m_joined_col_cnt(col_cnt) {} - virtual table_base * operator()(const table_base & t1, const table_base & t2) { + table_base * operator()(const table_base & t1, const table_base & t2) override { const hashtable_table & ht1 = static_cast(t1); const hashtable_table & ht2 = static_cast(t2); @@ -89,7 +89,7 @@ namespace datalog { table_join_fn * hashtable_table_plugin::mk_join_fn(const table_base & t1, const table_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if(t1.get_kind()!=get_kind() || t2.get_kind()!=get_kind()) { - return 0; + return nullptr; } return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } @@ -105,10 +105,10 @@ namespace datalog { public: our_row(const our_iterator_core & parent) : row_interface(parent.m_parent), m_parent(parent) {} - virtual void get_fact(table_fact & result) const { + void get_fact(table_fact & result) const override { result = *m_parent.m_inner; } - virtual table_element operator[](unsigned col) const { + table_element operator[](unsigned col) const override { return (*m_parent.m_inner)[col]; } @@ -121,15 +121,15 @@ namespace datalog { m_parent(t), m_inner(finished ? t.m_data.end() : t.m_data.begin()), m_end(t.m_data.end()), m_row_obj(*this) {} - virtual bool is_finished() const { + bool is_finished() const override { return m_inner==m_end; } - virtual row_interface & operator*() { + row_interface & operator*() override { SASSERT(!is_finished()); return m_row_obj; } - virtual void operator++() { + void operator++() override { SASSERT(!is_finished()); ++m_inner; } @@ -192,7 +192,7 @@ namespace datalog { const bv_iterator& m_parent; public: our_row(const bv_iterator & p) : caching_row_interface(p.m_bv), m_parent(p) {} - virtual void get_fact(table_fact& result) const { + void get_fact(table_fact& result) const override { if (result.size() < size()) { result.resize(size(), 0); } @@ -210,15 +210,15 @@ namespace datalog { } } - virtual bool is_finished() const { + bool is_finished() const override { return m_offset == m_bv.m_bv.size(); } - virtual row_interface & operator*() { + row_interface & operator*() override { SASSERT(!is_finished()); return m_row_obj; } - virtual void operator++() { + void operator++() override { SASSERT(!is_finished()); ++m_offset; while (!is_finished() && !m_bv.m_bv.get(m_offset)) { diff --git a/src/muz/rel/dl_table.h b/src/muz/rel/dl_table.h index c428191af..168108e65 100644 --- a/src/muz/rel/dl_table.h +++ b/src/muz/rel/dl_table.h @@ -61,10 +61,10 @@ namespace datalog { hashtable_table_plugin(relation_manager & manager) : table_plugin(symbol("hashtable"), manager) {} - virtual table_base * mk_empty(const table_signature & s); + table_base * mk_empty(const table_signature & s) override; - virtual table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); + table_join_fn * mk_join_fn(const table_base & t1, const table_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; }; class hashtable_table : public table_base { @@ -84,23 +84,23 @@ namespace datalog { hashtable_table_plugin & get_plugin() const { return static_cast(table_base::get_plugin()); } - virtual void add_fact(const table_fact & f) { + void add_fact(const table_fact & f) override { m_data.insert(f); } - virtual void remove_fact(const table_element* fact) { + void remove_fact(const table_element* fact) override { table_fact f(get_signature().size(), fact); m_data.remove(f); } - virtual bool contains_fact(const table_fact & f) const { + bool contains_fact(const table_fact & f) const override { return m_data.contains(f); } - virtual iterator begin() const; - virtual iterator end() const; + iterator begin() const override; + iterator end() const override; - virtual unsigned get_size_estimate_rows() const { return m_data.size(); } - virtual unsigned get_size_estimate_bytes() const { return m_data.size()*get_signature().size()*8; } - virtual bool knows_exact_size() const { return true; } + unsigned get_size_estimate_rows() const override { return m_data.size(); } + unsigned get_size_estimate_bytes() const override { return m_data.size()*get_signature().size()*8; } + bool knows_exact_size() const override { return true; } }; // ----------------------------------- @@ -118,9 +118,9 @@ namespace datalog { bitvector_table_plugin(relation_manager & manager) : table_plugin(symbol("bitvector"), manager) {} - virtual bool can_handle_signature(const table_signature & s); + bool can_handle_signature(const table_signature & s) override; - virtual table_base * mk_empty(const table_signature & s); + table_base * mk_empty(const table_signature & s) override; }; class bitvector_table : public table_base { @@ -137,11 +137,11 @@ namespace datalog { bitvector_table(bitvector_table_plugin & plugin, const table_signature & sig); public: - virtual void add_fact(const table_fact & f); - virtual void remove_fact(const table_element* fact); - virtual bool contains_fact(const table_fact & f) const; - virtual iterator begin() const; - virtual iterator end() const; + void add_fact(const table_fact & f) override; + void remove_fact(const table_element* fact) override; + bool contains_fact(const table_fact & f) const override; + iterator begin() const override; + iterator end() const override; }; diff --git a/src/muz/rel/dl_table_relation.cpp b/src/muz/rel/dl_table_relation.cpp index d8f1c7314..de55998f8 100644 --- a/src/muz/rel/dl_table_relation.cpp +++ b/src/muz/rel/dl_table_relation.cpp @@ -48,7 +48,7 @@ namespace datalog { relation_base * table_relation_plugin::mk_empty(const relation_signature & s) { table_signature tsig; if (!get_manager().relation_signature_to_table(s, tsig)) { - return 0; + return nullptr; } table_base * t = m_table_plugin.mk_empty(tsig); return alloc(table_relation, *this, s, t); @@ -57,7 +57,7 @@ namespace datalog { relation_base * table_relation_plugin::mk_full_relation(const relation_signature & s, func_decl* p, family_id kind) { table_signature tsig; if(!get_manager().relation_signature_to_table(s, tsig)) { - return 0; + return nullptr; } table_base * t = m_table_plugin.mk_full(p, tsig, kind); return alloc(table_relation, *this, s, t); @@ -82,7 +82,7 @@ namespace datalog { : convenient_relation_join_project_fn(s1, s2, col_cnt, cols1, cols2, removed_col_cnt, removed_cols), m_tfun(tfun) {} - virtual relation_base * operator()(const relation_base & t1, const relation_base & t2) { + relation_base * operator()(const relation_base & t1, const relation_base & t2) override { SASSERT(t1.from_table()); SASSERT(t2.from_table()); table_relation_plugin & plugin = static_cast(t1.get_plugin()); @@ -108,25 +108,25 @@ namespace datalog { relation_join_fn * table_relation_plugin::mk_join_fn(const relation_base & r1, const relation_base & r2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if(!r1.from_table() || !r2.from_table()) { - return 0; + return nullptr; } const table_relation & tr1 = static_cast(r1); const table_relation & tr2 = static_cast(r2); table_join_fn * tfun = get_manager().mk_join_fn(tr1.get_table(), tr2.get_table(), col_cnt, cols1, cols2); if(!tfun) { - return 0; + return nullptr; } return alloc(tr_join_project_fn, r1.get_signature(), r2.get_signature(), col_cnt, cols1, - cols2, 0, static_cast(0), tfun); + cols2, 0, static_cast(nullptr), tfun); } relation_join_fn * table_relation_plugin::mk_join_project_fn(const relation_base & r1, const relation_base & r2, unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if(!r1.from_table() || !r2.from_table()) { - return 0; + return nullptr; } const table_relation & tr1 = static_cast(r1); const table_relation & tr2 = static_cast(r2); @@ -146,7 +146,7 @@ namespace datalog { tr_transformer_fn(const relation_signature & rsig, table_transformer_fn * tfun) : m_tfun(tfun) { get_result_signature() = rsig; } - virtual relation_base * operator()(const relation_base & t) { + relation_base * operator()(const relation_base & t) override { SASSERT(t.from_table()); table_relation_plugin & plugin = static_cast(t.get_plugin()); @@ -168,7 +168,7 @@ namespace datalog { relation_transformer_fn * table_relation_plugin::mk_project_fn(const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { if(!t.from_table()) { - return 0; + return nullptr; } const table_relation & tr = static_cast(t); @@ -184,7 +184,7 @@ namespace datalog { relation_transformer_fn * table_relation_plugin::mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, const unsigned * permutation_cycle) { if(!t.from_table()) { - return 0; + return nullptr; } const table_relation & tr = static_cast(t); @@ -200,7 +200,7 @@ namespace datalog { relation_transformer_fn * table_relation_plugin::mk_permutation_rename_fn(const relation_base & t, const unsigned * permutation) { if(!t.from_table()) { - return 0; + return nullptr; } const table_relation & tr = static_cast(t); @@ -216,7 +216,7 @@ namespace datalog { relation_transformer_fn * table_relation_plugin::mk_select_equal_and_project_fn(const relation_base & t, const relation_element & value, unsigned col) { if(!t.from_table()) { - return 0; + return nullptr; } const table_relation & tr = static_cast(t); @@ -235,12 +235,12 @@ namespace datalog { by iterating through the table and calling \c add_fact of the target relation. */ class table_relation_plugin::universal_target_union_fn : public relation_union_fn { - virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { + void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) override { SASSERT(src.from_table()); const table_relation & tr_src = static_cast(src); relation_manager & rmgr = tr_src.get_manager(); - relation_signature sig = tr_src.get_signature(); + const relation_signature & sig = tr_src.get_signature(); SASSERT(tgt.get_signature()==sig); SASSERT(!delta || delta->get_signature()==sig); @@ -271,7 +271,7 @@ namespace datalog { public: tr_union_fn(table_union_fn * tfun) : m_tfun(tfun) {} - virtual void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) { + void operator()(relation_base & tgt, const relation_base & src, relation_base * delta) override { SASSERT(tgt.from_table()); SASSERT(src.from_table()); SASSERT(!delta || delta->from_table()); @@ -280,7 +280,7 @@ namespace datalog { const table_relation & tr_src = static_cast(src); table_relation * tr_delta = static_cast(delta); - (*m_tfun)(tr_tgt.get_table(), tr_src.get_table(), tr_delta ? &tr_delta->get_table() : 0); + (*m_tfun)(tr_tgt.get_table(), tr_src.get_table(), tr_delta ? &tr_delta->get_table() : nullptr); TRACE("dl_table_relation", tout << "# union => "; tr_tgt.get_table().display(tout);); } @@ -289,7 +289,7 @@ namespace datalog { relation_union_fn * table_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if(!src.from_table()) { - return 0; + return nullptr; } if(!tgt.from_table() || (delta && !delta->from_table())) { return alloc(universal_target_union_fn); @@ -299,7 +299,7 @@ namespace datalog { const table_relation * tr_delta = static_cast(delta); table_union_fn * tfun = get_manager().mk_union_fn(tr_tgt.get_table(), tr_src.get_table(), - tr_delta ? &tr_delta->get_table() : 0); + tr_delta ? &tr_delta->get_table() : nullptr); SASSERT(tfun); return alloc(tr_union_fn, tfun); @@ -311,7 +311,7 @@ namespace datalog { public: tr_mutator_fn(table_mutator_fn * tfun) : m_tfun(tfun) {} - virtual void operator()(relation_base & r) { + void operator()(relation_base & r) override { SASSERT(r.from_table()); table_relation & tr = static_cast(r); (*m_tfun)(tr.get_table()); @@ -322,7 +322,7 @@ namespace datalog { relation_mutator_fn * table_relation_plugin::mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(!t.from_table()) { - return 0; + return nullptr; } const table_relation & tr = static_cast(t); @@ -334,7 +334,7 @@ namespace datalog { relation_mutator_fn * table_relation_plugin::mk_filter_equal_fn(const relation_base & t, const relation_element & value, unsigned col) { if(!t.from_table()) { - return 0; + return nullptr; } const table_relation & tr = static_cast(t); @@ -349,7 +349,7 @@ namespace datalog { relation_mutator_fn * table_relation_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { bool condition_needs_transforming = false; if(!t.from_table() || condition_needs_transforming) { - return 0; + return nullptr; } const table_relation & tr = static_cast(t); table_mutator_fn * tfun = get_manager().mk_filter_interpreted_fn(tr.get_table(), condition); @@ -360,7 +360,7 @@ namespace datalog { relation_transformer_fn * table_relation_plugin::mk_filter_interpreted_and_project_fn(const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { if (!t.from_table()) - return 0; + return nullptr; const table_relation & tr = static_cast(t); table_transformer_fn * tfun = get_manager().mk_filter_interpreted_and_project_fn(tr.get_table(), @@ -377,7 +377,7 @@ namespace datalog { public: tr_intersection_filter_fn(table_intersection_filter_fn * tfun) : m_tfun(tfun) {} - virtual void operator()(relation_base & r, const relation_base & src) { + void operator()(relation_base & r, const relation_base & src) override { SASSERT(r.from_table()); SASSERT(src.from_table()); @@ -392,14 +392,14 @@ namespace datalog { relation_intersection_filter_fn * table_relation_plugin::mk_filter_by_intersection_fn(const relation_base & r, const relation_base & src, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * src_cols) { if(!r.from_table() || !src.from_table()) { - return 0; + return nullptr; } const table_relation & tr = static_cast(r); const table_relation & tr_neg = static_cast(src); table_intersection_filter_fn * tfun = get_manager().mk_filter_by_intersection_fn(tr.get_table(), tr_neg.get_table(), joined_col_cnt, r_cols, src_cols); if(!tfun) { - return 0; + return nullptr; } return alloc(tr_intersection_filter_fn, tfun); @@ -410,7 +410,7 @@ namespace datalog { const relation_base & negated_rel, unsigned joined_col_cnt, const unsigned * r_cols, const unsigned * negated_cols) { if(!r.from_table() || !negated_rel.from_table()) { - return 0; + return nullptr; } const table_relation & tr = static_cast(r); const table_relation & tr_neg = static_cast(negated_rel); diff --git a/src/muz/rel/dl_table_relation.h b/src/muz/rel/dl_table_relation.h index 16ff8c482..8ebb6d44d 100644 --- a/src/muz/rel/dl_table_relation.h +++ b/src/muz/rel/dl_table_relation.h @@ -46,40 +46,40 @@ namespace datalog { table_plugin & get_table_plugin() { return m_table_plugin; } - virtual bool can_handle_signature(const relation_signature & s); + bool can_handle_signature(const relation_signature & s) override; - virtual relation_base * mk_empty(const relation_signature & s); + relation_base * mk_empty(const relation_signature & s) override; virtual relation_base * mk_full_relation(const relation_signature & s, func_decl* p, family_id kind); relation_base * mk_from_table(const relation_signature & s, table_base * t); protected: - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, - const unsigned * permutation); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, - app * condition, unsigned removed_col_cnt, const unsigned * removed_cols); - virtual relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t, - const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, - const relation_base & negated_obj, unsigned joined_col_cnt, - const unsigned * t_cols, const unsigned * negated_cols); - virtual relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, - const relation_element & value, unsigned col); + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_join_fn * mk_join_project_fn(const relation_base & t1, const relation_base & t2, + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_transformer_fn * mk_permutation_rename_fn(const relation_base & t, + const unsigned * permutation) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; + relation_transformer_fn * mk_filter_interpreted_and_project_fn(const relation_base & t, + app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) override; + relation_intersection_filter_fn * mk_filter_by_intersection_fn(const relation_base & t, + const relation_base & src, unsigned joined_col_cnt, const unsigned * t_cols, const unsigned * src_cols) override; + relation_intersection_filter_fn * mk_filter_by_negation_fn(const relation_base & t, + const relation_base & negated_obj, unsigned joined_col_cnt, + const unsigned * t_cols, const unsigned * negated_cols) override; + relation_transformer_fn * mk_select_equal_and_project_fn(const relation_base & t, + const relation_element & value, unsigned col) override; }; class table_relation : public relation_base { @@ -107,24 +107,24 @@ namespace datalog { table_base & get_table() { return *m_table; } const table_base & get_table() const { return *m_table; } - virtual bool empty() const { return m_table->empty(); } + bool empty() const override { return m_table->empty(); } void add_table_fact(const table_fact & f); - virtual void add_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual relation_base * clone() const; - virtual relation_base * complement(func_decl* p) const; - virtual void to_formula(expr_ref& fml) const { get_table().to_formula(get_signature(), fml); } + void add_fact(const relation_fact & f) override; + bool contains_fact(const relation_fact & f) const override; + relation_base * clone() const override; + relation_base * complement(func_decl* p) const override; + void to_formula(expr_ref& fml) const override { get_table().to_formula(get_signature(), fml); } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { get_table().display(out); } - virtual void display_tuples(func_decl & pred, std::ostream & out) const; + void display_tuples(func_decl & pred, std::ostream & out) const override; - virtual unsigned get_size_estimate_rows() const { return m_table->get_size_estimate_rows(); } - virtual unsigned get_size_estimate_bytes() const { return m_table->get_size_estimate_bytes(); } - virtual bool knows_exact_size() const { return m_table->knows_exact_size(); } + unsigned get_size_estimate_rows() const override { return m_table->get_size_estimate_rows(); } + unsigned get_size_estimate_bytes() const override { return m_table->get_size_estimate_bytes(); } + bool knows_exact_size() const override { return m_table->knows_exact_size(); } }; }; diff --git a/src/muz/rel/dl_vector_relation.h b/src/muz/rel/dl_vector_relation.h index 85e7a78d1..fe1d603fe 100644 --- a/src/muz/rel/dl_vector_relation.h +++ b/src/muz/rel/dl_vector_relation.h @@ -55,12 +55,12 @@ namespace datalog { } } - virtual ~vector_relation() { + ~vector_relation() override { dealloc(m_eqs); dealloc(m_elems); } - virtual void swap(relation_base& other) { + void swap(relation_base& other) override { vector_relation& o = dynamic_cast(other); if (&o == this) return; std::swap(o.m_eqs, m_eqs); @@ -85,7 +85,7 @@ namespace datalog { } - virtual bool empty() const { return m_empty; } + bool empty() const override { return m_empty; } T& operator[](unsigned i) { return (*m_elems)[find(i)]; } @@ -93,7 +93,7 @@ namespace datalog { virtual void display_index(unsigned i, T const& t, std::ostream& out) const = 0; - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { if (empty()) { out << "empty\n"; return; diff --git a/src/muz/rel/doc.cpp b/src/muz/rel/doc.cpp index 23d24dfc3..a6cf98041 100644 --- a/src/muz/rel/doc.cpp +++ b/src/muz/rel/doc.cpp @@ -62,13 +62,13 @@ doc* doc_manager::allocate(tbv* t) { doc* doc_manager::allocate(tbv const& src) { return allocate(m.allocate(src)); } -doc* doc_manager::allocate(uint64 n) { +doc* doc_manager::allocate(uint64_t n) { return allocate(m.allocate(n)); } doc* doc_manager::allocate(rational const& r) { return allocate(m.allocate(r)); } -doc* doc_manager::allocate(uint64 n, unsigned hi, unsigned lo) { +doc* doc_manager::allocate(uint64_t n, unsigned hi, unsigned lo) { return allocate(m.allocate(n, hi, lo)); } doc* doc_manager::allocate(doc const& src, unsigned const* permutation) { @@ -447,7 +447,7 @@ doc* doc_manager::join(const doc& d1, const doc& d2, doc_manager& dm1, } else if (v1 != v2) { // columns don't match - return 0; + return nullptr; } SASSERT(well_formed(*d)); } diff --git a/src/muz/rel/doc.h b/src/muz/rel/doc.h index 419081892..37b747117 100644 --- a/src/muz/rel/doc.h +++ b/src/muz/rel/doc.h @@ -61,9 +61,9 @@ public: doc* allocate(doc const& src); doc* allocate(tbv const& src); doc* allocate(tbv * src); - doc* allocate(uint64 n); + doc* allocate(uint64_t n); doc* allocate(rational const& r); - doc* allocate(uint64 n, unsigned hi, unsigned lo); + doc* allocate(uint64_t n, unsigned hi, unsigned lo); doc* allocate(doc const& src, unsigned const* permutation); void deallocate(doc* src); void copy(doc& dst, doc const& src); @@ -373,7 +373,7 @@ class doc_ref { doc_manager& dm; doc* d; public: - doc_ref(doc_manager& dm):dm(dm),d(0) {} + doc_ref(doc_manager& dm):dm(dm),d(nullptr) {} doc_ref(doc_manager& dm, doc* d):dm(dm),d(d) {} ~doc_ref() { if (d) dm.deallocate(d); @@ -385,8 +385,8 @@ public: } doc& operator*() { return *d; } doc* operator->() { return d; } - doc* detach() { doc* r = d; d = 0; return r; } - operator bool() const { return d != 0; } + doc* detach() { doc* r = d; d = nullptr; return r; } + operator bool() const { return d != nullptr; } }; #endif /* DOC_H_ */ diff --git a/src/muz/rel/karr_relation.cpp b/src/muz/rel/karr_relation.cpp index c8a489d69..956244b4f 100644 --- a/src/muz/rel/karr_relation.cpp +++ b/src/muz/rel/karr_relation.cpp @@ -36,13 +36,13 @@ namespace datalog { { } - virtual bool empty() const { + bool empty() const override { return m_empty; } - virtual bool is_precise() const { return false; } + bool is_precise() const override { return false; } - virtual void add_fact(const relation_fact & f) { + void add_fact(const relation_fact & f) override { SASSERT(m_empty); SASSERT(!m_basis_valid); m_empty = false; @@ -60,12 +60,12 @@ namespace datalog { } } - virtual bool contains_fact(const relation_fact & f) const { + bool contains_fact(const relation_fact & f) const override { UNREACHABLE(); return false; } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { if (m_fn) { out << m_fn->get_name() << "\n"; } @@ -82,18 +82,18 @@ namespace datalog { } } - virtual karr_relation * clone() const { + karr_relation * clone() const override { karr_relation* result = alloc(karr_relation, m_plugin, m_fn, get_signature(), m_empty); result->copy(*this); return result; } - virtual karr_relation * complement(func_decl*) const { + karr_relation * complement(func_decl*) const override { UNREACHABLE(); - return 0; + return nullptr; } - virtual void to_formula(expr_ref& fml) const { + void to_formula(expr_ref& fml) const override { if (empty()) { fml = m.mk_false(); } @@ -111,8 +111,8 @@ namespace datalog { void filter_interpreted(app* cond) { rational one(1), mone(-1); - expr* e1 = 0, *e2 = 0, *en = 0; - var* v = 0, *w = 0; + expr* e1 = nullptr, *e2 = nullptr, *en = nullptr; + var* v = nullptr, *w = nullptr; rational n1, n2; expr_ref_vector conjs(m); flatten_and(cond, conjs); @@ -500,7 +500,7 @@ namespace datalog { } relation_base * karr_relation_plugin::mk_empty(const relation_signature & s) { - return alloc(karr_relation, *this, 0, s, true); + return alloc(karr_relation, *this, nullptr, s, true); } relation_base * karr_relation_plugin::mk_full(func_decl* p, const relation_signature & s) { @@ -514,11 +514,11 @@ namespace datalog { : convenient_relation_join_fn(o1_sig, o2_sig, col_cnt, cols1, cols2){ } - virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + relation_base * operator()(const relation_base & _r1, const relation_base & _r2) override { karr_relation const& r1 = get(_r1); karr_relation const& r2 = get(_r2); karr_relation_plugin& p = r1.get_plugin(); - karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + karr_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_join(r1, r2, m_cols1.size(), m_cols1.c_ptr(), m_cols2.c_ptr()); return result; } @@ -528,7 +528,7 @@ namespace datalog { const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(t1) || !check_kind(t2)) { - return 0; + return nullptr; } return alloc(join_fn, t1.get_signature(), t2.get_signature(), col_cnt, cols1, cols2); } @@ -540,10 +540,10 @@ namespace datalog { : convenient_relation_project_fn(orig_sig, removed_col_cnt, removed_cols) { } - virtual relation_base * operator()(const relation_base & _r) { + relation_base * operator()(const relation_base & _r) override { karr_relation const& r = get(_r); karr_relation_plugin& p = r.get_plugin(); - karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + karr_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_project(r, m_removed_cols.size(), m_removed_cols.c_ptr()); return result; } @@ -559,10 +559,10 @@ namespace datalog { rename_fn(karr_relation_plugin& p, const relation_signature & orig_sig, unsigned cycle_len, const unsigned * cycle) : convenient_relation_rename_fn(orig_sig, cycle_len, cycle) {} - virtual relation_base * operator()(const relation_base & _r) { + relation_base * operator()(const relation_base & _r) override { karr_relation const& r = get(_r); karr_relation_plugin& p = r.get_plugin(); - karr_relation* result = dynamic_cast(p.mk_full(0, get_result_signature())); + karr_relation* result = dynamic_cast(p.mk_full(nullptr, get_result_signature())); result->mk_rename(r, m_cycle.size(), m_cycle.c_ptr()); return result; } @@ -571,7 +571,7 @@ namespace datalog { relation_transformer_fn * karr_relation_plugin::mk_rename_fn(const relation_base & r, unsigned cycle_len, const unsigned * permutation_cycle) { if (!check_kind(r)) { - return 0; + return nullptr; } return alloc(rename_fn, *this, r.get_signature(), cycle_len, permutation_cycle); } @@ -676,7 +676,7 @@ namespace datalog { public: union_fn() {} - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { karr_relation& r = get(_r); karr_relation const& src = get(_src); @@ -687,7 +687,7 @@ namespace datalog { r.mk_union(src, &d); } else { - r.mk_union(src, 0); + r.mk_union(src, nullptr); } TRACE("dl", r.display(tout << "result:\n");); } @@ -696,7 +696,7 @@ namespace datalog { relation_union_fn * karr_relation_plugin::mk_union_fn(const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; + return nullptr; } return alloc(union_fn); } @@ -707,7 +707,7 @@ namespace datalog { filter_identical_fn(unsigned col_cnt, const unsigned * identical_cols) : m_identical_cols(col_cnt, identical_cols) {} - virtual void operator()(relation_base & _r) { + void operator()(relation_base & _r) override { karr_relation & r = get(_r); TRACE("dl", r.display(tout << "src:\n");); r.get_ineqs(); @@ -730,7 +730,7 @@ namespace datalog { relation_mutator_fn * karr_relation_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { if(!check_kind(t)) { - return 0; + return nullptr; } return alloc(filter_identical_fn, col_cnt, identical_cols); } @@ -747,7 +747,7 @@ namespace datalog { m_valid = arith.is_numeral(value, m_value) && m_value.is_int(); } - virtual void operator()(relation_base & _r) { + void operator()(relation_base & _r) override { karr_relation & r = get(_r); if (m_valid) { r.get_ineqs(); @@ -768,7 +768,7 @@ namespace datalog { if (check_kind(r)) { return alloc(filter_equal_fn, get_manager(), value, col); } - return 0; + return nullptr; } @@ -779,7 +779,7 @@ namespace datalog { m_cond(cond, t.get_plugin().get_ast_manager()) { } - void operator()(relation_base& t) { + void operator()(relation_base& t) override { get(t).filter_interpreted(m_cond); TRACE("dl", tout << mk_pp(m_cond, m_cond.get_manager()) << "\n"; t.display(tout);); } @@ -789,6 +789,6 @@ namespace datalog { if (check_kind(t)) { return alloc(filter_interpreted_fn, get(t), condition); } - return 0; + return nullptr; } }; diff --git a/src/muz/rel/karr_relation.h b/src/muz/rel/karr_relation.h index 47e10f09e..e8637f2bd 100644 --- a/src/muz/rel/karr_relation.h +++ b/src/muz/rel/karr_relation.h @@ -45,33 +45,33 @@ namespace datalog { a(get_ast_manager()) {} - virtual bool can_handle_signature(const relation_signature & sig) { + bool can_handle_signature(const relation_signature & sig) override { return get_manager().get_context().karr(); } static symbol get_name() { return symbol("karr_relation"); } - virtual relation_base * mk_empty(const relation_signature & s); + relation_base * mk_empty(const relation_signature & s) override; - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); + relation_base * mk_full(func_decl* p, const relation_signature & s) override; static karr_relation& get(relation_base& r); static karr_relation const & get(relation_base const& r); - virtual relation_join_fn * mk_join_fn( - const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); + relation_join_fn * mk_join_fn( + const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; private: bool dualizeI(matrix& dst, matrix const& src); diff --git a/src/muz/rel/rel_context.cpp b/src/muz/rel/rel_context.cpp index 9fb1e89e0..b9e30b970 100644 --- a/src/muz/rel/rel_context.cpp +++ b/src/muz/rel/rel_context.cpp @@ -96,7 +96,7 @@ namespace datalog { m(ctx.get_manager()), m_rmanager(ctx), m_answer(m), - m_last_result_relation(0), + m_last_result_relation(nullptr), m_ectx(ctx), m_sw(0) { @@ -121,7 +121,7 @@ namespace datalog { rel_context::~rel_context() { if (m_last_result_relation) { m_last_result_relation->deallocate(); - m_last_result_relation = 0; + m_last_result_relation = nullptr; } } @@ -215,7 +215,7 @@ namespace datalog { SASSERT(remaining_time_limit>restart_time); remaining_time_limit -= restart_time; } - uint64 new_restart_time = static_cast(restart_time)*m_context.initial_restart_timeout(); + uint64_t new_restart_time = static_cast(restart_time)*m_context.initial_restart_timeout(); if (new_restart_time > UINT_MAX) { restart_time = UINT_MAX; } diff --git a/src/muz/rel/rel_context.h b/src/muz/rel/rel_context.h index f4a87d496..0a31c4e9f 100644 --- a/src/muz/rel/rel_context.h +++ b/src/muz/rel/rel_context.h @@ -56,72 +56,72 @@ namespace datalog { public: rel_context(context& ctx); - virtual ~rel_context(); + ~rel_context() override; - virtual relation_manager & get_rmanager(); - virtual const relation_manager & get_rmanager() const; + relation_manager & get_rmanager() override; + const relation_manager & get_rmanager() const override; ast_manager& get_manager() const { return m; } context& get_context() const { return m_context; } - virtual relation_base & get_relation(func_decl * pred); - virtual relation_base * try_get_relation(func_decl * pred) const; - virtual bool is_empty_relation(func_decl* pred) const; - virtual expr_ref try_get_formula(func_decl * pred) const; - virtual expr_ref get_answer() { return m_answer; } + relation_base & get_relation(func_decl * pred) override; + relation_base * try_get_relation(func_decl * pred) const override; + bool is_empty_relation(func_decl* pred) const override; + expr_ref try_get_formula(func_decl * pred) const override; + expr_ref get_answer() override { return m_answer; } - virtual bool output_profile() const; + bool output_profile() const override; - virtual lbool query(expr* q); - virtual lbool query(unsigned num_rels, func_decl * const* rels); + lbool query(expr* q) override; + lbool query(unsigned num_rels, func_decl * const* rels) override; - virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, - symbol const * relation_names); + void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt, + symbol const * relation_names) override; - virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred); + void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) override; - virtual void collect_statistics(statistics& st) const; + void collect_statistics(statistics& st) const override; - virtual void updt_params(); + void updt_params() override; /** \brief Restrict the set of used predicates to \c res. The function deallocates unsused relations, it does not deal with rules. */ - virtual void restrict_predicates(func_decl_set const& predicates); + void restrict_predicates(func_decl_set const& predicates) override; - virtual void transform_rules(); + void transform_rules() override; - virtual bool try_get_size(func_decl* pred, unsigned& rel_size) const; + bool try_get_size(func_decl* pred, unsigned& rel_size) const override; /** \brief query result if it contains fact. */ - virtual bool result_contains_fact(relation_fact const& f); + bool result_contains_fact(relation_fact const& f) override; - virtual void collect_non_empty_predicates(func_decl_set& ps) { + void collect_non_empty_predicates(func_decl_set& ps) override { return get_rmanager().collect_non_empty_predicates(ps); } /** \brief add facts to relation */ - virtual void add_fact(func_decl* pred, relation_fact const& fact); - virtual void add_fact(func_decl* pred, table_fact const& fact); + void add_fact(func_decl* pred, relation_fact const& fact) override; + void add_fact(func_decl* pred, table_fact const& fact) override; /** \brief check if facts were added to relation */ - virtual bool has_facts(func_decl * pred) const; + bool has_facts(func_decl * pred) const override; /** \brief Store the relation \c rel under the predicate \c pred. The \c context object takes over the ownership of the relation object. */ - virtual void store_relation(func_decl * pred, relation_base * rel); + void store_relation(func_decl * pred, relation_base * rel) override; - virtual void display_output_facts(rule_set const& rules, std::ostream & out) const; - virtual void display_facts(std::ostream & out) const; + void display_output_facts(rule_set const& rules, std::ostream & out) const override; + void display_facts(std::ostream & out) const override; - virtual void display_profile(std::ostream& out); + void display_profile(std::ostream& out) override; - virtual lbool saturate(); + lbool saturate() override; }; }; diff --git a/src/muz/rel/tbv.cpp b/src/muz/rel/tbv.cpp index 69cc4819a..b96f32114 100644 --- a/src/muz/rel/tbv.cpp +++ b/src/muz/rel/tbv.cpp @@ -72,7 +72,7 @@ tbv* tbv_manager::allocate(tbv const& bv) { copy(*r, bv); return r; } -tbv* tbv_manager::allocate(uint64 val) { +tbv* tbv_manager::allocate(uint64_t val) { tbv* v = allocate0(); for (unsigned bit = std::min(64u, num_tbits()); bit-- > 0;) { if (val & (1ULL << bit)) { @@ -84,7 +84,7 @@ tbv* tbv_manager::allocate(uint64 val) { return v; } -tbv* tbv_manager::allocate(uint64 val, unsigned hi, unsigned lo) { +tbv* tbv_manager::allocate(uint64_t val, unsigned hi, unsigned lo) { tbv* v = allocateX(); SASSERT(64 >= num_tbits() && num_tbits() > hi && hi >= lo); set(*v, val, hi, lo); @@ -134,7 +134,7 @@ void tbv_manager::set(tbv& dst, unsigned index, tbit value) { } -void tbv_manager::set(tbv& dst, uint64 val, unsigned hi, unsigned lo) { +void tbv_manager::set(tbv& dst, uint64_t val, unsigned hi, unsigned lo) { SASSERT(lo <= hi && hi < num_tbits()); for (unsigned i = 0; i < hi - lo + 1; ++i) { set(dst, lo + i, (val & (1ULL << i))?BIT_1:BIT_0); diff --git a/src/muz/rel/tbv.h b/src/muz/rel/tbv.h index 1839700d6..346208a3f 100644 --- a/src/muz/rel/tbv.h +++ b/src/muz/rel/tbv.h @@ -51,9 +51,9 @@ public: tbv* allocate0(); tbv* allocateX(); tbv* allocate(tbv const& bv); - tbv* allocate(uint64 n); + tbv* allocate(uint64_t n); tbv* allocate(rational const& r); - tbv* allocate(uint64 n, unsigned hi, unsigned lo); + tbv* allocate(uint64_t n, unsigned hi, unsigned lo); tbv* allocate(tbv const& bv, unsigned const* permutation); tbv* allocate(char const* bv); @@ -80,7 +80,7 @@ public: std::ostream& display(std::ostream& out, tbv const& b, unsigned hi, unsigned lo) const; tbv* project(bit_vector const& to_delete, tbv const& src); bool is_well_formed(tbv const& b) const; // - does not contain BIT_z; - void set(tbv& dst, uint64 n, unsigned hi, unsigned lo); + void set(tbv& dst, uint64_t n, unsigned hi, unsigned lo); void set(tbv& dst, rational const& r, unsigned hi, unsigned lo); void set(tbv& dst, tbv const& other, unsigned hi, unsigned lo); void set(tbv& dst, unsigned index, tbit value); @@ -130,7 +130,7 @@ class tbv_ref { tbv_manager& mgr; tbv* d; public: - tbv_ref(tbv_manager& mgr):mgr(mgr),d(0) {} + tbv_ref(tbv_manager& mgr):mgr(mgr),d(nullptr) {} tbv_ref(tbv_manager& mgr, tbv* d):mgr(mgr),d(d) {} ~tbv_ref() { if (d) mgr.deallocate(d); @@ -143,7 +143,7 @@ public: tbv& operator*() { return *d; } tbv* operator->() { return d; } tbv* get() { return d; } - tbv* detach() { tbv* result = d; d = 0; return result; } + tbv* detach() { tbv* result = d; d = nullptr; return result; } }; diff --git a/src/muz/rel/udoc_relation.cpp b/src/muz/rel/udoc_relation.cpp index febabb36d..ea292a29e 100644 --- a/src/muz/rel/udoc_relation.cpp +++ b/src/muz/rel/udoc_relation.cpp @@ -203,7 +203,7 @@ namespace datalog { return dynamic_cast(r); } udoc_relation* udoc_plugin::get(relation_base* r) { - return r?dynamic_cast(r):0; + return r?dynamic_cast(r):nullptr; } udoc_relation const & udoc_plugin::get(relation_base const& r) { return dynamic_cast(r); @@ -261,7 +261,7 @@ namespace datalog { num_bits = 1; return true; } - uint64 n, sz; + uint64_t n, sz; ast_manager& m = get_ast_manager(); if (dl.is_numeral(e, n) && dl.try_get_size(m.get_sort(e), sz)) { num_bits = 0; @@ -277,7 +277,7 @@ namespace datalog { return bv.get_bv_size(s); if (m.is_bool(s)) return 1; - uint64 sz; + uint64_t sz; if (dl.try_get_size(s, sz)) { while (sz > 0) ++num_bits, sz /= 2; return num_bits; @@ -328,7 +328,7 @@ namespace datalog { t2.expand_column_vector(m_cols2); } - virtual relation_base * operator()(const relation_base & _r1, const relation_base & _r2) { + relation_base * operator()(const relation_base & _r1, const relation_base & _r2) override { udoc_relation const& r1 = get(_r1); udoc_relation const& r2 = get(_r2); TRACE("doc", r1.display(tout << "r1:\n"); r2.display(tout << "r2:\n");); @@ -351,7 +351,7 @@ namespace datalog { const relation_base & t1, const relation_base & t2, unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) { if (!check_kind(t1) || !check_kind(t2)) { - return 0; + return nullptr; } return alloc(join_fn, *this, get(t1), get(t2), col_cnt, cols1, cols2); } @@ -369,7 +369,7 @@ namespace datalog { } } - virtual relation_base * operator()(const relation_base & tb) { + relation_base * operator()(const relation_base & tb) override { TRACE("doc", tb.display(tout << "src:\n");); udoc_relation const& t = get(tb); udoc_plugin& p = t.get_plugin(); @@ -394,7 +394,7 @@ namespace datalog { const relation_base & t, unsigned col_cnt, const unsigned * removed_cols) { if (!check_kind(t)) - return 0; + return nullptr; return alloc(project_fn, get(t), col_cnt, removed_cols); } @@ -462,7 +462,7 @@ namespace datalog { } } - virtual relation_base * operator()(const relation_base & _r) { + relation_base * operator()(const relation_base & _r) override { udoc_relation const& r = get(_r); TRACE("doc", r.display(tout << "r:\n");); udoc_plugin& p = r.get_plugin(); @@ -487,20 +487,20 @@ namespace datalog { return alloc(rename_fn, get(r), cycle_len, permutation_cycle); } else { - return 0; + return nullptr; } } class udoc_plugin::union_fn : public relation_union_fn { public: union_fn() {} - virtual void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) { + void operator()(relation_base & _r, const relation_base & _src, relation_base * _delta) override { TRACE("doc", _r.display(tout << "dst:\n"); _src.display(tout << "src:\n");); udoc_relation& r = get(_r); udoc_relation const& src = get(_src); udoc_relation* d = get(_delta); doc_manager& dm = r.get_dm(); - udoc* d1 = 0; + udoc* d1 = nullptr; if (d) d1 = &d->get_udoc(); IF_VERBOSE(3, r.display(verbose_stream() << "orig: ");); r.get_plugin().mk_union(dm, r.get_udoc(), src.get_udoc(), d1); @@ -539,7 +539,7 @@ namespace datalog { const relation_base & tgt, const relation_base & src, const relation_base * delta) { if (!check_kind(tgt) || !check_kind(src) || (delta && !check_kind(*delta))) { - return 0; + return nullptr; } return alloc(union_fn); } @@ -574,7 +574,7 @@ namespace datalog { } } - virtual void operator()(relation_base & _r) { + void operator()(relation_base & _r) override { udoc_relation& r = get(_r); udoc& d = r.get_udoc(); doc_manager& dm = r.get_dm(); @@ -585,7 +585,7 @@ namespace datalog { }; relation_mutator_fn * udoc_plugin::mk_filter_identical_fn( const relation_base & t, unsigned col_cnt, const unsigned * identical_cols) { - return check_kind(t)?alloc(filter_identical_fn, t, col_cnt, identical_cols):0; + return check_kind(t)?alloc(filter_identical_fn, t, col_cnt, identical_cols):nullptr; } class udoc_plugin::filter_equal_fn : public relation_mutator_fn { doc_manager& dm; @@ -602,10 +602,10 @@ namespace datalog { SASSERT(num_bits == hi - lo); dm.tbvm().set(m_filter->pos(), r, hi-1, lo); } - virtual ~filter_equal_fn() { + ~filter_equal_fn() override { dm.deallocate(m_filter); } - virtual void operator()(relation_base & tb) { + void operator()(relation_base & tb) override { udoc_relation & t = get(tb); t.get_udoc().intersect(dm, *m_filter); SASSERT(t.get_udoc().well_formed(t.get_dm())); @@ -614,7 +614,7 @@ namespace datalog { relation_mutator_fn * udoc_plugin::mk_filter_equal_fn( const relation_base & t, const relation_element & value, unsigned col) { if (!check_kind(t)) - return 0; + return nullptr; return alloc(filter_equal_fn, *this, get(t), value, col); } @@ -869,7 +869,7 @@ namespace datalog { dm.set(*d, idx, BIT_1); result.intersect(dm, *d); } - else if ((m.is_eq(g, e1, e2) || m.is_iff(g, e1, e2)) && m.is_bool(e1)) { + else if (m.is_iff(g, e1, e2)) { udoc diff1, diff2; diff1.push_back(dm.allocateX()); diff2.push_back(dm.allocateX()); @@ -932,11 +932,11 @@ namespace datalog { m_udoc.display(dm, tout) << "\n";); } - virtual ~filter_interpreted_fn() { + ~filter_interpreted_fn() override { m_udoc.reset(dm); } - virtual void operator()(relation_base & tb) { + void operator()(relation_base & tb) override { udoc_relation & t = get(tb); udoc& u = t.get_udoc(); SASSERT(u.well_formed(dm)); @@ -951,7 +951,7 @@ namespace datalog { } }; relation_mutator_fn * udoc_plugin::mk_filter_interpreted_fn(const relation_base & t, app * condition) { - return check_kind(t)?alloc(filter_interpreted_fn, get(t), get_ast_manager(), condition):0; + return check_kind(t)?alloc(filter_interpreted_fn, get(t), get_ast_manager(), condition):nullptr; } class udoc_plugin::join_project_fn : public convenient_relation_join_project_fn { @@ -987,7 +987,7 @@ namespace datalog { // TBD: replace this by "join" given below. - virtual relation_base* operator()(relation_base const& t1, relation_base const& t2) { + relation_base* operator()(relation_base const& t1, relation_base const& t2) override { #if 1 return join(get(t1), get(t2)); #else @@ -1043,7 +1043,7 @@ namespace datalog { public: join_project_and_fn() {} - virtual relation_base* operator()(relation_base const& t1, relation_base const& t2) { + relation_base* operator()(relation_base const& t1, relation_base const& t2) override { udoc_relation *result = get(t1.clone()); result->get_udoc().intersect(result->get_dm(), get(t2).get_udoc()); return result; @@ -1055,7 +1055,7 @@ namespace datalog { unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, unsigned removed_col_cnt, const unsigned * removed_cols) { if (!check_kind(t1) || !check_kind(t2)) - return 0; + return nullptr; // special case where we have h(X) :- f(X), g(X). if (joined_col_cnt == removed_col_cnt && t1.get_signature().size() == joined_col_cnt && @@ -1121,7 +1121,7 @@ namespace datalog { neg.expand_column_vector(m_neg_cols); } - virtual void operator()(relation_base& tb, const relation_base& negb) { + void operator()(relation_base& tb, const relation_base& negb) override { udoc_relation& t = get(tb); udoc_relation const& n = get(negb); IF_VERBOSE(3, t.display(verbose_stream() << "dst:");); @@ -1183,7 +1183,7 @@ namespace datalog { const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, const unsigned *negated_cols) { if (!check_kind(t) || !check_kind(neg)) - return 0; + return nullptr; return alloc(negation_filter_fn, get(t), get(neg), joined_col_cnt, t_cols, negated_cols); } @@ -1223,10 +1223,10 @@ namespace datalog { t.compile_guard(guard, m_udoc, m_to_delete); } - virtual ~filter_proj_fn() { + ~filter_proj_fn() override { m_udoc.reset(dm); } - virtual relation_base* operator()(const relation_base & tb) { + relation_base* operator()(const relation_base & tb) override { udoc_relation const & t = get(tb); udoc const& u1 = t.get_udoc(); doc_manager& dm = t.get_dm(); @@ -1250,7 +1250,7 @@ namespace datalog { relation_transformer_fn * udoc_plugin::mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, unsigned removed_col_cnt, const unsigned * removed_cols) { - return check_kind(t)?alloc(filter_proj_fn, get(t), get_ast_manager(), condition, removed_col_cnt, removed_cols):0; + return check_kind(t)?alloc(filter_proj_fn, get(t), get_ast_manager(), condition, removed_col_cnt, removed_cols):nullptr; } diff --git a/src/muz/rel/udoc_relation.h b/src/muz/rel/udoc_relation.h index 15918d80d..01b2e8b43 100644 --- a/src/muz/rel/udoc_relation.h +++ b/src/muz/rel/udoc_relation.h @@ -39,21 +39,21 @@ namespace datalog { expr_ref to_formula(doc const& d) const; public: udoc_relation(udoc_plugin& p, relation_signature const& s); - virtual ~udoc_relation(); - virtual void reset(); - virtual void add_fact(const relation_fact & f); - virtual void add_new_fact(const relation_fact & f); - virtual bool contains_fact(const relation_fact & f) const; - virtual udoc_relation * clone() const; - virtual udoc_relation * complement(func_decl*) const; - virtual void to_formula(expr_ref& fml) const; + ~udoc_relation() override; + void reset() override; + void add_fact(const relation_fact & f) override; + void add_new_fact(const relation_fact & f) override; + bool contains_fact(const relation_fact & f) const override; + udoc_relation * clone() const override; + udoc_relation * complement(func_decl*) const override; + void to_formula(expr_ref& fml) const override; udoc_plugin& get_plugin() const; - virtual bool fast_empty() const { return m_elems.is_empty(); } - virtual bool empty() const; - virtual void display(std::ostream& out) const; - virtual bool is_precise() const { return true; } - virtual unsigned get_size_estimate_rows() const { return m_elems.size(); } - virtual unsigned get_size_estimate_bytes() const; + bool fast_empty() const override { return m_elems.is_empty(); } + bool empty() const override; + void display(std::ostream& out) const override; + bool is_precise() const override { return true; } + unsigned get_size_estimate_rows() const override { return m_elems.size(); } + unsigned get_size_estimate_bytes() const override; doc_manager& get_dm() const { return dm; } udoc const& get_udoc() const { return m_elems; } @@ -63,7 +63,7 @@ namespace datalog { unsigned get_num_cols() const { return m_column_info.size()-1; } unsigned column_idx(unsigned col) const { return m_column_info[col]; } unsigned column_num_bits(unsigned col) const { return m_column_info[col+1] - m_column_info[col]; } - void expand_column_vector(unsigned_vector& v, const udoc_relation* other = 0) const; + void expand_column_vector(unsigned_vector& v, const udoc_relation* other = nullptr) const; void extract_guard(expr* condition, expr_ref& guard, expr_ref& rest) const; bool is_guard(expr* g) const; bool is_guard(unsigned n, expr* const *g) const; @@ -113,38 +113,38 @@ namespace datalog { expr* mk_numeral(rational const& r, sort* s); public: udoc_plugin(relation_manager& rm); - ~udoc_plugin(); - virtual bool can_handle_signature(const relation_signature & s); + ~udoc_plugin() override; + bool can_handle_signature(const relation_signature & s) override; static symbol get_name() { return symbol("doc"); } - virtual relation_base * mk_empty(const relation_signature & s); - virtual relation_base * mk_full(func_decl* p, const relation_signature & s); - virtual relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, - unsigned col_cnt, const unsigned * cols1, const unsigned * cols2); - virtual relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, - const unsigned * removed_cols); - virtual relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, - const unsigned * permutation_cycle); - virtual relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, - const relation_base * delta); - virtual relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, - const unsigned * identical_cols); - virtual relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, - unsigned col); - virtual relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition); - virtual relation_intersection_filter_fn * mk_filter_by_negation_fn( + relation_base * mk_empty(const relation_signature & s) override; + relation_base * mk_full(func_decl* p, const relation_signature & s) override; + relation_join_fn * mk_join_fn(const relation_base & t1, const relation_base & t2, + unsigned col_cnt, const unsigned * cols1, const unsigned * cols2) override; + relation_transformer_fn * mk_project_fn(const relation_base & t, unsigned col_cnt, + const unsigned * removed_cols) override; + relation_transformer_fn * mk_rename_fn(const relation_base & t, unsigned permutation_cycle_len, + const unsigned * permutation_cycle) override; + relation_union_fn * mk_union_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_union_fn * mk_widen_fn(const relation_base & tgt, const relation_base & src, + const relation_base * delta) override; + relation_mutator_fn * mk_filter_identical_fn(const relation_base & t, unsigned col_cnt, + const unsigned * identical_cols) override; + relation_mutator_fn * mk_filter_equal_fn(const relation_base & t, const relation_element & value, + unsigned col) override; + relation_mutator_fn * mk_filter_interpreted_fn(const relation_base & t, app * condition) override; + relation_intersection_filter_fn * mk_filter_by_negation_fn( const relation_base& t, const relation_base& neg, unsigned joined_col_cnt, const unsigned *t_cols, - const unsigned *negated_cols); - virtual relation_transformer_fn * mk_filter_interpreted_and_project_fn( + const unsigned *negated_cols) override; + relation_transformer_fn * mk_filter_interpreted_and_project_fn( const relation_base & t, app * condition, - unsigned removed_col_cnt, const unsigned * removed_cols); - virtual relation_join_fn * mk_join_project_fn( + unsigned removed_col_cnt, const unsigned * removed_cols) override; + relation_join_fn * mk_join_project_fn( relation_base const& t1, relation_base const& t2, - unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, - unsigned removed_col_cnt, const unsigned * removed_cols); - + unsigned joined_col_cnt, const unsigned * cols1, const unsigned * cols2, + unsigned removed_col_cnt, const unsigned * removed_cols) override; + void disable_fast_pass() { m_disable_fast_pass = true; } }; }; diff --git a/src/muz/spacer/CMakeLists.txt b/src/muz/spacer/CMakeLists.txt index 37bc7f352..43fffd9fb 100644 --- a/src/muz/spacer/CMakeLists.txt +++ b/src/muz/spacer/CMakeLists.txt @@ -8,18 +8,25 @@ z3_add_component(spacer spacer_generalizers.cpp spacer_manager.cpp spacer_prop_solver.cpp - spacer_smt_context_manager.cpp spacer_sym_mux.cpp spacer_util.cpp - spacer_itp_solver.cpp - spacer_virtual_solver.cpp + spacer_iuc_solver.cpp spacer_legacy_mbp.cpp + spacer_proof_utils.cpp spacer_unsat_core_learner.cpp spacer_unsat_core_plugin.cpp spacer_matrix.cpp spacer_antiunify.cpp spacer_mev_array.cpp spacer_qe_project.cpp + spacer_sem_matcher.cpp + spacer_quant_generalizer.cpp + spacer_callback.cpp + spacer_json.cpp + spacer_iuc_proof.cpp + spacer_mbc.cpp + spacer_pdr.cpp + spacer_sat_answer.cpp COMPONENT_DEPENDENCIES arith_tactics core_tactics diff --git a/src/muz/spacer/spacer_antiunify.cpp b/src/muz/spacer/spacer_antiunify.cpp index 7dae59b6a..0edfb6598 100644 --- a/src/muz/spacer/spacer_antiunify.cpp +++ b/src/muz/spacer/spacer_antiunify.cpp @@ -28,6 +28,7 @@ Revision History: namespace spacer { + // Abstracts numeric values by variables struct var_abs_rewriter : public default_rewriter_cfg { ast_manager &m; @@ -56,8 +57,8 @@ struct var_abs_rewriter : public default_rewriter_cfg { { bool contains_const_child = false; app* a = to_app(t); - for (unsigned i=0, sz = a->get_num_args(); i < sz; ++i) { - if (m_util.is_numeral(a->get_arg(i))) { + for (expr * arg : *a) { + if (m_util.is_numeral(arg)) { contains_const_child = true; } } @@ -102,190 +103,73 @@ struct var_abs_rewriter : public default_rewriter_cfg { }; -/* -* construct m_g, which is a generalization of t, where every constant -* is replaced by a variable for any variable in m_g, remember the -* substitution to get back t and save it in m_substitutions -*/ -anti_unifier::anti_unifier(expr* t, ast_manager& man) : m(man), m_pinned(m), m_g(m) -{ - m_pinned.push_back(t); - obj_map substitution; +anti_unifier::anti_unifier(ast_manager &manager) : m(manager), m_pinned(m) {} - var_abs_rewriter var_abs_cfg(m, substitution); - rewriter_tpl var_abs_rw (m, false, var_abs_cfg); - var_abs_rw (t, m_g); - - m_substitutions.push_back(substitution); //TODO: refactor into vector, remove k +void anti_unifier::reset() { + m_subs.reset(); + m_cache.reset(); + m_todo.reset(); + m_pinned.reset(); } -/* traverses m_g and t in parallel. if they only differ in constants - * (i.e. m_g contains a variable, where t contains a constant), then - * add the substitutions, which need to be applied to m_g to get t, to - * m_substitutions. -*/ -bool anti_unifier::add_term(expr* t) { - m_pinned.push_back(t); +void anti_unifier::operator()(expr *e1, expr *e2, expr_ref &res, + substitution &s1, substitution &s2) { - ptr_vector todo; - ptr_vector todo2; - todo.push_back(m_g); - todo2.push_back(t); + reset(); + if (e1 == e2) {res = e1; s1.reset(); s2.reset(); return;} - ast_mark visited; + m_todo.push_back(expr_pair(e1, e2)); + while (!m_todo.empty()) { + const expr_pair &p = m_todo.back(); + SASSERT(is_app(p.first)); + SASSERT(is_app(p.second)); - arith_util util(m); + app * n1 = to_app(p.first); + app * n2 = to_app(p.second); - obj_map substitution; - - while (!todo.empty()) { - expr* current = todo.back(); - todo.pop_back(); - expr* current2 = todo2.back(); - todo2.pop_back(); - - if (!visited.is_marked(current)) { - visited.mark(current, true); - - if (is_var(current)) { - // TODO: for now we don't allow variables in the terms we want to antiunify - SASSERT(m_substitutions[0].contains(current)); - if (util.is_numeral(current2)) { - substitution.insert(current, current2); - } - else {return false;} - } - else { - SASSERT(is_app(current)); - - if (is_app(current2) && - to_app(current)->get_decl() == to_app(current2)->get_decl() && - to_app(current)->get_num_args() == to_app(current2)->get_num_args()) { - // TODO: what to do for numerals here? E.g. if we - // have 1 and 2, do they have the same decl or are - // the decls already different? - SASSERT (!util.is_numeral(current) || current == current2); - for (unsigned i = 0, num_args = to_app(current)->get_num_args(); - i < num_args; ++i) { - todo.push_back(to_app(current)->get_arg(i)); - todo2.push_back(to_app(current2)->get_arg(i)); - } - } - else { - return false; - } - } - } - } - - // we now know that the terms can be anti-unified, so add the cached substitution - m_substitutions.push_back(substitution); - return true; -} - -/* -* returns m_g, where additionally any variable, which has only equal -* substitutions, is substituted with that substitution -*/ -void anti_unifier::finalize() { - ptr_vector todo; - todo.push_back(m_g); - - ast_mark visited; - - obj_map generalization; - - arith_util util(m); - - // post-order traversel which ignores constants and handles them - // directly when the enclosing term of the constant is handled - while (!todo.empty()) { - expr* current = todo.back(); - SASSERT(is_app(current)); - - // if we haven't already visited current - if (!visited.is_marked(current)) { - bool existsUnvisitedParent = false; - - for (unsigned i = 0, sz = to_app(current)->get_num_args(); i < sz; ++i) { - expr* argument = to_app(current)->get_arg(i); - - if (!is_var(argument)) { - SASSERT(is_app(argument)); - // if we haven't visited the current parent yet - if(!visited.is_marked(argument)) { - // add it to the stack - todo.push_back(argument); - existsUnvisitedParent = true; - } - } - } - - // if we already visited all parents, we can visit current too - if (!existsUnvisitedParent) { - visited.mark(current, true); - todo.pop_back(); - - ptr_buffer arg_list; - for (unsigned i = 0, num_args = to_app(current)->get_num_args(); - i < num_args; ++i) { - expr* argument = to_app(current)->get_arg(i); - - if (is_var(argument)) { - // compute whether there are different - // substitutions for argument - bool containsDifferentSubstitutions = false; - - for (unsigned i=0, sz = m_substitutions.size(); i+1 < sz; ++i) { - SASSERT(m_substitutions[i].contains(argument)); - SASSERT(m_substitutions[i+1].contains(argument)); - - // TODO: how to check equality? - if (m_substitutions[i][argument] != - m_substitutions[i+1][argument]) - { - containsDifferentSubstitutions = true; - break; - } - } - - // if yes, use the variable - if (containsDifferentSubstitutions) { - arg_list.push_back(argument); - } - // otherwise use the concrete value instead - // and remove the substitutions - else - { - arg_list.push_back(m_substitutions[0][argument]); - - for (unsigned i=0, sz = m_substitutions.size(); i < sz; ++i) { - SASSERT(m_substitutions[i].contains(argument)); - m_substitutions[i].remove(argument); - } - } - } - else { - SASSERT(generalization.contains(argument)); - arg_list.push_back(generalization[argument]); - } - } - - SASSERT(to_app(current)->get_num_args() == arg_list.size()); - expr_ref application(m.mk_app(to_app(current)->get_decl(), - to_app(current)->get_num_args(), - arg_list.c_ptr()), m); - m_pinned.push_back(application); - generalization.insert(current, application); - } + unsigned num_arg1 = n1->get_num_args(); + unsigned num_arg2 = n2->get_num_args(); + if (n1->get_decl() != n2->get_decl() || num_arg1 != num_arg2) { + expr_ref v(m); + v = m.mk_var(m_subs.size(), get_sort(n1)); + m_pinned.push_back(v); + m_subs.push_back(expr_pair(n1, n2)); + m_cache.insert(n1, n2, v); } else { - todo.pop_back(); + expr *tmp; + unsigned todo_sz = m_todo.size(); + ptr_buffer kids; + for (unsigned i = 0; i < num_arg1; ++i) { + expr *arg1 = n1->get_arg(i); + expr *arg2 = n2->get_arg(i); + if (arg1 == arg2) {kids.push_back(arg1);} + else if (m_cache.find(arg1, arg2, tmp)) {kids.push_back(tmp);} + else {m_todo.push_back(expr_pair(arg1, arg2));} + } + if (m_todo.size() > todo_sz) {continue;} + + expr_ref u(m); + u = m.mk_app(n1->get_decl(), kids.size(), kids.c_ptr()); + m_pinned.push_back(u); + m_cache.insert(n1, n2, u); } } - m_g = generalization[m_g]; + expr *r; + VERIFY(m_cache.find(e1, e2, r)); + res = r; + + // create substitutions + s1.reserve(2, m_subs.size()); + s2.reserve(2, m_subs.size()); + + for (unsigned i = 0, sz = m_subs.size(); i < sz; ++i) { + expr_pair p = m_subs.get(i); + s1.insert(i, 0, expr_offset(p.first, 1)); + s2.insert(i, 0, expr_offset(p.second, 1)); + } } @@ -318,6 +202,8 @@ public: */ bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m, expr_ref& result) { + NOT_IMPLEMENTED_YET(); +#if 0 arith_util util(m); SASSERT(au.get_num_substitutions() > 0); @@ -411,6 +297,7 @@ bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m, result = expr_ref(m.mk_exists(vars.size(), sorts.c_ptr(), names.c_ptr(), body),m); return true; +#endif } bool naive_convex_closure::get_range(vector& v, @@ -441,7 +328,7 @@ struct subs_rewriter_cfg : public default_rewriter_cfg { bool reduce_var(var * t, expr_ref & result, proof_ref & result_pr) { result = m_c; - result_pr = 0; + result_pr = nullptr; return true; } }; @@ -453,7 +340,83 @@ void naive_convex_closure::substitute_vars_by_const(ast_manager& m, expr* t, subs_rw (t, res); } + +/// Construct a pattern by abstracting all numbers by variables +struct mk_num_pat_rewriter : public default_rewriter_cfg { + ast_manager &m; + arith_util m_arith; + + // -- mark already seen expressions + ast_mark m_seen; + // -- true if the expression is known to have a number as a sub-expression + ast_mark m_has_num; + // -- expressions created during the transformation + expr_ref_vector m_pinned; + // -- map from introduced variables to expressions they replace + app_ref_vector &m_subs; + + + // -- stack of expressions being processed to have access to expressions + // -- before rewriting + ptr_buffer m_stack; + + mk_num_pat_rewriter (ast_manager &manager, app_ref_vector& subs) : + m(manager), m_arith(m), m_pinned(m), m_subs(subs) {} + + bool pre_visit(expr * t) { + // -- don't touch multiplication + if (m_arith.is_mul(t)) return false; + + bool r = (!m_seen.is_marked(t) || m_has_num.is_marked(t)); + if (r) {m_stack.push_back (t);} + return r; + } + + + br_status reduce_app (func_decl * f, unsigned num, expr * const * args, + expr_ref & result, proof_ref & result_pr) { + expr *s; + s = m_stack.back(); + m_stack.pop_back(); + if (is_app(s)) { + app *a = to_app(s); + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { + if (m_has_num.is_marked(a->get_arg(i))) { + m_has_num.mark(a, true); + break; + } + } + } + return BR_FAILED; + } + + bool cache_all_results() const { return false; } + bool cache_results() const { return false; } + + bool get_subst(expr * s, expr * & t, proof * & t_pr) { + if (m_arith.is_numeral(s)) { + t = m.mk_var(m_subs.size(), m.get_sort(s)); + m_pinned.push_back(t); + m_subs.push_back(to_app(s)); + + m_has_num.mark(t, true); + m_seen.mark(t, true); + return true; + } + return false; + } + +}; + +void mk_num_pat(expr *e, expr_ref &result, app_ref_vector &subs) { + SASSERT(subs.empty()); + mk_num_pat_rewriter rw_cfg(result.m(), subs); + rewriter_tpl rw(result.m(), false, rw_cfg); + rw(e, result); +} + } template class rewriter_tpl; template class rewriter_tpl; +template class rewriter_tpl; diff --git a/src/muz/spacer/spacer_antiunify.h b/src/muz/spacer/spacer_antiunify.h index 2b86f67ec..af7f6f825 100644 --- a/src/muz/spacer/spacer_antiunify.h +++ b/src/muz/spacer/spacer_antiunify.h @@ -22,32 +22,36 @@ Revision History: #define _SPACER_ANTIUNIFY_H_ #include "ast/ast.h" - +#include "ast/substitution/substitution.h" +#include "util/obj_pair_hashtable.h" namespace spacer { +/** +\brief Anti-unifier for ground expressions +*/ class anti_unifier { -public: - anti_unifier(expr* t, ast_manager& m); - ~anti_unifier() {} + typedef std::pair expr_pair; + typedef pair_hash, obj_ptr_hash > expr_pair_hash; + typedef obj_pair_map cache_ty; - bool add_term(expr* t); - void finalize(); - - expr* get_generalization() {return m_g;} - unsigned get_num_substitutions() {return m_substitutions.size();} - obj_map get_substitution(unsigned index){ - SASSERT(index < m_substitutions.size()); - return m_substitutions[index]; - } - -private: - ast_manager& m; - // tracking all created expressions + ast_manager &m; expr_ref_vector m_pinned; - expr_ref m_g; + svector m_todo; + cache_ty m_cache; + svector m_subs; - vector> m_substitutions; +public: + anti_unifier(ast_manager& m); + + void reset(); + + /** + \brief Computes anti-unifier of two ground expressions. Returns + the anti-unifier and the corresponding substitutions + */ + void operator() (expr *e1, expr *e2, expr_ref &res, + substitution &s1, substitution &s2); }; class naive_convex_closure @@ -63,5 +67,8 @@ private: expr_ref& res); }; +/// Abstracts numbers in the given ground expression by variables +/// Returns the created pattern and the corresponding substitution. +void mk_num_pat(expr *e, expr_ref &result, app_ref_vector &subs); } #endif diff --git a/src/muz/spacer/spacer_callback.cpp b/src/muz/spacer/spacer_callback.cpp new file mode 100644 index 000000000..9c7c88ad9 --- /dev/null +++ b/src/muz/spacer/spacer_callback.cpp @@ -0,0 +1,38 @@ +/**++ +Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti + +Module Name: + + spacer_callback.cpp + +Abstract: + + SPACER plugin for handling events + +Author: + + Matteo Marescotti + +Notes: + +--*/ + +#include "spacer_callback.h" +#include "muz/spacer/spacer_context.h" + + +namespace spacer { + + void user_callback::new_lemma_eh(expr *lemma, unsigned level) { + m_new_lemma_eh(m_state, lemma, level); + } + + void user_callback::predecessor_eh() { + m_predecessor_eh(m_state); + } + + void user_callback::unfold_eh() { + m_unfold_eh(m_state); + } + +} \ No newline at end of file diff --git a/src/muz/spacer/spacer_callback.h b/src/muz/spacer/spacer_callback.h new file mode 100644 index 000000000..35805c7bc --- /dev/null +++ b/src/muz/spacer/spacer_callback.h @@ -0,0 +1,65 @@ +/**++ +Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti + +Module Name: + + spacer_callback.h + +Abstract: + + SPACER plugin for handling events + +Author: + + Matteo Marescotti + +Notes: + +--*/ + +#ifndef _SPACER_CALLBACK_H_ +#define _SPACER_CALLBACK_H_ + +#include "muz/spacer/spacer_context.h" +#include "muz/base/dl_engine_base.h" + + +namespace spacer { + + class user_callback : public spacer_callback { + private: + void *m_state; + const datalog::t_new_lemma_eh m_new_lemma_eh; + const datalog::t_predecessor_eh m_predecessor_eh; + const datalog::t_unfold_eh m_unfold_eh; + + public: + user_callback(context &context, + void *state, + const datalog::t_new_lemma_eh new_lemma_eh, + const datalog::t_predecessor_eh predecessor_eh, + const datalog::t_unfold_eh unfold_eh) : + spacer_callback(context), + m_state(state), + m_new_lemma_eh(new_lemma_eh), + m_predecessor_eh(predecessor_eh), + m_unfold_eh(unfold_eh) {} + + inline bool new_lemma() override { return m_new_lemma_eh != nullptr; } + + void new_lemma_eh(expr *lemma, unsigned level) override; + + inline bool predecessor() override { return m_predecessor_eh != nullptr; } + + void predecessor_eh() override; + + inline bool unfold() override { return m_unfold_eh != nullptr; } + + void unfold_eh() override; + + }; + +} + + +#endif //_SPACER_CALLBACK_H_ diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index b057730d8..acfee574a 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -51,1464 +51,126 @@ Notes: #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" +#include "smt/smt_solver.h" + +#include "muz/spacer/spacer_sat_answer.h" + namespace spacer { +/// pob -- proof obligation +pob::pob (pob* parent, pred_transformer& pt, + unsigned level, unsigned depth, bool add_to_parent): + m_ref_count (0), + m_parent (parent), m_pt (pt), + m_post (m_pt.get_ast_manager ()), + m_binding(m_pt.get_ast_manager()), + m_new_post (m_pt.get_ast_manager ()), + m_level (level), m_depth (depth), + m_open (true), m_use_farkas (true), m_weakness(0), + m_blocked_lvl(0) { + if (add_to_parent && m_parent) { + m_parent->add_child(*this); + } +} + +void pob::set_post(expr* post) { + app_ref_vector empty_binding(get_ast_manager()); + set_post(post, empty_binding); +} + +void pob::set_post(expr* post, app_ref_vector const &binding) { + normalize(post, m_post, + m_pt.get_context().simplify_pob(), + m_pt.get_context().use_euf_gen()); + + m_binding.reset(); + if (!binding.empty()) {m_binding.append(binding);} +} + +void pob::inherit(pob const &p) { + SASSERT(m_parent == p.m_parent); + SASSERT(&m_pt == &p.m_pt); + SASSERT(m_post == p.m_post); + SASSERT(!m_new_post); + + m_binding.reset(); + m_binding.append(p.m_binding); + + m_level = p.m_level; + m_depth = p.m_depth; + m_open = p.m_open; + m_use_farkas = p.m_use_farkas; + m_weakness = p.m_weakness; + + m_derivation = nullptr; +} + +void pob::clean () { + if (m_new_post) { + m_post = m_new_post; + m_new_post.reset(); + } +} + +void pob::close () { + if (!m_open) { return; } + + reset (); + m_open = false; + for (unsigned i = 0, sz = m_kids.size (); i < sz; ++i) + { m_kids [i]->close(); } +} + +void pob::get_skolems(app_ref_vector &v) { + for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { + expr* e; + e = m_binding.get(i); + v.push_back (mk_zk_const (get_ast_manager(), i, get_sort(e))); + } +} + + + // ---------------- -// pred_tansformer +// pob_queue -pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): - pm(pm), m(pm.get_manager()), - ctx(ctx), m_head(head, m), - m_sig(m), m_solver(pm, ctx.get_params(), head->get_name()), - m_reach_ctx (pm.mk_fresh3 ()), - m_pobs(*this), - m_frames(*this), - m_reach_facts(), m_rf_init_sz(0), - m_transition(m), m_initial_state(m), m_extend_lit(m), - m_all_init(false), - m_reach_case_vars(m) +pob* pob_queue::top () { - init_sig (); - app_ref v(m); - std::stringstream name; - name << m_head->get_name () << "_ext0"; - v = m.mk_const (symbol(name.str().c_str()), m.mk_bool_sort()); - m_extend_lit = m.mk_not (m.mk_const (pm.get_n_pred (v->get_decl ()))); + /// nothing in the queue + if (m_obligations.empty()) { return nullptr; } + /// top queue element is above max level + if (m_obligations.top()->level() > m_max_level) { return nullptr; } + /// top queue element is at the max level, but at a higher than base depth + if (m_obligations.top ()->level () == m_max_level && + m_obligations.top()->depth() > m_min_depth) { return nullptr; } + + /// there is something good in the queue + return m_obligations.top ().get (); } -pred_transformer::~pred_transformer() +void pob_queue::set_root(pob& root) { - rule2inst::iterator it2 = m_rule2inst.begin(), end2 = m_rule2inst.end(); - for (; it2 != end2; ++it2) { - dealloc(it2->m_value); - } - rule2expr::iterator it3 = m_rule2transition.begin(), end3 = m_rule2transition.end(); - for (; it3 != end3; ++it3) { - m.dec_ref(it3->m_value); - } + m_root = &root; + m_max_level = root.level (); + m_min_depth = root.depth (); + reset(); } -std::ostream& pred_transformer::display(std::ostream& out) const +pob_queue::~pob_queue() {} + +void pob_queue::reset() { - if (!rules().empty()) { out << "rules\n"; } - datalog::rule_manager& rm = ctx.get_datalog_context().get_rule_manager(); - for (unsigned i = 0; i < rules().size(); ++i) { - rm.display_smt2(*rules()[i], out) << "\n"; - } - out << "transition\n" << mk_pp(transition(), m) << "\n"; - return out; + while (!m_obligations.empty()) { m_obligations.pop(); } + if (m_root) { m_obligations.push(m_root); } } -void pred_transformer::collect_statistics(statistics& st) const -{ - m_solver.collect_statistics(st); - st.update("SPACER num propagations", m_stats.m_num_propagations); - st.update("SPACER num properties", m_frames.lemma_size ()); - st.update("SPACER num invariants", m_stats.m_num_invariants); - - st.update ("time.spacer.init_rules.pt.init", m_initialize_watch.get_seconds ()); - st.update ("time.spacer.solve.pt.must_reachable", - m_must_reachable_watch.get_seconds ()); +void pob_queue::push(pob &n) { + TRACE("pob_queue", + tout << "pob_queue::push(" << n.post()->get_id() << ")\n";); + m_obligations.push (&n); + n.get_context().new_pob_eh(&n); } -void pred_transformer::reset_statistics() -{ - m_solver.reset_statistics(); - //m_reachable.reset_statistics(); - m_stats.reset(); - m_initialize_watch.reset (); - m_must_reachable_watch.reset (); -} - -void pred_transformer::init_sig() -{ - for (unsigned i = 0; i < m_head->get_arity(); ++i) { - sort * arg_sort = m_head->get_domain(i); - std::stringstream name_stm; - name_stm << m_head->get_name() << '_' << i; - func_decl_ref stm(m); - stm = m.mk_func_decl(symbol(name_stm.str().c_str()), 0, (sort*const*)0, arg_sort); - m_sig.push_back(pm.get_o_pred(stm, 0)); - } -} - -void pred_transformer::ensure_level(unsigned level) -{ - if (is_infty_level(level)) { return; } - - while (m_frames.size() <= level) { - m_frames.add_frame (); - m_solver.add_level (); - } -} - -bool pred_transformer::is_must_reachable(expr* state, model_ref* model) -{ - scoped_watch _t_(m_must_reachable_watch); - SASSERT (state); - // XXX This seems to mis-handle the case when state is - // reachable using the init rule of the current transformer - if (m_reach_facts.empty()) { return false; } - - m_reach_ctx->push (); - m_reach_ctx->assert_expr (state); - m_reach_ctx->assert_expr (m.mk_not (m_reach_case_vars.back ())); - lbool res = m_reach_ctx->check_sat (0, NULL); - if (model) { m_reach_ctx->get_model(*model); } - m_reach_ctx->pop (1); - return (res == l_true); -} - - - - -reach_fact* pred_transformer::get_used_reach_fact (model_evaluator_util& mev, - bool all) -{ - expr_ref v (m); - - for (unsigned i = all ? 0 : m_rf_init_sz, sz = m_reach_case_vars.size (); - i < sz; i++) { - VERIFY (mev.eval (m_reach_case_vars.get (i), v, false)); - if (m.is_false (v)) { - return m_reach_facts.get (i); - } - } - - UNREACHABLE (); - return NULL; -} - -reach_fact *pred_transformer::get_used_origin_reach_fact (model_evaluator_util& mev, - unsigned oidx) -{ - expr_ref b(m), v(m); - reach_fact *res = NULL; - - for (unsigned i = 0, sz = m_reach_case_vars.size (); i < sz; i++) { - pm.formula_n2o (m_reach_case_vars.get (i), v, oidx); - VERIFY(mev.eval (v, b, false)); - - if (m.is_false (b)) { - res = m_reach_facts.get (i); - break; - } - } - SASSERT (res); - return res; -} - -datalog::rule const* pred_transformer::find_rule(model &model, - bool& is_concrete, - vector& reach_pred_used, - unsigned& num_reuse_reach) -{ - typedef obj_map tag2rule; - TRACE ("spacer_verbose", - datalog::rule_manager& rm = ctx.get_datalog_context().get_rule_manager(); - tag2rule::iterator it = m_tag2rule.begin(); - tag2rule::iterator end = m_tag2rule.end(); - for (; it != end; ++it) { - expr* pred = it->m_key; - tout << mk_pp(pred, m) << ":\n"; - if (it->m_value) { rm.display_smt2(*(it->m_value), tout) << "\n"; } - } - ); - - // find a rule whose tag is true in the model; - // prefer a rule where the model intersects with reach facts of all predecessors; - // also find how many predecessors' reach facts are true in the model - expr_ref vl(m); - datalog::rule const* r = ((datalog::rule*)0); - tag2rule::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); - for (; it != end; ++it) { - expr* tag = it->m_key; - if (model.eval(to_app(tag)->get_decl(), vl) && m.is_true(vl)) { - r = it->m_value; - is_concrete = true; - num_reuse_reach = 0; - reach_pred_used.reset (); - unsigned tail_sz = r->get_uninterpreted_tail_size (); - for (unsigned i = 0; i < tail_sz; i++) { - bool used = false; - func_decl* d = r->get_tail(i)->get_decl(); - pred_transformer const& pt = ctx.get_pred_transformer (d); - if (!pt.has_reach_facts()) { is_concrete = false; } - else { - expr_ref v(m); - pm.formula_n2o (pt.get_last_reach_case_var (), v, i); - model.eval (to_app (v.get ())->get_decl (), vl); - used = m.is_false (vl); - is_concrete = is_concrete && used; - } - - reach_pred_used.push_back (used); - if (used) { num_reuse_reach++; } - } - if (is_concrete) { break; } - } - } - // SASSERT (r); - // reached by a reachability fact - if (!r) { is_concrete = true; } - return r; -} - -void pred_transformer::find_predecessors(datalog::rule const& r, ptr_vector& preds) const -{ - preds.reset(); - unsigned tail_sz = r.get_uninterpreted_tail_size(); - for (unsigned ti = 0; ti < tail_sz; ti++) { - preds.push_back(r.get_tail(ti)->get_decl()); - } -} - -void pred_transformer::find_predecessors(vector >& preds) const -{ - preds.reset(); - obj_map::iterator it = m_tag2rule.begin(), end = m_tag2rule.end(); - for (; it != end; it++) { - datalog::rule const& r = *it->m_value; - unsigned tail_sz = r.get_uninterpreted_tail_size(); - for (unsigned ti = 0; ti < tail_sz; ti++) { - preds.push_back(std::make_pair (r.get_tail(ti)->get_decl(), ti)); - } - } -} - - -void pred_transformer::remove_predecessors(expr_ref_vector& literals) -{ - // remove tags - for (unsigned i = 0; i < literals.size(); ) { - expr* l = literals[i].get(); - m.is_not(l, l); - if (m_tag2rule.contains(l)) { - literals[i] = literals.back(); - literals.pop_back(); - } else { - ++i; - } - } -} - -void pred_transformer::simplify_formulas() -{ - m_frames.simplify_formulas (); -} - - -expr_ref pred_transformer::get_formulas(unsigned level, bool add_axioms) -{ - expr_ref_vector res(m); - if (add_axioms) { - res.push_back(pm.get_background()); - res.push_back((level == 0)?initial_state():transition()); - } - m_frames.get_frame_geq_lemmas (level, res); - return pm.mk_and(res); -} - -bool pred_transformer::propagate_to_next_level (unsigned src_level) -{return m_frames.propagate_to_next_level (src_level);} - - -/// \brief adds a lema to the solver and to child solvers -void pred_transformer::add_lemma_core(lemma* lemma) -{ - unsigned lvl = lemma->level(); - expr* l = lemma->get_expr(); - SASSERT(!lemma->is_ground() || is_clause(m, l)); - SASSERT(!is_quantifier(l) || is_clause(m, to_quantifier(l)->get_expr())); - - TRACE("spacer", tout << "add-lemma-core: " << pp_level (lvl) - << " " << head ()->get_name () - << " " << mk_pp (l, m) << "\n";); - - TRACE("core_array_eq", tout << "add-lemma-core: " << pp_level (lvl) - << " " << head ()->get_name () - << " " << mk_pp (l, m) << "\n";); - - STRACE ("spacer.expand-add", - tout << "add-lemma: " << pp_level (lvl) << " " - << head ()->get_name () << " " - << mk_epp (l, m) << "\n\n";); - - - if (is_infty_level(lvl)) { m_stats.m_num_invariants++; } - - if (lemma->is_ground()) { - if (is_infty_level(lvl)) { m_solver.assert_expr(l); } - else { - ensure_level (lvl); - m_solver.assert_expr (l, lvl); - } - } - - for (unsigned i = 0, sz = m_use.size (); i < sz; ++i) - { m_use [i]->add_lemma_from_child(*this, lemma, next_level(lvl)); } -} - -bool pred_transformer::add_lemma (expr *e, unsigned lvl) { - lemma_ref lem = alloc(lemma, m, e, lvl); - return m_frames.add_lemma(lem.get()); -} - -void pred_transformer::add_lemma_from_child (pred_transformer& child, - lemma* lemma, unsigned lvl) -{ - ensure_level(lvl); - expr_ref_vector fmls(m); - mk_assumptions(child.head(), lemma->get_expr(), fmls); - - for (unsigned i = 0; i < fmls.size(); ++i) { - expr_ref_vector inst(m); - expr* a = to_app(fmls.get(i))->get_arg(0); - expr* l = to_app(fmls.get(i))->get_arg(1); - if (get_context().use_instantiate()) - { lemma->mk_insts(inst, l); } - for (unsigned j=0; j < inst.size(); j++) { - inst.set(j, m.mk_implies(a, inst.get(j))); - } - if (lemma->is_ground() || get_context().use_qlemmas()) - { inst.push_back(fmls.get(i)); } - SASSERT (!inst.empty ()); - for (unsigned j = 0; j < inst.size(); ++j) { - TRACE("spacer_detail", tout << "child property: " - << mk_pp(inst.get (j), m) << "\n";); - if (is_infty_level(lvl)) - { m_solver.assert_expr(inst.get(j)); } - else - { m_solver.assert_expr(inst.get(j), lvl); } - } - } - -} - -expr* pred_transformer::mk_fresh_reach_case_var () -{ - std::stringstream name; - func_decl_ref decl(m); - - name << head ()->get_name () << "#reach_case_" << m_reach_case_vars.size (); - decl = m.mk_func_decl (symbol (name.str ().c_str ()), 0, - (sort*const*)0, m.mk_bool_sort ()); - m_reach_case_vars.push_back (m.mk_const (pm.get_n_pred (decl))); - return m_reach_case_vars.back (); -} - -expr* pred_transformer::get_reach_case_var (unsigned idx) const -{return m_reach_case_vars.get (idx);} - - -void pred_transformer::add_reach_fact (reach_fact *fact) -{ - timeit _timer (is_trace_enabled("spacer_timeit"), - "spacer::pred_transformer::add_reach_fact", - verbose_stream ()); - - TRACE ("spacer", - tout << "add_reach_fact: " << head()->get_name() << " " - << (fact->is_init () ? "INIT " : "") - << mk_pp(fact->get (), m) << "\n";); - - // -- avoid duplicates - if (fact == nullptr || get_reach_fact(fact->get())) { return; } - - // all initial facts are grouped together - SASSERT (!fact->is_init () || m_reach_facts.empty () || - m_reach_facts.back ()->is_init ()); - - m_reach_facts.push_back (fact); - if (fact->is_init()) { m_rf_init_sz++; } - - - // update m_reach_ctx - expr_ref last_var (m); - expr_ref new_var (m); - expr_ref fml (m); - - if (!m_reach_case_vars.empty()) { last_var = m_reach_case_vars.back(); } - if (fact->is_init () || !ctx.get_params ().spacer_reach_as_init ()) - { new_var = mk_fresh_reach_case_var(); } - else { - new_var = extend_initial (fact->get ())->get_arg (0); - m_reach_case_vars.push_back (new_var); - } - - SASSERT (m_reach_facts.size () == m_reach_case_vars.size ()); - - if (last_var) - { fml = m.mk_or(m.mk_not(last_var), fact->get(), new_var); } - else - { fml = m.mk_or(fact->get(), new_var); } - - m_reach_ctx->assert_expr (fml); - TRACE ("spacer", - tout << "updating reach ctx: " << mk_pp(fml, m) << "\n";); - - lemma lem(m, fml, infty_level()); - // update users; reach facts are independent of levels - for (unsigned i = 0; i < m_use.size(); ++i) { - m_use[i]->add_lemma_from_child (*this, &lem, infty_level ()); - } -} - -expr_ref pred_transformer::get_reachable() -{ - expr_ref res(m); - res = m.mk_false(); - - if (!m_reach_facts.empty()) { - expr_substitution sub(m); - expr_ref c(m), v(m); - for (unsigned i = 0, sz = sig_size(); i < sz; ++i) { - c = m.mk_const(pm.o2n(sig(i), 0)); - v = m.mk_var(i, sig(i)->get_range()); - sub.insert(c, v); - } - scoped_ptr rep = mk_expr_simp_replacer(m); - rep->set_substitution(&sub); - - expr_ref_vector args(m); - for (unsigned i = 0, sz = m_reach_facts.size (); i < sz; ++i) { - reach_fact *f; - f = m_reach_facts.get(i); - expr_ref r(m); - r = f->get(); - const ptr_vector &aux = f->aux_vars(); - if (!aux.empty()) { - // -- existentially quantify auxiliary variables - r = mk_exists (m, aux.size(), aux.c_ptr(), r); - // XXX not sure how this interacts with variable renaming later on. - // XXX For now, simply dissallow existentially quantified auxiliaries - NOT_IMPLEMENTED_YET(); - } - (*rep)(r); - - args.push_back (r); - } - res = mk_or(args); - } - return res; -} - -expr* pred_transformer::get_last_reach_case_var () const -{ - return m_reach_case_vars.empty () ? NULL : m_reach_case_vars.back (); -} - -expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) -{ - expr_ref result(m.mk_true(), m), v(m), c(m); - - expr_ref_vector lemmas (m); - m_frames.get_frame_lemmas (level == -1 ? infty_level() : level, lemmas); - if (!lemmas.empty()) { result = pm.mk_and(lemmas); } - - // replace local constants by bound variables. - expr_substitution sub(m); - 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(c, v); - } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - (*rep)(result); - - // adjust result according to model converter. - unsigned arity = m_head->get_arity(); - model_ref md = alloc(model, m); - if (arity == 0) { - md->register_decl(m_head, result); - } else { - func_interp* fi = alloc(func_interp, m, arity); - fi->set_else(result); - md->register_decl(m_head, fi); - } - model_converter_ref mc = ctx.get_model_converter(); - apply(mc, md, 0); - if (p_orig->get_arity() == 0) { - result = md->get_const_interp(p_orig); - } else { - result = md->get_func_interp(p_orig)->get_interp(); - } - return result; -} - -/** - * get an origin summary used by this transformer in the given model - * level is the level at which may summaries are obtained - * oidx is the origin index of this predicate in the model - * must indicates whether a must or a may summary is requested - * - * returns an implicant of the summary - */ -expr_ref pred_transformer::get_origin_summary (model_evaluator_util &mev, - unsigned level, - unsigned oidx, - bool must, - const ptr_vector **aux) -{ - expr_ref_vector summary (m); - expr_ref v(m); - - if (!must) { // use may summary - summary.push_back (get_formulas (level, false)); - // -- no auxiliary variables in lemmas - *aux = NULL; - } else { // find must summary to use - reach_fact *f = get_used_origin_reach_fact (mev, oidx); - summary.push_back (f->get ()); - *aux = &f->aux_vars (); - } - - SASSERT (!summary.empty ()); - - // -- convert to origin - for (unsigned i = 0; i < summary.size(); ++i) { - pm.formula_n2o (summary.get (i), v, oidx); - summary[i] = v; - } - - // -- pick an implicant - expr_ref_vector literals (m); - compute_implicant_literals (mev, summary, literals); - - return get_manager ().mk_and (literals); -} - - -void pred_transformer::add_cover(unsigned level, expr* property) -{ - // replace bound variables by local constants. - expr_ref result(property, m), v(m), c(m); - expr_substitution sub(m); - 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); - } - scoped_ptr rep = mk_default_expr_replacer(m); - rep->set_substitution(&sub); - (*rep)(result); - TRACE("spacer", tout << "cover:\n" << mk_pp(result, m) << "\n";); - - // add the 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); - } -} - -void pred_transformer::propagate_to_infinity (unsigned level) -{m_frames.propagate_to_infinity (level);} - - - -/// \brief Returns true if the obligation is already blocked by current lemmas -bool pred_transformer::is_blocked (pob &n, unsigned &uses_level) -{ - ensure_level (n.level ()); - prop_solver::scoped_level _sl (m_solver, n.level ()); - m_solver.set_core (NULL); - m_solver.set_model (NULL); - - expr_ref_vector post(m), aux(m); - post.push_back (n.post ()); - lbool res = m_solver.check_assumptions (post, aux, 0, NULL, 0); - if (res == l_false) { uses_level = m_solver.uses_level(); } - return res == l_false; -} - -bool pred_transformer::is_qblocked (pob &n) -{ - // XXX Trivial implementation to get us started - smt::kernel solver (m, get_manager ().fparams2()); - expr_ref_vector frame_lemmas(m); - m_frames.get_frame_geq_lemmas (n.level (), frame_lemmas); - - // assert all lemmas - for (unsigned i = 0, sz = frame_lemmas.size (); i < sz; ++i) - { solver.assert_expr(frame_lemmas.get(i)); } - // assert cti - solver.assert_expr (n.post ()); - lbool res = solver.check (); - - return res == l_false; -} - -// -// check if predicate transformer has a satisfiable predecessor state. -// returns either a satisfiable predecessor state or -// return a property that blocks state and is implied by the -// predicate transformer (or some unfolding of it). -// -lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, - model_ref* model, unsigned& uses_level, - bool& is_concrete, datalog::rule const*& r, - vector& reach_pred_used, - unsigned& num_reuse_reach) -{ - TRACE("spacer", - tout << "is-reachable: " << head()->get_name() << " level: " - << n.level() << " depth: " << n.depth () << "\n"; - tout << mk_pp(n.post(), m) << "\n";); - timeit _timer (is_trace_enabled("spacer_timeit"), - "spacer::pred_transformer::is_reachable", - verbose_stream ()); - - ensure_level(n.level()); - - // prepare the solver - prop_solver::scoped_level _sl(m_solver, n.level()); - prop_solver::scoped_subset_core _sc (m_solver, !n.use_farkas_generalizer ()); - m_solver.set_core(core); - m_solver.set_model(model); - - expr_ref_vector post (m), reach_assumps (m); - post.push_back (n.post ()); - - // populate reach_assumps - - // XXX eager_reach_check must always be - // XXX enabled. Otherwise, we can get into an infinite loop in - // XXX which a model is consistent with a must-summary, but the - // XXX appropriate assumption is not set correctly by the model. - // XXX Original code handled reachability-events differently. - if (/* ctx.get_params ().eager_reach_check () && */ - n.level () > 0 && !m_all_init) { - obj_map::iterator it = m_tag2rule.begin (), - end = m_tag2rule.end (); - for (; it != end; ++it) { - datalog::rule const* r = it->m_value; - if (!r) { continue; } - find_predecessors(*r, m_predicates); - if (m_predicates.empty()) { continue; } - for (unsigned i = 0; i < m_predicates.size(); i++) { - const pred_transformer &pt = - ctx.get_pred_transformer (m_predicates [i]); - if (pt.has_reach_facts()) { - expr_ref a(m); - pm.formula_n2o (pt.get_last_reach_case_var (), a, i); - reach_assumps.push_back (m.mk_not (a)); - } else if (ctx.get_params().spacer_init_reach_facts()) { - reach_assumps.push_back (m.mk_not (it->m_key)); - break; - } - } - } - } - - TRACE ("spacer", - if (!reach_assumps.empty ()) { - tout << "reach assumptions\n"; - for (unsigned i = 0; i < reach_assumps.size (); i++) { - tout << mk_pp (reach_assumps.get (i), m) << "\n"; - } - } - ); - - // check local reachability; - // result is either sat (with some reach assumps) or - // unsat (even with no reach assumps) - expr *bg = m_extend_lit.get (); - lbool is_sat = m_solver.check_assumptions (post, reach_assumps, 1, &bg, 0); - - TRACE ("spacer", - if (!reach_assumps.empty ()) { - tout << "reach assumptions used\n"; - for (unsigned i = 0; i < reach_assumps.size (); i++) { - tout << mk_pp (reach_assumps.get (i), m) << "\n"; - } - } - ); - - if (is_sat == l_true || is_sat == l_undef) { - if (core) { core->reset(); } - if (model) { - r = find_rule (**model, is_concrete, reach_pred_used, num_reuse_reach); - TRACE ("spacer", tout << "reachable " - << "is_concrete " << is_concrete << " rused: "; - for (unsigned i = 0, sz = reach_pred_used.size (); i < sz; ++i) - tout << reach_pred_used [i]; - tout << "\n";); - } - - return is_sat; - } - if (is_sat == l_false) { - SASSERT (reach_assumps.empty ()); - TRACE ("spacer", tout << "unreachable with lemmas\n"; - if (core) { - tout << "Core:\n"; - for (unsigned i = 0; i < core->size (); i++) { - tout << mk_pp (core->get(i), m) << "\n"; - } - } - ); - uses_level = m_solver.uses_level(); - return l_false; - } - UNREACHABLE(); - return l_undef; -} - -bool pred_transformer::is_invariant(unsigned level, expr* lemma, - unsigned& solver_level, expr_ref_vector* core) -{ - expr_ref_vector conj(m), aux(m); - expr_ref glemma(m); - - if (false && is_quantifier(lemma)) { - SASSERT(is_forall(lemma)); - app_ref_vector tmp(m); - ground_expr(to_quantifier(lemma)->get_expr (), glemma, tmp); - lemma = glemma.get(); - } - - conj.push_back(mk_not(m, lemma)); - flatten_and (conj); - - prop_solver::scoped_level _sl(m_solver, level); - prop_solver::scoped_subset_core _sc (m_solver, true); - m_solver.set_core(core); - m_solver.set_model(0); - expr * bg = m_extend_lit.get (); - lbool r = m_solver.check_assumptions (conj, aux, 1, &bg, 1); - if (r == l_false) { - solver_level = m_solver.uses_level (); - CTRACE ("spacer", level < m_solver.uses_level (), - tout << "Checking at level " << level - << " but only using " << m_solver.uses_level () << "\n";); - SASSERT (level <= solver_level); - } - return r == l_false; -} - -bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state, - unsigned& uses_level) -{ - manager& pm = get_manager(); - expr_ref_vector conj(m), core(m); - expr_ref states(m); - states = m.mk_not(pm.mk_and(state)); - mk_assumptions(head(), states, conj); - prop_solver::scoped_level _sl(m_solver, level); - prop_solver::scoped_subset_core _sc (m_solver, true); - m_solver.set_core(&core); - m_solver.set_model (0); - expr_ref_vector aux (m); - conj.push_back (m_extend_lit); - lbool res = m_solver.check_assumptions (state, aux, conj.size (), conj.c_ptr (), 1); - if (res == l_false) { - state.reset(); - state.append(core); - uses_level = m_solver.uses_level(); - } - TRACE ("core_array_eq", - tout << "check_inductive: " - << "states: " << mk_pp (states, m) - << " is: " << res << "\n" - << "with transition: " << mk_pp (m_transition, m) << "\n";); - return res == l_false; -} - -void pred_transformer::mk_assumptions(func_decl* head, expr* fml, - expr_ref_vector& result) -{ - expr_ref tmp1(m), tmp2(m); - expr_substitution sub (m); - proof_ref pr (m.mk_asserted (m.mk_true ()), m); - obj_map::iterator it = m_tag2rule.begin(), - end = m_tag2rule.end(); - for (; it != end; ++it) { - expr* tag = it->m_key; - datalog::rule const* r = it->m_value; - if (!r) { continue; } - find_predecessors(*r, m_predicates); - for (unsigned i = 0; i < m_predicates.size(); i++) { - func_decl* d = m_predicates[i]; - if (d == head) { - tmp1 = m.mk_implies(tag, fml); - pm.formula_n2o(tmp1, tmp2, i); - result.push_back(tmp2); - } - } - } -} - -void pred_transformer::initialize(decl2rel const& pts) -{ - m_initial_state = m.mk_false(); - m_transition = m.mk_true(); - init_rules(pts, m_initial_state, m_transition); - th_rewriter rw(m); - rw(m_transition); - rw(m_initial_state); - - m_solver.assert_expr (m_transition); - m_solver.assert_expr (m_initial_state, 0); - TRACE("spacer", - tout << "Initial state: " << mk_pp(m_initial_state, m) << "\n"; - tout << "Transition: " << mk_pp(m_transition, m) << "\n";); - SASSERT(is_app(m_initial_state)); - //m_reachable.add_init(to_app(m_initial_state)); - - -} - -void pred_transformer::init_reach_facts () -{ - expr_ref_vector v(m); - reach_fact_ref fact; - - rule2expr::iterator it = m_rule2tag.begin (), end = m_rule2tag.end (); - for (; it != end; ++it) { - const datalog::rule* r = it->m_key; - if (r->get_uninterpreted_tail_size() == 0) { - fact = alloc (reach_fact, m, *r, m_rule2transition.find (r), - get_aux_vars (*r), true); - add_reach_fact (fact.get ()); - } - } -} - -void pred_transformer::init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition) -{ - expr_ref_vector transitions(m); - ptr_vector tr_rules; - datalog::rule const* rule; - expr_ref_vector disj(m), init_conds (m); - app_ref pred(m); - vector is_init; - for (unsigned i = 0; i < rules().size(); ++i) { - init_rule(pts, *rules()[i], is_init, tr_rules, transitions); - } - SASSERT (is_init.size () == transitions.size ()); - switch(transitions.size()) { - case 0: - transition = m.mk_false(); - break; - case 1: { - std::stringstream name; - // create a dummy tag. - name << head()->get_name() << "_dummy"; - pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); - rule = tr_rules[0]; - m_tag2rule.insert(pred, rule); - m_rule2tag.insert(rule, pred.get()); - transitions [0] = m.mk_implies (pred, transitions.get (0)); - transitions.push_back (m.mk_or (pred, m_extend_lit->get_arg (0))); - if (!is_init [0]) { init_conds.push_back(m.mk_not(pred)); } - - transition = pm.mk_and(transitions); - break; - } - default: - disj.push_back (m_extend_lit->get_arg (0)); - for (unsigned i = 0; i < transitions.size(); ++i) { - std::stringstream name; - name << head()->get_name() << "_tr" << i; - pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); - rule = tr_rules[i]; - m_tag2rule.insert(pred, rule); - m_rule2tag.insert(rule, pred); - disj.push_back(pred); - transitions[i] = m.mk_implies(pred, transitions[i].get()); - // update init conds - if (!is_init[i]) { - init_conds.push_back (m.mk_not (pred)); - } - } - transitions.push_back(m.mk_or(disj.size(), disj.c_ptr())); - transition = pm.mk_and(transitions); - break; - } - // mk init condition - init = pm.mk_and (init_conds); - if (init_conds.empty ()) { // no rule has uninterpreted tail - m_all_init = true; - } -} - -void pred_transformer::init_rule( - decl2rel const& pts, - datalog::rule const& rule, - vector& is_init, - ptr_vector& rules, - expr_ref_vector& transitions) -{ - scoped_watch _t_(m_initialize_watch); - - // Predicates that are variable representatives. Other predicates at - // positions the variables occur are made equivalent with these. - expr_ref_vector conj(m); - app_ref_vector& var_reprs = *(alloc(app_ref_vector, m)); - ptr_vector aux_vars; - - unsigned ut_size = rule.get_uninterpreted_tail_size(); - unsigned t_size = rule.get_tail_size(); - SASSERT(ut_size <= t_size); - init_atom(pts, rule.get_head(), var_reprs, conj, UINT_MAX); - for (unsigned i = 0; i < ut_size; ++i) { - if (rule.is_neg_tail(i)) { - throw default_exception("SPACER does not support negated predicates in rule tails"); - } - init_atom(pts, rule.get_tail(i), var_reprs, conj, i); - } - // -- substitute free variables - expr_ref fml(m); - { - expr_ref_vector tail(m); - for (unsigned i = ut_size; i < t_size; ++i) - { tail.push_back(rule.get_tail(i)); } - fml = mk_and (tail); - - ground_free_vars (fml, var_reprs, aux_vars, ut_size == 0); - SASSERT(check_filled(var_reprs)); - - expr_ref tmp(m); - var_subst (m, false)(fml, - var_reprs.size (), (expr*const*)var_reprs.c_ptr(), tmp); - flatten_and (tmp, conj); - fml = mk_and(conj); - conj.reset (); - } - - th_rewriter rw(m); - rw(fml); - if (ctx.get_params().spacer_blast_term_ite()) { - blast_term_ite (fml); - rw(fml); - } - TRACE("spacer", tout << mk_pp(fml, m) << "\n";); - - // allow quantifiers in init rule - SASSERT(ut_size == 0 || is_ground(fml)); - if (m.is_false(fml)) { - // no-op. - } else { - is_init.push_back (ut_size == 0); - transitions.push_back(fml); - m.inc_ref(fml); - m_rule2transition.insert(&rule, fml.get()); - rules.push_back(&rule); - } - m_rule2inst.insert(&rule,&var_reprs); - m_rule2vars.insert(&rule, aux_vars); - TRACE("spacer", - tout << rule.get_decl()->get_name() << "\n"; - for (unsigned i = 0; i < var_reprs.size(); ++i) { - tout << mk_pp(var_reprs[i].get(), m) << " "; - } - tout << "\n";); -} - -bool pred_transformer::check_filled(app_ref_vector const& v) const -{ - for (unsigned i = 0; i < v.size(); ++i) { - if (!v[i]) { return false; } - } - return true; -} - -// create constants for free variables in tail. -void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, - ptr_vector& aux_vars, bool is_init) -{ - expr_free_vars fv; - fv(e); - - while (vars.size() < fv.size()) { - vars.push_back(0); - } - for (unsigned i = 0; i < fv.size(); ++i) { - if (fv[i] && !vars[i].get()) { - vars[i] = m.mk_fresh_const("aux", fv[i]); - vars[i] = m.mk_const (pm.get_n_pred (vars.get (i)->get_decl ())); - aux_vars.push_back(vars[i].get()); - } - } - -} - -// create names for variables used in relations. -void pred_transformer::init_atom( - decl2rel const& pts, - app * atom, - app_ref_vector& var_reprs, - expr_ref_vector& conj, - unsigned tail_idx - ) -{ - unsigned arity = atom->get_num_args(); - func_decl* head = atom->get_decl(); - pred_transformer& pt = *pts.find(head); - for (unsigned i = 0; i < arity; i++) { - app_ref rep(m); - - if (tail_idx == UINT_MAX) { - rep = m.mk_const(pm.o2n(pt.sig(i), 0)); - } else { - rep = m.mk_const(pm.o2o(pt.sig(i), 0, tail_idx)); - } - - expr * arg = atom->get_arg(i); - if (is_var(arg)) { - var * v = to_var(arg); - unsigned var_idx = v->get_idx(); - if (var_idx >= var_reprs.size()) { - var_reprs.resize(var_idx+1); - } - expr * repr = var_reprs[var_idx].get(); - if (repr) { - conj.push_back(m.mk_eq(rep, repr)); - } else { - var_reprs[var_idx] = rep; - } - } else { - SASSERT(is_app(arg)); - conj.push_back(m.mk_eq(rep, arg)); - } - } -} - -void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r) -{ - r.push_back(pm.get_background()); - r.push_back((lvl == 0)?initial_state():transition()); - for (unsigned i = 0; i < rules().size(); ++i) { - add_premises(pts, lvl, *rules()[i], r); - } -} - -void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r) -{ - find_predecessors(rule, m_predicates); - for (unsigned i = 0; i < m_predicates.size(); ++i) { - expr_ref tmp(m); - func_decl* head = m_predicates[i]; - pred_transformer& pt = *pts.find(head); - expr_ref inv = pt.get_formulas(lvl, false); - if (!m.is_true(inv)) { - pm.formula_n2o(inv, tmp, i, true); - r.push_back(tmp); - } - } -} - -void pred_transformer::inherit_properties(pred_transformer& other) -{ - m_frames.inherit_frames (other.m_frames); -} - - -lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : - m_ref_count(0), m(manager), - m_body(body, m), m_cube(m), - m_bindings(m), m_lvl(lvl), - m_pob(0), m_new_pob(false) { - SASSERT(m_body); - normalize(m_body, m_body); -} - -lemma::lemma(pob_ref const &p) : - m_ref_count(0), m(p->get_ast_manager()), - m_body(m), m_cube(m), - m_bindings(m), m_lvl(p->level()), - m_pob(p), m_new_pob(m_pob) {SASSERT(m_pob);} - -lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : - m_ref_count(0), - m(p->get_ast_manager()), - m_body(m), m_cube(m), - m_bindings(m), m_lvl(p->level()), - m_pob(p), m_new_pob(m_pob) -{ - update_cube(p, cube); - set_level(lvl); -} - -void lemma::mk_expr_core() { - if (m_body) return; - - if (m_pob) { - mk_cube_core(); - - // make a clause by negating the cube - m_body = ::push_not(::mk_and(m_cube)); - normalize(m_body, m_body); - - if (!m_pob->is_ground() && has_zk_const(m_body)) { - app_ref_vector zks(m); - m_pob->get_skolems(zks); - zks.reverse(); - expr_abstract(m, 0, - zks.size(), (expr* const*)zks.c_ptr(), m_body, - m_body); - ptr_buffer sorts; - svector names; - for (unsigned i=0, sz=zks.size(); i < sz; ++i) { - sorts.push_back(get_sort(zks.get(i))); - names.push_back(zks.get(i)->get_decl()->get_name()); - } - m_body = m.mk_quantifier(true, zks.size(), - sorts.c_ptr(), - names.c_ptr(), - m_body, 0, symbol(m_body->get_id())); - if (m_new_pob) { - add_binding(m_pob->get_binding()); - } - } - m_new_pob = false; - return; - } - else if (!m_cube.empty()) { - m_body = ::push_not(::mk_and(m_cube)); - normalize(m_body, m_body); - return; - } - else { - UNREACHABLE(); - } - SASSERT(m_body); -} -void lemma::mk_cube_core() { - if (!m_cube.empty()) {return;} - expr_ref cube(m); - if (m_pob || m_body) { - if(m_pob) { - cube = m_pob->post(); - } - else if (m_body) { - // no quantifiers for now - SASSERT(!is_quantifier(m_body)); - cube = m_body; - cube = ::push_not(cube); - } - flatten_and(cube, m_cube); - if (m_cube.empty()) { - m_cube.push_back(m.mk_true()); - } - else { - std::sort(m_cube.c_ptr(), m_cube.c_ptr() + m_cube.size(), ast_lt_proc()); - } - } - else { - UNREACHABLE(); - } -} -bool lemma::is_false() { - // a lemma is false if - // 1. it is defined by a cube, and the cube contains a single literal 'true' - // 2. it is defined by a body, and the body is a single literal false - // 3. it is defined by a pob, and the pob post is false - if (m_cube.size() == 1) {return m.is_true(m_cube.get(0));} - else if (m_body) {return m.is_false(m_body);} - else if (m_pob) {return m.is_true(m_pob->post());} - - return false; -} -expr* lemma::get_expr() { - mk_expr_core(); - return m_body; -} -expr_ref_vector const &lemma::get_cube() { - mk_cube_core(); - return m_cube; -} - -void lemma::update_cube (pob_ref const &p, expr_ref_vector &cube) { - SASSERT(m_pob); - SASSERT(m_pob.get() == p.get()); - m_cube.reset(); - m_body.reset(); - m_cube.append(cube); - if (m_cube.empty()) {m_cube.push_back(m.mk_true());} -} - -void lemma::mk_insts(expr_ref_vector &out, expr* e) -{ - expr *lem = e == nullptr ? get_expr() : e; - if (!is_quantifier (lem) || m_bindings.empty()) {return;} - - expr *body = to_quantifier(lem)->get_expr(); - unsigned num_decls = to_quantifier(lem)->get_num_decls(); - expr_ref inst(m); - var_subst vs(m, false); - for (unsigned i = 0, - sz = m_bindings.size() / num_decls, - off = 0; - i < sz; - ++i, off += num_decls) { - inst.reset(); - vs.reset(); - vs(body, num_decls, (expr**) m_bindings.c_ptr() + off, inst); - out.push_back(inst); - } -} - -bool pred_transformer::frames::add_lemma(lemma *lem) -{ - TRACE("spacer", tout << "add-lemma: " << pp_level(lem->level()) << " " - << m_pt.head()->get_name() << " " - << mk_pp(lem->get_expr(), m_pt.get_ast_manager()) << "\n";); - - for (unsigned i = 0, sz = m_lemmas.size(); i < sz; ++i) { - if (m_lemmas [i]->get_expr() == lem->get_expr()) { - // extend bindings if needed - if (!lem->get_bindings().empty()) { - m_lemmas [i]->add_binding(lem->get_bindings()); - } - // if the lemma is at a higher level, skip it - // XXX if there are new bindings, we need to assert new instances - if (m_lemmas [i]->level() >= lem->level()) { - TRACE("spacer", tout << "Already at a higher level: " - << pp_level(m_lemmas [i]->level()) << "\n";); - return false; - } - - // update level of the existing lemma - m_lemmas [i]->set_level(lem->level()); - // assert lemma in the solver - m_pt.add_lemma_core(m_lemmas[i]); - // move the lemma to its new place to maintain sortedness - for (unsigned j = i; (j + 1) < sz && m_lt(m_lemmas [j + 1], m_lemmas[j]); ++j) { - m_lemmas.swap (j, j+1); - } - - return true; - } - } - - // did not find, create new lemma - m_lemmas.push_back(lem); - m_sorted = false; - m_pt.add_lemma_core(lem); - return true; -} - - -void pred_transformer::frames::propagate_to_infinity (unsigned level) -{ - for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i) - if (m_lemmas[i]->level() >= level && !is_infty_level(m_lemmas [i]->level())) { - m_lemmas [i]->set_level (infty_level ()); - m_pt.add_lemma_core (m_lemmas [i]); - m_sorted = false; - } -} - -void pred_transformer::frames::sort () -{ - if (m_sorted) { return; } - - m_sorted = true; - std::sort(m_lemmas.c_ptr(), m_lemmas.c_ptr() + m_lemmas.size (), m_lt); -} - -bool pred_transformer::frames::propagate_to_next_level (unsigned level) -{ - sort (); - bool all = true; - - - if (m_lemmas.empty()) { return all; } - - unsigned tgt_level = next_level (level); - m_pt.ensure_level (tgt_level); - - for (unsigned i = 0, sz = m_lemmas.size(); i < sz && m_lemmas [i]->level() <= level;) { - if (m_lemmas [i]->level () < level) - {++i; continue;} - - - unsigned solver_level; - expr * curr = m_lemmas [i]->get_expr (); - if (m_pt.is_invariant(tgt_level, curr, solver_level)) { - m_lemmas [i]->set_level (solver_level); - m_pt.add_lemma_core (m_lemmas [i]); - - // percolate the lemma up to its new place - for (unsigned j = i; (j+1) < sz && m_lt (m_lemmas[j+1], m_lemmas[j]); ++j) { - m_lemmas.swap(j, j + 1); - } - } else { - all = false; - ++i; - } - } - - return all; -} - -void pred_transformer::frames::simplify_formulas () -{ - // number of subsumed lemmas - unsigned num_sumbsumed = 0; - - // ensure that the lemmas are sorted - sort(); - ast_manager &m = m_pt.get_ast_manager (); - - tactic_ref simplifier = mk_unit_subsumption_tactic (m); - lemma_ref_vector new_lemmas; - - unsigned lemmas_size = m_lemmas.size (); - goal_ref g (alloc (goal, m, false, false, false)); - - unsigned j = 0; - // for every frame + infinity frame - for (unsigned i = 0; i <= m_size; ++i) { - g->reset_all (); - // normalize level - unsigned level = i < m_size ? i : infty_level (); - - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); - goal_ref_buffer result; - - // simplify lemmas of the current level - // XXX lemmas of higher levels can be assumed in background - // XXX decide what to do with non-ground lemmas! - unsigned begin = j; - for (; j < lemmas_size && m_lemmas[j]->level() <= level; ++j) { - if (m_lemmas[j]->level() == level) { - g->assert_expr(m_lemmas[j]->get_expr()); - } - } - unsigned end = j; - - unsigned sz = end - begin; - // no lemmas at current level, move to next level - if (sz <= 0) {continue;} - - // exactly one lemma at current level, nothing to - // simplify. move to next level - if (sz == 1) { - new_lemmas.push_back(m_lemmas[begin]); - continue; - } - - // more than one lemma at current level. simplify. - (*simplifier)(g, result, mc, pc, core); - SASSERT(result.size () == 1); - goal *r = result[0]; - - // no simplification happened, copy all the lemmas - if (r->size () == sz) { - for (unsigned n = begin; n < end; ++n) { - new_lemmas.push_back (m_lemmas[n]); - } - } - // something got simplified, find out which lemmas remain - else { - num_sumbsumed += (sz - r->size()); - // For every expression in the result, copy corresponding - // lemma into new_lemmas - // XXX linear search. optimize if needed. - for (unsigned k = 0; k < r->size(); ++k) { - bool found = false; - for (unsigned n = begin; n < end; ++n) { - if (m_lemmas[n]->get_expr() == r->form(k)) { - new_lemmas.push_back(m_lemmas[n]); - found = true; - break; - } - } - if (!found) { - verbose_stream() << "Failed to find a lemma for: " - << mk_pp(r->form(k), m) << "\n"; - verbose_stream() << "Available lemmas are: "; - for (unsigned n = begin; n < end; ++n) { - verbose_stream() << n << ": " - << mk_pp(m_lemmas[n]->get_expr(), m) - << "\n"; - } - } - ENSURE(found); - SASSERT(found); - } - } - } - - SASSERT(new_lemmas.size() + num_sumbsumed == m_lemmas.size()); - ENSURE(new_lemmas.size() + num_sumbsumed == m_lemmas.size()); - if (new_lemmas.size() < m_lemmas.size()) { - m_lemmas.reset(); - m_lemmas.append(new_lemmas); - m_sorted = false; - sort(); - } -} - -pob* pred_transformer::pobs::mk_pob(pob *parent, - unsigned level, unsigned depth, - expr *post, app_ref_vector const &b) { - - if (!m_pt.ctx.get_params().spacer_reuse_pobs()) { - pob* n = alloc(pob, parent, m_pt, level, depth); - n->set_post(post, b); - return n; - } - - // create a new pob and set its post to normalize it - pob p(parent, m_pt, level, depth, false); - p.set_post(post, b); - - if (m_pobs.contains(p.post())) { - auto &buf = m_pobs[p.post()]; - for (unsigned i = 0, sz = buf.size(); i < sz; ++i) { - pob *f = buf.get(i); - if (f->parent() == parent) { - f->inherit(p); - return f; - } - } - } - - pob* n = alloc(pob, parent, m_pt, level, depth); - n->set_post(post, b); - m_pinned.push_back(n); - - if (m_pobs.contains(n->post())) { - m_pobs[n->post()].push_back(n); - } - else { - pob_buffer buf; - buf.push_back(n); - m_pobs.insert(n->post(), buf); - } - return n; -} - -app* pred_transformer::extend_initial (expr *e) -{ - // create fresh extend literal - app_ref v(m); - std::stringstream name; - name << m_head->get_name() << "_ext"; - v = m.mk_fresh_const (name.str ().c_str (), - m.mk_bool_sort ()); - v = m.mk_const (pm.get_n_pred (v->get_decl ())); - - expr_ref ic(m); - - // -- extend the initial condition - ic = m.mk_or (m_extend_lit, e, v); - m_solver.assert_expr (ic); - - // -- remember the new extend literal - m_extend_lit = m.mk_not (v); - - return m_extend_lit; -} - - // ---------------- // derivation @@ -1521,6 +183,233 @@ derivation::derivation (pob& parent, datalog::rule const& rule, m_trans (trans, m_parent.get_ast_manager ()), m_evars (evars) {} + + +void derivation::add_premise (pred_transformer &pt, + unsigned oidx, + expr* summary, + bool must, + const ptr_vector *aux_vars) +{m_premises.push_back (premise (pt, oidx, summary, must, aux_vars));} + + + +pob *derivation::create_first_child (model &mdl) { + if (m_premises.empty()) { return nullptr; } + m_active = 0; + return create_next_child(mdl); +} + +void derivation::exist_skolemize(expr* fml, app_ref_vector &vars, expr_ref &res) { + ast_manager &m = get_ast_manager(); + if (vars.empty()) {res = fml; return;} + if (m.is_true(fml) || m.is_false(fml)) {res = fml; return;} + + { + std::stable_sort (vars.c_ptr(), vars.c_ptr() + vars.size(), sk_lt_proc()); + unsigned i, j, end; + app_ref v(m); + for (i = 1, j = 1, end = vars.size(); i < end; ++i) { + if (vars.get(j-1) != vars.get(i)) { + v = vars.get(i); // keep ref + vars.set(j++, v); + } + } + vars.shrink(j); + } + + TRACE("spacer", tout << "Skolemizing: "; + for (auto v : vars) tout << " " << mk_pp(v, m) << " "; + tout << "\nfrom " << mk_pp(fml, m) << "\n"; + ); + + app_ref_vector pinned(m); + + expr_safe_replace sub(m); + for (unsigned i = 0, sz = vars.size(); i < sz; ++i) { + expr* e; + e = vars.get(i); + pinned.push_back (mk_zk_const (m, i, get_sort(e))); + sub.insert (e, pinned.back()); + } + sub(fml, res); +} + +pob *derivation::create_next_child(model &mdl) +{ + timeit _timer (is_trace_enabled("spacer_timeit"), + "spacer::derivation::create_next_child", + verbose_stream ()); + + ast_manager &m = get_ast_manager (); + expr_ref_vector summaries (m); + app_ref_vector vars(m); + + // -- find first may premise + while (m_active < m_premises.size() && m_premises[m_active].is_must()) { + summaries.push_back (m_premises[m_active].get_summary ()); + vars.append (m_premises[m_active].get_ovars ()); + ++m_active; + } + if (m_active >= m_premises.size()) { return nullptr; } + + // -- update m_trans with the pre-image of m_trans over the must summaries + summaries.push_back (m_trans); + m_trans = mk_and (summaries); + summaries.reset (); + + if (!vars.empty()) { + timeit _timer1 (is_trace_enabled("spacer_timeit"), + "create_next_child::qproject1", + verbose_stream ()); + vars.append(m_evars); + m_evars.reset(); + pt().mbp(vars, m_trans, mdl, + true, pt().get_context().use_ground_pob()); + m_evars.append (vars); + vars.reset(); + } + + if (!mdl.is_true(m_premises[m_active].get_summary())) { + IF_VERBOSE(1, verbose_stream() << "Summary unexpectendly not true\n";); + return nullptr; + } + + + // create the post-condition by computing a post-image over summaries + // that precede currently active premise + for (unsigned i = m_active + 1; i < m_premises.size(); ++i) { + summaries.push_back (m_premises [i].get_summary ()); + vars.append (m_premises [i].get_ovars ()); + } + summaries.push_back (m_trans); + expr_ref post(m); + post = mk_and(summaries); + summaries.reset (); + + if (!vars.empty()) { + timeit _timer2(is_trace_enabled("spacer_timeit"), + "create_next_child::qproject2", + verbose_stream ()); + // include m_evars in case they can eliminated now as well + vars.append(m_evars); + pt().mbp(vars, post, mdl, + true, pt().get_context().use_ground_pob()); + //qe::reduce_array_selects (*mev.get_model (), post); + } + else { + // if no variables to eliminate, don't forget about m_evars + // that occur in m_trans + vars.append(m_evars); + } + + if (!vars.empty()) { + // existentially quantify out vars from post and skolemize the result + exist_skolemize(post.get(), vars, post); + } + + get_manager ().formula_o2n (post.get (), post, + m_premises [m_active].get_oidx (), + vars.empty()); + + + /* The level and depth are taken from the parent, not the sibling. + The reasoning is that the sibling has not been checked before, + and lower level is a better starting point. */ + pob *n = m_premises[m_active].pt().mk_pob(&m_parent, + prev_level (m_parent.level ()), + m_parent.depth (), post, vars); + IF_VERBOSE (1, verbose_stream () + << "\n\tcreate_child: " << n->pt ().head ()->get_name () + << " (" << n->level () << ", " << n->depth () << ") " + << (n->use_farkas_generalizer () ? "FAR " : "SUB ") + << n->post ()->get_id (); + verbose_stream().flush ();); + return n; +} + +pob *derivation::create_next_child () +{ + if (m_active + 1 >= m_premises.size()) { return nullptr; } + + // update the summary of the active node to some must summary + + // construct a new model consistent with the must summary of m_active premise + pred_transformer &pt = m_premises[m_active].pt (); + + ast_manager &m = get_ast_manager (); + manager &pm = get_manager (); + + expr_ref_vector summaries (m); + + for (unsigned i = m_active + 1; i < m_premises.size (); ++i) + { summaries.push_back(m_premises [i].get_summary()); } + + // -- orient transition relation towards m_active premise + expr_ref active_trans (m); + pm.formula_o2n (m_trans, active_trans, + m_premises[m_active].get_oidx (), false); + summaries.push_back (active_trans); + + // if not true, bail out, the must summary of m_active is not strong enough + // this is possible if m_post was weakened for some reason + model_ref mdl; + if (!pt.is_must_reachable(mk_and(summaries), &mdl)) { return nullptr; } + mdl->set_model_completion(false); + + // find must summary used + reach_fact *rf = pt.get_used_rf (*mdl, true); + + // get an implicant of the summary + expr_ref_vector u(m), lits(m); + u.push_back (rf->get ()); + compute_implicant_literals (*mdl, u, lits); + expr_ref v(m); + v = mk_and (lits); + + // XXX The summary is not used by anyone after this point + m_premises[m_active].set_summary (v, true, &(rf->aux_vars ())); + + + /** HACK: needs a rewrite + * compute post over the new must summary this must be done here + * because the must summary is currently described over new + * variables. However, we store it over old-variables, but we do + * not update the model. So we must get rid of all of the + * new-variables at this point. + */ + { + pred_transformer &pt = m_premises[m_active].pt (); + app_ref_vector vars (m); + + summaries.reset (); + summaries.push_back (v); + summaries.push_back (active_trans); + m_trans = mk_and (summaries); + + // variables to eliminate + vars.append (rf->aux_vars ().size (), rf->aux_vars ().c_ptr ()); + for (unsigned i = 0, sz = pt.head ()->get_arity (); i < sz; ++i) + { vars.push_back(m.mk_const(pm.o2n(pt.sig(i), 0))); } + + if (!vars.empty ()) { + vars.append(m_evars); + m_evars.reset(); + this->pt().mbp(vars, m_trans, *mdl, + true, this->pt().get_context().use_ground_pob()); + // keep track of implicitly quantified variables + m_evars.append (vars); + vars.reset(); + } + } + + m_active++; + + return create_next_child (*mdl); +} + +/// derivation::premise + derivation::premise::premise (pred_transformer &pt, unsigned oidx, expr *summary, bool must, const ptr_vector *aux_vars) : @@ -1568,336 +457,1743 @@ void derivation::premise::set_summary (expr * summary, bool must, } -void derivation::add_premise (pred_transformer &pt, - unsigned oidx, - expr* summary, - bool must, - const ptr_vector *aux_vars) -{m_premises.push_back (premise (pt, oidx, summary, must, aux_vars));} +/// Lemma - - -pob *derivation::create_first_child (model_evaluator_util &mev) -{ - if (m_premises.empty()) { return NULL; } - m_active = 0; - return create_next_child(mev); +lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : + m_ref_count(0), m(manager), + m_body(body, m), m_cube(m), + m_zks(m), m_bindings(m), m_lvl(lvl), m_init_lvl(m_lvl), + m_pob(nullptr), m_ctp(nullptr), m_external(false), m_bumped(0) { + SASSERT(m_body); + normalize(m_body, m_body); } -pob *derivation::create_next_child (model_evaluator_util &mev) -{ - timeit _timer (is_trace_enabled("spacer_timeit"), - "spacer::derivation::create_next_child", - verbose_stream ()); - - ast_manager &m = get_ast_manager (); - expr_ref_vector summaries (m); - app_ref_vector vars (m); - - bool use_native_mbp = get_context ().use_native_mbp (); - bool ground = get_context ().use_ground_cti (); - // -- find first may premise - while (m_active < m_premises.size() && m_premises[m_active].is_must()) { - summaries.push_back (m_premises[m_active].get_summary ()); - vars.append (m_premises[m_active].get_ovars ()); - ++m_active; - } - if (m_active >= m_premises.size()) { return NULL; } - - // -- update m_trans with the pre-image of m_trans over the must summaries - summaries.push_back (m_trans); - m_trans = get_manager ().mk_and (summaries); - summaries.reset (); - - if (!vars.empty()) { - timeit _timer1 (is_trace_enabled("spacer_timeit"), - "create_next_child::qproject1", - verbose_stream ()); - qe_project (m, vars, m_trans, mev.get_model (), true, use_native_mbp, !ground); - //qe::reduce_array_selects (*mev.get_model (), m_trans); - // remember variables that need to be existentially quantified - m_evars.append (vars); - } - - if (!mev.is_true (m_premises[m_active].get_summary())) { - IF_VERBOSE(1, verbose_stream() << "Summary unexpectendly not true\n";); - return NULL; - } - - - // create the post condition by compute post-image over summaries - // that precede currently active premise - vars.reset (); - for (unsigned i = m_active + 1; i < m_premises.size(); ++i) { - summaries.push_back (m_premises [i].get_summary ()); - vars.append (m_premises [i].get_ovars ()); - } - summaries.push_back (m_trans); - - expr_ref post(m); - post = get_manager ().mk_and (summaries); - summaries.reset (); - if (!vars.empty()) { - timeit _timer2 (is_trace_enabled("spacer_timeit"), - "create_next_child::qproject2", - verbose_stream ()); - qe_project (m, vars, post, mev.get_model (), true, use_native_mbp, !ground); - //qe::reduce_array_selects (*mev.get_model (), post); - - // remember variables that need to be existentially quantified - m_evars.append (vars); - } - - get_manager ().formula_o2n (post.get (), post, - m_premises [m_active].get_oidx (), m_evars.empty()); - - - /* The level and depth are taken from the parent, not the sibling. - The reasoning is that the sibling has not been checked before, - and lower level is a better starting point. */ - pob *n = m_premises[m_active].pt().mk_pob(&m_parent, - prev_level (m_parent.level ()), - m_parent.depth (), post, m_evars); - - IF_VERBOSE (1, verbose_stream () - << "\n\tcreate_child: " << n->pt ().head ()->get_name () - << " (" << n->level () << ", " << n->depth () << ") " - << (n->use_farkas_generalizer () ? "FAR " : "SUB ") - << n->post ()->get_id (); - verbose_stream().flush ();); - return n; +lemma::lemma(pob_ref const &p) : + m_ref_count(0), m(p->get_ast_manager()), + m_body(m), m_cube(m), + m_zks(m), m_bindings(m), m_lvl(p->level()), m_init_lvl(m_lvl), + m_pob(p), m_ctp(nullptr), m_external(false), m_bumped(0) { + SASSERT(m_pob); + m_pob->get_skolems(m_zks); + add_binding(m_pob->get_binding()); } -pob *derivation::create_next_child () +lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : + m_ref_count(0), + m(p->get_ast_manager()), + m_body(m), m_cube(m), + m_zks(m), m_bindings(m), m_lvl(p->level()), m_init_lvl(m_lvl), + m_pob(p), m_ctp(nullptr), m_external(false), m_bumped(0) { - if (m_active + 1 >= m_premises.size()) { return NULL; } + if (m_pob) { + m_pob->get_skolems(m_zks); + add_binding(m_pob->get_binding()); + } + update_cube(p, cube); + set_level(lvl); +} - bool use_native_mbp = get_context ().use_native_mbp (); - bool ground = get_context ().use_ground_cti (); - - // update the summary of the active node to some must summary - - // construct a new model consistent with the must summary of m_active premise - pred_transformer &pt = m_premises[m_active].pt (); - model_ref model; - - ast_manager &m = get_ast_manager (); - manager &pm = get_manager (); - - expr_ref_vector summaries (m); - - for (unsigned i = m_active + 1; i < m_premises.size (); ++i) - { summaries.push_back(m_premises [i].get_summary()); } - - // -- orient transition relation towards m_active premise - expr_ref active_trans (m); - pm.formula_o2n (m_trans, active_trans, - m_premises[m_active].get_oidx (), false); - summaries.push_back (active_trans); - - // if not true, bail out, the must summary of m_active is not strong enough - // this is possible if m_post was weakened for some reason - if (!pt.is_must_reachable(pm.mk_and(summaries), &model)) { return NULL; } - - model_evaluator_util mev (m); - mev.set_model (*model); - // find must summary used - - reach_fact *rf = pt.get_used_reach_fact (mev, true); - - // get an implicant of the summary - expr_ref_vector u(m), lits (m); - u.push_back (rf->get ()); - compute_implicant_literals (mev, u, lits); - expr_ref v(m); - v = pm.mk_and (lits); - - // XXX The summary is not used by anyone after this point - m_premises[m_active].set_summary (v, true, &(rf->aux_vars ())); +void lemma::add_skolem(app *zk, app *b) { + SASSERT(m_bindings.size() == m_zks.size()); + // extend bindings + m_bindings.push_back(b); + // extend skolems + m_zks.push_back(zk); +} - /** HACK: needs a rewrite - * compute post over the new must summary this must be done here - * because the must summary is currently described over new - * variables. However, we store it over old-variables, but we do - * not update the model. So we must get rid of all of the - * new-variables at this point. - */ - { - pred_transformer &pt = m_premises[m_active].pt (); - app_ref_vector vars (m); +void lemma::mk_expr_core() { + if (m_body) {return;} - summaries.reset (); - summaries.push_back (v); - summaries.push_back (active_trans); - m_trans = pm.mk_and (summaries); + if (m_pob) {mk_cube_core();} - // variables to eliminate - vars.append (rf->aux_vars ().size (), rf->aux_vars ().c_ptr ()); - for (unsigned i = 0, sz = pt.head ()->get_arity (); i < sz; ++i) - { vars.push_back(m.mk_const(pm.o2n(pt.sig(i), 0))); } + SASSERT(!m_cube.empty()); + m_body = ::mk_and(m_cube); + // normalize works better with a cube + normalize(m_body, m_body); + m_body = ::push_not(m_body); - if (!vars.empty ()) { - qe_project (m, vars, m_trans, mev.get_model (), true, use_native_mbp, - !ground); - // keep track of implicitly quantified variables - m_evars.append (vars); + if (!m_zks.empty() && has_zk_const(m_body)) { + app_ref_vector zks(m); + zks.append(m_zks); + zks.reverse(); + expr_abstract(m, 0, + zks.size(), (expr* const*)zks.c_ptr(), m_body, + m_body); + ptr_buffer sorts; + svector names; + for (unsigned i=0, sz=zks.size(); i < sz; ++i) { + sorts.push_back(get_sort(zks.get(i))); + names.push_back(zks.get(i)->get_decl()->get_name()); + } + m_body = m.mk_quantifier(true, zks.size(), + sorts.c_ptr(), + names.c_ptr(), + m_body, 15, symbol(m_body->get_id())); + } + SASSERT(m_body); +} +void lemma::mk_cube_core() { + if (!m_cube.empty()) {return;} + expr_ref cube(m); + if (m_pob || m_body) { + if (m_pob) {cube = m_pob->post();} + else if (m_body) { + // no quantifiers for now + SASSERT(!is_quantifier(m_body)); + cube = m_body; + cube = ::push_not(cube); + } + flatten_and(cube, m_cube); + if (m_cube.empty()) { + m_cube.push_back(m.mk_true()); + } + else { + std::sort(m_cube.c_ptr(), m_cube.c_ptr() + m_cube.size(), ast_lt_proc()); + } + } + else { + UNREACHABLE(); + } +} +bool lemma::is_false() { + // a lemma is false if + // 1. it is defined by a cube, and the cube contains a single literal 'true' + // 2. it is defined by a body, and the body is a single literal false + // 3. it is defined by a pob, and the pob post is false + if (m_cube.size() == 1) {return m.is_true(m_cube.get(0));} + else if (m_body) {return m.is_false(m_body);} + else if (m_pob) {return m.is_true(m_pob->post());} + + return false; +} +expr* lemma::get_expr() { + mk_expr_core(); + return m_body; +} +expr_ref_vector const &lemma::get_cube() { + mk_cube_core(); + return m_cube; +} + +void lemma::update_cube (pob_ref const &p, expr_ref_vector &cube) { + SASSERT(m_pob); + SASSERT(m_pob.get() == p.get()); + m_cube.reset(); + m_body.reset(); + m_cube.append(cube); + if (m_cube.empty()) {m_cube.push_back(m.mk_true());} + + // after the cube is updated, if there are no skolems, + // convert the lemma to quantifier-free + bool is_quant = false; + for (unsigned i = 0, sz = cube.size(); !is_quant && i < sz; ++i) { + is_quant = has_zk_const(cube.get(i)); + } + + if (!is_quant) { + m_zks.reset(); + m_bindings.reset(); + } +} + +bool lemma::has_binding(app_ref_vector const &binding) { + unsigned num_decls = m_zks.size(); + + SASSERT(binding.size() == num_decls); + + if (num_decls == 0) return true; + + for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { + unsigned i = 0; + for (; i < num_decls; ++i) { + if (m_bindings.get(off + i) != binding.get(i)) { + break; + } + } + if (i == num_decls) return true; + } + return false; +} +void lemma::add_binding(app_ref_vector const &binding) { + if (!has_binding(binding)) { + m_bindings.append(binding); + + TRACE("spacer", + tout << "new binding: "; + for (unsigned i = 0; i < binding.size(); i++) + tout << mk_pp(binding.get(i), m) << " "; + tout << "\n";); + } +} +void lemma::instantiate(expr * const * exprs, expr_ref &result, expr *e) { + expr *lem = e == nullptr ? get_expr() : e; + if (!is_quantifier (lem) || m_bindings.empty()) {return;} + + expr *body = to_quantifier(lem)->get_expr(); + unsigned num_decls = to_quantifier(lem)->get_num_decls(); + var_subst vs(m, false); + vs(body, num_decls, exprs, result); +} + +void lemma::set_level (unsigned lvl) { + if (m_pob){m_pob->blocked_at(lvl);} + m_lvl = lvl; +} + + +void lemma::mk_insts(expr_ref_vector &out, expr* e) +{ + expr *lem = e == nullptr ? get_expr() : e; + if (!is_quantifier (lem) || m_bindings.empty()) {return;} + + unsigned num_decls = to_quantifier(lem)->get_num_decls(); + expr_ref inst(m); + for (unsigned off = 0, sz = m_bindings.size(); off < sz; off += num_decls) { + instantiate((expr * const *) m_bindings.c_ptr() + off, inst, e); + out.push_back(inst); + inst.reset(); + } +} + +// ---------------- +// pred_tansformer +pred_transformer::pt_rule &pred_transformer::pt_rules::mk_rule(const pred_transformer::pt_rule &v) { + pt_rule *p = nullptr; + if (find_by_rule(v.rule(), p)) + return *p; + + p = alloc(pt_rule, v); + m_rules.insert(&p->rule(), p); + if (p->tag()) m_tags.insert(p->tag(), p); + return *p; +} + +pred_transformer::pred_transformer(context& ctx, manager& pm, func_decl* head): + pm(pm), m(pm.get_manager()), + ctx(ctx), m_head(head, m), + m_sig(m), + m_reach_solver (ctx.mk_solver2()), + m_pobs(*this), + m_frames(*this), + m_reach_facts(), m_rf_init_sz(0), + m_transition_clause(m), m_transition(m), m_init(m), + m_extend_lit0(m), m_extend_lit(m), + m_all_init(false) +{ + m_solver = alloc(prop_solver, m, ctx.mk_solver0(), ctx.mk_solver1(), + ctx.get_params(), head->get_name()); + init_sig (); + + m_extend_lit = mk_extend_lit(); + m_extend_lit0 = m_extend_lit; +} + +app_ref pred_transformer::mk_extend_lit() { + app_ref v(m); + std::stringstream name; + name << m_head->get_name () << "_ext0"; + v = m.mk_const (symbol(name.str().c_str()), m.mk_bool_sort()); + return app_ref(m.mk_not (m.mk_const (pm.get_n_pred (v->get_decl ()))), m); +} + + +std::ostream& pred_transformer::display(std::ostream& out) const +{ + if (!rules().empty()) { out << "rules\n"; } + datalog::rule_manager& rm = ctx.get_datalog_context().get_rule_manager(); + for (unsigned i = 0; i < rules().size(); ++i) { + rm.display_smt2(*rules()[i], out) << "\n"; + } + out << "transition\n" << mk_pp(transition(), m) << "\n"; + return out; +} + +void pred_transformer::collect_statistics(statistics& st) const +{ + m_solver->collect_statistics(st); + + // -- number of times a lemma has been propagated to a higher level + // -- during push + st.update("SPACER num propagations", m_stats.m_num_propagations); + // -- number of lemmas in all current frames + st.update("SPACER num active lemmas", m_frames.lemma_size ()); + // -- number of lemmas that are inductive invariants + st.update("SPACER num invariants", m_stats.m_num_invariants); + // -- number of proof obligations (0 if pobs are not reused) + st.update("SPACER num pobs", m_pobs.size()); + + // -- number of reach facts created + st.update("SPACER num reach queries", m_stats.m_num_reach_queries); + + st.update("SPACER num ctp blocked", m_stats.m_num_ctp_blocked); + st.update("SPACER num is_invariant", m_stats.m_num_is_invariant); + st.update("SPACER num lemma jumped", m_stats.m_num_lemma_level_jump); + + // -- time in rule initialization + st.update ("time.spacer.init_rules.pt.init", m_initialize_watch.get_seconds ()); + // -- time is must_reachable() + st.update ("time.spacer.solve.pt.must_reachable", + m_must_reachable_watch.get_seconds ()); + st.update("time.spacer.ctp", m_ctp_watch.get_seconds()); + st.update("time.spacer.mbp", m_mbp_watch.get_seconds()); +} + +void pred_transformer::reset_statistics() +{ + m_solver->reset_statistics(); + //m_reachable.reset_statistics(); + m_stats.reset(); + m_initialize_watch.reset (); + m_must_reachable_watch.reset (); + m_ctp_watch.reset(); + m_mbp_watch.reset(); +} + +void pred_transformer::init_sig() +{ + for (unsigned i = 0; i < m_head->get_arity(); ++i) { + sort * arg_sort = m_head->get_domain(i); + std::stringstream name_stm; + name_stm << m_head->get_name() << '_' << i; + func_decl_ref stm(m); + stm = m.mk_func_decl(symbol(name_stm.str().c_str()), 0, (sort*const*)nullptr, arg_sort); + m_sig.push_back(pm.get_o_pred(stm, 0)); + } +} + +void pred_transformer::ensure_level(unsigned level) +{ + if (is_infty_level(level)) { return; } + + while (m_frames.size() <= level) { + m_frames.add_frame (); + m_solver->add_level (); + } +} + +bool pred_transformer::is_must_reachable(expr* state, model_ref* model) +{ + scoped_watch _t_(m_must_reachable_watch); + SASSERT (state); + // XXX This seems to mis-handle the case when state is + // reachable using the init rule of the current transformer + if (m_reach_facts.empty()) { return false; } + + m_reach_solver->push (); + m_reach_solver->assert_expr (state); + m_reach_solver->assert_expr (m.mk_not (m_reach_facts.back()->tag())); + lbool res = m_reach_solver->check_sat (0, nullptr); + if (model) { m_reach_solver->get_model(*model); } + m_reach_solver->pop (1); + return (res == l_true); +} + + + + +reach_fact* pred_transformer::get_used_rf (model& mdl, bool all) { + expr_ref v (m); + model::scoped_model_completion _sc_(mdl, false); + + for (auto *rf : m_reach_facts) { + if (!all && rf->is_init()) continue; + if (mdl.is_false(rf->tag())) return rf; + } + UNREACHABLE(); + return nullptr; +} + +reach_fact *pred_transformer::get_used_origin_rf(model& mdl, unsigned oidx) { + expr_ref b(m), v(m); + model::scoped_model_completion _sc_(mdl, false); + for (auto *rf : m_reach_facts) { + pm.formula_n2o (rf->tag(), v, oidx); + if (mdl.is_false(v)) return rf; + } + UNREACHABLE(); + return nullptr; +} + +const datalog::rule *pred_transformer::find_rule(model &model) { + expr_ref val(m); + + for (auto &kv : m_pt_rules) { + app *tag = kv.m_value->tag(); + if (model.eval(tag->get_decl(), val) && m.is_true(val)) { + return &kv.m_value->rule(); + } + } + return nullptr; +} + +const datalog::rule *pred_transformer::find_rule(model &model, + bool& is_concrete, + vector& reach_pred_used, + unsigned& num_reuse_reach) +{ + // find a rule whose tag is true in the model; + // prefer a rule where the model intersects with reach facts of all predecessors; + // also find how many predecessors' reach facts are true in the model + expr_ref vl(m); + const datalog::rule *r = ((datalog::rule*)nullptr); + //for (auto &entry : m_tag2rule) { + for (auto &kv : m_pt_rules) { + app* tag = kv.m_value->tag(); + if (model.eval(tag->get_decl(), vl) && m.is_true(vl)) { + r = &kv.m_value->rule(); + is_concrete = true; + num_reuse_reach = 0; + reach_pred_used.reset(); + unsigned tail_sz = r->get_uninterpreted_tail_size(); + for (unsigned i = 0; i < tail_sz; i++) { + bool used = false; + func_decl* d = r->get_tail(i)->get_decl(); + const pred_transformer &pt = ctx.get_pred_transformer(d); + if (!pt.has_rfs()) {is_concrete = false;} + else { + expr_ref v(m); + pm.formula_n2o(pt.get_last_rf_tag (), v, i); + model.eval(to_app (v.get ())->get_decl (), vl); + used = m.is_false (vl); + is_concrete = is_concrete && used; + } + + reach_pred_used.push_back (used); + if (used) {num_reuse_reach++;} + } + if (is_concrete) {break;} + } + } + // SASSERT (r); + // reached by a reachability fact + if (!r) { is_concrete = true; } + return r; +} + +void pred_transformer::find_predecessors(datalog::rule const& r, ptr_vector& preds) const +{ + preds.reset(); + unsigned tail_sz = r.get_uninterpreted_tail_size(); + for (unsigned ti = 0; ti < tail_sz; ti++) { + preds.push_back(r.get_tail(ti)->get_decl()); + } +} + +void pred_transformer::simplify_formulas() +{m_frames.simplify_formulas ();} + + +expr_ref pred_transformer::get_formulas(unsigned level) const +{ + expr_ref_vector res(m); + m_frames.get_frame_geq_lemmas (level, res); + return mk_and(res); +} + +bool pred_transformer::propagate_to_next_level (unsigned src_level) +{return m_frames.propagate_to_next_level (src_level);} + + +/// \brief adds a lemma to the solver and to child solvers +void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only) +{ + unsigned lvl = lemma->level(); + expr* l = lemma->get_expr(); + SASSERT(!lemma->is_ground() || is_clause(m, l)); + SASSERT(!is_quantifier(l) || is_clause(m, to_quantifier(l)->get_expr())); + + TRACE("spacer", tout << "add-lemma-core: " << pp_level (lvl) + << " " << head ()->get_name () + << " " << mk_pp (l, m) << "\n";); + + TRACE("core_array_eq", tout << "add-lemma-core: " << pp_level (lvl) + << " " << head ()->get_name () + << " " << mk_pp (l, m) << "\n";); + + STRACE ("spacer.expand-add", + tout << "** add-lemma: " << pp_level (lvl) << " " + << head ()->get_name () << " " + << mk_epp (l, m) << "\n"; + + if (!lemma->is_ground()) { + tout << "Bindings: " << lemma->get_bindings() << "\n"; + } + tout << "\n"; + ); + + + if (is_infty_level(lvl)) { m_stats.m_num_invariants++; } + + if (lemma->is_ground()) { + if (is_infty_level(lvl)) { m_solver->assert_expr(l); } + else { + ensure_level (lvl); + m_solver->assert_expr (l, lvl); } } - m_active++; - - return create_next_child (mev); + for (unsigned i = 0, sz = m_use.size (); i < sz; ++i) + { m_use [i]->add_lemma_from_child(*this, lemma, + next_level(lvl), ground_only); } } -pob::pob (pob* parent, pred_transformer& pt, - unsigned level, unsigned depth, bool add_to_parent): - m_ref_count (0), - m_parent (parent), m_pt (pt), - m_post (m_pt.get_ast_manager ()), - m_binding(m_pt.get_ast_manager()), - m_new_post (m_pt.get_ast_manager ()), - m_level (level), m_depth (depth), - m_open (true), m_use_farkas (true), m_weakness(0) { - if(add_to_parent && m_parent) { - m_parent->add_child(*this); - } +bool pred_transformer::add_lemma (expr *e, unsigned lvl) { + lemma_ref lem = alloc(lemma, m, e, lvl); + return m_frames.add_lemma(lem.get()); } - -void pob::set_post(expr* post) { - app_ref_vector b(get_ast_manager()); - set_post(post, b); -} - -void pob::set_post(expr* post, app_ref_vector const &b) { - normalize(post, m_post, - m_pt.get_context().get_params().spacer_simplify_pob(), - m_pt.get_context().get_params().spacer_use_eqclass()); - - m_binding.reset(); - if (b.empty()) return; - - m_binding.append(b); - - std::sort (m_binding.c_ptr(), m_binding.c_ptr() + m_binding.size(), ast_lt_proc()); - - // skolemize implicit existential quantifier - ast_manager &m = get_ast_manager(); - app_ref_vector pinned(m); - - expr_safe_replace sub(m); - for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { - expr* e; - - e = m_binding.get(i); - pinned.push_back (mk_zk_const (m, i, get_sort(e))); - sub.insert (e, pinned.back()); - } - sub(m_post); -} - -void pob::inherit(pob const &p) { - SASSERT(m_parent == p.m_parent); - SASSERT(&m_pt == &p.m_pt); - SASSERT(m_post == p.m_post); - SASSERT(!m_new_post); - - m_binding.reset(); - m_binding.append(p.m_binding); - - m_level = p.m_level; - m_depth = p.m_depth; - m_open = p.m_open; - m_use_farkas = p.m_use_farkas; - m_weakness = p.m_weakness; - - m_derivation = nullptr; -} - -void pob::clean () { - if(m_new_post) { - m_post = m_new_post; - m_new_post.reset(); - } -} - -void pob::close () { - if(!m_open) { return; } - - reset (); - m_open = false; - for (unsigned i = 0, sz = m_kids.size (); i < sz; ++i) - { m_kids [i]->close(); } -} - -void pob::get_skolems(app_ref_vector &v) { - for (unsigned i = 0, sz = m_binding.size(); i < sz; ++i) { - expr* e; - - e = m_binding.get(i); - v.push_back (mk_zk_const (get_ast_manager(), i, get_sort(e))); - } -} - - - -// ---------------- -// pob_queue - -pob* pob_queue::top () +void pred_transformer::add_lemma_from_child (pred_transformer& child, + lemma* lemma, unsigned lvl, + bool ground_only) { - /// nothing in the queue - if (m_obligations.empty()) { return NULL; } - /// top queue element is above max level - if (m_obligations.top()->level() > m_max_level) { return NULL; } - /// top queue element is at the max level, but at a higher than base depth - if (m_obligations.top ()->level () == m_max_level && - m_obligations.top()->depth() > m_min_depth) { return NULL; } + ensure_level(lvl); + expr_ref_vector fmls(m); + mk_assumptions(child.head(), lemma->get_expr(), fmls); + + for (unsigned i = 0; i < fmls.size(); ++i) { + expr_ref_vector inst(m); + expr* a = to_app(fmls.get(i))->get_arg(0); + expr* l = to_app(fmls.get(i))->get_arg(1); + if (!lemma->is_ground() && get_context().use_instantiate()) { + expr_ref grnd_lemma(m); + app_ref_vector tmp(m); + lemma->mk_insts(inst, l); + // -- take ground instance of the current lemma + ground_expr(to_quantifier(l)->get_expr(), grnd_lemma, tmp); + inst.push_back(grnd_lemma); + } + for (unsigned j=0; j < inst.size(); j++) { + inst.set(j, m.mk_implies(a, inst.get(j))); + } + if (lemma->is_ground() || (get_context().use_qlemmas() && !ground_only)) { + inst.push_back(fmls.get(i)); + } + SASSERT (!inst.empty ()); + for (unsigned j = 0; j < inst.size(); ++j) { + TRACE("spacer_detail", tout << "child property: " + << mk_pp(inst.get (j), m) << "\n";); + if (is_infty_level(lvl)) { + m_solver->assert_expr(inst.get(j)); + } + else { + m_solver->assert_expr(inst.get(j), lvl); + } + } + } - /// there is something good in the queue - return m_obligations.top ().get (); } -void pob_queue::set_root(pob& root) +app_ref pred_transformer::mk_fresh_rf_tag () { - m_root = &root; - m_max_level = root.level (); - m_min_depth = root.depth (); - reset(); + std::stringstream name; + func_decl_ref decl(m); + + name << head ()->get_name () << "#reach_tag_" << m_reach_facts.size (); + decl = m.mk_func_decl (symbol (name.str ().c_str ()), 0, + (sort*const*)nullptr, m.mk_bool_sort ()); + return app_ref(m.mk_const (pm.get_n_pred (decl)), m); } -pob_queue::~pob_queue() {} - -void pob_queue::reset() +void pred_transformer::add_rf (reach_fact *rf) { - while (!m_obligations.empty()) { m_obligations.pop(); } - if (m_root) { m_obligations.push(m_root); } + timeit _timer (is_trace_enabled("spacer_timeit"), + "spacer::pred_transformer::add_rf", + verbose_stream ()); + + TRACE ("spacer", + tout << "add_rf: " << head()->get_name() << " " + << (rf->is_init () ? "INIT " : "") + << mk_pp(rf->get (), m) << "\n";); + + // -- avoid duplicates + if (!rf || get_rf(rf->get())) {return;} + + // all initial facts are grouped together + SASSERT (!rf->is_init () || m_reach_facts.empty () || + m_reach_facts.back ()->is_init ()); + + // create tags + app_ref last_tag(m); + app_ref new_tag(m); + expr_ref fml(m); + + if (!m_reach_facts.empty()) {last_tag = m_reach_facts.back()->tag();} + if (rf->is_init ()) + new_tag = mk_fresh_rf_tag(); + else + // side-effect: updates m_solver with rf + new_tag = to_app(extend_initial(rf->get())->get_arg(0)); + rf->set_tag(new_tag); + + // add to m_reach_facts + m_reach_facts.push_back (rf); + if (rf->is_init()) {m_rf_init_sz++;} + + // update m_reach_solver + if (last_tag) {fml = m.mk_or(m.mk_not(last_tag), rf->get(), rf->tag());} + else {fml = m.mk_or(rf->get(), rf->tag());} + m_reach_solver->assert_expr (fml); + TRACE ("spacer", tout << "updating reach ctx: " << fml << "\n";); + + // update solvers of other pred_transformers + // XXX wrap rf into a lemma to fit the API + lemma fake_lemma(m, fml, infty_level()); + // update users; reach facts are independent of levels + for (auto use : m_use) + use->add_lemma_from_child (*this, &fake_lemma, infty_level()); } +expr_ref pred_transformer::get_reachable() +{ + expr_ref res(m); + res = m.mk_false(); + + if (!m_reach_facts.empty()) { + expr_substitution sub(m); + expr_ref c(m), v(m); + for (unsigned i = 0, sz = sig_size(); i < sz; ++i) { + c = m.mk_const(pm.o2n(sig(i), 0)); + v = m.mk_var(i, sig(i)->get_range()); + sub.insert(c, v); + } + scoped_ptr rep = mk_expr_simp_replacer(m); + rep->set_substitution(&sub); + + expr_ref_vector args(m); + for (unsigned i = 0, sz = m_reach_facts.size (); i < sz; ++i) { + reach_fact *f; + f = m_reach_facts.get(i); + expr_ref r(m); + r = f->get(); + const ptr_vector &aux = f->aux_vars(); + if (!aux.empty()) { + // -- existentially quantify auxiliary variables + r = mk_exists (m, aux.size(), aux.c_ptr(), r); + // XXX not sure how this interacts with variable renaming later on. + // XXX For now, simply dissallow existentially quantified auxiliaries + NOT_IMPLEMENTED_YET(); + } + (*rep)(r); + + args.push_back (r); + } + res = mk_or(args); + } + return res; +} + +expr* pred_transformer::get_last_rf_tag () const +{return m_reach_facts.empty() ? nullptr : m_reach_facts.back()->tag();} + +expr_ref pred_transformer::get_cover_delta(func_decl* p_orig, int level) +{ + expr_ref result(m.mk_true(), m), v(m), c(m); + + expr_ref_vector lemmas (m); + m_frames.get_frame_lemmas (level == -1 ? infty_level() : level, lemmas); + if (!lemmas.empty()) { result = mk_and(lemmas); } + + // replace local constants by bound variables. + expr_substitution sub(m); + 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(c, v); + } + scoped_ptr rep = mk_default_expr_replacer(m); + rep->set_substitution(&sub); + (*rep)(result); + + // adjust result according to model converter. + unsigned arity = m_head->get_arity(); + model_ref md = alloc(model, m); + if (arity == 0) { + md->register_decl(m_head, result); + } else { + func_interp* fi = alloc(func_interp, m, arity); + fi->set_else(result); + md->register_decl(m_head, fi); + } + model_converter_ref mc = ctx.get_model_converter(); + apply(mc, md); + if (p_orig->get_arity() == 0) { + result = md->get_const_interp(p_orig); + } else { + result = md->get_func_interp(p_orig)->get_interp(); + } + return result; +} + +/** + * get an origin summary used by this transformer in the given model + * level is the level at which may summaries are obtained + * oidx is the origin index of this predicate in the model + * must indicates whether a must or a may summary is requested + * + * returns an implicant of the summary + */ +expr_ref pred_transformer::get_origin_summary (model &mdl, + unsigned level, + unsigned oidx, + bool must, + const ptr_vector **aux) +{ + model::scoped_model_completion _sc_(mdl, false); + expr_ref_vector summary (m); + expr_ref v(m); + + if (!must) { // use may summary + summary.push_back (get_formulas(level)); + // -- no auxiliary variables in lemmas + *aux = nullptr; + } else { // find must summary to use + reach_fact *f = get_used_origin_rf(mdl, oidx); + summary.push_back (f->get ()); + *aux = &f->aux_vars (); + } + + SASSERT (!summary.empty ()); + + // -- convert to origin + for (unsigned i = 0; i < summary.size(); ++i) { + pm.formula_n2o (summary.get (i), v, oidx); + summary[i] = v; + } + + // bail out of if the model is insufficient + if (!mdl.is_true(summary)) return expr_ref(m); + + // -- pick an implicant + expr_ref_vector lits(m); + compute_implicant_literals (mdl, summary, lits); + return mk_and(lits); +} + + +void pred_transformer::add_cover(unsigned level, expr* property) +{ + // replace bound variables by local constants. + expr_ref result(property, m), v(m), c(m); + expr_substitution sub(m); + 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); + } + scoped_ptr rep = mk_default_expr_replacer(m); + rep->set_substitution(&sub); + (*rep)(result); + TRACE("spacer", tout << "cover:\n" << mk_pp(result, m) << "\n";); + + // add the 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); + } +} + +void pred_transformer::propagate_to_infinity (unsigned level) +{m_frames.propagate_to_infinity (level);} + + + +/// \brief Returns true if the obligation is already blocked by current lemmas +bool pred_transformer::is_blocked (pob &n, unsigned &uses_level) +{ + ensure_level (n.level ()); + prop_solver::scoped_level _sl (*m_solver, n.level ()); + m_solver->set_core (nullptr); + m_solver->set_model (nullptr); + + expr_ref_vector post(m), _aux(m); + post.push_back (n.post ()); + // this only uses the lemmas at the current level + // transition relation is irrelevant + // XXX quic3: not all lemmas are asserted at the post-condition + lbool res = m_solver->check_assumptions (post, _aux, _aux, + 0, nullptr, 0); + if (res == l_false) { uses_level = m_solver->uses_level(); } + return res == l_false; +} + + +bool pred_transformer::is_qblocked (pob &n) { + // XXX currently disabled + return false; + params_ref p; + p.set_bool("arith.ignore_int", true); + p.set_bool("array.weak", true); + p.set_bool("mbqi", false); + scoped_ptr s; + s = mk_smt_solver(m, p, symbol::null); + s->updt_params(p); + // XXX force parameters to be set + s->push(); + s->pop(1); + + expr_ref_vector frame_lemmas(m); + m_frames.get_frame_geq_lemmas (n.level (), frame_lemmas); + + // assert all lemmas + bool has_quant = false; + for (unsigned i = 0, sz = frame_lemmas.size (); i < sz; ++i) + { + has_quant = has_quant || is_quantifier(frame_lemmas.get(i)); + s->assert_expr(frame_lemmas.get(i)); + } + if (!has_quant) return false; + + // assert cti + s->assert_expr(n.post()); + lbool res = s->check_sat(0, 0); + + // if (res == l_false) { + // expr_ref_vector core(m); + // solver->get_itp_core(core); + // expr_ref c(m); + // c = mk_and(core); + // STRACE("spacer.expand-add", tout << "core: " << mk_epp(c,m) << "\n";); + // } + return res == l_false; +} + + +void pred_transformer::mbp(app_ref_vector &vars, expr_ref &fml, model &mdl, + bool reduce_all_selects, bool force) { + scoped_watch _t_(m_mbp_watch); + qe_project(m, vars, fml, mdl, reduce_all_selects, use_native_mbp(), !force); +} + +// +// check if predicate transformer has a satisfiable predecessor state. +// returns either a satisfiable predecessor state or +// return a property that blocks state and is implied by the +// predicate transformer (or some unfolding of it). +// +lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, + model_ref* model, unsigned& uses_level, + bool& is_concrete, datalog::rule const*& r, + vector& reach_pred_used, + unsigned& num_reuse_reach) +{ + TRACE("spacer", + tout << "is-reachable: " << head()->get_name() << " level: " + << n.level() << " depth: " << n.depth () << "\n"; + tout << mk_pp(n.post(), m) << "\n";); + timeit _timer (is_trace_enabled("spacer_timeit"), + "spacer::pred_transformer::is_reachable", + verbose_stream ()); + + ensure_level(n.level()); + + // prepare the solver + prop_solver::scoped_level _sl(*m_solver, n.level()); + prop_solver::scoped_subset_core _sc (*m_solver, !n.use_farkas_generalizer ()); + prop_solver::scoped_weakness _sw(*m_solver, 0, + ctx.weak_abs() ? n.weakness() : UINT_MAX); + m_solver->set_core(core); + m_solver->set_model(model); + + expr_ref_vector post (m), reach_assumps (m); + post.push_back (n.post ()); + + // populate reach_assumps + if (n.level () > 0 && !m_all_init) { + for (auto &kv : m_pt_rules) { + datalog::rule const* r = &kv.m_value->rule(); + find_predecessors(*r, m_predicates); + if (m_predicates.empty()) {continue;} + for (unsigned i = 0; i < m_predicates.size(); i++) { + const pred_transformer &pt = + ctx.get_pred_transformer(m_predicates[i]); + if (pt.has_rfs()) { + expr_ref a(m); + pm.formula_n2o(pt.get_last_rf_tag(), a, i); + reach_assumps.push_back(m.mk_not (a)); + } else { + reach_assumps.push_back(m.mk_not (kv.m_value->tag())); + break; + } + } + } + } + + TRACE ("spacer", + if (!reach_assumps.empty ()) { + tout << "reach assumptions\n"; + for (unsigned i = 0; i < reach_assumps.size (); i++) { + tout << mk_pp (reach_assumps.get (i), m) << "\n"; + } + } + ); + + // check local reachability; + // result is either sat (with some reach assumps) or + // unsat (even with no reach assumps) + expr *bg = m_extend_lit.get (); + lbool is_sat = m_solver->check_assumptions (post, reach_assumps, + m_transition_clause, 1, &bg, 0); + + TRACE ("spacer", + if (!reach_assumps.empty ()) { + tout << "reach assumptions used\n"; + for (unsigned i = 0; i < reach_assumps.size (); i++) { + tout << mk_pp (reach_assumps.get (i), m) << "\n"; + } + } + ); + + if (is_sat == l_true || is_sat == l_undef) { + if (core) { core->reset(); } + if (model && model->get()) { + r = find_rule(**model, is_concrete, reach_pred_used, num_reuse_reach); + TRACE ("spacer", tout << "reachable " + << "is_concrete " << is_concrete << " rused: "; + for (unsigned i = 0, sz = reach_pred_used.size (); i < sz; ++i) + tout << reach_pred_used [i]; + tout << "\n";); + } + + return is_sat; + } + if (is_sat == l_false) { + SASSERT (reach_assumps.empty ()); + TRACE ("spacer", tout << "unreachable with lemmas\n"; + if (core) { + tout << "Core:\n"; + for (unsigned i = 0; i < core->size (); i++) { + tout << mk_pp (core->get(i), m) << "\n"; + } + } + ); + uses_level = m_solver->uses_level(); + return l_false; + } + UNREACHABLE(); + return l_undef; +} + +/// returns true if lemma is blocked by an existing model +bool pred_transformer::is_ctp_blocked(lemma *lem) { + if (!ctx.use_ctp()) {return false;} + + if (!lem->has_ctp()) {return false;} + scoped_watch _t_(m_ctp_watch); + + model_ref &ctp = lem->get_ctp(); + + // -- find rule of the ctp + const datalog::rule *r; + r = find_rule(*ctp); + if (r == nullptr) {return false;} + + // -- find predicates along the rule + find_predecessors(*r, m_predicates); + + // check if any lemma blocks the ctp model + for (unsigned i = 0, sz = m_predicates.size(); i < sz; ++i) { + pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); + expr_ref lemmas(m), val(m); + lemmas = pt.get_formulas(lem->level()); + pm.formula_n2o(lemmas.get(), lemmas, i); + if (ctp->is_false(lemmas)) return false; + } + + // lem is blocked by ctp since none of the lemmas at the previous + // level block ctp + return true; +} + +bool pred_transformer::is_invariant(unsigned level, lemma* lem, + unsigned& solver_level, + expr_ref_vector* core) +{ + m_stats.m_num_is_invariant++; + if (is_ctp_blocked(lem)) { + m_stats.m_num_ctp_blocked++; + return false; + } + + expr_ref lemma_expr(m); + lemma_expr = lem->get_expr(); + + expr_ref_vector conj(m), aux(m); + expr_ref gnd_lemma(m); + + + if (!get_context().use_qlemmas() && !lem->is_ground()) { + app_ref_vector tmp(m); + ground_expr(to_quantifier(lemma_expr)->get_expr (), gnd_lemma, tmp); + lemma_expr = gnd_lemma.get(); + } + + conj.push_back(mk_not(m, lemma_expr)); + flatten_and (conj); + + prop_solver::scoped_level _sl(*m_solver, level); + prop_solver::scoped_subset_core _sc (*m_solver, true); + prop_solver::scoped_weakness _sw (*m_solver, 1, + ctx.weak_abs() ? lem->weakness() : UINT_MAX); + model_ref mdl; + model_ref *mdl_ref_ptr = nullptr; + 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); + if (r == l_false) { + solver_level = m_solver->uses_level (); + lem->reset_ctp(); + if (level < m_solver->uses_level()) {m_stats.m_num_lemma_level_jump++;} + SASSERT (level <= solver_level); + } + else if (r == l_true) { + // optionally remove unused symbols from the model + if (mdl_ref_ptr) {lem->set_ctp(*mdl_ref_ptr);} + } + else {lem->reset_ctp();} + + return r == l_false; +} + +bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state, + unsigned& uses_level, unsigned weakness) +{ + expr_ref_vector conj(m), core(m); + expr_ref states(m); + states = mk_and(state); + states = m.mk_not(states); + mk_assumptions(head(), states, conj); + prop_solver::scoped_level _sl(*m_solver, level); + prop_solver::scoped_subset_core _sc (*m_solver, true); + prop_solver::scoped_weakness _sw (*m_solver, 1, + ctx.weak_abs() ? weakness : UINT_MAX); + m_solver->set_core(&core); + m_solver->set_model (nullptr); + expr_ref_vector aux (m); + conj.push_back (m_extend_lit); + lbool res = m_solver->check_assumptions (state, aux, + m_transition_clause, + conj.size (), conj.c_ptr (), 1); + if (res == l_false) { + state.reset(); + state.append(core); + uses_level = m_solver->uses_level(); + } + TRACE ("core_array_eq", + tout << "check_inductive: " + << "states: " << mk_pp (states, m) + << " is: " << res << "\n" + << "with transition: " << mk_pp (m_transition, m) << "\n";); + return res == l_false; +} + +void pred_transformer::mk_assumptions(func_decl* head, expr* fml, + expr_ref_vector& result) +{ + expr_ref tmp1(m), tmp2(m); + for (auto& kv : m_pt_rules) { + expr* tag = kv.m_value->tag(); + datalog::rule const& r = kv.m_value->rule(); + find_predecessors(r, m_predicates); + for (unsigned i = 0; i < m_predicates.size(); i++) { + func_decl* d = m_predicates[i]; + if (d == head) { + tmp1 = m.mk_implies(tag, fml); + pm.formula_n2o(tmp1, tmp2, i); + result.push_back(tmp2); + } + } + } +} + +void pred_transformer::initialize(decl2rel const& pts) +{ + m_init = m.mk_false(); + m_transition = m.mk_true(); + init_rules(pts); + th_rewriter rw(m); + rw(m_transition); + rw(m_init); + + m_solver->assert_expr (m_transition); + m_solver->assert_expr (m_init, 0); + TRACE("spacer", + tout << "Initial state: " << mk_pp(m_init, m) << "\n"; + tout << "Transition: " << mk_pp(m_transition, m) << "\n";); + SASSERT(is_app(m_init)); + //m_reachable.add_init(to_app(m_init)); + + +} + +void pred_transformer::init_rfs () +{ + expr_ref_vector v(m); + reach_fact_ref fact; + + for (auto &kv : m_pt_rules) { + pt_rule &ptr = *kv.m_value; + const datalog::rule& r = ptr.rule(); + if (ptr.is_init()) { + fact = alloc(reach_fact, m, r, ptr.trans(), ptr.auxs(), true); + add_rf(fact.get()); + } + } +} + +void pred_transformer::init_rules(decl2rel const& pts) { + expr_ref_vector transitions(m), not_inits(m); + app_ref tag(m); + for (auto r : m_rules) { + init_rule(pts, *r); + } + + if (m_pt_rules.empty()) { + m_transition = m.mk_false(); + m_transition_clause.reset(); + } + else { + unsigned i = 0; + expr_ref_vector transitions(m); + m_transition_clause.push_back (m_extend_lit->get_arg(0)); + for (auto &kv : m_pt_rules) { + pt_rule &r = *kv.m_value; + std::string name = head()->get_name().str() + "__tr" + std::to_string(i); + tag = m.mk_const(symbol(name.c_str()), m.mk_bool_sort()); + m_pt_rules.set_tag(tag, r); + m_transition_clause.push_back(tag); + transitions.push_back(m.mk_implies(r.tag(), r.trans())); + if (!r.is_init()) {not_inits.push_back(m.mk_not(tag));} + ++i; + } + + if (!ctx.use_inc_clause()) { + transitions.push_back(mk_or(m_transition_clause)); + m_transition_clause.reset(); + } + m_transition = mk_and(transitions); + } + // mk init condition -- disables all non-initial transitions + m_init = mk_and(not_inits); + // no rule has uninterpreted tail + if (not_inits.empty ()) {m_all_init = true;} +} + +#ifdef Z3DEBUG +static bool is_all_non_null(app_ref_vector const& apps) { + for (auto *a : apps) if (!a) return false; + return true; +} +#endif + +void pred_transformer::init_rule(decl2rel const& pts, datalog::rule const& rule) { + scoped_watch _t_(m_initialize_watch); + + // Predicates that are variable representatives. Other predicates at + // positions the variables occur are made equivalent with these. + expr_ref_vector side(m); + app_ref_vector var_reprs(m); + ptr_vector aux_vars; + + unsigned ut_size = rule.get_uninterpreted_tail_size(); + unsigned t_size = rule.get_tail_size(); + SASSERT(ut_size <= t_size); + init_atom(pts, rule.get_head(), var_reprs, side, UINT_MAX); + for (unsigned i = 0; i < ut_size; ++i) { + if (rule.is_neg_tail(i)) { + throw default_exception("SPACER does not support " + "negated predicates in rule tails"); + } + init_atom(pts, rule.get_tail(i), var_reprs, side, i); + } + // -- substitute free variables + expr_ref trans(m); + { + expr_ref_vector tail(m); + for (unsigned i = ut_size; i < t_size; ++i) + tail.push_back(rule.get_tail(i)); + trans= mk_and (tail); + + ground_free_vars(trans, var_reprs, aux_vars, ut_size == 0); + SASSERT(is_all_non_null(var_reprs)); + + expr_ref tmp(m); + var_subst(m, false)(trans, var_reprs.size (), + (expr*const*)var_reprs.c_ptr(), tmp); + flatten_and (tmp, side); + trans = mk_and(side); + side.reset (); + } + + // rewrite and simplify + th_rewriter rw(m); + rw(trans); + if (ctx.blast_term_ite()) {blast_term_ite(trans, 3); rw(trans);} + TRACE("spacer_init_rule", tout << mk_pp(trans, m) << "\n";); + + // allow quantifiers in init rule + SASSERT(ut_size == 0 || is_ground(trans)); + if (!m.is_false(trans)) { + pt_rule &ptr = m_pt_rules.mk_rule(m, rule); + ptr.set_trans(trans); + ptr.set_auxs(aux_vars); + ptr.set_reps(var_reprs); + } + + // TRACE("spacer", + // tout << rule.get_decl()->get_name() << "\n"; + // tout << var_reprs << "\n";); +} + + +// create constants for free variables in tail. +void pred_transformer::ground_free_vars(expr* e, app_ref_vector& vars, + ptr_vector& aux_vars, bool is_init) { + expr_free_vars fv; + fv(e); + + while (vars.size() < fv.size()) {vars.push_back(nullptr);} + + for (unsigned i = 0; i < fv.size(); ++i) { + if (fv[i] && !vars[i].get()) { + // AG: is it useful to make names unique across rules? + app_ref v(m); + v = m.mk_fresh_const("aux", fv[i]); + v = m.mk_const (pm.get_n_pred(v->get_decl ())); + vars[i] = v; + aux_vars.push_back(v); + } + } + +} + +// create names for variables used in relations. +void pred_transformer::init_atom(decl2rel const &pts, app *atom, + app_ref_vector &var_reprs, + expr_ref_vector &side, unsigned tail_idx) { + unsigned arity = atom->get_num_args(); + func_decl* head = atom->get_decl(); + pred_transformer& pt = *pts.find(head); + for (unsigned i = 0; i < arity; i++) { + app_ref rep(m); + + if (tail_idx == UINT_MAX) { + rep = m.mk_const(pm.o2n(pt.sig(i), 0)); + } else { + rep = m.mk_const(pm.o2o(pt.sig(i), 0, tail_idx)); + } + + expr * arg = atom->get_arg(i); + if (is_var(arg)) { + var * v = to_var(arg); + unsigned var_idx = v->get_idx(); + if (var_idx >= var_reprs.size()) { + var_reprs.resize(var_idx+1); + } + expr * repr = var_reprs[var_idx].get(); + if (repr) { + side.push_back(m.mk_eq(rep, repr)); + } else { + var_reprs[var_idx] = rep; + } + } else { + SASSERT(is_app(arg)); + side.push_back(m.mk_eq(rep, arg)); + } + } +} + +void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r) +{ + if (lvl == 0) {r.push_back(m_init);} + else { + r.push_back(m_transition); + if (!m_transition_clause.empty()) { + expr_ref c(m); + c = mk_or(m_transition_clause); + r.push_back(c); + } + } + for (unsigned i = 0; i < rules().size(); ++i) { + add_premises(pts, lvl, *rules()[i], r); + } +} + +void pred_transformer::add_premises(decl2rel const& pts, unsigned lvl, + datalog::rule& rule, expr_ref_vector& r) +{ + find_predecessors(rule, m_predicates); + for (unsigned i = 0; i < m_predicates.size(); ++i) { + expr_ref tmp(m); + func_decl* head = m_predicates[i]; + pred_transformer& pt = *pts.find(head); + expr_ref inv = pt.get_formulas(lvl); + if (!m.is_true(inv)) { + pm.formula_n2o(inv, tmp, i, true); + r.push_back(tmp); + } + } +} + +void pred_transformer::inherit_lemmas(pred_transformer& other) +{m_frames.inherit_frames (other.m_frames);} + +app* pred_transformer::extend_initial (expr *e) +{ + // create fresh extend literal + app_ref v(m); + std::stringstream name; + name << m_head->get_name() << "_ext"; + v = m.mk_fresh_const (name.str ().c_str (), + m.mk_bool_sort ()); + v = m.mk_const (pm.get_n_pred (v->get_decl ())); + + expr_ref ic(m); + + // -- extend the initial condition + ic = m.mk_or (m_extend_lit, e, v); + m_solver->assert_expr (ic); + + // -- remember the new extend literal + m_extend_lit = m.mk_not (v); + + return m_extend_lit; +} + + +/// \brief Update a given solver with all constraints representing +/// this pred_transformer +void pred_transformer::updt_solver(prop_solver *solver) { + + solver->assert_expr(m_transition); + solver->assert_expr(m_init, 0); + + // -- facts derivable at the head + expr_ref last_tag(m); + last_tag = m_extend_lit0; + for (auto *rf : m_reach_facts) { + if (rf->is_init()) continue; // already in m_init + solver->assert_expr(m.mk_or(last_tag, rf->get(), rf->tag())); + last_tag = m.mk_not(rf->tag()); + } + SASSERT(last_tag == m_extend_lit); + + // -- lemmas + app_ref_vector _unused(m); + expr_ref_vector fmls(m); + // -- assert lemmas + for (auto *u : m_frames.lemmas()) { + // instances + u->mk_insts(fmls); + + // extra ground instance + if (!u->is_ground()) { + expr_ref gnd(m); + ground_expr(u->get_expr(), gnd, _unused); + fmls.push_back(gnd); + } + + // (quantified) lemma + if (u->is_ground() || get_context().use_qlemmas()) + fmls.push_back(u->get_expr()); + + // send to solver + if (is_infty_level(u->level())) + solver->assert_exprs(fmls); + else { + for (unsigned i = 0; i <= u->level(); ++i) + solver->assert_exprs(fmls, i); + } + fmls.reset(); + } + + // -- lemmas and rfs from other predicates + for (auto &kv : m_pt_rules) { + const datalog::rule &r = kv.m_value->rule(); + find_predecessors(r, m_predicates); + if (m_predicates.empty()) continue; + + for (unsigned i = 0, sz = m_predicates.size(); i < sz; ++i) { + const pred_transformer &pt = ctx.get_pred_transformer(m_predicates[i]); + // assert lemmas of pt + updt_solver_with_lemmas(solver, pt, to_app(kv.m_value->tag()), i); + // assert rfs of pt + update_solver_with_rfs(solver, pt, to_app(kv.m_value->tag()), i); + } + } +} + +void pred_transformer::updt_solver_with_lemmas(prop_solver *solver, + const pred_transformer &pt, + app* rule_tag, unsigned pos) { + app_ref_vector _unused(m); + expr_ref_vector fmls(m); + for (auto *u : pt.m_frames.lemmas()) { + expr_ref e(m), gnd(m); + e = u->get_expr(); + pm.formula_n2o(e, e, pos); + u->mk_insts(fmls, e); + + if (!u->is_ground()) { + // special ground instance + ground_expr(u->get_expr(), gnd, _unused); + pm.formula_n2o(gnd, gnd, pos); + fmls.push_back(gnd); + } + + // quantified formula + if (u->is_ground() || get_context().use_qlemmas()) + fmls.push_back(e); + + // add tag + for (unsigned i = 0, sz = fmls.size(); i < sz; ++i) + fmls.set(i, m.mk_implies(rule_tag, fmls.get(i))); + + // send to solver + if (is_infty_level(u->level())) + solver->assert_exprs(fmls); + else { + for (unsigned i = 1, end = next_level(u->level()); i <= end; ++i) + solver->assert_exprs(fmls, i); + } + fmls.reset(); + } +} + +void pred_transformer::update_solver_with_rfs(prop_solver *solver, + const pred_transformer &pt, + app *rule_tag, unsigned pos) { + expr_ref not_rule_tag(m); + not_rule_tag = m.mk_not(rule_tag); + + expr_ref last_tag(m); + for (auto *rf : pt.m_reach_facts) { + expr_ref e(m); + if (!last_tag) { + e = m.mk_or(m.mk_not(rule_tag), rf->get(), rf->tag()); + } + else { + expr *args[4] = { not_rule_tag, last_tag, rf->get(), rf->tag() }; + e = m.mk_or(4, args); + } + last_tag = m.mk_not(rf->tag()); + pm.formula_n2o(e.get(), e, pos); + solver->assert_expr(e); + } +} + +/// 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";); + + unsigned i = 0; + for (auto *old_lemma : m_lemmas) { + if (old_lemma->get_expr() == new_lemma->get_expr()) { + m_pt.get_context().new_lemma_eh(m_pt, new_lemma); + + // register existing lemma with the pob + if (new_lemma->has_pob()) { + pob_ref &pob = new_lemma->get_pob(); + if (!pob->lemmas().contains(old_lemma)) + pob->add_lemma(old_lemma); + } + + // extend bindings if needed + if (!new_lemma->get_bindings().empty()) { + old_lemma->add_binding(new_lemma->get_bindings()); + } + // if the lemma is at a higher level, skip it, + if (old_lemma->level() >= new_lemma->level()) { + TRACE("spacer", tout << "Already at a higher level: " + << pp_level(old_lemma->level()) << "\n";); + // but, since the instances might be new, assert the + // instances that have been copied into m_lemmas[i] + if (!new_lemma->get_bindings().empty()) { + m_pt.add_lemma_core(old_lemma, true); + } + if (is_infty_level(old_lemma->level())) { + old_lemma->bump(); + if (old_lemma->get_bumped() >= 100) { + IF_VERBOSE(1, verbose_stream() << "Adding lemma to oo " + << old_lemma->get_bumped() << " " + << mk_pp(old_lemma->get_expr(), + m_pt.get_ast_manager()) << "\n";); + throw default_exception("Stuck on a lemma"); + } + } + // no new lemma added + return false; + } + + // update level of the existing lemma + old_lemma->set_level(new_lemma->level()); + // assert lemma in the solver + m_pt.add_lemma_core(old_lemma, false); + // move the lemma to its new place to maintain sortedness + unsigned sz = m_lemmas.size(); + for (unsigned j = i; + (j + 1) < sz && m_lt(m_lemmas[j + 1], m_lemmas[j]); ++j) { + m_lemmas.swap (j, j+1); + } + return true; + } + i++; + } + + // new_lemma is really new + m_lemmas.push_back(new_lemma); + // XXX because m_lemmas is reduced, keep secondary vector of all lemmas + // XXX so that pob can refer to its lemmas without creating reference cycles + m_pinned_lemmas.push_back(new_lemma); + m_sorted = false; + m_pt.add_lemma_core(new_lemma); + + if (new_lemma->has_pob()) {new_lemma->get_pob()->add_lemma(new_lemma);} + + if (!new_lemma->external()) { + m_pt.get_context().new_lemma_eh(m_pt, new_lemma); + } + return true; +} + + +void pred_transformer::frames::propagate_to_infinity (unsigned level) +{ + for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i) + if (m_lemmas[i]->level() >= level && !is_infty_level(m_lemmas [i]->level())) { + m_lemmas [i]->set_level (infty_level ()); + m_pt.add_lemma_core (m_lemmas [i]); + m_sorted = false; + } +} + +void pred_transformer::frames::sort () +{ + if (m_sorted) { return; } + + m_sorted = true; + std::sort(m_lemmas.c_ptr(), m_lemmas.c_ptr() + m_lemmas.size (), m_lt); +} + +bool pred_transformer::frames::propagate_to_next_level (unsigned level) +{ + sort (); + bool all = true; + + + if (m_lemmas.empty()) { return all; } + + unsigned tgt_level = next_level (level); + m_pt.ensure_level (tgt_level); + + for (unsigned i = 0, sz = m_lemmas.size(); i < sz && m_lemmas [i]->level() <= level;) { + if (m_lemmas [i]->level () < level) {++i; continue;} + + unsigned solver_level; + if (m_pt.is_invariant(tgt_level, m_lemmas.get(i), solver_level)) { + m_lemmas [i]->set_level (solver_level); + m_pt.add_lemma_core (m_lemmas.get(i)); + + // percolate the lemma up to its new place + for (unsigned j = i; (j+1) < sz && m_lt (m_lemmas[j+1], m_lemmas[j]); ++j) { + m_lemmas.swap(j, j + 1); + } + ++m_pt.m_stats.m_num_propagations; + } else { + all = false; + ++i; + } + } + + return all; +} + +void pred_transformer::frames::simplify_formulas () +{ + // number of subsumed lemmas + unsigned num_sumbsumed = 0; + + // ensure that the lemmas are sorted + sort(); + ast_manager &m = m_pt.get_ast_manager(); + + tactic_ref simplifier = mk_unit_subsumption_tactic(m); + lemma_ref_vector new_lemmas; + + unsigned lemmas_size = m_lemmas.size(); + goal_ref g(alloc (goal, m, false, false, false)); + + unsigned j = 0; + // for every frame + infinity frame + for (unsigned i = 0; i <= m_size; ++i) { + g->reset_all (); + // normalize level + unsigned level = i < m_size ? i : infty_level (); + + goal_ref_buffer result; + + // simplify lemmas of the current level + // XXX lemmas of higher levels can be assumed in background + // XXX decide what to do with non-ground lemmas! + unsigned begin = j; + for (; j < lemmas_size && m_lemmas[j]->level() <= level; ++j) { + if (m_lemmas[j]->level() == level) { + g->assert_expr(m_lemmas[j]->get_expr()); + } + } + unsigned end = j; + + unsigned sz = end - begin; + // no lemmas at current level, move to next level + if (sz <= 0) {continue;} + + // exactly one lemma at current level, nothing to + // simplify. move to next level + if (sz == 1) { + new_lemmas.push_back(m_lemmas[begin]); + continue; + } + + // more than one lemma at current level. simplify. + (*simplifier)(g, result); + SASSERT(result.size () == 1); + goal *r = result[0]; + + // no simplification happened, copy all the lemmas + if (r->size () == sz) { + for (unsigned n = begin; n < end; ++n) { + new_lemmas.push_back (m_lemmas[n]); + } + } + // something got simplified, find out which lemmas remain + else { + num_sumbsumed += (sz - r->size()); + // For every expression in the result, copy corresponding + // lemma into new_lemmas + // XXX linear search. optimize if needed. + for (unsigned k = 0; k < r->size(); ++k) { + bool found = false; + for (unsigned n = begin; n < end; ++n) { + if (m_lemmas[n]->get_expr() == r->form(k)) { + new_lemmas.push_back(m_lemmas[n]); + found = true; + break; + } + } + if (!found) { + verbose_stream() << "Failed to find a lemma for: " + << mk_pp(r->form(k), m) << "\n"; + verbose_stream() << "Available lemmas are: "; + for (unsigned n = begin; n < end; ++n) { + verbose_stream() << n << ": " + << mk_pp(m_lemmas[n]->get_expr(), m) + << "\n"; + } + + verbose_stream() << "Simplified goal is:\n"; + for (unsigned k = 0; k < r->size(); ++k) + verbose_stream() << k << ": " + << mk_pp(r->form(k), m) << "\n"; + } + ENSURE(found); + SASSERT(found); + } + } + } + + SASSERT(new_lemmas.size() + num_sumbsumed == m_lemmas.size()); + ENSURE(new_lemmas.size() + num_sumbsumed == m_lemmas.size()); + if (new_lemmas.size() < m_lemmas.size()) { + m_lemmas.reset(); + m_lemmas.append(new_lemmas); + m_sorted = false; + sort(); + } +} + +/// pred_transformer::pobs + +pob* pred_transformer::pobs::mk_pob(pob *parent, + unsigned level, unsigned depth, + expr *post, app_ref_vector const &b) { + + if (!m_pt.ctx.reuse_pobs()) { + pob* n = alloc(pob, parent, m_pt, level, depth); + n->set_post(post, b); + return n; + } + + // create a new pob and set its post to normalize it + pob p(parent, m_pt, level, depth, false); + p.set_post(post, b); + + if (m_pobs.contains(p.post())) { + auto &buf = m_pobs[p.post()]; + for (unsigned i = 0, sz = buf.size(); i < sz; ++i) { + pob *f = buf.get(i); + if (f->parent() == parent) { + f->inherit(p); + return f; + } + } + } + + pob* n = alloc(pob, parent, m_pt, level, depth); + n->set_post(post, b); + m_pinned.push_back(n); + + if (m_pobs.contains(n->post())) { + m_pobs[n->post()].push_back(n); + } + else { + pob_buffer buf; + buf.push_back(n); + m_pobs.insert(n->post(), buf); + } + return n; +} + + + + // ---------------- // context -context::context(fixedpoint_params const& params, - ast_manager& m) : +context::context(fp_params const& params, ast_manager& m) : m_params(params), m(m), - m_context(0), - m_pm(params.pdr_max_num_contexts(), m), + m_context(nullptr), + m_pm(m), m_query_pred(m), - m_query(0), + m_query(nullptr), m_pob_queue(), m_last_result(l_undef), m_inductive_lvl(0), m_expanded_lvl(0), - m_use_native_mbp(params.spacer_native_mbp ()), - m_ground_cti (params.spacer_ground_cti ()), - m_instantiate (params.spacer_instantiate ()), - m_use_qlemmas (params.spacer_qlemmas ()), - m_weak_abs(params.spacer_weak_abs()), - m_use_restarts(params.spacer_restarts()), - m_restart_initial_threshold(params.spacer_restart_initial_threshold()) -{} + m_json_marshaller(this) { + ref pool0_base = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + ref pool1_base = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + ref pool2_base = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + + unsigned max_num_contexts = params.spacer_max_num_contexts(); + m_pool0 = alloc(solver_pool, pool0_base.get(), max_num_contexts); + m_pool1 = alloc(solver_pool, pool1_base.get(), max_num_contexts); + m_pool2 = alloc(solver_pool, pool2_base.get(), max_num_contexts); + + updt_params(); +} context::~context() { @@ -1905,16 +2201,62 @@ context::~context() reset(); } +void context::updt_params() { + m_random.set_seed(m_params.spacer_random_seed()); + m_children_order = static_cast(m_params.spacer_order_children()); + m_simplify_pob = m_params.spacer_simplify_pob(); + m_use_euf_gen = m_params.spacer_use_euf_gen(); + m_use_ctp = m_params.spacer_ctp(); + m_use_inc_clause = m_params.spacer_use_inc_clause(); + m_blast_term_ite = m_params.spacer_blast_term_ite(); + m_reuse_pobs = m_params.spacer_reuse_pobs(); + m_use_ind_gen = m_params.spacer_use_inductive_generalizer(); + m_use_array_eq_gen = m_params.spacer_use_array_eq_generalizer(); + m_validate_lemmas = m_params.spacer_validate_lemmas(); + m_max_level = m_params.spacer_max_level (); + m_use_propagate = m_params.spacer_propagate (); + m_reset_obligation_queue = m_params.spacer_reset_pob_queue(); + m_push_pob = m_params.spacer_push_pob(); + m_push_pob_max_depth = m_params.spacer_push_pob_max_depth(); + m_use_lemma_as_pob = m_params.spacer_use_lemma_as_cti(); + m_elim_aux = m_params.spacer_elim_aux(); + m_reach_dnf = m_params.spacer_reach_dnf(); + m_use_derivations = m_params.spacer_use_derivations(); + m_validate_result = m_params.validate(); + m_use_eq_prop = m_params.spacer_eq_prop(); + m_ground_pob = m_params.spacer_ground_pobs(); + m_q3_qgen = m_params.spacer_q3_use_qgen(); + m_use_gpdr = m_params.spacer_gpdr(); + m_simplify_formulas_pre = m_params.spacer_simplify_lemmas_pre(); + m_simplify_formulas_post = m_params.spacer_simplify_lemmas_post(); + m_use_native_mbp = m_params.spacer_native_mbp (); + m_instantiate = m_params.spacer_q3_instantiate (); + m_use_qlemmas = m_params.spacer_q3(); + m_weak_abs = m_params.spacer_weak_abs(); + 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(); + + if (m_use_gpdr) { + // set options to be compatible with GPDR + m_weak_abs = false; + m_push_pob = false; + m_use_qlemmas = false; + m_ground_pob = true; + m_reset_obligation_queue = false; + m_use_derivations = false; + m_use_lemma_as_pob = false; + } +} + + void context::reset() { TRACE("spacer", tout << "\n";); m_pob_queue.reset(); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - dealloc(it->m_value); - } + for (auto &entry: m_rels) {dealloc(entry.m_value);} m_rels.reset(); - m_query = 0; + m_query = nullptr; m_last_result = l_undef; m_inductive_lvl = 0; } @@ -1923,23 +2265,22 @@ void context::init_rules(datalog::rule_set& rules, decl2rel& rels) { scoped_watch _t_(m_init_rules_watch); m_context = &rules.get_context(); + // Allocate collection of predicate transformers - datalog::rule_set::decl2rules::iterator dit = rules.begin_grouped_rules(), dend = rules.end_grouped_rules(); - decl2rel::obj_map_entry* e; - for (; dit != dend; ++dit) { + for (auto dit = rules.begin_grouped_rules(), + dend = rules.end_grouped_rules(); dit != dend; ++dit) { func_decl* pred = dit->m_key; TRACE("spacer", tout << mk_pp(pred, m) << "\n";); SASSERT(!rels.contains(pred)); - e = rels.insert_if_not_there2(pred, alloc(pred_transformer, *this, - get_manager(), pred)); + auto *e = rels.insert_if_not_there2(pred, alloc(pred_transformer, *this, + get_manager(), pred)); datalog::rule_vector const& pred_rules = *dit->m_value; - for (unsigned i = 0; i < pred_rules.size(); ++i) { - e->get_data().m_value->add_rule(pred_rules[i]); - } + for (auto rule : pred_rules) {e->get_data().m_value->add_rule(rule);} } - datalog::rule_set::iterator rit = rules.begin(), rend = rules.end(); - for (; rit != rend; ++rit) { - datalog::rule* r = *rit; + + // Allocate predicate transformers for predicates that are used + // but don't have rules + for (auto *r : rules) { pred_transformer* pt; unsigned utz = r->get_uninterpreted_tail_size(); for (unsigned i = 0; i < utz; ++i) { @@ -1950,56 +2291,65 @@ void context::init_rules(datalog::rule_set& rules, decl2rel& rels) } } } + // Initialize use list dependencies - decl2rel::iterator it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - func_decl* pred = it->m_key; - pred_transformer* pt = it->m_value, *pt_user; - obj_hashtable const& deps = rules.get_dependencies().get_deps(pred); - obj_hashtable::iterator itf = deps.begin(), endf = deps.end(); - for (; itf != endf; ++itf) { - TRACE("spacer", tout << mk_pp(pred, m) << " " << mk_pp(*itf, m) << "\n";); - pt_user = rels.find(*itf); + for (auto &entry : rels) { + func_decl* pred = entry.m_key; + pred_transformer* pt = entry.m_value, *pt_user = nullptr; + for (auto dep : rules.get_dependencies().get_deps(pred)) { + TRACE("spacer", tout << mk_pp(pred, m) << " " << mk_pp(dep, m) << "\n";); + rels.find(dep, pt_user); pt_user->add_use(pt); } } // Initialize the predicate transformers. - it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - pred_transformer& rel = *it->m_value; - rel.initialize(rels); - TRACE("spacer", rel.display(tout); ); + for (auto &entry : rels) { + pred_transformer* rel = entry.m_value; + rel->initialize(rels); + TRACE("spacer", rel->display(tout); ); } // initialize reach facts - it = rels.begin (), end = rels.end (); - for (; it != end; ++it) - { it->m_value->init_reach_facts(); } + for (auto &entry : rels) {entry.m_value->init_rfs();} +} + +void context::inherit_lemmas(const decl2rel &rels) { + for (auto &entry : rels) { + pred_transformer *pt = nullptr; + if (m_rels.find(entry.m_key, pt)) { + entry.m_value->inherit_lemmas(*pt); + } + } } void context::update_rules(datalog::rule_set& rules) { decl2rel rels; - init_lemma_generalizers(rules); + // SMT params must be set before any expression is asserted to any + // solver + init_global_smt_params(); + // constructs new pred transformers and asserts trans and init init_rules(rules, rels); - decl2rel::iterator it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - pred_transformer* pt = 0; - if (m_rels.find(it->m_key, pt)) { - it->m_value->inherit_properties(*pt); - } - } + // inherits lemmas from m_rels into rels + inherit_lemmas(rels); + // switch context to new rels + init(rels); + // re-initialize lemma generalizers + init_lemma_generalizers(); +} + +void context::init(const decl2rel &rels) { + // reset context. Current state is all stored in rels reset(); - it = rels.begin(), end = rels.end(); - for (; it != end; ++it) { - m_rels.insert(it->m_key, it->m_value); - } + // re-initialize context + for (auto &entry : rels) + {m_rels.insert(entry.m_key, entry.m_value);} } unsigned context::get_num_levels(func_decl* p) { - pred_transformer* pt = 0; + pred_transformer* pt = nullptr; if (m_rels.find(p, pt)) { return pt->get_num_levels(); } else { @@ -2010,7 +2360,7 @@ unsigned context::get_num_levels(func_decl* p) expr_ref context::get_cover_delta(int level, func_decl* p_orig, func_decl* p) { - pred_transformer* pt = 0; + pred_transformer* pt = nullptr; if (m_rels.find(p, pt)) { return pt->get_cover_delta(p_orig, level); } else { @@ -2021,7 +2371,7 @@ 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) { - pred_transformer* pt = 0; + pred_transformer* pt = nullptr; if (!m_rels.find(p, pt)) { pt = alloc(pred_transformer, *this, get_manager(), p); m_rels.insert(p, pt); @@ -2036,7 +2386,7 @@ void context::add_invariant (func_decl *p, expr *property) expr_ref context::get_reachable(func_decl *p) { - pred_transformer* pt = 0; + pred_transformer* pt = nullptr; if (!m_rels.find(p, pt)) { return expr_ref(m.mk_false(), m); } return pt->get_reachable(); @@ -2044,12 +2394,13 @@ expr_ref context::get_reachable(func_decl *p) bool context::validate() { - if (!m_params.pdr_validate_result()) { return true; } + if (!m_validate_result) { return true; } std::stringstream msg; switch(m_last_result) { case l_true: { +#if 0 expr_ref cex(m); cex = get_ground_sat_answer(); if (!cex.get()) { @@ -2057,6 +2408,14 @@ bool context::validate() throw default_exception("Cex validation failed\n"); return false; } +#endif + proof_ref cex(m); + cex = get_ground_refutation(); + if (!cex.get()) { + IF_VERBOSE(0, verbose_stream() << "Cex validation failed\n";); + throw default_exception("Cex validation failed\n"); + return false; + } break; } case l_false: { @@ -2068,27 +2427,26 @@ bool context::validate() get_level_property(m_inductive_lvl, refs, rs); inductive_property ex(m, mc, rs); ex.to_model(model); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); var_subst vs(m, false); - for (; it != end; ++it) { - ptr_vector const& rules = it->m_value->rules(); - TRACE ("spacer", tout << "PT: " << it->m_value->head ()->get_name ().str () + for (auto& kv : m_rels) { + ptr_vector const& rules = kv.m_value->rules(); + TRACE ("spacer", tout << "PT: " << kv.m_value->head ()->get_name ().str () << "\n";); - for (unsigned i = 0; i < rules.size(); ++i) { - datalog::rule& r = *rules[i]; + for (auto* rp : rules) { + datalog::rule& r = *rp; TRACE ("spacer", get_datalog_context (). get_rule_manager (). display_smt2(r, tout) << "\n";); - model->eval(r.get_head(), tmp); + tmp = (*model)(r.get_head()); expr_ref_vector fmls(m); fmls.push_back(m.mk_not(tmp)); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); for (unsigned j = 0; j < utsz; ++j) { - model->eval(r.get_tail(j), tmp); + tmp = (*model)(r.get_tail(j)); fmls.push_back(tmp); } for (unsigned j = utsz; j < tsz; ++j) { @@ -2107,9 +2465,10 @@ bool context::validate() fv.reverse (); tmp = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), tmp); } - smt::kernel solver(m, m_pm.fparams2()); - solver.assert_expr(tmp); - lbool res = solver.check(); + ref sol = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); + sol->assert_expr(tmp); + lbool res = sol->check_sat(0, nullptr); if (res != l_false) { msg << "rule validation failed when checking: " << mk_pp(tmp, m); @@ -2136,56 +2495,83 @@ void context::reset_lemma_generalizers() m_lemma_generalizers.reset(); } -void context::init_lemma_generalizers(datalog::rule_set& rules) +// initialize global SMT parameters shared by all solvers +void context::init_global_smt_params() { + m.toggle_proof_mode(PGM_ENABLED); + params_ref p; + if (!m_use_eq_prop) { + p.set_uint("arith.propagation_mode", BP_NONE); + p.set_bool("arith.auto_config_simplex", true); + p.set_bool("arith.propagate_eqs", false); + p.set_bool("arith.eager_eq_axioms", false); + } + p.set_uint("random_seed", m_params.spacer_random_seed()); + + p.set_bool("dump_benchmarks", m_params.spacer_dump_benchmarks()); + p.set_double("dump_threshold", m_params.spacer_dump_threshold()); + + // mbqi + p.set_bool("mbqi", m_params.spacer_mbqi()); + + if (!m_ground_pob) { + p.set_uint("phase_selection", PS_CACHING_CONSERVATIVE2); + p.set_uint("restart_strategy", RS_GEOMETRIC); + p.set_double("restart_factor", 1.5); + p.set_uint("qi.quick_checker", MC_UNSAT); + p.set_double("qi.eager_threshold", 10.0); + p.set_double("qi.lazy_threshold", 20.0); + + // options that we used to set, but are not user visible and + // possibly not very useful + // fparams.m_ng_lift_ite = LI_FULL; + // fparams.m_eliminate_bounds = true; + // fparams.m_pi_use_database = true; + } + + m_pool0->updt_params(p); + m_pool1->updt_params(p); + m_pool2->updt_params(p); +} +void context::init_lemma_generalizers() { reset_lemma_generalizers(); - m.toggle_proof_mode(PGM_ENABLED); - smt_params &fparams = m_pm.fparams (); - if (!m_params.spacer_eq_prop ()) { - fparams.m_arith_bound_prop = BP_NONE; - fparams.m_arith_auto_config_simplex = true; - fparams.m_arith_propagate_eqs = false; - fparams.m_arith_eager_eq_axioms = false; + + if (m_q3_qgen) { + m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, + *this, 0, true)); + m_lemma_generalizers.push_back(alloc(lemma_quantifier_generalizer, *this, + m_params.spacer_q3_qgen_normalize())); } - fparams.m_random_seed = m_params.spacer_random_seed (); - fparams.m_dump_benchmarks = m_params.spacer_vs_dump_benchmarks(); - fparams.m_dump_min_time = m_params.spacer_vs_dump_min_time(); - fparams.m_dump_recheck = m_params.spacer_vs_recheck(); - - fparams.m_mbqi = m_params.spacer_mbqi(); - - if (get_params().spacer_use_eqclass()) { + if (m_use_euf_gen) { m_lemma_generalizers.push_back (alloc(lemma_eq_generalizer, *this)); } // -- AG: commented out because it is causing performance issues at the moment //m_lemma_generalizers.push_back (alloc (unsat_core_generalizer, *this)); - if (m_params.pdr_use_inductive_generalizer()) { + if (m_use_ind_gen) { m_lemma_generalizers.push_back(alloc(lemma_bool_inductive_generalizer, *this, 0)); } - if (m_params.spacer_use_array_eq_generalizer()) { + if (m_use_array_eq_gen) { m_lemma_generalizers.push_back(alloc(lemma_array_eq_generalizer, *this)); } - if (get_params().spacer_lemma_sanity_check()) { + if (m_validate_lemmas) { m_lemma_generalizers.push_back(alloc(lemma_sanity_checker, *this)); } } void context::get_level_property(unsigned lvl, expr_ref_vector& res, - vector& rs) const -{ - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer* r = it->m_value; + vector& rs) 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, false); + expr_ref conj = r->get_formulas(lvl); m_pm.formula_n2o(0, false, conj); res.push_back(conj); ptr_vector sig(r->head()->get_arity(), r->sig()); @@ -2193,12 +2579,9 @@ void context::get_level_property(unsigned lvl, expr_ref_vector& res, } } -void context::simplify_formulas() -{ - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer* r = it->m_value; - r->simplify_formulas(); +void context::simplify_formulas() { + for (auto& kv : m_rels) { + kv.m_value->simplify_formulas(); } } @@ -2206,7 +2589,14 @@ lbool context::solve(unsigned from_lvl) { m_last_result = l_undef; try { - m_last_result = solve_core (from_lvl); + if (m_use_gpdr) { + SASSERT(from_lvl == 0); + m_last_result = gpdr_solve_core(); + } + else { + m_last_result = solve_core (from_lvl); + } + if (m_last_result == l_false) { simplify_formulas(); m_last_result = l_false; @@ -2272,7 +2662,7 @@ unsigned context::get_cex_depth() pred_transformer* pt; // get and discard query rule - fact = m_query->get_last_reach_fact (); + fact = m_query->get_last_rf (); r = &fact->get_rule (); unsigned cex_depth = 0; @@ -2300,7 +2690,7 @@ unsigned context::get_cex_depth() // get current pt and fact pt = pts.get (curr); // check for depth marker - if (pt == NULL) { + if (pt == nullptr) { ++cex_depth; // insert new marker if there are pts at higher depth if (curr + 1 < pts.size()) { pts.push_back(NULL); } @@ -2347,7 +2737,7 @@ void context::get_rules_along_trace(datalog::rule_ref_vector& rules) pred_transformer* pt; // get query rule - fact = m_query->get_last_reach_fact (); + fact = m_query->get_last_rf (); r = &fact->get_rule (); rules.push_back (const_cast (r)); TRACE ("spacer", @@ -2433,19 +2823,31 @@ expr_ref context::mk_unsat_answer() const return ex.to_expr(); } + +proof_ref context::get_ground_refutation() { + if (m_last_result != l_true) { + IF_VERBOSE(0, verbose_stream() + << "Sat answer unavailable when result is false\n";); + return proof_ref(m); + } + + ground_sat_answer_op op(*this); + return op(*m_query); +} expr_ref context::get_ground_sat_answer() { if (m_last_result != l_true) { - verbose_stream () << "Sat answer unavailable when result is false\n"; - return expr_ref (m); + IF_VERBOSE(0, verbose_stream() + << "Sat answer unavailable when result is false\n";); + return expr_ref(m); } // treat the following as queues: read from left to right and insert at the right reach_fact_ref_vector reach_facts; ptr_vector preds; ptr_vector pts; - expr_ref_vector cex (m), // pre-order list of ground instances of predicates - cex_facts (m); // equalities for the ground cex using signature constants + expr_ref_vector cex (m); // pre-order list of ground instances of predicates + expr_ref_vector cex_facts (m); // equalities for the ground cex using signature constants // temporary reach_fact *reach_fact; @@ -2454,7 +2856,7 @@ expr_ref context::get_ground_sat_answer() datalog::rule const* r; // get and discard query rule - reach_fact = m_query->get_last_reach_fact (); + reach_fact = m_query->get_last_rf (); r = &reach_fact->get_rule (); // initialize queues @@ -2476,8 +2878,8 @@ expr_ref context::get_ground_sat_answer() { cex.push_back(m.mk_const(preds[0])); } // smt context to obtain local cexes - scoped_ptr cex_ctx = alloc (smt::kernel, m, m_pm.fparams2 ()); - model_evaluator_util mev (m); + ref cex_ctx = + mk_smt_solver(m, params_ref::get_empty(), symbol::null); // preorder traversal of the query derivation tree for (unsigned curr = 0; curr < pts.size (); curr++) { @@ -2496,6 +2898,7 @@ expr_ref context::get_ground_sat_answer() // get child pts preds.reset(); pt->find_predecessors(*r, preds); + for (unsigned j = 0; j < preds.size (); j++) { child_pts.push_back (&(get_pred_transformer (preds[j]))); } @@ -2511,13 +2914,12 @@ expr_ref context::get_ground_sat_answer() SASSERT (child_reach_facts.size () == u_tail_sz); for (unsigned i = 0; i < u_tail_sz; i++) { expr_ref ofml (m); - child_pts.get (i)->get_manager ().formula_n2o - (child_reach_facts[i]->get (), ofml, i); + m_pm.formula_n2o(child_reach_facts[i]->get(), ofml, i); cex_ctx->assert_expr (ofml); } - cex_ctx->assert_expr (pt->transition ()); - cex_ctx->assert_expr (pt->rule2tag (r)); - lbool res = cex_ctx->check (); + cex_ctx->assert_expr(pt->transition()); + cex_ctx->assert_expr(pt->rule2tag(r)); + lbool res = cex_ctx->check_sat(0, nullptr); CTRACE("cex", res == l_false, tout << "Cex fact: " << mk_pp(cex_fact, m) << "\n"; for (unsigned i = 0; i < u_tail_sz; i++) @@ -2530,41 +2932,35 @@ expr_ref context::get_ground_sat_answer() model_ref local_mdl; cex_ctx->get_model (local_mdl); cex_ctx->pop (1); - - model_evaluator_util mev (m); - mev.set_model (*local_mdl); - for (unsigned i = 0; i < child_pts.size (); i++) { - pred_transformer& ch_pt = *(child_pts.get (i)); - unsigned sig_size = ch_pt.sig_size (); - expr_ref_vector ground_fact_conjs (m); - expr_ref_vector ground_arg_vals (m); + local_mdl->set_model_completion(true); + for (unsigned i = 0; i < child_pts.size(); i++) { + pred_transformer& ch_pt = *(child_pts.get(i)); + unsigned sig_size = ch_pt.sig_size(); + expr_ref_vector ground_fact_conjs(m); + expr_ref_vector ground_arg_vals(m); for (unsigned j = 0; j < sig_size; j++) { - expr_ref sig_arg (m), sig_val (m); - sig_arg = m.mk_const (ch_pt.get_manager ().o2o (ch_pt.sig (j), 0, i)); - VERIFY(mev.eval (sig_arg, sig_val, true)); - ground_fact_conjs.push_back (m.mk_eq (sig_arg, sig_val)); - ground_arg_vals.push_back (sig_val); + expr_ref sig_arg(m), sig_val(m); + sig_arg = m.mk_const (m_pm.o2o(ch_pt.sig(j), 0, i)); + sig_val = (*local_mdl)(sig_arg); + ground_fact_conjs.push_back(m.mk_eq(sig_arg, sig_val)); + ground_arg_vals.push_back(sig_val); } if (ground_fact_conjs.size () > 0) { - expr_ref ground_fact (m); - ground_fact = m.mk_and (ground_fact_conjs.size (), ground_fact_conjs.c_ptr ()); - ch_pt.get_manager ().formula_o2n (ground_fact, ground_fact, i); + expr_ref ground_fact(m); + ground_fact = mk_and(ground_fact_conjs); + m_pm.formula_o2n(ground_fact, ground_fact, i); cex_facts.push_back (ground_fact); } else { cex_facts.push_back (m.mk_true ()); } - cex.push_back (m.mk_app (ch_pt.head (), sig_size, ground_arg_vals.c_ptr ())); + cex.push_back(m.mk_app(ch_pt.head(), + sig_size, ground_arg_vals.c_ptr())); } } - TRACE ("spacer", - tout << "ground cex\n"; - for (unsigned i = 0; i < cex.size (); i++) { - tout << mk_pp (cex.get (i), m) << "\n"; - } - ); + TRACE ("spacer", tout << "ground cex\n" << cex << "\n";); - return expr_ref (m.mk_and (cex.size (), cex.c_ptr ()), m); + return expr_ref(m.mk_and(cex.size(), cex.c_ptr()), m); } ///this is where everything starts @@ -2579,17 +2975,28 @@ lbool context::solve_core (unsigned from_lvl) pob *root = m_query->mk_pob(nullptr,from_lvl,0,m.mk_true()); m_pob_queue.set_root (*root); - unsigned max_level = get_params ().spacer_max_level (); + unsigned max_level = m_max_level; - for (unsigned i = 0; i < max_level; ++i) { + for (unsigned i = from_lvl; i < max_level; ++i) { checkpoint(); m_expanded_lvl = infty_level (); m_stats.m_max_query_lvl = lvl; if (check_reachability()) { return l_true; } - if (lvl > 0 && !get_params ().spacer_skip_propagate ()) - if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { return l_false; } + if (lvl > 0 && m_use_propagate) + if (propagate(m_expanded_lvl, lvl, UINT_MAX)) { dump_json(); return l_false; } + + dump_json(); + + if (is_inductive()){ + return l_false; + } + + for (unsigned i = 0; i < m_callbacks.size(); i++){ + if (m_callbacks[i]->unfold()) + m_callbacks[i]->unfold_eh(); + } m_pob_queue.inc_level (); lvl = m_pob_queue.max_level (); @@ -2622,7 +3029,9 @@ bool context::check_reachability () pob_ref last_reachable; - if (get_params().spacer_reset_obligation_queue()) { m_pob_queue.reset(); } + pob_ref_buffer new_pobs; + + if (m_reset_obligation_queue) { m_pob_queue.reset(); } unsigned initial_size = m_stats.m_num_lemmas; unsigned threshold = m_restart_initial_threshold; @@ -2635,7 +3044,7 @@ bool context::check_reachability () while (last_reachable) { checkpoint (); node = last_reachable; - last_reachable = NULL; + last_reachable = nullptr; if (m_pob_queue.is_root(*node)) { return true; } if (is_reachable (*node->parent())) { last_reachable = node->parent (); @@ -2688,40 +3097,45 @@ bool context::check_reachability () } node = m_pob_queue.top (); + m_pob_queue.pop(); + size_t old_sz = m_pob_queue.size(); + (void)old_sz; SASSERT (node->level () <= m_pob_queue.max_level ()); - switch (expand_node(*node)) { + switch (expand_pob(*node, new_pobs)) { case l_true: - SASSERT (m_pob_queue.top () == node.get ()); - m_pob_queue.pop (); + SASSERT(m_pob_queue.size() == old_sz); + SASSERT(new_pobs.empty()); last_reachable = node; last_reachable->close (); - if (m_pob_queue.is_root(*node)) { return true; } + if (m_pob_queue.is_root(*node)) {return true;} break; case l_false: - SASSERT (m_pob_queue.top () == node.get ()); - m_pob_queue.pop (); + SASSERT(m_pob_queue.size() == old_sz); + for (auto pob : new_pobs) { + if (is_requeue(*pob)) {m_pob_queue.push(*pob);} + } - if (node->is_dirty()) { node->clean(); } - - node->inc_level (); - if (get_params ().pdr_flexible_trace () && - (node->level () >= m_pob_queue.max_level () || - m_pob_queue.max_level () - node->level () - <= get_params ().pdr_flexible_trace_depth ())) - { m_pob_queue.push(*node); } - - if (m_pob_queue.is_root(*node)) { return false; } + if (m_pob_queue.is_root(*node)) {return false;} break; case l_undef: - // SASSERT (m_pob_queue.top () != node.get ()); + SASSERT(m_pob_queue.size() == old_sz); + for (auto pob : new_pobs) {m_pob_queue.push(*pob);} break; } + new_pobs.reset(); } UNREACHABLE(); return false; } +/// returns true if the given pob can be re-scheduled +bool context::is_requeue(pob &n) { + if (!m_push_pob) {return false;} + unsigned max_depth = m_push_pob_max_depth; + return (n.level() >= m_pob_queue.max_level() || + m_pob_queue.max_level() - n.level() <= max_depth); +} /// check whether node n is concretely reachable bool context::is_reachable(pob &n) { @@ -2747,18 +3161,18 @@ bool context::is_reachable(pob &n) // used in case n is unreachable unsigned uses_level = infty_level (); - model_ref model; + model_ref mdl; // used in case n is reachable bool is_concrete; - const datalog::rule * r = NULL; + const datalog::rule * r = nullptr; // denotes which predecessor's (along r) reach facts are used vector reach_pred_used; unsigned num_reuse_reach = 0; unsigned saved = n.level (); n.m_level = infty_level (); - lbool res = n.pt().is_reachable(n, NULL, &model, + lbool res = n.pt().is_reachable(n, nullptr, &mdl, uses_level, is_concrete, r, reach_pred_used, num_reuse_reach); n.m_level = saved; @@ -2772,17 +3186,15 @@ bool context::is_reachable(pob &n) SASSERT(res == l_true); SASSERT(is_concrete); - model_evaluator_util mev (m); - mev.set_model(*model); // -- update must summary if (r && r->get_uninterpreted_tail_size () > 0) { - reach_fact_ref rf = mk_reach_fact (n, mev, *r); - n.pt ().add_reach_fact (rf.get ()); + reach_fact_ref rf = n.pt().mk_rf (n, *mdl, *r); + n.pt ().add_rf (rf.get ()); } // if n has a derivation, create a new child and report l_undef // otherwise if n has no derivation or no new children, report l_true - pob *next = NULL; + pob *next = nullptr; scoped_ptr deriv; if (n.has_derivation()) {deriv = n.detach_derivation();} @@ -2816,23 +3228,47 @@ bool context::is_reachable(pob &n) return next ? is_reachable(*next) : true; } -//this processes a goal and creates sub-goal -lbool context::expand_node(pob& n) +void context::dump_json() { + if (m_params.spacer_print_json().size()) { + std::ofstream of; + of.open(m_params.spacer_print_json().bare_str()); + m_json_marshaller.marshal(of); + of.close(); + } +} + +void context::predecessor_eh() +{ + for (unsigned i = 0; i < m_callbacks.size(); i++) { + if (m_callbacks[i]->predecessor()) + m_callbacks[i]->predecessor_eh(); + } +} + +/// Checks whether the given pob is reachable +/// returns l_true if reachable, l_false if unreachable +/// returns l_undef if reachability cannot be decided +/// out contains new pobs to add to the queue in case the result is l_undef +lbool context::expand_pob(pob& n, pob_ref_buffer &out) +{ + SASSERT(out.empty()); + pob::on_expand_event _evt(n); TRACE ("spacer", - tout << "expand-node: " << n.pt().head()->get_name() + tout << "expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() - << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" + << " depth: " << (n.depth () - m_pob_queue.min_depth ()) + << " fvsz: " << n.get_free_vars_size() << "\n" << mk_pp(n.post(), m) << "\n";); STRACE ("spacer.expand-add", - tout << "expand-node: " << n.pt().head()->get_name() + tout << "** expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" << mk_epp(n.post(), m) << "\n\n";); TRACE ("core_array_eq", - tout << "expand-node: " << n.pt().head()->get_name() + tout << "expand-pob: " << n.pt().head()->get_name() << " level: " << n.level() << " depth: " << (n.depth () - m_pob_queue.min_depth ()) << "\n" << mk_pp(n.post(), m) << "\n";); @@ -2854,29 +3290,32 @@ lbool context::expand_node(pob& n) // used in case n is reachable bool is_concrete; - const datalog::rule * r = NULL; + const datalog::rule * r = nullptr; // denotes which predecessor's (along r) reach facts are used vector reach_pred_used; unsigned num_reuse_reach = 0; - if (get_params().pdr_flexible_trace() && n.pt().is_blocked(n, uses_level)) { + if (m_push_pob && n.pt().is_blocked(n, uses_level)) { // if (!m_pob_queue.is_root (n)) n.close (); IF_VERBOSE (1, verbose_stream () << " K " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); - + n.inc_level(); + out.push_back(&n); return l_false; } - smt_params &fparams = m_pm.fparams(); - flet _arith_ignore_int_(fparams.m_arith_ignore_int, - m_weak_abs && n.weakness() < 1); - flet _array_weak_(fparams.m_array_weak, - m_weak_abs && n.weakness() < 2); + if (/* XXX noop */ n.pt().is_qblocked(n)) { + STRACE("spacer.expand-add", + tout << "This pob can be blocked by instantiation\n";); + } + + predecessor_eh(); lbool res = n.pt ().is_reachable (n, &cube, &model, uses_level, is_concrete, r, reach_pred_used, num_reuse_reach); + if (model) model->set_model_completion(false); checkpoint (); IF_VERBOSE (1, verbose_stream () << "." << std::flush;); switch (res) { @@ -2885,21 +3324,19 @@ lbool context::expand_node(pob& n) // update stats m_stats.m_num_reuse_reach += num_reuse_reach; - model_evaluator_util mev (m); - mev.set_model (*model); // must-reachable if (is_concrete) { // -- update must summary if (r && r->get_uninterpreted_tail_size() > 0) { - reach_fact_ref rf = mk_reach_fact (n, mev, *r); + reach_fact_ref rf = n.pt().mk_rf (n, *model, *r); checkpoint (); - n.pt ().add_reach_fact (rf.get ()); + n.pt ().add_rf (rf.get ()); checkpoint (); } // if n has a derivation, create a new child and report l_undef // otherwise if n has no derivation or no new children, report l_true - pob *next = NULL; + pob *next = nullptr; scoped_ptr deriv; if (n.has_derivation()) {deriv = n.detach_derivation();} @@ -2914,10 +3351,8 @@ lbool context::expand_node(pob& n) // move derivation over to the next obligation next->set_derivation (deriv.detach()); - // remove the current node from the queue if it is at the top - if (m_pob_queue.top() == &n) { m_pob_queue.pop(); } - - m_pob_queue.push (*next); + // this is the new node to add + out.push_back (next); } } @@ -2929,17 +3364,20 @@ lbool context::expand_node(pob& n) } // create a child of n - VERIFY(create_children (n, *r, mev, reach_pred_used)); + + out.push_back(&n); + VERIFY(create_children (n, *r, *model, reach_pred_used, out)); IF_VERBOSE(1, verbose_stream () << " U " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); return l_undef; } + case l_false: // n is unreachable, create new summary facts - case l_false: { + { timeit _timer (is_trace_enabled("spacer_timeit"), - "spacer::expand_node::false", + "spacer::expand_pob::false", verbose_stream ()); // -- only update expanded level when new lemmas are generated at it. @@ -2962,6 +3400,11 @@ lbool context::expand_node(pob& n) checkpoint (); (*m_lemma_generalizers[i])(lemma); } + DEBUG_CODE( + lemma_sanity_checker sanity_checker(*this); + sanity_checker(lemma); + ); + TRACE("spacer", tout << "invariant state: " << (is_infty_level(lemma->level())?"(inductive)":"") @@ -2971,10 +3414,17 @@ lbool context::expand_node(pob& n) if (v) { m_stats.m_num_lemmas++; } // Optionally update the node to be the negation of the lemma - if (v && get_params().spacer_use_lemma_as_cti()) { + if (v && m_use_lemma_as_pob) { n.new_post (mk_and(lemma->get_cube())); n.set_farkas_generalizer (false); + // XXX hack while refactoring is in progress + n.clean(); } + + // schedule the node to be placed back in the queue + n.inc_level(); + out.push_back(&n); + CASSERT("spacer", n.level() == 0 || check_invariant(n.level()-1)); @@ -2989,27 +3439,29 @@ lbool context::expand_node(pob& n) if (n.weakness() < 100 /* MAX_WEAKENSS */) { bool has_new_child = false; SASSERT(m_weak_abs); - m_stats.m_expand_node_undef++; + m_stats.m_expand_pob_undef++; if (r && r->get_uninterpreted_tail_size() > 0) { - model_evaluator_util mev(m); - mev.set_model(*model); // do not trust reach_pred_used for (unsigned i = 0, sz = reach_pred_used.size(); i < sz; ++i) { reach_pred_used[i] = false; } - has_new_child = create_children(n,*r,mev,reach_pred_used); + has_new_child = create_children(n, *r, *model, reach_pred_used, out); } IF_VERBOSE(1, verbose_stream() << " UNDEF " << std::fixed << std::setprecision(2) << watch.get_seconds () << "\n";); - if (has_new_child) { return l_undef; } + if (has_new_child) { + // ensure that n is placed back in the queue + out.push_back(&n); + return l_undef; + } // -- failed to create a child, bump weakness and repeat // -- the recursion is bounded by the levels of weakness supported + SASSERT(out.empty()); n.bump_weakness(); - return expand_node(n); + return expand_pob(n, out); } - TRACE("spacer", tout << "unknown state: " - << mk_pp(m_pm.mk_and(cube), m) << "\n";); + TRACE("spacer", tout << "unknown state: " << mk_and(cube) << "\n";); throw unknown_exception(); } UNREACHABLE(); @@ -3036,9 +3488,11 @@ bool context::propagate(unsigned min_prop_lvl, if (full_prop_lvl < max_prop_lvl) { full_prop_lvl = max_prop_lvl; } - if (m_params.pdr_simplify_formulas_pre()) { + if (m_simplify_formulas_pre) { simplify_formulas(); } + STRACE ("spacer.expand-add", tout << "Propagating\n";); + IF_VERBOSE (1, verbose_stream () << "Propagating: " << std::flush;); for (unsigned lvl = min_prop_lvl; lvl <= full_prop_lvl; lvl++) { @@ -3052,18 +3506,17 @@ bool context::propagate(unsigned min_prop_lvl, tout << "In full propagation\n";); bool all_propagated = true; - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { + for (auto & kv : m_rels) { checkpoint(); - pred_transformer& r = *it->m_value; + pred_transformer& r = *kv.m_value; all_propagated = r.propagate_to_next_level(lvl) && all_propagated; } //CASSERT("spacer", check_invariant(lvl)); if (all_propagated) { - for (it = m_rels.begin(); it != end; ++it) { + for (auto& kv : m_rels) { checkpoint (); - pred_transformer& r = *it->m_value; + pred_transformer& r = *kv.m_value; r.propagate_to_infinity (lvl); } if (lvl <= max_prop_lvl) { @@ -3079,7 +3532,7 @@ bool context::propagate(unsigned min_prop_lvl, return true; } else if (all_propagated && lvl > max_prop_lvl) { break; } } - if (m_params.pdr_simplify_formulas_post()) { + if (m_simplify_formulas_post) { simplify_formulas(); } @@ -3087,54 +3540,52 @@ bool context::propagate(unsigned min_prop_lvl, return false; } -reach_fact *context::mk_reach_fact (pob& n, model_evaluator_util &mev, - const datalog::rule& r) +reach_fact *pred_transformer::mk_rf(pob& n, model &mdl, const datalog::rule& r) { + SASSERT(&n.pt() == this); timeit _timer1 (is_trace_enabled("spacer_timeit"), - "mk_reach_fact", + "mk_rf", verbose_stream ()); expr_ref res(m); reach_fact_ref_vector child_reach_facts; - pred_transformer& pt = n.pt (); - ptr_vector preds; - pt.find_predecessors (r, preds); + find_predecessors (r, preds); expr_ref_vector path_cons (m); - path_cons.push_back (pt.get_transition (r)); + path_cons.push_back (get_transition (r)); app_ref_vector vars (m); for (unsigned i = 0; i < preds.size (); i++) { func_decl* pred = preds[i]; - pred_transformer& ch_pt = get_pred_transformer (pred); + pred_transformer& ch_pt = ctx.get_pred_transformer (pred); // get a reach fact of body preds used in the model expr_ref o_ch_reach (m); - reach_fact *kid = ch_pt.get_used_origin_reach_fact (mev, i); + reach_fact *kid = ch_pt.get_used_origin_rf(mdl, i); child_reach_facts.push_back (kid); - m_pm.formula_n2o (kid->get (), o_ch_reach, i); + pm.formula_n2o (kid->get (), o_ch_reach, i); path_cons.push_back (o_ch_reach); // collect o-vars to eliminate for (unsigned j = 0; j < pred->get_arity (); j++) - { vars.push_back(m.mk_const(m_pm.o2o(ch_pt.sig(j), 0, i))); } + { vars.push_back(m.mk_const(pm.o2o(ch_pt.sig(j), 0, i))); } const ptr_vector &v = kid->aux_vars (); for (unsigned j = 0, sz = v.size (); j < sz; ++j) - { vars.push_back(m.mk_const(m_pm.n2o(v [j]->get_decl(), i))); } + { vars.push_back(m.mk_const(pm.n2o(v [j]->get_decl(), i))); } } // collect aux vars to eliminate - ptr_vector& aux_vars = pt.get_aux_vars (r); - bool elim_aux = get_params ().spacer_elim_aux (); + ptr_vector& aux_vars = get_aux_vars (r); + bool elim_aux = ctx.elim_aux(); if (elim_aux) { vars.append(aux_vars.size(), aux_vars.c_ptr()); } - res = m_pm.mk_and (path_cons); + res = mk_and (path_cons); // -- pick an implicant from the path condition - if (get_params().spacer_reach_dnf()) { + if (ctx.reach_dnf()) { expr_ref_vector u(m), lits(m); u.push_back (res); - compute_implicant_literals (mev, u, lits); - res = m_pm.mk_and (lits); + compute_implicant_literals (mdl, u, lits); + res = mk_and (lits); } @@ -3149,9 +3600,9 @@ reach_fact *context::mk_reach_fact (pob& n, model_evaluator_util &mev, { timeit _timer1 (is_trace_enabled("spacer_timeit"), - "mk_reach_fact::qe_project", + "mk_rf::qe_project", verbose_stream ()); - qe_project (m, vars, res, mev.get_model (), false, m_use_native_mbp); + mbp(vars, res, mdl, false, true /* force or skolemize */); } @@ -3179,102 +3630,94 @@ reach_fact *context::mk_reach_fact (pob& n, model_evaluator_util &mev, \brief create children states from model cube. */ bool context::create_children(pob& n, datalog::rule const& r, - model_evaluator_util &mev, - const vector &reach_pred_used) + model &mdl, + const vector &reach_pred_used, + pob_ref_buffer &out) { - scoped_watch _w_ (m_create_children_watch); pred_transformer& pt = n.pt(); - expr* const T = pt.get_transition(r); - expr* const phi = n.post(); TRACE("spacer", tout << "Model:\n"; - model_smt2_pp(tout, m, *mev.get_model (), 0); + model_smt2_pp(tout, m, mdl, 0); tout << "\n"; - tout << "Transition:\n" << mk_pp(T, m) << "\n"; - tout << "Phi:\n" << mk_pp(phi, m) << "\n";); + tout << "Transition:\n" << mk_pp(pt.get_transition(r), m) << "\n"; + tout << "Pob:\n" << mk_pp(n.post(), m) << "\n";); SASSERT (r.get_uninterpreted_tail_size () > 0); ptr_vector preds; pt.find_predecessors(r, preds); - ptr_vector pred_pts; - - for (ptr_vector::iterator it = preds.begin (); - it != preds.end (); it++) { - pred_pts.push_back (&get_pred_transformer (*it)); - } - - expr_ref_vector forms(m), Phi(m); // obtain all formulas to consider for model generalization - forms.push_back(T); - forms.push_back(phi); + expr_ref_vector forms(m), lits(m); + forms.push_back(pt.get_transition(r)); + forms.push_back(n.post()); - compute_implicant_literals (mev, forms, Phi); - - //pt.remove_predecessors (Phi); + compute_implicant_literals (mdl, forms, lits); + expr_ref phi = mk_and (lits); + // primed variables of the head app_ref_vector vars(m); - unsigned sig_size = pt.head()->get_arity(); - for (unsigned i = 0; i < sig_size; ++i) { + for (unsigned i = 0, sz = pt.head()->get_arity(); i < sz; ++i) { vars.push_back(m.mk_const(m_pm.o2n(pt.sig(i), 0))); } + // local variables of the rule ptr_vector& aux_vars = pt.get_aux_vars(r); vars.append(aux_vars.size(), aux_vars.c_ptr()); + // skolems of the pob n.get_skolems(vars); - expr_ref phi1 = m_pm.mk_and (Phi); - qe_project (m, vars, phi1, mev.get_model (), true, - m_use_native_mbp, !m_ground_cti); + n.pt().mbp(vars, phi, mdl, true, use_ground_pob()); //qe::reduce_array_selects (*mev.get_model (), phi1); - SASSERT (!m_ground_cti || vars.empty ()); + SASSERT (!m_ground_pob || vars.empty ()); TRACE ("spacer", - tout << "Implicant\n"; - tout << mk_pp (m_pm.mk_and (Phi), m) << "\n"; - tout << "Projected Implicant\n" << mk_pp (phi1, m) << "\n"; + tout << "Implicant:\n"; + tout << lits << "\n"; + tout << "After MBP:\n" << phi << "\n"; + if (!vars.empty()) + tout << "Failed to eliminate: " << vars << "\n"; ); - // expand literals. Ideally, we do not want to split aliasing - // equalities. Unfortunately, the interface does not allow for - // that yet. - // XXX This mixes up with derivation. Needs more thought. - // Phi.reset (); - // flatten_and (phi1, Phi); - // if (!Phi.empty ()) - // { - // expand_literals (m, Phi); - // phi1 = m_pm.mk_and (Phi); - // } + if (m_use_gpdr && preds.size() > 1) { + SASSERT(vars.empty()); + return gpdr_create_split_children(n, r, phi, mdl, out); + } + derivation *deriv = alloc(derivation, n, r, phi, vars); + + // pick an order to process children + unsigned_vector kid_order; + kid_order.resize(preds.size(), 0); + for (unsigned i = 0, sz = preds.size(); i < sz; ++i) kid_order[i] = i; + if (m_children_order == CO_REV_RULE) { + kid_order.reverse(); + } + else if (m_children_order == CO_RANDOM) { + shuffle(kid_order.size(), kid_order.c_ptr(), m_random); + } - derivation *deriv = alloc (derivation, n, r, phi1, vars); for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { - unsigned j; - if (get_params ().spacer_order_children () == 1) - // -- reverse order - { j = sz - i - 1; } - else - // -- default order - { j = i; } + unsigned j = kid_order[i]; - pred_transformer &pt = get_pred_transformer (preds [j]); + pred_transformer &pt = get_pred_transformer(preds.get(j)); - const ptr_vector *aux = NULL; + const ptr_vector *aux = nullptr; expr_ref sum(m); - // XXX This is a bit confusing. The summary is returned over - // XXX o-variables. But it is simpler if it is returned over n-variables instead. - sum = pt.get_origin_summary (mev, prev_level (n.level ()), - j, reach_pred_used [j], &aux); - deriv->add_premise (pt, j, sum, reach_pred_used [j], aux); + sum = pt.get_origin_summary (mdl, prev_level(n.level()), + j, reach_pred_used[j], &aux); + if (!sum) { + dealloc(deriv); + return false; + } + deriv->add_premise (pt, j, sum, reach_pred_used[j], aux); } // create post for the first child and add to queue - pob* kid = deriv->create_first_child (mev); + pob* kid = deriv->create_first_child (mdl); // -- failed to create derivation, cleanup and bail out if (!kid) { @@ -3285,16 +3728,17 @@ bool context::create_children(pob& n, datalog::rule const& r, kid->set_derivation (deriv); // Optionally disable derivation optimization - if (!get_params().spacer_use_derivations()) { kid->reset_derivation(); } + if (!m_use_derivations) { kid->reset_derivation(); } // -- deriviation is abstract if the current weak model does // -- not satisfy 'T && phi'. It is possible to recover from // -- that more gracefully. For now, we just remove the // -- derivation completely forcing it to be recomputed - if (m_weak_abs && (!mev.is_true(T) || !mev.is_true(phi))) + if (m_weak_abs && (!mdl.is_true(pt.get_transition(r)) || + !mdl.is_true(n.post()))) { kid->reset_derivation(); } - m_pob_queue.push (*kid); + out.push_back(kid); m_stats.m_num_queries++; return true; } @@ -3304,52 +3748,66 @@ bool context::create_children(pob& n, datalog::rule const& r, void context::collect_statistics(statistics& st) const { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (it = m_rels.begin(); it != end; ++it) { - it->m_value->collect_statistics(st); + m_pool0->collect_statistics(st); + m_pool1->collect_statistics(st); + m_pool2->collect_statistics(st); + + for (auto const& kv : m_rels) { + kv.m_value->collect_statistics(st); } + + // -- number of times a pob for some predicate transformer has + // -- been created st.update("SPACER num queries", m_stats.m_num_queries); - st.update("SPACER num reach queries", m_stats.m_num_reach_queries); + // -- number of times a reach fact was true in some model st.update("SPACER num reuse reach facts", m_stats.m_num_reuse_reach); + // -- maximum level at which any query was asked st.update("SPACER max query lvl", m_stats.m_max_query_lvl); + // -- maximum depth st.update("SPACER max depth", m_stats.m_max_depth); + // -- level at which safe inductive invariant was found st.update("SPACER inductive level", m_inductive_lvl); + // -- length of the counterexample st.update("SPACER cex depth", m_stats.m_cex_depth); - st.update("SPACER expand node undef", m_stats.m_expand_node_undef); + // -- number of times expand_pobresulted in undef + st.update("SPACER expand pob undef", m_stats.m_expand_pob_undef); + // -- number of distinct lemmas constructed st.update("SPACER num lemmas", m_stats.m_num_lemmas); + // -- number of restarts taken st.update("SPACER restarts", m_stats.m_num_restarts); + // -- time to initialize the rules st.update ("time.spacer.init_rules", m_init_rules_watch.get_seconds ()); + // -- time in the main solve loop st.update ("time.spacer.solve", m_solve_watch.get_seconds ()); + // -- time in lemma propagation (i.e., pushing) st.update ("time.spacer.solve.propagate", m_propagate_watch.get_seconds ()); + // -- time in reachability (i.e., blocking) st.update ("time.spacer.solve.reach", m_reach_watch.get_seconds ()); + // -- time in deciding whether a pob is must-reachable st.update ("time.spacer.solve.reach.is-reach", m_is_reach_watch.get_seconds ()); + // -- time in creating new predecessors st.update ("time.spacer.solve.reach.children", m_create_children_watch.get_seconds ()); - m_pm.collect_statistics(st); + st.update("spacer.random_seed", m_params.spacer_random_seed()); + st.update("spacer.lemmas_imported", m_stats.m_num_lemmas_imported); + st.update("spacer.lemmas_discarded", m_stats.m_num_lemmas_discarded); for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) { m_lemma_generalizers[i]->collect_statistics(st); } - - // brunch out - verbose_stream () << "BRUNCH_STAT max_query_lvl " << m_stats.m_max_query_lvl << "\n"; - verbose_stream () << "BRUNCH_STAT num_queries " << m_stats.m_num_queries << "\n"; - verbose_stream () << "BRUNCH_STAT num_reach_queries " << m_stats.m_num_reach_queries << "\n"; - verbose_stream () << "BRUNCH_STAT num_reach_reuse " << m_stats.m_num_reuse_reach << "\n"; - verbose_stream () << "BRUNCH_STAT inductive_lvl " << m_inductive_lvl << "\n"; - verbose_stream () << "BRUNCH_STAT max_depth " << m_stats.m_max_depth << "\n"; - verbose_stream () << "BRUNCH_STAT cex_depth " << m_stats.m_cex_depth << "\n"; } void context::reset_statistics() { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (it = m_rels.begin(); it != end; ++it) { - it->m_value->reset_statistics(); + m_pool0->reset_statistics(); + m_pool1->reset_statistics(); + m_pool2->reset_statistics(); + + for (auto & kv : m_rels) { + kv.m_value->reset_statistics(); } m_stats.reset(); - m_pm.reset_statistics(); for (unsigned i = 0; i < m_lemma_generalizers.size(); ++i) { m_lemma_generalizers[i]->reset_statistics(); @@ -3365,10 +3823,9 @@ void context::reset_statistics() bool context::check_invariant(unsigned lvl) { - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { + for (auto &entry : m_rels) { checkpoint(); - if (!check_invariant(lvl, it->m_key)) { + if (!check_invariant(lvl, entry.m_key)) { return false; } } @@ -3377,17 +3834,18 @@ bool context::check_invariant(unsigned lvl) bool context::check_invariant(unsigned lvl, func_decl* fn) { - smt::kernel ctx(m, m_pm.fparams2()); + ref ctx = mk_smt_solver(m, params_ref::get_empty(), symbol::null); pred_transformer& pt = *m_rels.find(fn); expr_ref_vector conj(m); - expr_ref inv = pt.get_formulas(next_level(lvl), false); + expr_ref inv = pt.get_formulas(next_level(lvl)); if (m.is_true(inv)) { return true; } pt.add_premises(m_rels, lvl, conj); conj.push_back(m.mk_not(inv)); expr_ref fml(m.mk_and(conj.size(), conj.c_ptr()), m); - ctx.assert_expr(fml); - lbool result = ctx.check(); - TRACE("spacer", tout << "Check invariant level: " << lvl << " " << result << "\n" << mk_pp(fml, m) << "\n";); + ctx->assert_expr(fml); + lbool result = ctx->check_sat(0, nullptr); + TRACE("spacer", tout << "Check invariant level: " << lvl << " " << result + << "\n" << mk_pp(fml, m) << "\n";); return result == l_false; } @@ -3396,10 +3854,9 @@ expr_ref context::get_constraints (unsigned level) expr_ref res(m); expr_ref_vector constraints(m); - decl2rel::iterator it = m_rels.begin(), end = m_rels.end(); - for (; it != end; ++it) { - pred_transformer& r = *it->m_value; - expr_ref c = r.get_formulas(level, false); + for (auto & kv : m_rels) { + pred_transformer& r = *kv.m_value; + expr_ref c = r.get_formulas(level); if (m.is_true(c)) { continue; } @@ -3415,30 +3872,68 @@ expr_ref context::get_constraints (unsigned level) } if (constraints.empty()) { return expr_ref(m.mk_true(), m); } - return m_pm.mk_and (constraints); + return mk_and (constraints); } -void context::add_constraints (unsigned level, expr_ref c) +void context::add_constraint (expr *c, unsigned level) { - if (!c.get()) { return; } + if (!c) { return; } if (m.is_true(c)) { return; } - expr_ref_vector constraints (m); - constraints.push_back (c); - flatten_and (constraints); - - for (unsigned i = 0, sz = constraints.size(); i < sz; ++i) { - expr *c = constraints.get (i); expr *e1, *e2; if (m.is_implies(c, e1, e2)) { SASSERT (is_app (e1)); - pred_transformer *r = 0; - if (m_rels.find (to_app (e1)->get_decl (), r)) - { r->add_lemma(e2, level); } + pred_transformer *r = nullptr; + if (m_rels.find (to_app (e1)->get_decl (), r)){ + lemma_ref lem = alloc(lemma, m, e2, level); + lem.get()->set_external(true); + if (r->add_lemma(lem.get())) { + this->m_stats.m_num_lemmas_imported++; + } + else{ + this->m_stats.m_num_lemmas_discarded++; + } } } } +void context::new_lemma_eh(pred_transformer &pt, lemma *lem) { + if (m_params.spacer_print_json().size()) + m_json_marshaller.register_lemma(lem); + bool handle=false; + for (unsigned i = 0; i < m_callbacks.size(); i++) { + handle|=m_callbacks[i]->new_lemma(); + } + if (!handle) + return; + if ((is_infty_level(lem->level()) && m_params.spacer_p3_share_invariants()) || + (!is_infty_level(lem->level()) && m_params.spacer_p3_share_lemmas())) { + expr_ref_vector args(m); + for (unsigned i = 0; i < pt.sig_size(); ++i) { + args.push_back(m.mk_const(pt.get_manager().o2n(pt.sig(i), 0))); + } + expr *app = m.mk_app(pt.head(), pt.sig_size(), args.c_ptr()); + expr *lemma = m.mk_implies(app, lem->get_expr()); + for (unsigned i = 0; i < m_callbacks.size(); i++) { + if (m_callbacks[i]->new_lemma()) + m_callbacks[i]->new_lemma_eh(lemma, lem->level()); + } + } +} + +void context::new_pob_eh(pob *p) { + if (m_params.spacer_print_json().size()) + m_json_marshaller.register_pob(p); +} + +bool context::is_inductive() { + // check that inductive level (F infinity) of the query predicate + // contains a constant false + + return false; +} + +/// pob_lt operator inline bool pob_lt::operator() (const pob *pn1, const pob *pn2) const { SASSERT (pn1); @@ -3495,7 +3990,7 @@ inline bool pob_lt::operator() (const pob *pn1, const pob *pn2) const /** XXX Identical nodes. This should not happen. However, * currently, when propagating reachability, we might call - * expand_node() twice on the same node, causing it to generate + * expand_pob() twice on the same node, causing it to generate * the same proof obligation multiple times */ return &n1 < &n2; } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index cb422d08f..37b035b98 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -28,11 +28,12 @@ Notes: #undef max #endif #include - +#include "util/scoped_ptr_vector.h" #include "muz/spacer/spacer_manager.h" #include "muz/spacer/spacer_prop_solver.h" +#include "muz/spacer/spacer_json.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { class rule_set; @@ -41,6 +42,8 @@ namespace datalog { namespace spacer { +class model_search; + class pred_transformer; class derivation; class pob_queue; @@ -52,6 +55,7 @@ typedef obj_map decl2rel; class pob; typedef ref pob_ref; typedef sref_vector pob_ref_vector; +typedef sref_buffer pob_ref_buffer; class reach_fact; typedef ref reach_fact_ref; @@ -66,6 +70,9 @@ class reach_fact { const datalog::rule &m_rule; reach_fact_ref_vector m_justification; + // variable used to tag this reach fact in an incremental disjunction + app_ref m_tag; + bool m_init; public: @@ -73,10 +80,10 @@ public: expr* fact, const ptr_vector &aux_vars, bool init = false) : m_ref_count (0), m_fact (fact, m), m_aux_vars (aux_vars), - m_rule(rule), m_init (init) {} + m_rule(rule), m_tag(m), m_init (init) {} reach_fact (ast_manager &m, const datalog::rule &rule, expr* fact, bool init = false) : - m_ref_count (0), m_fact (fact, m), m_rule(rule), m_init (init) {} + m_ref_count (0), m_fact (fact, m), m_rule(rule), m_tag(m), m_init (init) {} bool is_init () {return m_init;} const datalog::rule& get_rule () {return m_rule;} @@ -87,6 +94,9 @@ public: expr *get () {return m_fact.get ();} const ptr_vector &aux_vars () {return m_aux_vars;} + app* tag() const {SASSERT(m_tag); return m_tag;} + void set_tag(app* tag) {m_tag = tag;} + void inc_ref () {++m_ref_count;} void dec_ref () { @@ -110,10 +120,14 @@ class lemma { ast_manager &m; expr_ref m_body; expr_ref_vector m_cube; + app_ref_vector m_zks; app_ref_vector m_bindings; - unsigned m_lvl; + unsigned m_lvl; // current level of the lemma + unsigned m_init_lvl; // level at which lemma was created pob_ref m_pob; - bool m_new_pob; + model_ref m_ctp; // counter-example to pushing + bool m_external; + unsigned m_bumped; void mk_expr_core(); void mk_cube_core(); @@ -124,6 +138,15 @@ public: // lemma(const lemma &other) = delete; ast_manager &get_ast_manager() {return m;} + + model_ref& get_ctp() {return m_ctp;} + bool has_ctp() {return !is_inductive() && m_ctp;} + void set_ctp(model_ref &v) {m_ctp = v;} + void reset_ctp() {m_ctp.reset();} + + void bump() {m_bumped++;} + unsigned get_bumped() {return m_bumped;} + expr *get_expr(); bool is_false(); expr_ref_vector const &get_cube(); @@ -131,21 +154,30 @@ public: bool has_pob() {return m_pob;} pob_ref &get_pob() {return m_pob;} + inline unsigned weakness(); + void add_skolem(app *zk, app* b); + + inline void set_external(bool ext){m_external = ext;} + inline bool external() { return m_external;} + + bool is_inductive() const {return is_infty_level(m_lvl);} unsigned level () const {return m_lvl;} - void set_level (unsigned lvl) {m_lvl = lvl;} + unsigned init_level() const {return m_init_lvl;} + void set_level (unsigned lvl); app_ref_vector& get_bindings() {return m_bindings;} - void add_binding(app_ref_vector const &binding) {m_bindings.append(binding);} + bool has_binding(app_ref_vector const &binding); + void add_binding(app_ref_vector const &binding); + void instantiate(expr * const * exprs, expr_ref &result, expr *e = nullptr); void mk_insts(expr_ref_vector& inst, expr* e = nullptr); bool is_ground () {return !is_quantifier (get_expr());} void inc_ref () {++m_ref_count;} - void dec_ref () - { - SASSERT (m_ref_count > 0); - --m_ref_count; - if(m_ref_count == 0) { dealloc(this); } - } + void dec_ref () { + SASSERT (m_ref_count > 0); + --m_ref_count; + if (m_ref_count == 0) {dealloc(this);} + } }; struct lemma_lt_proc : public std::binary_function { @@ -167,8 +199,13 @@ struct lemma_lt_proc : public std::binary_function { class pred_transformer { struct stats { - unsigned m_num_propagations; - unsigned m_num_invariants; + unsigned m_num_propagations; // num of times lemma is pushed higher + unsigned m_num_invariants; // num of infty lemmas found + unsigned m_num_ctp_blocked; // num of time ctp blocked lemma pushing + unsigned m_num_is_invariant; // num of times lemmas are pushed + unsigned m_num_lemma_level_jump; // lemma learned at higher level than expected + unsigned m_num_reach_queries; + stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; @@ -177,12 +214,13 @@ class pred_transformer { #include "muz/spacer/spacer_legacy_frames.h" class frames { private: - pred_transformer &m_pt; - lemma_ref_vector m_lemmas; - unsigned m_size; + pred_transformer &m_pt; // parent pred_transformer + lemma_ref_vector m_pinned_lemmas; // all created lemmas + lemma_ref_vector m_lemmas; // active lemmas + unsigned m_size; // num of frames - bool m_sorted; - lemma_lt_proc m_lt; + bool m_sorted; // true if m_lemmas is sorted by m_lt + lemma_lt_proc m_lt; // sort order for m_lemmas void sort (); @@ -191,42 +229,42 @@ class pred_transformer { ~frames() {} void simplify_formulas (); - pred_transformer& pt () {return m_pt;} + pred_transformer& pt() const {return m_pt;} + const lemma_ref_vector &lemmas() const {return m_lemmas;} - void get_frame_lemmas (unsigned level, expr_ref_vector &out) { - for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i) - if(m_lemmas[i]->level() == level) { - out.push_back(m_lemmas[i]->get_expr()); + void get_frame_lemmas (unsigned level, expr_ref_vector &out) const { + for (auto &lemma : m_lemmas) { + if (lemma->level() == level) { + out.push_back(lemma->get_expr()); } + } } - void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) { - for (unsigned i = 0, sz = m_lemmas.size (); i < sz; ++i) - if(m_lemmas [i]->level() >= level) { - out.push_back(m_lemmas[i]->get_expr()); + void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) const { + for (auto &lemma : m_lemmas) { + if (lemma->level() >= level) { + 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++;} void inherit_frames (frames &other) { - for (unsigned i = 0, sz = other.m_lemmas.size (); i < sz; ++i) { - lemma_ref lem = alloc(lemma, m_pt.get_ast_manager(), - other.m_lemmas[i]->get_expr (), - other.m_lemmas[i]->level()); - lem->add_binding(other.m_lemmas[i]->get_bindings()); - add_lemma(lem.get()); + for (auto &other_lemma : other.m_lemmas) { + lemma_ref new_lemma = alloc(lemma, m_pt.get_ast_manager(), + other_lemma->get_expr(), + other_lemma->level()); + new_lemma->add_binding(other_lemma->get_bindings()); + add_lemma(new_lemma.get()); } m_sorted = false; } - bool add_lemma (lemma *lem); + bool add_lemma (lemma *new_lemma); void propagate_to_infinity (unsigned level); bool propagate_to_next_level (unsigned level); - - }; /** @@ -250,122 +288,179 @@ class pred_transformer { app_ref_vector b(m_pt.get_ast_manager()); return mk_pob (parent, level, depth, post, b); } + unsigned size() const {return m_pinned.size();} }; - typedef obj_map rule2expr; - typedef obj_map > rule2apps; + class pt_rule { + const datalog::rule &m_rule; + expr_ref m_trans; // ground version of m_rule + ptr_vector m_auxs; // auxiliary variables in m_trans + app_ref_vector m_reps; // map from fv in m_rule to ground constants + app_ref m_tag; // a unique tag for the rule - manager& pm; // spacer-manager - ast_manager& m; // manager - context& ctx; + public: + pt_rule(ast_manager &m, const datalog::rule &r) : + m_rule(r), m_trans(m), m_reps(m), m_tag(m) {} - func_decl_ref m_head; // predicate - func_decl_ref_vector m_sig; // signature - ptr_vector m_use; // places where 'this' is referenced. - ptr_vector m_rules; // rules used to derive transformer - prop_solver m_solver; // solver context - solver* m_reach_ctx; // context for reachability facts - pobs m_pobs; - frames m_frames; - reach_fact_ref_vector m_reach_facts; // reach facts - /// Number of initial reachability facts - unsigned m_rf_init_sz; - obj_map m_tag2rule; // map tag predicate to rule. - rule2expr m_rule2tag; // map rule to predicate tag. - rule2inst m_rule2inst; // map rules to instantiations of indices - rule2expr m_rule2transition; // map rules to transition - rule2apps m_rule2vars; // map rule to auxiliary variables - expr_ref m_transition; // transition relation. - expr_ref m_initial_state; // initial state. - app_ref m_extend_lit; // literal to extend initial state + const datalog::rule &rule() const {return m_rule;} + + void set_tag(expr *tag) {SASSERT(is_app(tag)); set_tag(to_app(tag));} + void set_tag(app* tag) {m_tag = tag;} + app* tag() const {return m_tag;} + ptr_vector &auxs() {return m_auxs;} + void set_auxs(ptr_vector &v) {m_auxs.reset(); m_auxs.append(v);} + void set_reps(app_ref_vector &v) {m_reps.reset(); m_reps.append(v);} + + void set_trans(expr_ref &v) {m_trans=v;} + expr* trans() const {return m_trans;} + bool is_init() const {return m_rule.get_uninterpreted_tail_size() == 0;} + }; + + class pt_rules { + typedef obj_map rule2ptrule; + typedef obj_map tag2ptrule; + typedef rule2ptrule::iterator iterator; + rule2ptrule m_rules; + tag2ptrule m_tags; + public: + pt_rules() {} + ~pt_rules() {for (auto &kv : m_rules) {dealloc(kv.m_value);}} + + bool find_by_rule(const datalog::rule &r, pt_rule* &ptr) { + return m_rules.find(&r, ptr); + } + bool find_by_tag(const expr* tag, pt_rule* &ptr) { + return m_tags.find(tag, ptr); + } + pt_rule &mk_rule(ast_manager &m, const datalog::rule &r) { + return mk_rule(pt_rule(m, r)); + } + pt_rule &mk_rule(const pt_rule &v); + void set_tag(expr* tag, pt_rule &v) { + pt_rule *p; + VERIFY(find_by_rule(v.rule(), p)); + p->set_tag(tag); + m_tags.insert(tag, p); + } + + bool empty() {return m_rules.empty();} + iterator begin() {return m_rules.begin();} + iterator end() {return m_rules.end();} + + }; + + manager& pm; // spacer::manager + ast_manager& m; // ast_manager + context& ctx; // spacer::context + + func_decl_ref m_head; // predicate + func_decl_ref_vector m_sig; // signature + ptr_vector m_use; // places where 'this' is referenced. + pt_rules m_pt_rules; // pt rules used to derive transformer + ptr_vector m_rules; // rules used to derive transformer + scoped_ptr m_solver; // solver context + ref m_reach_solver; // context for reachability facts + pobs m_pobs; // proof obligations created so far + frames m_frames; // frames with lemmas + reach_fact_ref_vector m_reach_facts; // reach facts + unsigned m_rf_init_sz; // number of reach fact from INIT + expr_ref_vector m_transition_clause; // extra clause for trans + expr_ref m_transition; // transition relation + expr_ref m_init; // initial condition + app_ref m_extend_lit0; // first literal used to extend initial state + app_ref m_extend_lit; // current literal to extend initial state bool m_all_init; // true if the pt has no uninterpreted body in any rule - ptr_vector m_predicates; + ptr_vector m_predicates; // temp vector used with find_predecessors() stats m_stats; stopwatch m_initialize_watch; stopwatch m_must_reachable_watch; - - - - /// Auxiliary variables to represent different disjunctive - /// cases of must summaries. Stored over 'n' (a.k.a. new) - /// versions of the variables - expr_ref_vector m_reach_case_vars; + stopwatch m_ctp_watch; + stopwatch m_mbp_watch; void init_sig(); + app_ref mk_extend_lit(); void ensure_level(unsigned level); - void add_lemma_core (lemma *lemma); - void add_lemma_from_child (pred_transformer &child, lemma *lemma, unsigned lvl); + void add_lemma_core (lemma *lemma, bool ground_only = false); + void add_lemma_from_child (pred_transformer &child, lemma *lemma, + unsigned lvl, bool ground_only = false); void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result); // Initialization - void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition); - void init_rule(decl2rel const& pts, datalog::rule const& rule, vector& is_init, - ptr_vector& rules, expr_ref_vector& transition); - void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx); + void init_rules(decl2rel const& pts); + void init_rule(decl2rel const& pts, datalog::rule const& rule); + void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, + expr_ref_vector& side, unsigned tail_idx); void simplify_formulas(tactic& tac, expr_ref_vector& fmls); - // Debugging - bool check_filled(app_ref_vector const& v) const; - void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r); - expr* mk_fresh_reach_case_var (); + app_ref mk_fresh_rf_tag (); public: pred_transformer(context& ctx, manager& pm, func_decl* head); - ~pred_transformer(); + ~pred_transformer() {} inline bool use_native_mbp (); - reach_fact *get_reach_fact (expr *v) - { - for (unsigned i = 0, sz = m_reach_facts.size (); i < sz; ++i) - if(v == m_reach_facts [i]->get()) { return m_reach_facts[i]; } - return NULL; + reach_fact *get_rf (expr *v) { + for (auto *rf : m_reach_facts) { + if (v == rf->get()) {return rf;} } + return nullptr; + } + void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; - void add_rule(datalog::rule* r) { m_rules.push_back(r); } - void add_use(pred_transformer* pt) { if(!m_use.contains(pt)) { m_use.insert(pt); } } + void add_rule(datalog::rule* r) {m_rules.push_back(r);} + void add_use(pred_transformer* pt) {if (!m_use.contains(pt)) {m_use.insert(pt);}} void initialize(decl2rel const& pts); - func_decl* head() const { return m_head; } - ptr_vector const& rules() const { return m_rules; } - func_decl* sig(unsigned i) const { return m_sig[i]; } // signature - func_decl* const* sig() { return m_sig.c_ptr(); } - unsigned sig_size() const { return m_sig.size(); } - expr* transition() const { return m_transition; } - expr* initial_state() const { return m_initial_state; } - expr* rule2tag(datalog::rule const* r) { return m_rule2tag.find(r); } - unsigned get_num_levels() { return m_frames.size (); } + func_decl* head() const {return m_head;} + ptr_vector const& rules() const {return m_rules;} + func_decl* sig(unsigned i) const {return m_sig[i];} // signature + func_decl* const* sig() {return m_sig.c_ptr();} + unsigned sig_size() const {return m_sig.size();} + expr* transition() const {return m_transition;} + expr* init() const {return m_init;} + expr* rule2tag(datalog::rule const* r) { + pt_rule *p; + return m_pt_rules.find_by_rule(*r, p) ? p->tag() : nullptr; + } + 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); - expr_ref get_reachable (); + expr_ref get_reachable(); std::ostream& display(std::ostream& strm) const; void collect_statistics(statistics& st) const; void reset_statistics(); - bool is_must_reachable (expr* state, model_ref* model = 0); + bool is_must_reachable(expr* state, model_ref* model = nullptr); /// \brief Returns reachability fact active in the given model /// all determines whether initial reachability facts are included as well - reach_fact *get_used_reach_fact (model_evaluator_util& mev, bool all = true); + reach_fact *get_used_rf(model& mdl, bool all = true); /// \brief Returns reachability fact active in the origin of the given model - reach_fact* get_used_origin_reach_fact (model_evaluator_util &mev, unsigned oidx); - expr_ref get_origin_summary (model_evaluator_util &mev, - unsigned level, unsigned oidx, bool must, - const ptr_vector **aux); + reach_fact* get_used_origin_rf(model &mdl, unsigned oidx); + expr_ref get_origin_summary(model &mdl, + unsigned level, unsigned oidx, bool must, + const ptr_vector **aux); - void remove_predecessors(expr_ref_vector& literals); - void find_predecessors(datalog::rule const& r, ptr_vector& predicates) const; - void find_predecessors(vector >& predicates) const; - datalog::rule const* find_rule(model &mev, bool& is_concrete, + bool is_ctp_blocked(lemma *lem); + const datalog::rule *find_rule(model &mdl); + const datalog::rule *find_rule(model &mev, bool& is_concrete, vector& reach_pred_used, unsigned& num_reuse_reach); - expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); } - ptr_vector& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); } + expr* get_transition(datalog::rule const& r) { + pt_rule *p; + return m_pt_rules.find_by_rule(r, p) ? p->trans() : nullptr; + } + ptr_vector& get_aux_vars(datalog::rule const& r) { + pt_rule *p = nullptr; + VERIFY(m_pt_rules.find_by_rule(r, p)); + return p->auxs(); + } bool propagate_to_next_level(unsigned level); void propagate_to_infinity(unsigned level); @@ -373,13 +468,14 @@ public: bool add_lemma(expr * lemma, unsigned lvl); bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);} expr* get_reach_case_var (unsigned idx) const; - bool has_reach_facts () const { return !m_reach_facts.empty () ;} + bool has_rfs () const { return !m_reach_facts.empty () ;} /// initialize reachability facts using initial rules - void init_reach_facts (); - void add_reach_fact (reach_fact *fact); // add reachability fact - reach_fact* get_last_reach_fact () const { return m_reach_facts.back (); } - expr* get_last_reach_case_var () const; + void init_rfs (); + reach_fact *mk_rf(pob &n, model &mdl, const datalog::rule &r); + void add_rf (reach_fact *fact); // add reachability fact + reach_fact* get_last_rf () const { return m_reach_facts.back (); } + expr* get_last_rf_tag () const; pob* mk_pob(pob *parent, unsigned level, unsigned depth, expr *post, app_ref_vector const &b){ @@ -396,22 +492,30 @@ public: datalog::rule const*& r, vector& reach_pred_used, unsigned& num_reuse_reach); - bool is_invariant(unsigned level, expr* lemma, - unsigned& solver_level, expr_ref_vector* core = 0); - bool check_inductive(unsigned level, expr_ref_vector& state, - unsigned& assumes_level); + bool is_invariant(unsigned level, lemma* lem, + unsigned& solver_level, + expr_ref_vector* core = nullptr); - expr_ref get_formulas(unsigned level, bool add_axioms); + bool is_invariant(unsigned level, expr* lem, + unsigned& solver_level, expr_ref_vector* core = nullptr) { + // XXX only needed for legacy_frames to compile + UNREACHABLE(); return false; + } + + bool check_inductive(unsigned level, expr_ref_vector& state, + unsigned& assumes_level, unsigned weakness = UINT_MAX); + + expr_ref get_formulas(unsigned level) const; void simplify_formulas(); context& get_context () const {return ctx;} - manager& get_manager() const { return pm; } - ast_manager& get_ast_manager() const { return m; } + manager& get_manager() const {return pm;} + ast_manager& get_ast_manager() const {return m;} void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r); - void inherit_properties(pred_transformer& other); + void inherit_lemmas(pred_transformer& other); void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector& aux_vars, bool is_init); @@ -424,6 +528,19 @@ public: /// \brief Returns true if the obligation is already blocked by current quantified lemmas bool is_qblocked (pob &n); + /// \brief interface to Model Based Projection + void mbp(app_ref_vector &vars, expr_ref &fml, model &mdl, + bool reduce_all_selects, bool force = false); + + void updt_solver(prop_solver *solver); + + void updt_solver_with_lemmas(prop_solver *solver, + const pred_transformer &pt, + app *rule_tag, unsigned pos); + void update_solver_with_rfs(prop_solver *solver, + const pred_transformer &pt, + app *rule_tag, unsigned pos); + }; @@ -458,12 +575,20 @@ class pob { /// derivation representing the position of this node in the parent's rule scoped_ptr m_derivation; + /// pobs created as children of this pob (at any time, not + /// necessarily currently active) ptr_vector m_kids; + // lemmas created to block this pob (at any time, not necessarily active) + ptr_vector m_lemmas; + + // depth -> watch + std::map m_expand_watches; + unsigned m_blocked_lvl; public: pob (pob* parent, pred_transformer& pt, unsigned level, unsigned depth=0, bool add_to_parent=true); - ~pob() {if(m_parent) { m_parent->erase_child(*this); }} + ~pob() {if (m_parent) { m_parent->erase_child(*this); }} unsigned weakness() {return m_weakness;} void bump_weakness() {m_weakness++;} @@ -475,7 +600,7 @@ public: void set_derivation (derivation *d) {m_derivation = d;} bool has_derivation () const {return (bool)m_derivation;} derivation &get_derivation() const {return *m_derivation.get ();} - void reset_derivation () {set_derivation (NULL);} + void reset_derivation () {set_derivation (nullptr);} /// detaches derivation from the node without deallocating derivation* detach_derivation () {return m_derivation.detach ();} @@ -488,43 +613,62 @@ public: unsigned level () const { return m_level; } unsigned depth () const {return m_depth;} + unsigned width () const {return m_kids.size();} + unsigned blocked_at(unsigned lvl=0){return (m_blocked_lvl = std::max(lvl, m_blocked_lvl)); } bool use_farkas_generalizer () const {return m_use_farkas;} void set_farkas_generalizer (bool v) {m_use_farkas = v;} expr* post() const { return m_post.get (); } void set_post(expr *post); - void set_post(expr *post, app_ref_vector const &b); + void set_post(expr *post, app_ref_vector const &binding); /// indicate that a new post should be set for the node - void new_post(expr *post) {if(post != m_post) {m_new_post = post;}} + void new_post(expr *post) {if (post != m_post) {m_new_post = post;}} /// true if the node needs to be updated outside of the priority queue bool is_dirty () {return m_new_post;} /// clean a dirty node void clean(); - void reset () {clean (); m_derivation = NULL; m_open = true;} + void reset () {clean (); m_derivation = nullptr; m_open = true;} bool is_closed () const { return !m_open; } void close(); + const ptr_vector &children() {return m_kids;} void add_child (pob &v) {m_kids.push_back (&v);} void erase_child (pob &v) {m_kids.erase (&v);} - bool is_ground () { return m_binding.empty (); } + const ptr_vector &lemmas() {return m_lemmas;} + void add_lemma(lemma* new_lemma) {m_lemmas.push_back(new_lemma);} + + bool is_ground () const { return m_binding.empty (); } + unsigned get_free_vars_size() const { return m_binding.size(); } app_ref_vector const &get_binding() const {return m_binding;} /* - * Return skolem variables that appear in post + * Returns a map from variable id to skolems that implicitly + * represent them in the pob. Note that only some (or none) of the + * skolems returned actually appear in the post of the pob. */ void get_skolems(app_ref_vector& v); - void inc_ref () {++m_ref_count;} - void dec_ref () - { - --m_ref_count; - if(m_ref_count == 0) { dealloc(this); } - } + void on_expand() { m_expand_watches[m_depth].start(); if (m_parent.get()){m_parent.get()->on_expand();} } + void off_expand() { m_expand_watches[m_depth].stop(); if (m_parent.get()){m_parent.get()->off_expand();} }; + double get_expand_time(unsigned depth) { return m_expand_watches[depth].get_seconds();} + void inc_ref () {++m_ref_count;} + void dec_ref () { + --m_ref_count; + if (m_ref_count == 0) {dealloc(this);} + } + + class on_expand_event + { + pob &m_p; + public: + on_expand_event(pob &p) : m_p(p) {m_p.on_expand();} + ~on_expand_event() {m_p.off_expand();} + }; }; @@ -546,7 +690,7 @@ struct pob_ref_gt : {return gt (n1.get (), n2.get ());} }; - +inline unsigned lemma::weakness() {return m_pob ? m_pob->weakness() : UINT_MAX;} /** */ class derivation { @@ -563,19 +707,19 @@ class derivation { public: premise (pred_transformer &pt, unsigned oidx, expr *summary, bool must, - const ptr_vector *aux_vars = NULL); + const ptr_vector *aux_vars = nullptr); premise (const premise &p); - bool is_must () {return m_must;} - expr * get_summary () {return m_summary.get ();} - app_ref_vector &get_ovars () {return m_ovars;} - unsigned get_oidx () {return m_oidx;} - pred_transformer &pt () {return m_pt;} + bool is_must() {return m_must;} + expr * get_summary() {return m_summary.get ();} + app_ref_vector &get_ovars() {return m_ovars;} + unsigned get_oidx() {return m_oidx;} + pred_transformer &pt() {return m_pt;} /// \brief Updated the summary. /// The new summary is over n-variables. - void set_summary (expr * summary, bool must, - const ptr_vector *aux_vars = NULL); + void set_summary(expr * summary, bool must, + const ptr_vector *aux_vars = nullptr); }; @@ -595,17 +739,19 @@ class derivation { app_ref_vector m_evars; /// -- create next child using given model as the guide /// -- returns NULL if there is no next child - pob* create_next_child (model_evaluator_util &mev); + pob* create_next_child (model &mdl); + /// existentially quantify vars and skolemize the result + void exist_skolemize(expr *fml, app_ref_vector &vars, expr_ref &res); public: derivation (pob& parent, datalog::rule const& rule, expr *trans, app_ref_vector const &evars); void add_premise (pred_transformer &pt, unsigned oidx, - expr * summary, bool must, const ptr_vector *aux_vars = NULL); + expr * summary, bool must, const ptr_vector *aux_vars = nullptr); /// creates the first child. Must be called after all the premises /// are added. The model must be valid for the premises /// Returns NULL if no child exits - pob *create_first_child (model_evaluator_util &mev); + pob *create_first_child (model &mdl); /// Create the next child. Must summary of the currently active /// premise must be consistent with the transition relation @@ -616,6 +762,7 @@ public: ast_manager &get_ast_manager () const {return m_parent.get_ast_manager ();} manager &get_manager () const {return m_parent.get_manager ();} context &get_context() const {return m_parent.get_context();} + pred_transformer &pt() const {return m_parent.pt();} }; @@ -628,30 +775,28 @@ class pob_queue { pob_ref_gt> m_obligations; public: - pob_queue(): m_root(NULL), m_max_level(0), m_min_depth(0) {} + pob_queue(): m_root(nullptr), m_max_level(0), m_min_depth(0) {} ~pob_queue(); void reset(); pob * top (); void pop () {m_obligations.pop ();} - void push (pob &n) {m_obligations.push (&n);} + void push (pob &n); - void inc_level () - { - SASSERT (!m_obligations.empty () || m_root); - m_max_level++; - m_min_depth++; - if(m_root && m_obligations.empty()) { m_obligations.push(m_root); } - } + void inc_level () { + SASSERT (!m_obligations.empty () || m_root); + m_max_level++; + m_min_depth++; + if (m_root && m_obligations.empty()) { m_obligations.push(m_root); } + } - pob& get_root() const { return *m_root.get (); } + pob& get_root() const {return *m_root.get ();} void set_root(pob& n); bool is_root (pob& n) const {return m_root.get () == &n;} - unsigned max_level () {return m_max_level;} - unsigned min_depth () {return m_min_depth;} - size_t size () {return m_obligations.size ();} - + unsigned max_level() const {return m_max_level;} + unsigned min_depth() const {return m_min_depth;} + size_t size() const {return m_obligations.size();} }; @@ -670,18 +815,55 @@ public: }; +class spacer_callback { +protected: + context &m_context; + +public: + spacer_callback(context &context) : m_context(context) {} + + virtual ~spacer_callback() = default; + + context &get_context() { return m_context; } + + virtual inline bool new_lemma() { return false; } + + virtual void new_lemma_eh(expr *lemma, unsigned level) {} + + virtual inline bool predecessor() { return false; } + + virtual void predecessor_eh() {} + + virtual inline bool unfold() { return false; } + + virtual void unfold_eh() {} + + virtual inline bool propagate() { return false; } + + virtual void propagate_eh() {} + +}; + +// order in which children are processed +enum spacer_children_order { + CO_RULE, // same order as in the rule + CO_REV_RULE, // reverse order of the rule + CO_RANDOM // random shuffle +}; + class context { struct stats { unsigned m_num_queries; - unsigned m_num_reach_queries; unsigned m_num_reuse_reach; unsigned m_max_query_lvl; unsigned m_max_depth; unsigned m_cex_depth; - unsigned m_expand_node_undef; + unsigned m_expand_pob_undef; unsigned m_num_lemmas; unsigned m_num_restarts; + unsigned m_num_lemmas_imported; + unsigned m_num_lemmas_discarded; stats() { reset(); } void reset() { memset(this, 0, sizeof(*this)); } }; @@ -694,10 +876,19 @@ class context { stopwatch m_create_children_watch; stopwatch m_init_rules_watch; - fixedpoint_params const& m_params; + fp_params const& m_params; ast_manager& m; datalog::context* m_context; manager m_pm; + + // three solver pools for different queries + scoped_ptr m_pool0; + scoped_ptr m_pool1; + scoped_ptr m_pool2; + + + random_gen m_random; + spacer_children_order m_children_order; decl2rel m_rels; // Map from relation predicate to fp-operator. func_decl_ref m_query_pred; pred_transformer* m_query; @@ -710,27 +901,64 @@ class context { model_converter_ref m_mc; proof_converter_ref m_pc; bool m_use_native_mbp; - bool m_ground_cti; bool m_instantiate; bool m_use_qlemmas; bool m_weak_abs; bool m_use_restarts; + bool m_simplify_pob; + bool m_use_euf_gen; + bool m_use_ctp; + bool m_use_inc_clause; + bool m_blast_term_ite; + bool m_reuse_pobs; + bool m_use_ind_gen; + bool m_use_array_eq_gen; + bool m_validate_lemmas; + bool m_use_propagate; + bool m_reset_obligation_queue; + bool m_push_pob; + bool m_use_lemma_as_pob; + bool m_elim_aux; + bool m_reach_dnf; + bool m_use_derivations; + bool m_validate_result; + bool m_use_eq_prop; + bool m_ground_pob; + bool m_q3_qgen; + bool m_use_gpdr; + bool m_simplify_formulas_pre; + bool m_simplify_formulas_post; + bool m_pdr_bfs; + unsigned m_push_pob_max_depth; + unsigned m_max_level; unsigned m_restart_initial_threshold; + scoped_ptr_vector m_callbacks; + json_marshaller m_json_marshaller; + + // Solve using gpdr strategy + lbool gpdr_solve_core(); + bool gpdr_check_reachability(unsigned lvl, model_search &ms); + bool gpdr_create_split_children(pob &n, const datalog::rule &r, + expr *trans, + model &mdl, + pob_ref_buffer &out); // Functions used by search. - lbool solve_core (unsigned from_lvl = 0); - bool check_reachability (); + lbool solve_core(unsigned from_lvl = 0); + bool is_requeue(pob &n); + bool check_reachability(); bool propagate(unsigned min_prop_lvl, unsigned max_prop_lvl, unsigned full_prop_lvl); bool is_reachable(pob &n); - lbool expand_node(pob& n); - reach_fact *mk_reach_fact (pob& n, model_evaluator_util &mev, - datalog::rule const& r); - bool create_children(pob& n, datalog::rule const& r, - model_evaluator_util &model, - const vector& reach_pred_used); + lbool expand_pob(pob &n, pob_ref_buffer &out); + bool create_children(pob& n, const datalog::rule &r, + model &mdl, + const vector& reach_pred_used, + pob_ref_buffer &out); + expr_ref mk_sat_answer(); expr_ref mk_unsat_answer() const; + unsigned get_cex_depth (); // Generate inductive property void get_level_property(unsigned lvl, expr_ref_vector& res, @@ -738,102 +966,112 @@ class context { // Initialization - void init_lemma_generalizers(datalog::rule_set& rules); + void init_lemma_generalizers(); + void reset_lemma_generalizers(); + void inherit_lemmas(const decl2rel& rels); + void init_global_smt_params(); + void init_rules(datalog::rule_set& rules, decl2rel& transformers); + // (re)initialize context with new relations + void init(const decl2rel &rels); + bool validate(); bool check_invariant(unsigned lvl); bool check_invariant(unsigned lvl, func_decl* fn); void checkpoint(); - void init_rules(datalog::rule_set& rules, decl2rel& transformers); - void simplify_formulas(); - void reset_lemma_generalizers(); + void dump_json(); - bool validate(); - - unsigned get_cex_depth (); + void predecessor_eh(); + void updt_params(); public: /** Initial values of predicates are stored in corresponding relations in dctx. - We check whether there is some reachable state of the relation checked_relation. */ - context( - fixedpoint_params const& params, - ast_manager& m); - + context(fp_params const& params, ast_manager& m); ~context(); - fixedpoint_params const& get_params() const { return m_params; } - bool use_native_mbp () {return m_use_native_mbp;} - bool use_ground_cti () {return m_ground_cti;} - bool use_instantiate () { return m_instantiate; } - bool use_qlemmas () {return m_use_qlemmas; } - ast_manager& get_ast_manager() const { return m; } - manager& get_manager() { return m_pm; } - decl2rel const& get_pred_transformers() const { return m_rels; } - pred_transformer& get_pred_transformer(func_decl* p) const - { return *m_rels.find(p); } - datalog::context& get_datalog_context() const - { SASSERT(m_context); return *m_context; } + 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 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;} + bool blast_term_ite() {return m_blast_term_ite;} + bool reuse_pobs() {return m_reuse_pobs;} + bool elim_aux() {return m_elim_aux;} + bool reach_dnf() {return m_reach_dnf;} + + ast_manager& get_ast_manager() const {return m;} + manager& get_manager() {return m_pm;} + decl2rel const& get_pred_transformers() const {return m_rels;} + pred_transformer& get_pred_transformer(func_decl* p) const {return *m_rels.find(p);} + + datalog::context& get_datalog_context() const { + SASSERT(m_context); return *m_context; + } + + void update_rules(datalog::rule_set& rules); + lbool solve(unsigned from_lvl = 0); + lbool solve_from_lvl (unsigned from_lvl); + + expr_ref get_answer(); /** * get bottom-up (from query) sequence of ground predicate instances * (for e.g. P(0,1,0,0,3)) that together form a ground derivation to query */ - expr_ref get_ground_sat_answer (); + expr_ref get_ground_sat_answer (); + proof_ref get_ground_refutation(); + void get_rules_along_trace (datalog::rule_ref_vector& rules); void collect_statistics(statistics& st) const; void reset_statistics(); - - std::ostream& display(std::ostream& strm) const; - - void display_certificate(std::ostream& strm) const {} - - lbool solve(unsigned from_lvl = 0); - - lbool solve_from_lvl (unsigned from_lvl); - void reset(); - void set_query(func_decl* q) { m_query_pred = q; } - - void set_unsat() { m_last_result = l_false; } - - void set_model_converter(model_converter_ref& mc) { m_mc = mc; } - - void get_rules_along_trace (datalog::rule_ref_vector& rules); + std::ostream& display(std::ostream& out) const; + void display_certificate(std::ostream& out) const {NOT_IMPLEMENTED_YET();} + pob& get_root() const {return m_pob_queue.get_root();} + void set_query(func_decl* q) {m_query_pred = q;} + void set_unsat() {m_last_result = l_false;} + void set_model_converter(model_converter_ref& mc) {m_mc = mc;} model_converter_ref get_model_converter() { return m_mc; } - void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; } - - void update_rules(datalog::rule_set& rules); - - void set_axioms(expr* axioms) { m_pm.set_background(axioms); } + scoped_ptr_vector &callbacks() {return m_callbacks;} 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); - expr_ref get_reachable (func_decl* p); - void add_invariant (func_decl *pred, expr* property); - model_ref get_model(); - proof_ref get_proof() const; - pob& get_root() const { return m_pob_queue.get_root(); } - expr_ref get_constraints (unsigned lvl); - void add_constraints (unsigned lvl, expr_ref c); + void add_constraint (expr *c, unsigned lvl); + + void new_lemma_eh(pred_transformer &pt, lemma *lem); + void new_pob_eh(pob *p); + + bool is_inductive(); + + + // three different solvers with three different sets of parameters + // different solvers are used for different types of queries in spacer + solver* mk_solver0() {return m_pool0->mk_solver();} + solver* mk_solver1() {return m_pool1->mk_solver();} + solver* mk_solver2() {return m_pool2->mk_solver();} }; inline bool pred_transformer::use_native_mbp () {return ctx.use_native_mbp ();} diff --git a/src/muz/spacer/spacer_dl_interface.cpp b/src/muz/spacer/spacer_dl_interface.cpp index 78776e074..807e9b751 100644 --- a/src/muz/spacer/spacer_dl_interface.cpp +++ b/src/muz/spacer/spacer_dl_interface.cpp @@ -34,6 +34,7 @@ Revision History: #include "model/model_smt2_pp.h" #include "ast/scoped_proof.h" #include "muz/transforms/dl_transforms.h" +#include "muz/spacer/spacer_callback.h" using namespace spacer; @@ -42,7 +43,7 @@ dl_interface::dl_interface(datalog::context& ctx) : m_ctx(ctx), m_spacer_rules(ctx), m_old_rules(ctx), - m_context(0), + m_context(nullptr), m_refs(ctx.get_manager()) { m_context = alloc(spacer::context, ctx.get_params(), ctx.get_manager()); @@ -92,19 +93,14 @@ lbool dl_interface::query(expr * query) datalog::rule_set old_rules(rules0); func_decl_ref query_pred(m); rm.mk_query(query, m_ctx.get_rules()); - expr_ref bg_assertion = m_ctx.get_background_assertion(); check_reset(); TRACE("spacer", - if (!m.is_true(bg_assertion)) { - tout << "axioms:\n"; - tout << mk_pp(bg_assertion, m) << "\n"; - } - tout << "query: " << mk_pp(query, m) << "\n"; - tout << "rules:\n"; - m_ctx.display_rules(tout); - ); + tout << "query: " << mk_pp(query, m) << "\n"; + tout << "rules:\n"; + m_ctx.display_rules(tout); + ); apply_default_transformation(m_ctx); @@ -160,7 +156,6 @@ lbool dl_interface::query(expr * query) m_context->set_proof_converter(m_ctx.get_proof_converter()); m_context->set_model_converter(m_ctx.get_model_converter()); m_context->set_query(query_pred); - m_context->set_axioms(bg_assertion); m_context->update_rules(m_spacer_rules); if (m_spacer_rules.get_rules().empty()) { @@ -169,7 +164,7 @@ lbool dl_interface::query(expr * query) return l_false; } - return m_context->solve(); + return m_context->solve(m_ctx.get_params().spacer_min_level()); } @@ -254,7 +249,6 @@ lbool dl_interface::query_from_lvl(expr * query, unsigned lvl) m_context->set_proof_converter(m_ctx.get_proof_converter()); m_context->set_model_converter(m_ctx.get_model_converter()); m_context->set_query(query_pred); - m_context->set_axioms(bg_assertion); m_context->update_rules(m_spacer_rules); if (m_spacer_rules.get_rules().empty()) { @@ -352,3 +346,14 @@ proof_ref dl_interface::get_proof() { return m_context->get_proof(); } + +void dl_interface::add_callback(void *state, + const datalog::t_new_lemma_eh new_lemma_eh, + const datalog::t_predecessor_eh predecessor_eh, + const datalog::t_unfold_eh unfold_eh){ + m_context->callbacks().push_back(alloc(user_callback, *m_context, state, new_lemma_eh, predecessor_eh, unfold_eh)); +} + +void dl_interface::add_constraint (expr *c, unsigned lvl){ + m_context->add_constraint(c, lvl); +} diff --git a/src/muz/spacer/spacer_dl_interface.h b/src/muz/spacer/spacer_dl_interface.h index 1581762d5..9fc60dcf0 100644 --- a/src/muz/spacer/spacer_dl_interface.h +++ b/src/muz/spacer/spacer_dl_interface.h @@ -45,39 +45,46 @@ class dl_interface : public datalog::engine_base { public: dl_interface(datalog::context& ctx); - ~dl_interface(); + ~dl_interface() override; - lbool query(expr* query); + lbool query(expr* query) override; - lbool query_from_lvl(expr* query, unsigned lvl); + lbool query_from_lvl(expr* query, unsigned lvl) override; - void display_certificate(std::ostream& out) const; + void display_certificate(std::ostream& out) const override; - void collect_statistics(statistics& st) const; + void collect_statistics(statistics& st) const override; - void reset_statistics(); + void reset_statistics() override; - expr_ref get_answer(); + expr_ref get_answer() override; - expr_ref get_ground_sat_answer(); + expr_ref get_ground_sat_answer() override; - void get_rules_along_trace(datalog::rule_ref_vector& rules); + void get_rules_along_trace(datalog::rule_ref_vector& rules) override; - unsigned get_num_levels(func_decl* pred); + unsigned get_num_levels(func_decl* pred) override; - expr_ref get_cover_delta(int level, func_decl* pred); + expr_ref get_cover_delta(int level, func_decl* pred) override; - void add_cover(int level, func_decl* pred, expr* property); + void add_cover(int level, func_decl* pred, expr* property) override; - void add_invariant(func_decl* pred, expr* property); + void add_invariant(func_decl* pred, expr* property) override; - expr_ref get_reachable(func_decl *pred); + expr_ref get_reachable(func_decl *pred) override; - void updt_params(); + void updt_params() override; - model_ref get_model(); + model_ref get_model() override; - proof_ref get_proof(); + proof_ref get_proof() override; + + void add_callback(void *state, + const datalog::t_new_lemma_eh new_lemma_eh, + const datalog::t_predecessor_eh predecessor_eh, + const datalog::t_unfold_eh unfold_eh) override; + + void add_constraint (expr *c, unsigned lvl) override; }; } diff --git a/src/muz/spacer/spacer_farkas_learner.h b/src/muz/spacer/spacer_farkas_learner.h index 724b18b73..0e696f4f1 100644 --- a/src/muz/spacer/spacer_farkas_learner.h +++ b/src/muz/spacer/spacer_farkas_learner.h @@ -24,12 +24,6 @@ Revision History: namespace spacer { - - - - - - class farkas_learner { typedef obj_hashtable expr_set; diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index 94c419493..b0fb6c2d3 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -21,11 +21,16 @@ Revision History: #include "muz/spacer/spacer_context.h" #include "muz/spacer/spacer_generalizers.h" +#include "ast/ast_util.h" #include "ast/expr_abstract.h" #include "ast/rewriter/var_subst.h" #include "ast/for_each_expr.h" -#include "ast/factor_equivs.h" - +#include "ast/rewriter/factor_equivs.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/substitution/matcher.h" +#include "ast/expr_functors.h" +#include "smt/smt_solver.h" +#include "qe/qe_term_graph.h" namespace spacer { void lemma_sanity_checker::operator()(lemma_ref &lemma) { @@ -33,9 +38,23 @@ void lemma_sanity_checker::operator()(lemma_ref &lemma) { expr_ref_vector cube(lemma->get_ast_manager()); cube.append(lemma->get_cube()); ENSURE(lemma->get_pob()->pt().check_inductive(lemma->level(), - cube, uses_level)); + cube, uses_level, + lemma->weakness())); } +namespace{ + class contains_array_op_proc : public i_expr_pred { + ast_manager &m; + family_id m_array_fid; + public: + contains_array_op_proc(ast_manager &manager) : + m(manager), m_array_fid(m.mk_family_id("array")) + {} + virtual bool operator()(expr *e) { + return is_app(e) && to_app(e)->get_family_id() == m_array_fid; + } + }; +} // ------------------------ // lemma_bool_inductive_generalizer @@ -50,6 +69,9 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { pred_transformer &pt = lemma->get_pob()->pt(); ast_manager &m = pt.get_ast_manager(); + contains_array_op_proc has_array_op(m); + check_pred has_arrays(has_array_op, m); + expr_ref_vector cube(m); cube.append(lemma->get_cube()); @@ -58,14 +80,21 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { ptr_vector processed; expr_ref_vector extra_lits(m); + unsigned weakness = lemma->weakness(); + unsigned i = 0, num_failures = 0; while (i < cube.size() && (!m_failure_limit || num_failures < m_failure_limit)) { expr_ref lit(m); lit = cube.get(i); + if (m_array_only && !has_arrays(lit)) { + processed.push_back(lit); + ++i; + continue; + } cube[i] = true_expr; if (cube.size() > 1 && - pt.check_inductive(lemma->level(), cube, uses_level)) { + pt.check_inductive(lemma->level(), cube, uses_level, weakness)) { num_failures = 0; dirty = true; for (i = 0; i < cube.size() && @@ -82,7 +111,7 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) { SASSERT(extra_lits.size() > 1); for (unsigned j = 0, sz = extra_lits.size(); !found && j < sz; ++j) { cube[i] = extra_lits.get(j); - if (pt.check_inductive(lemma->level(), cube, uses_level)) { + if (pt.check_inductive(lemma->level(), cube, uses_level, weakness)) { num_failures = 0; dirty = true; found = true; @@ -130,10 +159,11 @@ void unsat_core_generalizer::operator()(lemma_ref &lemma) unsigned old_sz = lemma->get_cube().size(); unsigned old_level = lemma->level(); + (void)old_level; unsigned uses_level; expr_ref_vector core(m); - VERIFY(pt.is_invariant(old_level, lemma->get_expr(), uses_level, &core)); + VERIFY(pt.is_invariant(lemma->level(), lemma.get(), uses_level, &core)); CTRACE("spacer", old_sz > core.size(), tout << "unsat core reduced lemma from: " @@ -161,7 +191,7 @@ class collect_array_proc { sort *m_sort; public: collect_array_proc(ast_manager &m, func_decl_set& s) : - m_au(m), m_symbs(s), m_sort(NULL) {} + m_au(m), m_symbs(s), m_sort(nullptr) {} void operator()(app* a) { @@ -176,118 +206,131 @@ public: }; } +bool lemma_array_eq_generalizer::is_array_eq (ast_manager &m, expr* e) { + + expr *e1 = nullptr, *e2 = nullptr; + if (m.is_eq(e, e1, e2) && is_app(e1) && is_app(e2)) { + app *a1 = to_app(e1); + app *a2 = to_app(e2); + array_util au(m); + if (a1->get_family_id() == null_family_id && + a2->get_family_id() == null_family_id && + au.is_array(a1) && au.is_array(a2)) + return true; + } + return false; +} + void lemma_array_eq_generalizer::operator() (lemma_ref &lemma) { - TRACE("core_array_eq", tout << "Looking for equalities\n";); - // -- find array constants ast_manager &m = lemma->get_ast_manager(); - manager &pm = m_ctx.get_manager(); - (void)pm; + expr_ref_vector core(m); expr_ref v(m); func_decl_set symb; collect_array_proc cap(m, symb); + + // -- find array constants core.append (lemma->get_cube()); v = mk_and(core); for_each_expr(cap, v); - TRACE("core_array_eq", + CTRACE("core_array_eq", symb.size() > 1 && symb.size() <= 8, tout << "found " << symb.size() << " array variables in: \n" - << mk_pp(v, m) << "\n";); + << v << "\n";); - // too few constants - if (symb.size() <= 1) { return; } - // too many constants, skip this - if (symb.size() >= 8) { return; } + // too few constants or too many constants + if (symb.size() <= 1 || symb.size() > 8) { return; } - // -- for every pair of variables, try an equality - typedef func_decl_set::iterator iterator; + // -- for every pair of constants (A, B), check whether the + // -- equality (A=B) generalizes a literal in the lemma + ptr_vector vsymbs; - for (iterator it = symb.begin(), end = symb.end(); - it != end; ++it) - { vsymbs.push_back(*it); } + for (auto * fdecl : symb) {vsymbs.push_back(fdecl);} + // create all equalities expr_ref_vector eqs(m); + for (unsigned i = 0, sz = vsymbs.size(); i < sz; ++i) { + for (unsigned j = i + 1; j < sz; ++j) { + eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)), + m.mk_const(vsymbs.get(j)))); + } + } - for (unsigned i = 0, sz = vsymbs.size(); i < sz; ++i) - for (unsigned j = i + 1; j < sz; ++j) - { eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)), - m.mk_const(vsymbs.get(j)))); } - - smt::kernel solver(m, m_ctx.get_manager().fparams2()); + // smt-solver to check whether a literal is generalized. using + // default params. There has to be a simpler way to approximate + // this check + ref sol = mk_smt_solver(m, params_ref::get_empty(), symbol::null); + // literals of the new lemma expr_ref_vector lits(m); - for (unsigned i = 0, core_sz = core.size(); i < core_sz; ++i) { - SASSERT(lits.size() == i); - solver.push(); - solver.assert_expr(core.get(i)); - for (unsigned j = 0, eqs_sz = eqs.size(); j < eqs_sz; ++j) { - solver.push(); - solver.assert_expr(eqs.get(j)); - lbool res = solver.check(); - solver.pop(1); + lits.append(core); + expr *t = nullptr; + bool dirty = false; + for (unsigned i = 0, sz = core.size(); i < sz; ++i) { + // skip a literal is it is already an array equality + if (m.is_not(lits.get(i), t) && is_array_eq(m, t)) continue; + solver::scoped_push _pp_(*sol); + sol->assert_expr(lits.get(i)); + for (auto *e : eqs) { + solver::scoped_push _p_(*sol); + sol->assert_expr(e); + lbool res = sol->check_sat(0, nullptr); if (res == l_false) { TRACE("core_array_eq", - tout << "strengthened " << mk_pp(core.get(i), m) - << " with " << mk_pp(m.mk_not(eqs.get(j)), m) << "\n";); - lits.push_back(m.mk_not(eqs.get(j))); + tout << "strengthened " << mk_pp(lits.get(i), m) + << " with " << mk_pp(mk_not(m, e), m) << "\n";); + lits[i] = mk_not(m, e); + dirty = true; break; } } - solver.pop(1); - if (lits.size() == i) { lits.push_back(core.get(i)); } } - /** - HACK: if the first 3 arguments of pt are boolean, assume - they correspond to SeaHorn encoding and condition the equality on them. - */ - // pred_transformer &pt = n.pt (); - // if (pt.sig_size () >= 3 && - // m.is_bool (pt.sig (0)->get_range ()) && - // m.is_bool (pt.sig (1)->get_range ()) && - // m.is_bool (pt.sig (2)->get_range ())) - // { - // lits.push_back (m.mk_const (pm.o2n(pt.sig (0), 0))); - // lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (1), 0)))); - // lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (2), 0)))); - // } + // nothing changed + if (!dirty) return; - TRACE("core_array_eq", tout << "new possible core " - << mk_pp(pm.mk_and(lits), m) << "\n";); + TRACE("core_array_eq", + tout << "new possible core " << mk_and(lits) << "\n";); pred_transformer &pt = lemma->get_pob()->pt(); - // -- check if it is consistent with the transition relation + // -- check if the generalized result is consistent with trans unsigned uses_level1; - if (pt.check_inductive(lemma->level(), lits, uses_level1)) { + if (pt.check_inductive(lemma->level(), lits, uses_level1, lemma->weakness())) { TRACE("core_array_eq", tout << "Inductive!\n";); - lemma->update_cube(lemma->get_pob(),lits); + lemma->update_cube(lemma->get_pob(), lits); lemma->set_level(uses_level1); - return; - } else - { TRACE("core_array_eq", tout << "Not-Inductive!\n";);} + } + else + {TRACE("core_array_eq", tout << "Not-Inductive!\n";);} } void lemma_eq_generalizer::operator() (lemma_ref &lemma) { TRACE("core_eq", tout << "Transforming equivalence classes\n";); - ast_manager &m = m_ctx.get_ast_manager(); - expr_ref_vector core(m); - core.append (lemma->get_cube()); + if (lemma->get_cube().empty()) return; - bool dirty; - expr_equiv_class eq_classes(m); - factor_eqs(core, eq_classes); - // create all possible equalities to allow for simple inductive generalization - dirty = equiv_to_expr_full(eq_classes, core); - if (dirty) { + ast_manager &m = m_ctx.get_ast_manager(); + qe::term_graph egraph(m); + egraph.add_lits(lemma->get_cube()); + + // -- expand the cube with all derived equalities + expr_ref_vector core(m); + egraph.to_lits(core, true); + + // -- if the core looks different from the original cube + if (core.size() != lemma->get_cube().size() || + core.get(0) != lemma->get_cube().get(0)) { + // -- update the lemma lemma->update_cube(lemma->get_pob(), core); } } + + }; diff --git a/src/muz/spacer/spacer_generalizers.h b/src/muz/spacer/spacer_generalizers.h index aa542ec04..45ae472bd 100644 --- a/src/muz/spacer/spacer_generalizers.h +++ b/src/muz/spacer/spacer_generalizers.h @@ -30,8 +30,8 @@ namespace spacer { class lemma_sanity_checker : public lemma_generalizer { public: lemma_sanity_checker(context& ctx) : lemma_generalizer(ctx) {} - virtual ~lemma_sanity_checker() {} - virtual void operator()(lemma_ref &lemma); + ~lemma_sanity_checker() override {} + void operator()(lemma_ref &lemma) override; }; /** @@ -48,16 +48,19 @@ class lemma_bool_inductive_generalizer : public lemma_generalizer { }; unsigned m_failure_limit; + bool m_array_only; stats m_st; public: - lemma_bool_inductive_generalizer(context& ctx, unsigned failure_limit) : - lemma_generalizer(ctx), m_failure_limit(failure_limit) {} - virtual ~lemma_bool_inductive_generalizer() {} - virtual void operator()(lemma_ref &lemma); + lemma_bool_inductive_generalizer(context& ctx, unsigned failure_limit, + bool array_only = false) : + lemma_generalizer(ctx), m_failure_limit(failure_limit), + m_array_only(array_only) {} + ~lemma_bool_inductive_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();} }; class unsat_core_generalizer : public lemma_generalizer { @@ -72,28 +75,69 @@ class unsat_core_generalizer : public lemma_generalizer { stats m_st; public: unsat_core_generalizer(context &ctx) : lemma_generalizer(ctx) {} - virtual ~unsat_core_generalizer() {} - virtual void operator()(lemma_ref &lemma); + ~unsat_core_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();} }; class lemma_array_eq_generalizer : public lemma_generalizer { +private: + bool is_array_eq(ast_manager &m, expr *e); public: lemma_array_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {} - virtual ~lemma_array_eq_generalizer() {} - virtual void operator()(lemma_ref &lemma); + ~lemma_array_eq_generalizer() override {} + void operator()(lemma_ref &lemma) override; }; class lemma_eq_generalizer : public lemma_generalizer { public: lemma_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {} - virtual ~lemma_eq_generalizer() {} + ~lemma_eq_generalizer() override {} + void operator()(lemma_ref &lemma) override; +}; + +class lemma_quantifier_generalizer : public lemma_generalizer { + struct stats { + unsigned count; + unsigned num_failures; + stopwatch watch; + stats() {reset();} + void reset() {count = 0; num_failures = 0; watch.reset();} + }; + + ast_manager &m; + arith_util m_arith; + stats m_st; + expr_ref_vector m_cube; + + bool m_normalize_cube; + int m_offset; +public: + lemma_quantifier_generalizer(context &ctx, bool normalize_cube = true); + virtual ~lemma_quantifier_generalizer() {} virtual void operator()(lemma_ref &lemma); -}; + virtual void collect_statistics(statistics& st) const; + virtual void reset_statistics() {m_st.reset();} +private: + bool generalize(lemma_ref &lemma, app *term); + void find_candidates(expr *e, app_ref_vector &candidate); + bool is_ub(var *var, expr *e); + bool is_lb(var *var, expr *e); + void mk_abs_cube (lemma_ref &lemma, app *term, var *var, + expr_ref_vector &gnd_cube, + expr_ref_vector &abs_cube, + expr *&lb, expr *&ub, unsigned &stride); + + bool match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk); + void cleanup(expr_ref_vector& cube, app_ref_vector const &zks, expr_ref &bind); + + bool find_stride(expr_ref_vector &c, expr_ref &pattern, unsigned &stride); }; +} + #endif diff --git a/src/muz/spacer/spacer_itp_solver.cpp b/src/muz/spacer/spacer_itp_solver.cpp deleted file mode 100644 index bcf6b07ae..000000000 --- a/src/muz/spacer/spacer_itp_solver.cpp +++ /dev/null @@ -1,355 +0,0 @@ -/** -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_itp_solver.cpp - -Abstract: - - A solver that produces interpolated unsat cores - -Author: - - Arie Gurfinkel - -Notes: - ---*/ -#include"muz/spacer/spacer_itp_solver.h" -#include"ast/ast.h" -#include"muz/spacer/spacer_util.h" -#include"muz/spacer/spacer_farkas_learner.h" -#include"ast/rewriter/expr_replacer.h" -#include"muz/spacer/spacer_unsat_core_learner.h" -#include"muz/spacer/spacer_unsat_core_plugin.h" - -namespace spacer { -void itp_solver::push () -{ - m_defs.push_back (def_manager (*this)); - m_solver.push (); -} - -void itp_solver::pop (unsigned n) -{ - m_solver.pop (n); - unsigned lvl = m_defs.size (); - SASSERT (n <= lvl); - unsigned new_lvl = lvl-n; - while (m_defs.size() > new_lvl) { - m_num_proxies -= m_defs.back ().m_defs.size (); - m_defs.pop_back (); - } -} - -app* itp_solver::fresh_proxy () -{ - if (m_num_proxies == m_proxies.size()) { - std::stringstream name; - name << "spacer_proxy!" << m_proxies.size (); - app_ref res(m); - res = m.mk_const (symbol (name.str ().c_str ()), - m.mk_bool_sort ()); - m_proxies.push_back (res); - - // -- add the new proxy to proxy eliminator - proof_ref pr(m); - pr = m.mk_asserted (m.mk_true ()); - m_elim_proxies_sub.insert (res, m.mk_true (), pr); - - } - return m_proxies.get (m_num_proxies++); -} - -app* itp_solver::mk_proxy (expr *v) -{ - { - expr *e = v; - m.is_not (v, e); - if (is_uninterp_const(e)) { return to_app(v); } - } - - def_manager &def = m_defs.size () > 0 ? m_defs.back () : m_base_defs; - return def.mk_proxy (v); -} - -bool itp_solver::mk_proxies (expr_ref_vector &v, unsigned from) -{ - bool dirty = false; - for (unsigned i = from, sz = v.size(); i < sz; ++i) { - app *p = mk_proxy (v.get (i)); - dirty |= (v.get (i) != p); - v[i] = p; - } - return dirty; -} - -void itp_solver::push_bg (expr *e) -{ - if (m_assumptions.size () > m_first_assumption) - { m_assumptions.shrink(m_first_assumption); } - m_assumptions.push_back (e); - m_first_assumption = m_assumptions.size (); -} - -void itp_solver::pop_bg (unsigned n) -{ - if (n == 0) { return; } - - if (m_assumptions.size () > m_first_assumption) - { m_assumptions.shrink(m_first_assumption); } - m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0; - m_assumptions.shrink (m_first_assumption); -} - -unsigned itp_solver::get_num_bg () {return m_first_assumption;} - -lbool itp_solver::check_sat (unsigned num_assumptions, expr * const *assumptions) -{ - // -- remove any old assumptions - if (m_assumptions.size () > m_first_assumption) - { m_assumptions.shrink(m_first_assumption); } - - // -- replace theory literals in background assumptions with proxies - mk_proxies (m_assumptions); - // -- in case mk_proxies added new literals, they are all background - m_first_assumption = m_assumptions.size (); - - m_assumptions.append (num_assumptions, assumptions); - m_is_proxied = mk_proxies (m_assumptions, m_first_assumption); - - lbool res; - res = m_solver.check_sat (m_assumptions.size (), m_assumptions.c_ptr ()); - set_status (res); - return res; -} - - -app* itp_solver::def_manager::mk_proxy (expr *v) -{ - app* r; - if (m_expr2proxy.find(v, r)) { return r; } - - ast_manager &m = m_parent.m; - app_ref proxy(m); - app_ref def(m); - proxy = m_parent.fresh_proxy (); - def = m.mk_or (m.mk_not (proxy), v); - m_defs.push_back (def); - m_expr2proxy.insert (v, proxy); - m_proxy2def.insert (proxy, def); - - m_parent.assert_expr (def.get ()); - return proxy; -} - -bool itp_solver::def_manager::is_proxy (app *k, app_ref &def) -{ - app *r = NULL; - bool found = m_proxy2def.find (k, r); - def = r; - return found; -} - -void itp_solver::def_manager::reset () -{ - m_expr2proxy.reset (); - m_proxy2def.reset (); - m_defs.reset (); -} - -bool itp_solver::def_manager::is_proxy_def (expr *v) -{ - // XXX This might not be the most robust way to check - return m_defs.contains (v); -} - -bool itp_solver::is_proxy(expr *e, app_ref &def) -{ - if (!is_uninterp_const(e)) { return false; } - - app *a = to_app (e); - - for (int i = m_defs.size (); i > 0; --i) - if (m_defs[i-1].is_proxy (a, def)) - { return true; } - - if (m_base_defs.is_proxy (a, def)) - { return true; } - - return false; -} - -void itp_solver::collect_statistics (statistics &st) const -{ - m_solver.collect_statistics (st); - st.update ("time.itp_solver.itp_core", m_itp_watch.get_seconds ()); -} - -void itp_solver::reset_statistics () -{ - m_itp_watch.reset (); -} - -void itp_solver::get_unsat_core (ptr_vector &core) -{ - m_solver.get_unsat_core (core); - undo_proxies_in_core (core); -} -void itp_solver::undo_proxies_in_core (ptr_vector &r) -{ - app_ref e(m); - expr_fast_mark1 bg; - for (unsigned i = 0; i < m_first_assumption; ++i) - { bg.mark(m_assumptions.get(i)); } - - // expand proxies - unsigned j = 0; - for (unsigned i = 0, sz = r.size(); i < sz; ++i) { - // skip background assumptions - if (bg.is_marked(r[i])) { continue; } - - // -- undo proxies, but only if they were introduced in check_sat - if (m_is_proxied && is_proxy(r[i], e)) { - SASSERT (m.is_or (e)); - r[j] = e->get_arg (1); - } else if (i != j) { r[j] = r[i]; } - j++; - } - r.shrink (j); -} - -void itp_solver::undo_proxies (expr_ref_vector &r) -{ - app_ref e(m); - // expand proxies - for (unsigned i = 0, sz = r.size (); i < sz; ++i) - if (is_proxy(r.get(i), e)) { - SASSERT (m.is_or (e)); - r[i] = e->get_arg (1); - } -} - -void itp_solver::get_unsat_core (expr_ref_vector &_core) -{ - ptr_vector core; - get_unsat_core (core); - _core.append (core.size (), core.c_ptr ()); -} - -void itp_solver::elim_proxies (expr_ref_vector &v) -{ - expr_ref f = mk_and (v); - scoped_ptr rep = mk_expr_simp_replacer (m); - rep->set_substitution (&m_elim_proxies_sub); - (*rep) (f); - v.reset (); - flatten_and (f, v); -} - -void itp_solver::get_itp_core (expr_ref_vector &core) -{ - scoped_watch _t_ (m_itp_watch); - - typedef obj_hashtable expr_set; - expr_set B; - for (unsigned i = m_first_assumption, sz = m_assumptions.size(); i < sz; ++i) { - expr *a = m_assumptions.get (i); - app_ref def(m); - if (is_proxy(a, def)) { B.insert(def.get()); } - B.insert (a); - } - - proof_ref pr(m); - pr = get_proof (); - - if (!m_new_unsat_core) { - // old code - farkas_learner learner_old; - learner_old.set_split_literals(m_split_literals); - - learner_old.get_lemmas (pr, B, core); - elim_proxies (core); - simplify_bounds (core); // XXX potentially redundant - } else { - // new code - unsat_core_learner learner(m); - - if (m_farkas_optimized) { - if (true) // TODO: proper options - { - unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m); - learner.register_plugin(plugin_farkas_lemma_optimized); - } - else - { - unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m); - learner.register_plugin(plugin_farkas_lemma_bounded); - } - - } else { - unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, m_farkas_a_const); - learner.register_plugin(plugin_farkas_lemma); - } - - if (m_minimize_unsat_core) { - unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m); - learner.register_plugin(plugin_min_cut); - } else { - unsat_core_plugin_lemma* plugin_lemma = alloc(unsat_core_plugin_lemma, learner); - learner.register_plugin(plugin_lemma); - } - - learner.compute_unsat_core(pr, B, core); - - elim_proxies (core); - simplify_bounds (core); // XXX potentially redundant - -// // debug -// expr_ref_vector core2(m); -// unsat_core_learner learner2(m); -// -// unsat_core_plugin_farkas_lemma* plugin_farkas_lemma2 = alloc(unsat_core_plugin_farkas_lemma, learner2, m_split_literals); -// learner2.register_plugin(plugin_farkas_lemma2); -// unsat_core_plugin_lemma* plugin_lemma2 = alloc(unsat_core_plugin_lemma, learner2); -// learner2.register_plugin(plugin_lemma2); -// learner2.compute_unsat_core(pr, B, core2); -// -// elim_proxies (core2); -// simplify_bounds (core2); -// -// IF_VERBOSE(2, -// verbose_stream () << "Itp Core:\n" -// << mk_pp (mk_and (core), m) << "\n";); -// IF_VERBOSE(2, -// verbose_stream () << "Itp Core2:\n" -// << mk_pp (mk_and (core2), m) << "\n";); - //SASSERT(mk_and (core) == mk_and (core2)); - } - - IF_VERBOSE(2, - verbose_stream () << "Itp Core:\n" - << mk_pp (mk_and (core), m) << "\n";); - -} - -void itp_solver::refresh () -{ - // only refresh in non-pushed state - SASSERT (m_defs.size () == 0); - expr_ref_vector assertions (m); - for (unsigned i = 0, e = m_solver.get_num_assertions(); i < e; ++i) { - expr* a = m_solver.get_assertion (i); - if (!m_base_defs.is_proxy_def(a)) { assertions.push_back(a); } - - } - m_base_defs.reset (); - NOT_IMPLEMENTED_YET (); - // solver interface does not have a reset method. need to introduce it somewhere. - // m_solver.reset (); - for (unsigned i = 0, e = assertions.size (); i < e; ++i) - { m_solver.assert_expr(assertions.get(i)); } -} - -} diff --git a/src/muz/spacer/spacer_itp_solver.h b/src/muz/spacer/spacer_itp_solver.h deleted file mode 100644 index 8194379b8..000000000 --- a/src/muz/spacer/spacer_itp_solver.h +++ /dev/null @@ -1,177 +0,0 @@ -/** -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_itp_solver.h - -Abstract: - - A solver that produces interpolated unsat cores - -Author: - - Arie Gurfinkel - -Notes: - ---*/ -#ifndef SPACER_ITP_SOLVER_H_ -#define SPACER_ITP_SOLVER_H_ - -#include"solver/solver.h" -#include"ast/expr_substitution.h" -#include"util/stopwatch.h" -namespace spacer { -class itp_solver : public solver { -private: - struct def_manager { - itp_solver &m_parent; - obj_map m_expr2proxy; - obj_map m_proxy2def; - - expr_ref_vector m_defs; - - def_manager(itp_solver &parent) : - m_parent(parent), m_defs(m_parent.m) - {} - - bool is_proxy(app *k, app_ref &v); - app* mk_proxy(expr *v); - void reset(); - bool is_proxy_def(expr *v); - - }; - - friend struct def_manager; - ast_manager &m; - solver &m_solver; - app_ref_vector m_proxies; - unsigned m_num_proxies; - vector m_defs; - def_manager m_base_defs; - expr_ref_vector m_assumptions; - unsigned m_first_assumption; - bool m_is_proxied; - - stopwatch m_itp_watch; - - expr_substitution m_elim_proxies_sub; - bool m_split_literals; - bool m_new_unsat_core; - bool m_minimize_unsat_core; - bool m_farkas_optimized; - bool m_farkas_a_const; - - bool is_proxy(expr *e, app_ref &def); - void undo_proxies_in_core(ptr_vector &v); - app* mk_proxy(expr *v); - app* fresh_proxy(); - void elim_proxies(expr_ref_vector &v); -public: - itp_solver(solver &solver, bool new_unsat_core, bool minimize_unsat_core, bool farkas_optimized, bool farkas_a_const, bool split_literals = false) : - m(solver.get_manager()), - m_solver(solver), - m_proxies(m), - m_num_proxies(0), - m_base_defs(*this), - m_assumptions(m), - m_first_assumption(0), - m_is_proxied(false), - m_elim_proxies_sub(m, false, true), - m_split_literals(split_literals), - m_new_unsat_core(new_unsat_core), - m_minimize_unsat_core(minimize_unsat_core), - m_farkas_optimized(farkas_optimized), - m_farkas_a_const(farkas_a_const) - {} - - virtual ~itp_solver() {} - - /* itp solver specific */ - virtual void get_unsat_core(expr_ref_vector &core); - virtual void get_itp_core(expr_ref_vector &core); - void set_split_literals(bool v) {m_split_literals = v;} - bool mk_proxies(expr_ref_vector &v, unsigned from = 0); - void undo_proxies(expr_ref_vector &v); - - void push_bg(expr *e); - void pop_bg(unsigned n); - unsigned get_num_bg(); - - void get_full_unsat_core(ptr_vector &core) - {m_solver.get_unsat_core(core);} - - /* solver interface */ - - virtual solver* translate(ast_manager &m, params_ref const &p) - {return m_solver.translate(m, p);} - virtual void updt_params(params_ref const &p) - {m_solver.updt_params(p);} - virtual void collect_param_descrs(param_descrs &r) - {m_solver.collect_param_descrs(r);} - virtual void set_produce_models(bool f) - {m_solver.set_produce_models(f);} - virtual void assert_expr(expr *t) - {m_solver.assert_expr(t);} - - virtual void assert_expr(expr *t, expr *a) - {NOT_IMPLEMENTED_YET();} - - virtual void push(); - virtual void pop(unsigned n); - virtual unsigned get_scope_level() const - {return m_solver.get_scope_level();} - - virtual lbool check_sat(unsigned num_assumptions, expr * const *assumptions); - virtual void set_progress_callback(progress_callback *callback) - {m_solver.set_progress_callback(callback);} - virtual unsigned get_num_assertions() const - {return m_solver.get_num_assertions();} - virtual expr * get_assertion(unsigned idx) const - {return m_solver.get_assertion(idx);} - virtual unsigned get_num_assumptions() const - {return m_solver.get_num_assumptions();} - virtual expr * get_assumption(unsigned idx) const - {return m_solver.get_assumption(idx);} - virtual std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const - { return m_solver.display(out, n, es); } - - /* check_sat_result interface */ - - virtual void collect_statistics(statistics &st) const ; - virtual void reset_statistics(); - virtual void get_unsat_core(ptr_vector &r); - virtual void get_model(model_ref &m) {m_solver.get_model(m);} - virtual proof *get_proof() {return m_solver.get_proof();} - virtual std::string reason_unknown() const - {return m_solver.reason_unknown();} - virtual void set_reason_unknown(char const* msg) - {m_solver.set_reason_unknown(msg);} - virtual void get_labels(svector &r) - {m_solver.get_labels(r);} - virtual ast_manager &get_manager() const {return m;} - - virtual void refresh(); - - class scoped_mk_proxy { - itp_solver &m_s; - expr_ref_vector &m_v; - public: - scoped_mk_proxy(itp_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) - {m_s.mk_proxies(m_v);} - ~scoped_mk_proxy() - {m_s.undo_proxies(m_v);} - }; - - class scoped_bg { - itp_solver &m_s; - unsigned m_bg_sz; - public: - scoped_bg(itp_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {} - ~scoped_bg() - {if (m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); }} - }; -}; -} -#endif diff --git a/src/muz/spacer/spacer_iuc_proof.cpp b/src/muz/spacer/spacer_iuc_proof.cpp new file mode 100644 index 000000000..b6a522b76 --- /dev/null +++ b/src/muz/spacer/spacer_iuc_proof.cpp @@ -0,0 +1,280 @@ +#include +#include "ast/ast_pp_dot.h" + +#include "muz/spacer/spacer_iuc_proof.h" +#include "ast/for_each_expr.h" +#include "ast/array_decl_plugin.h" +#include "ast/proofs/proof_utils.h" +#include "muz/spacer/spacer_proof_utils.h" +#include "muz/spacer/spacer_util.h" +namespace spacer { + +/* + * ==================================== + * init + * ==================================== + */ +iuc_proof::iuc_proof(ast_manager& m, proof* pr, const expr_set& core_lits) : + m(m), m_pr(pr,m) { + for (auto lit : core_lits) m_core_lits.insert(lit); + // init A-marks and B-marks + collect_core_symbols(); + compute_marks(); +} + +iuc_proof::iuc_proof(ast_manager& m, proof* pr, const expr_ref_vector& core_lits) : + m(m), m_pr(pr,m) { + for (auto lit : core_lits) m_core_lits.insert(lit); + // init A-marks and B-marks + collect_core_symbols(); + compute_marks(); +} +/* + * ==================================== + * methods for computing symbol colors + * ==================================== + */ +class collect_pure_proc { + func_decl_set& m_symbs; +public: + collect_pure_proc(func_decl_set& s):m_symbs(s) {} + + void operator()(app* a) { + if (a->get_family_id() == null_family_id) { + m_symbs.insert(a->get_decl()); + } + } + void operator()(var*) {} + void operator()(quantifier*) {} +}; + +void iuc_proof::collect_core_symbols() +{ + expr_mark visited; + collect_pure_proc proc(m_core_symbols); + for (auto lit : m_core_lits) + for_each_expr(proc, visited, lit); +} + +class is_pure_expr_proc { + func_decl_set const& m_symbs; + array_util m_au; +public: + struct non_pure {}; + + is_pure_expr_proc(func_decl_set const& s, ast_manager& m): + m_symbs(s), + m_au (m) + {} + + void operator()(app* a) { + if (a->get_family_id() == null_family_id) { + if (!m_symbs.contains(a->get_decl())) { + throw non_pure(); + } + } + else if (a->get_family_id () == m_au.get_family_id () && + a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) { + throw non_pure(); + } + } + void operator()(var*) {} + void operator()(quantifier*) {} +}; + +bool iuc_proof::is_core_pure(expr* e) const +{ + is_pure_expr_proc proc(m_core_symbols, m); + try { + for_each_expr(proc, e); + } + catch (is_pure_expr_proc::non_pure) + {return false;} + + return true; +} + +void iuc_proof::compute_marks() +{ + proof_post_order it(m_pr, m); + while (it.hasNext()) + { + proof* cur = it.next(); + if (m.get_num_parents(cur) == 0) + { + switch(cur->get_decl_kind()) + { + case PR_ASSERTED: + if (m_core_lits.contains(m.get_fact(cur))) + m_b_mark.mark(cur, true); + else + m_a_mark.mark(cur, true); + break; + case PR_HYPOTHESIS: + m_h_mark.mark(cur, true); + break; + default: + break; + } + } + else + { + // collect from parents whether derivation of current node + // contains A-axioms, B-axioms and hypothesis + bool need_to_mark_a = false; + bool need_to_mark_b = false; + bool need_to_mark_h = false; + + for (unsigned i = 0; i < m.get_num_parents(cur); ++i) + { + SASSERT(m.is_proof(cur->get_arg(i))); + proof* premise = to_app(cur->get_arg(i)); + + need_to_mark_a |= m_a_mark.is_marked(premise); + need_to_mark_b |= m_b_mark.is_marked(premise); + need_to_mark_h |= m_h_mark.is_marked(premise); + } + + // if current node is application of a lemma, then all + // active hypotheses are removed + if (cur->get_decl_kind() == PR_LEMMA) need_to_mark_h = false; + + // save results + m_a_mark.mark(cur, need_to_mark_a); + m_b_mark.mark(cur, need_to_mark_b); + m_h_mark.mark(cur, need_to_mark_h); + } + } +} + +/* + * ==================================== + * statistics + * ==================================== + */ + +// debug method +void iuc_proof::dump_farkas_stats() +{ + unsigned fl_total = 0; + unsigned fl_lowcut = 0; + + proof_post_order it(m_pr, m); + while (it.hasNext()) + { + proof* cur = it.next(); + + // if node is theory lemma + if (is_farkas_lemma(m, cur)) + { + fl_total++; + + // check whether farkas lemma is to be interpolated (could + // potentially miss farkas lemmas, which are interpolated, + // because we potentially don't want to use the lowest + // cut) + bool has_blue_nonred_parent = false; + for (unsigned i = 0; i < m.get_num_parents(cur); ++i) { + proof* premise = to_app(cur->get_arg(i)); + if (!is_a_marked(premise) && is_b_marked(premise)) { + has_blue_nonred_parent = true; + break; + } + } + + if (has_blue_nonred_parent && is_a_marked(cur)) + { + SASSERT(is_b_marked(cur)); + fl_lowcut++; + } + } + } + + IF_VERBOSE(1, verbose_stream() + << "\n total farkas lemmas " << fl_total + << " farkas lemmas in lowest cut " << fl_lowcut << "\n";); +} + +void iuc_proof::display_dot(std::ostream& out) { + out << "digraph proof { \n"; + + std::unordered_map ids; + unsigned last_id = 0; + + proof_post_order it(m_pr, m); + while (it.hasNext()) + { + proof* curr = it.next(); + + SASSERT(ids.count(curr->get_id()) == 0); + ids.insert(std::make_pair(curr->get_id(), last_id)); + + std::string color = "white"; + if (this->is_a_marked(curr) && !this->is_b_marked(curr)) + color = "red"; + else if (!this->is_a_marked(curr) && this->is_b_marked(curr)) + color = "blue"; + else if (this->is_a_marked(curr) && this->is_b_marked(curr) ) + color = "purple"; + + // compute node label + std::ostringstream label_ostream; + label_ostream << mk_epp(m.get_fact(curr), m) << "\n"; + std::string label = escape_dot(label_ostream.str()); + + // compute edge-label + std::string edge_label = ""; + if (m.get_num_parents(curr) == 0) { + switch (curr->get_decl_kind()) + { + case PR_ASSERTED: + edge_label = "asserted:"; + break; + case PR_HYPOTHESIS: + edge_label = "hyp:"; + color = "grey"; + break; + case PR_TH_LEMMA: + if (is_farkas_lemma(m, curr)) + edge_label = "th_axiom(farkas):"; + else if (is_arith_lemma(m, curr)) + edge_label = "th_axiom(arith):"; + else + edge_label = "th_axiom:"; + break; + default: + edge_label = "unknown axiom:"; + } + } + else { + if (curr->get_decl_kind() == PR_LEMMA) + edge_label = "lemma:"; + else if (curr->get_decl_kind() == PR_TH_LEMMA) { + if (is_farkas_lemma(m, curr)) + edge_label = "th_lemma(farkas):"; + else if (is_arith_lemma(m, curr)) + edge_label = "th_lemma(arith):"; + else + edge_label = "th_lemma(other):"; + } + } + + // generate entry for node in dot-file + out << "node_" << last_id << " " << "[" + << "shape=box,style=\"filled\"," + << "label=\"" << edge_label << " " << label << "\", " + << "fillcolor=\"" << color << "\"" << "]\n"; + + // add entry for each edge to that node + for (unsigned i = m.get_num_parents(curr); i > 0 ; --i) + { + proof* premise = to_app(curr->get_arg(i-1)); + unsigned pid = ids.at(premise->get_id()); + out << "node_" << pid << " -> " << "node_" << last_id << ";\n"; + } + + ++last_id; + } + out << "\n}" << std::endl; +} +} diff --git a/src/muz/spacer/spacer_iuc_proof.h b/src/muz/spacer/spacer_iuc_proof.h new file mode 100644 index 000000000..ed36dbf5b --- /dev/null +++ b/src/muz/spacer/spacer_iuc_proof.h @@ -0,0 +1,67 @@ +#ifndef IUC_PROOF_H_ +#define IUC_PROOF_H_ + +#include +#include "ast/ast.h" + +namespace spacer { +typedef obj_hashtable expr_set; +typedef obj_hashtable func_decl_set; + +/* + * An iuc_proof is a proof together with information of the + * coloring of the axioms. + */ +class iuc_proof +{ +public: + + // Constructs an iuc_proof given an ast_manager, a proof, and a set of + // literals core_lits that might be included in the unsat core + iuc_proof(ast_manager& m, proof* pr, const expr_set& core_lits); + iuc_proof(ast_manager& m, proof* pr, const expr_ref_vector &core_lits); + + // returns the proof object + proof* get() {return m_pr.get();} + + // returns true if all uninterpreted symbols of e are from the core literals + // requires that m_core_symbols has already been computed + bool is_core_pure(expr* e) const; + + bool is_a_marked(proof* p) {return m_a_mark.is_marked(p);} + bool is_b_marked(proof* p) {return m_b_mark.is_marked(p);} + bool is_h_marked(proof* p) {return m_h_mark.is_marked(p);} + + bool is_b_pure (proof *p) { + return !is_h_marked(p) && !this->is_a_marked(p) && is_core_pure(m.get_fact(p)); + } + + void display_dot(std::ostream &out); + // debug method + void dump_farkas_stats(); +private: + ast_manager& m; + proof_ref m_pr; + + ast_mark m_a_mark; + ast_mark m_b_mark; + ast_mark m_h_mark; + + // -- literals that are part of the core + expr_set m_core_lits; + + // symbols that occur in any literals in the core + func_decl_set m_core_symbols; + + // collect symbols occurring in B (the core) + void collect_core_symbols(); + + // compute for each formula of the proof whether it derives + // from A or from B + void compute_marks(); +}; + + +} + +#endif /* IUC_PROOF_H_ */ diff --git a/src/muz/spacer/spacer_iuc_solver.cpp b/src/muz/spacer/spacer_iuc_solver.cpp new file mode 100644 index 000000000..27b8ee357 --- /dev/null +++ b/src/muz/spacer/spacer_iuc_solver.cpp @@ -0,0 +1,469 @@ +/** +Copyright (c) 2017 Arie Gurfinkel + +Module Name: + + spacer_iuc_solver.cpp + +Abstract: + + A solver that produces interpolated unsat cores (IUCs) + +Author: + + Arie Gurfinkel + +Notes: + +--*/ +#include"muz/spacer/spacer_iuc_solver.h" +#include"ast/ast.h" +#include"muz/spacer/spacer_util.h" +#include"ast/proofs/proof_utils.h" +#include"muz/spacer/spacer_farkas_learner.h" +#include"ast/rewriter/expr_replacer.h" +#include"muz/spacer/spacer_unsat_core_learner.h" +#include"muz/spacer/spacer_unsat_core_plugin.h" +#include "muz/spacer/spacer_iuc_proof.h" + +namespace spacer { +void iuc_solver::push () +{ + m_defs.push_back (def_manager (*this)); + m_solver.push (); +} + +void iuc_solver::pop (unsigned n) +{ + m_solver.pop (n); + unsigned lvl = m_defs.size (); + SASSERT (n <= lvl); + unsigned new_lvl = lvl-n; + while (m_defs.size() > new_lvl) { + m_num_proxies -= m_defs.back ().m_defs.size (); + m_defs.pop_back (); + } +} + +app* iuc_solver::fresh_proxy () +{ + if (m_num_proxies == m_proxies.size()) { + std::stringstream name; + name << "spacer_proxy!" << m_proxies.size (); + app_ref res(m); + res = m.mk_const (symbol (name.str ().c_str ()), + m.mk_bool_sort ()); + m_proxies.push_back (res); + + // -- add the new proxy to proxy eliminator + proof_ref pr(m); + pr = m.mk_asserted (m.mk_true ()); + m_elim_proxies_sub.insert (res, m.mk_true (), pr); + + } + return m_proxies.get (m_num_proxies++); +} + +app* iuc_solver::mk_proxy (expr *v) +{ + { + expr *e = v; + m.is_not (v, e); + if (is_uninterp_const(e)) { return to_app(v); } + } + + def_manager &def = m_defs.size () > 0 ? m_defs.back () : m_base_defs; + return def.mk_proxy (v); +} + +bool iuc_solver::mk_proxies (expr_ref_vector &v, unsigned from) +{ + bool dirty = false; + for (unsigned i = from, sz = v.size(); i < sz; ++i) { + app *p = mk_proxy (v.get (i)); + dirty |= (v.get (i) != p); + v[i] = p; + } + return dirty; +} + +void iuc_solver::push_bg (expr *e) +{ + if (m_assumptions.size () > m_first_assumption) + { m_assumptions.shrink(m_first_assumption); } + m_assumptions.push_back (e); + m_first_assumption = m_assumptions.size (); +} + +void iuc_solver::pop_bg (unsigned n) +{ + if (n == 0) { return; } + + if (m_assumptions.size () > m_first_assumption) { + m_assumptions.shrink(m_first_assumption); + } + m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0; + m_assumptions.shrink (m_first_assumption); +} + +unsigned iuc_solver::get_num_bg () {return m_first_assumption;} + +lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions) +{ + // -- remove any old assumptions + m_assumptions.shrink(m_first_assumption); + + // -- replace theory literals in background assumptions with proxies + mk_proxies (m_assumptions); + // -- in case mk_proxies added new literals, they are all background + m_first_assumption = m_assumptions.size (); + + m_assumptions.append (num_assumptions, assumptions); + m_is_proxied = mk_proxies (m_assumptions, m_first_assumption); + + return set_status (m_solver.check_sat (m_assumptions)); +} + +lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube, + vector const & clauses) { + if (clauses.empty()) + return check_sat(cube.size(), cube.c_ptr()); + + // -- remove any old assumptions + m_assumptions.shrink(m_first_assumption); + + // -- replace theory literals in background assumptions with proxies + mk_proxies(m_assumptions); + // -- in case mk_proxies added new literals, they are all background + m_first_assumption = m_assumptions.size(); + + m_assumptions.append(cube); + m_is_proxied = mk_proxies(m_assumptions, m_first_assumption); + + return set_status (m_solver.check_sat_cc(m_assumptions, clauses)); +} + + +app* iuc_solver::def_manager::mk_proxy (expr *v) +{ + app* r; + if (m_expr2proxy.find(v, r)) + return r; + + ast_manager &m = m_parent.m; + app* proxy = m_parent.fresh_proxy (); + app* def = m.mk_or (m.mk_not (proxy), v); + m_defs.push_back (def); + m_expr2proxy.insert (v, proxy); + m_proxy2def.insert (proxy, def); + + m_parent.assert_expr (def); + return proxy; +} + +bool iuc_solver::def_manager::is_proxy (app *k, app_ref &def) +{ + app *r = nullptr; + bool found = m_proxy2def.find (k, r); + def = r; + return found; +} + +void iuc_solver::def_manager::reset () +{ + m_expr2proxy.reset (); + m_proxy2def.reset (); + m_defs.reset (); +} + +bool iuc_solver::def_manager::is_proxy_def (expr *v) +{ + // XXX This might not be the most robust way to check + return m_defs.contains (v); +} + +bool iuc_solver::is_proxy(expr *e, app_ref &def) +{ + if (!is_uninterp_const(e)) + return false; + + app* a = to_app (e); + + for (int i = m_defs.size (); i-- > 0; ) + if (m_defs[i].is_proxy (a, def)) + return true; + + return m_base_defs.is_proxy (a, def); +} + +void iuc_solver::collect_statistics (statistics &st) const +{ + m_solver.collect_statistics (st); + st.update ("time.iuc_solver.get_iuc", m_iuc_sw.get_seconds()); + st.update ("time.iuc_solver.get_iuc.hyp_reduce1", m_hyp_reduce1_sw.get_seconds()); + st.update ("time.iuc_solver.get_iuc.hyp_reduce2", m_hyp_reduce2_sw.get_seconds()); + st.update ("time.iuc_solver.get_iuc.learn_core", m_learn_core_sw.get_seconds()); + + st.update("iuc_solver.num_proxies", m_proxies.size()); +} + +void iuc_solver::reset_statistics () +{ + m_iuc_sw.reset(); + m_hyp_reduce1_sw.reset(); + m_hyp_reduce2_sw.reset(); + m_learn_core_sw.reset(); +} + +void iuc_solver::get_unsat_core (expr_ref_vector &core) { + m_solver.get_unsat_core (core); + undo_proxies_in_core (core); +} + +void iuc_solver::undo_proxies_in_core (expr_ref_vector &r) +{ + app_ref e(m); + expr_fast_mark1 bg; + for (unsigned i = 0; i < m_first_assumption; ++i) { + bg.mark(m_assumptions.get(i)); + } + + // expand proxies + unsigned j = 0; + for (expr* rr : r) { + // skip background assumptions + if (bg.is_marked(rr)) + continue; + + // -- undo proxies, but only if they were introduced in check_sat + if (m_is_proxied && is_proxy(rr, e)) { + SASSERT (m.is_or (e)); + r[j++] = e->get_arg (1); + } + else { + r[j++] = rr; + } + } + r.shrink (j); +} + +void iuc_solver::undo_proxies (expr_ref_vector &r) +{ + app_ref e(m); + // expand proxies + for (unsigned i = 0, sz = r.size (); i < sz; ++i) + if (is_proxy(r.get(i), e)) { + SASSERT (m.is_or (e)); + r[i] = e->get_arg (1); + } +} + +void iuc_solver::elim_proxies (expr_ref_vector &v) +{ + expr_ref f = mk_and (v); + scoped_ptr rep = mk_expr_simp_replacer (m); + rep->set_substitution (&m_elim_proxies_sub); + (*rep)(f); + v.reset(); + flatten_and(f, v); +} + +void iuc_solver::get_iuc(expr_ref_vector &core) +{ + scoped_watch _t_ (m_iuc_sw); + + typedef obj_hashtable expr_set; + expr_set core_lits; + for (unsigned i = m_first_assumption, sz = m_assumptions.size(); i < sz; ++i) { + expr *a = m_assumptions.get (i); + app_ref def(m); + if (is_proxy(a, def)) { core_lits.insert(def.get()); } + core_lits.insert (a); + } + + if (m_iuc == 0) { + // ORIGINAL PDR CODE + // AG: deprecated + proof_ref pr(m); + pr = get_proof (); + + farkas_learner learner_old; + learner_old.set_split_literals(m_split_literals); + + learner_old.get_lemmas (pr, core_lits, core); + elim_proxies (core); + simplify_bounds (core); // XXX potentially redundant + } + else { + // NEW IUC + proof_ref res(get_proof(), m); + + // -- old hypothesis reducer while the new one is broken + if (m_old_hyp_reducer) { + scoped_watch _t_ (m_hyp_reduce1_sw); + // AG: deprecated + // pre-process proof in order to get a proof which is + // better suited for unsat-core-extraction + if (m_print_farkas_stats) { + iuc_proof iuc_before(m, res.get(), core_lits); + verbose_stream() << "\nOld reduce_hypotheses. Before:"; + iuc_before.dump_farkas_stats(); + } + + proof_utils::reduce_hypotheses(res); + proof_utils::permute_unit_resolution(res); + + if (m_print_farkas_stats) { + iuc_proof iuc_after(m, res.get(), core_lits); + verbose_stream() << "Old reduce_hypothesis. After:"; + iuc_after.dump_farkas_stats(); + } + } + // -- new hypothesis reducer + else + { +#if 0 + static unsigned bcnt = 0; + { + bcnt++; + TRACE("spacer", tout << "Dumping pf bcnt: " << bcnt << "\n";); + if (bcnt == 123) { + std::ofstream ofs; + ofs.open("/tmp/bpf_" + std::to_string(bcnt) + ".dot"); + iuc_proof iuc_pf_before(m, res.get(), core_lits); + iuc_pf_before.display_dot(ofs); + ofs.close(); + + proof_checker pc(m); + expr_ref_vector side(m); + ENSURE(pc.check(res, side)); + } + } +#endif + scoped_watch _t_ (m_hyp_reduce2_sw); + + // pre-process proof for better iuc extraction + if (m_print_farkas_stats) { + iuc_proof iuc_before(m, res.get(), core_lits); + verbose_stream() << "\n New hypothesis_reducer. Before:"; + iuc_before.dump_farkas_stats(); + } + + proof_ref pr1(m); + { + scoped_watch _t_ (m_hyp_reduce1_sw); + theory_axiom_reducer ta_reducer(m); + pr1 = ta_reducer.reduce (res.get()); + } + + proof_ref pr2(m); + { + scoped_watch _t_ (m_hyp_reduce2_sw); + hypothesis_reducer hyp_reducer(m); + pr2 = hyp_reducer.reduce(pr1); + } + + res = pr2; + + if (m_print_farkas_stats) { + iuc_proof iuc_after(m, res.get(), core_lits); + verbose_stream() << "New hypothesis_reducer. After:"; + iuc_after.dump_farkas_stats(); + } + } + + iuc_proof iuc_pf(m, res, core_lits); + +#if 0 + static unsigned cnt = 0; + { + cnt++; + TRACE("spacer", tout << "Dumping pf cnt: " << cnt << "\n";); + if (cnt == 123) { + std::ofstream ofs; + ofs.open("/tmp/pf_" + std::to_string(cnt) + ".dot"); + iuc_pf.display_dot(ofs); + ofs.close(); + proof_checker pc(m); + expr_ref_vector side(m); + ENSURE(pc.check(res, side)); + } + } +#endif + unsat_core_learner learner(m, iuc_pf); + + unsat_core_plugin* plugin; + // -- register iuc plugins + switch (m_iuc_arith) { + case 0: + case 1: + plugin = + alloc(unsat_core_plugin_farkas_lemma, + learner, m_split_literals, + (m_iuc_arith == 1) /* use constants from A */); + learner.register_plugin(plugin); + break; + case 2: + SASSERT(false && "Broken"); + plugin = alloc(unsat_core_plugin_farkas_lemma_optimized, learner, m); + learner.register_plugin(plugin); + break; + case 3: + plugin = alloc(unsat_core_plugin_farkas_lemma_bounded, learner, m); + learner.register_plugin(plugin); + break; + default: + UNREACHABLE(); + break; + } + + switch (m_iuc) { + case 1: + // -- iuc based on the lowest cut in the proof + plugin = alloc(unsat_core_plugin_lemma, learner); + learner.register_plugin(plugin); + break; + case 2: + // -- iuc based on the smallest cut in the proof + plugin = alloc(unsat_core_plugin_min_cut, learner, m); + learner.register_plugin(plugin); + break; + default: + UNREACHABLE(); + break; + } + + { + scoped_watch _t_ (m_learn_core_sw); + // compute interpolating unsat core + learner.compute_unsat_core(core); + } + + elim_proxies (core); + // AG: this should be taken care of by minimizing the iuc cut + simplify_bounds (core); + } + + IF_VERBOSE(2, + verbose_stream () << "IUC Core:\n" << core << "\n";); +} + +void iuc_solver::refresh () +{ + // only refresh in non-pushed state + SASSERT (m_defs.empty()); + expr_ref_vector assertions (m); + for (unsigned i = 0, e = m_solver.get_num_assertions(); i < e; ++i) { + expr* a = m_solver.get_assertion (i); + if (!m_base_defs.is_proxy_def(a)) { assertions.push_back(a); } + + } + m_base_defs.reset (); + NOT_IMPLEMENTED_YET (); + // solver interface does not have a reset method. need to introduce it somewhere. + // m_solver.reset (); + for (unsigned i = 0, e = assertions.size (); i < e; ++i) + { m_solver.assert_expr(assertions.get(i)); } +} + +} diff --git a/src/muz/spacer/spacer_iuc_solver.h b/src/muz/spacer/spacer_iuc_solver.h new file mode 100644 index 000000000..c0a0072ed --- /dev/null +++ b/src/muz/spacer/spacer_iuc_solver.h @@ -0,0 +1,181 @@ +/** +Copyright (c) 2017 Arie Gurfinkel + +Module Name: + + spacer_iuc_solver.h + +Abstract: + + A solver that produces interpolated unsat cores + +Author: + + Arie Gurfinkel + +Notes: + +--*/ +#ifndef SPACER_IUC_SOLVER_H_ +#define SPACER_IUC_SOLVER_H_ + +#include"solver/solver.h" +#include"ast/expr_substitution.h" +#include"util/stopwatch.h" +namespace spacer { +class iuc_solver : public solver { +private: + struct def_manager { + iuc_solver & m_parent; + expr_ref_vector m_defs; + obj_map m_expr2proxy; + obj_map m_proxy2def; + + def_manager(iuc_solver &parent) : + m_parent(parent), m_defs(m_parent.m) + {} + + bool is_proxy(app *k, app_ref &v); + app* mk_proxy(expr *v); + void reset(); + bool is_proxy_def(expr *v); + + }; + + friend struct def_manager; + ast_manager& m; + solver& m_solver; + app_ref_vector m_proxies; + unsigned m_num_proxies; + vector m_defs; + def_manager m_base_defs; + expr_ref_vector m_assumptions; + unsigned m_first_assumption; + bool m_is_proxied; + + stopwatch m_iuc_sw; + stopwatch m_hyp_reduce1_sw; + stopwatch m_hyp_reduce2_sw; + stopwatch m_learn_core_sw; + + expr_substitution m_elim_proxies_sub; + bool m_split_literals; + unsigned m_iuc; + unsigned m_iuc_arith; + bool m_print_farkas_stats; + bool m_old_hyp_reducer; + bool is_proxy(expr *e, app_ref &def); + void undo_proxies_in_core(expr_ref_vector &v); + app* mk_proxy(expr *v); + app* fresh_proxy(); + void elim_proxies(expr_ref_vector &v); +public: + iuc_solver(solver &solver, unsigned iuc, unsigned iuc_arith, + bool print_farkas_stats, bool old_hyp_reducer, + bool split_literals = false) : + m(solver.get_manager()), + m_solver(solver), + m_proxies(m), + m_num_proxies(0), + m_base_defs(*this), + m_assumptions(m), + m_first_assumption(0), + m_is_proxied(false), + m_elim_proxies_sub(m, false, true), + m_split_literals(split_literals), + m_iuc(iuc), + m_iuc_arith(iuc_arith), + m_print_farkas_stats(print_farkas_stats), + m_old_hyp_reducer(old_hyp_reducer) + {} + + ~iuc_solver() override {} + + /* iuc solver specific */ + virtual void get_iuc(expr_ref_vector &core); + void set_split_literals(bool v) { m_split_literals = v; } + bool mk_proxies(expr_ref_vector &v, unsigned from = 0); + void undo_proxies(expr_ref_vector &v); + + void push_bg(expr *e); + void pop_bg(unsigned n); + unsigned get_num_bg(); + + void get_full_unsat_core(ptr_vector &core) { + expr_ref_vector _core(m); + m_solver.get_unsat_core(_core); + core.append(_core.size(), _core.c_ptr()); + } + + /* solver interface */ + + solver* translate(ast_manager &m, params_ref const &p) override { + return m_solver.translate(m, p); + } + void updt_params(params_ref const &p) override { m_solver.updt_params(p); } + void reset_params(params_ref const &p) override { m_solver.reset_params(p); } + const params_ref &get_params() const override { return m_solver.get_params(); } + void push_params() override { m_solver.push_params(); } + void pop_params() override { m_solver.pop_params(); } + void collect_param_descrs(param_descrs &r) override { m_solver.collect_param_descrs(r); } + void set_produce_models(bool f) override { m_solver.set_produce_models(f); } + void assert_expr_core(expr *t) override { m_solver.assert_expr(t); } + void assert_expr_core2(expr *t, expr *a) override { NOT_IMPLEMENTED_YET(); } + expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } + + void push() override; + void pop(unsigned n) override; + unsigned get_scope_level() const override { return m_solver.get_scope_level(); } + + lbool check_sat(unsigned num_assumptions, expr * const *assumptions) override; + lbool check_sat_cc(const expr_ref_vector &cube, vector const & clauses) override; + void set_progress_callback(progress_callback *callback) override { + m_solver.set_progress_callback(callback); + } + unsigned get_num_assertions() const override { return m_solver.get_num_assertions(); } + expr * get_assertion(unsigned idx) const override { return m_solver.get_assertion(idx); } + unsigned get_num_assumptions() const override { return m_solver.get_num_assumptions(); } + expr * get_assumption(unsigned idx) const override { return m_solver.get_assumption(idx); } + std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const override { + return m_solver.display(out, n, es); + } + + /* check_sat_result interface */ + + void collect_statistics(statistics &st) const override ; + virtual void reset_statistics(); + + void get_unsat_core(expr_ref_vector &r) override; + void get_model_core(model_ref &m) override {m_solver.get_model(m);} + proof *get_proof() override {return m_solver.get_proof();} + std::string reason_unknown() const override { return m_solver.reason_unknown(); } + void set_reason_unknown(char const* msg) override { m_solver.set_reason_unknown(msg); } + void get_labels(svector &r) override { m_solver.get_labels(r); } + ast_manager& get_manager() const override { return m; } + + virtual void refresh(); + + class scoped_mk_proxy { + iuc_solver &m_s; + expr_ref_vector &m_v; + public: + scoped_mk_proxy(iuc_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) { + m_s.mk_proxies(m_v); + } + ~scoped_mk_proxy() { m_s.undo_proxies(m_v); } + }; + + class scoped_bg { + iuc_solver &m_s; + unsigned m_bg_sz; + public: + scoped_bg(iuc_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {} + ~scoped_bg() { + if (m_s.get_num_bg() > m_bg_sz) { + m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); + } + } + }; +}; +} +#endif diff --git a/src/muz/spacer/spacer_json.cpp b/src/muz/spacer/spacer_json.cpp new file mode 100644 index 000000000..a99a8f298 --- /dev/null +++ b/src/muz/spacer/spacer_json.cpp @@ -0,0 +1,191 @@ +/**++ +Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti + +Module Name: + + spacer_json.cpp + +Abstract: + + SPACER json marshalling support + +Author: + + Matteo Marescotti + +Notes: + +--*/ + +#include +#include "spacer_context.h" +#include "spacer_json.h" +#include "spacer_util.h" + +namespace spacer { + +static std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m) { + + mk_epp pp = mk_epp(t, m); + std::ostringstream ss; + ss << pp; + out << "\""; + for (auto &c:ss.str()) { + switch (c) { + case '"': + out << "\\\""; + break; + case '\\': + out << "\\\\"; + break; + case '\b': + out << "\\b"; + break; + case '\f': + out << "\\f"; + break; + case '\n': + out << "\\n"; + break; + case '\r': + out << "\\r"; + break; + case '\t': + out << "\\t"; + break; + default: + if ('\x00' <= c && c <= '\x1f') { + out << "\\u" + << std::hex << std::setw(4) << std::setfill('0') << (int) c; + } else { + out << c; + } + } + } + out << "\""; + return out; +} + +static std::ostream &json_marshal(std::ostream &out, lemma *l) { + out << "{" + << R"("init_level":")" << l->init_level() + << R"(", "level":")" << l->level() + << R"(", "expr":)"; + json_marshal(out, l->get_expr(), l->get_ast_manager()); + out << "}"; + return out; +} + +static std::ostream &json_marshal(std::ostream &out, const lemma_ref_vector &lemmas) { + + std::ostringstream ls; + for (auto l:lemmas) { + ls << ((unsigned)ls.tellp() == 0 ? "" : ","); + json_marshal(ls, l); + } + out << "[" << ls.str() << "]"; + return out; + } + + +void json_marshaller::register_lemma(lemma *l) { + if (l->has_pob()) { + m_relations[&*l->get_pob()][l->get_pob()->depth()].push_back(l); + } +} + +void json_marshaller::register_pob(pob *p) { + m_relations[p]; +} + +void json_marshaller::marshal_lemmas_old(std::ostream &out) const { + unsigned pob_id = 0; + for (auto &pob_map:m_relations) { + std::ostringstream pob_lemmas; + for (auto &depth_lemmas : pob_map.second) { + pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",") + << "\"" << depth_lemmas.first << "\":"; + json_marshal(pob_lemmas, depth_lemmas.second); + } + if (pob_lemmas.tellp()) { + out << ((unsigned)out.tellp() == 0 ? "" : ",\n"); + out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; + } + pob_id++; + } +} +void json_marshaller::marshal_lemmas_new(std::ostream &out) const { + unsigned pob_id = 0; + for (auto &pob_map:m_relations) { + std::ostringstream pob_lemmas; + pob *n = pob_map.first; + unsigned i = 0; + for (auto *l : n->lemmas()) { + pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",") + << "\"" << i++ << "\":"; + lemma_ref_vector lemmas_vec; + lemmas_vec.push_back(l); + json_marshal(pob_lemmas, lemmas_vec); + } + + if (pob_lemmas.tellp()) { + out << ((unsigned)out.tellp() == 0 ? "" : ",\n"); + out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}"; + } + pob_id++; + } +} + +std::ostream &json_marshaller::marshal(std::ostream &out) const { + std::ostringstream nodes; + std::ostringstream edges; + std::ostringstream lemmas; + + if (m_old_style) + marshal_lemmas_old(lemmas); + else + marshal_lemmas_new(lemmas); + + unsigned pob_id = 0; + unsigned depth = 0; + while (true) { + double root_expand_time = m_ctx->get_root().get_expand_time(depth); + bool a = false; + pob_id = 0; + for (auto &pob_map:m_relations) { + pob *n = pob_map.first; + double expand_time = n->get_expand_time(depth); + if (expand_time > 0) { + a = true; + std::ostringstream pob_expr; + json_marshal(pob_expr, n->post(), n->get_ast_manager()); + + nodes << ((unsigned)nodes.tellp() == 0 ? "" : ",\n") << + "{\"id\":\"" << depth << n << + "\",\"relative_time\":\"" << expand_time / root_expand_time << + "\",\"absolute_time\":\"" << std::setprecision(2) << expand_time << + "\",\"predicate\":\"" << n->pt().head()->get_name() << + "\",\"expr_id\":\"" << n->post()->get_id() << + "\",\"pob_id\":\"" << pob_id << + "\",\"depth\":\"" << depth << + "\",\"expr\":" << pob_expr.str() << "}"; + if (n->parent()) { + edges << ((unsigned)edges.tellp() == 0 ? "" : ",\n") << + "{\"from\":\"" << depth << n->parent() << + "\",\"to\":\"" << depth << n << "\"}"; + } + } + pob_id++; + } + if (!a) { + break; + } + depth++; + } + out << "{\n\"nodes\":[\n" << nodes.str() << "\n],\n"; + out << "\"edges\":[\n" << edges.str() << "\n],\n"; + out << "\"lemmas\":{\n" << lemmas.str() << "\n}\n}\n"; + return out; +} + +} diff --git a/src/muz/spacer/spacer_json.h b/src/muz/spacer/spacer_json.h new file mode 100644 index 000000000..131e72678 --- /dev/null +++ b/src/muz/spacer/spacer_json.h @@ -0,0 +1,61 @@ +/**++ +Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti + +Module Name: + + spacer_json.h + +Abstract: + + SPACER json marshalling support + +Author: + + Matteo Marescotti + +Notes: + +--*/ + +#ifndef Z3_SPACER_JSON_H +#define Z3_SPACER_JSON_H + +#include +#include +#include "util/ref.h" +#include "util/ref_vector.h" + +class ast; + +class ast_manager; + +namespace spacer { + +class lemma; +typedef sref_vector lemma_ref_vector; +class context; +class pob; + + +class json_marshaller { + context *m_ctx; + bool m_old_style; + std::map> m_relations; + + void marshal_lemmas_old(std::ostream &out) const; + void marshal_lemmas_new(std::ostream &out) const; +public: + json_marshaller(context *ctx, bool old_style = false) : + m_ctx(ctx), m_old_style(old_style) {} + + void register_lemma(lemma *l); + + void register_pob(pob *p); + + std::ostream &marshal(std::ostream &out) const; +}; + +} + + +#endif //Z3_SPACER_JSON_H diff --git a/src/muz/spacer/spacer_legacy_frames.cpp b/src/muz/spacer/spacer_legacy_frames.cpp index 679736add..a21df5038 100644 --- a/src/muz/spacer/spacer_legacy_frames.cpp +++ b/src/muz/spacer/spacer_legacy_frames.cpp @@ -34,7 +34,7 @@ #include "util/luby.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" namespace spacer { @@ -46,11 +46,8 @@ void pred_transformer::legacy_frames::simplify_formulas(tactic& tac, ast_manager &m = m_pt.get_ast_manager(); goal_ref g(alloc(goal, m, false, false, false)); for (unsigned j = 0; j < v.size(); ++j) { g->assert_expr(v[j].get()); } - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); goal_ref_buffer result; - tac(g, result, mc, pc, core); + tac(g, result); SASSERT(result.size() == 1); goal* r = result[0]; v.reset(); diff --git a/src/muz/spacer/spacer_legacy_mbp.cpp b/src/muz/spacer/spacer_legacy_mbp.cpp index 9f03e6d2f..76b543f04 100644 --- a/src/muz/spacer/spacer_legacy_mbp.cpp +++ b/src/muz/spacer/spacer_legacy_mbp.cpp @@ -71,10 +71,11 @@ void qe_project(ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& expr_substitution sub(m); proof_ref pr(m.mk_asserted(m.mk_true()), m); expr_ref bval(m); + model::scoped_model_completion _scm(*M, true); for (unsigned i = 0; i < vars.size(); i++) { if (m.is_bool(vars.get(i))) { // obtain the interpretation of the ith var using model completion - VERIFY(M->eval(vars.get(i), bval, true)); + bval = (*M)(vars.get(i)); sub.insert(vars.get(i), bval, pr); } else { arith_vars.push_back(vars.get(i)); @@ -99,14 +100,14 @@ void qe_project(ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& ); { scoped_no_proof _sp(m); - qe::arith_project(*M, arith_vars, fml, map); + spacer_qe::arith_project(*M, arith_vars, fml, map); } SASSERT(arith_vars.empty()); TRACE("spacer", tout << "Projected arith vars:\n" << mk_pp(fml, m) << "\n"; ); } - SASSERT(M->eval(fml, bval, true) && m.is_true(bval)); // M |= fml + SASSERT(M->is_true(fml)); vars.reset(); vars.append(arith_vars); } diff --git a/src/muz/spacer/spacer_legacy_mev.cpp b/src/muz/spacer/spacer_legacy_mev.cpp index 16e2cc734..b62b24b0d 100644 --- a/src/muz/spacer/spacer_legacy_mev.cpp +++ b/src/muz/spacer/spacer_legacy_mev.cpp @@ -80,7 +80,7 @@ void model_evaluator::reset() m_visited.reset(); m_numbers.reset(); m_refs.reset(); - m_model = 0; + m_model = nullptr; } @@ -138,7 +138,6 @@ void model_evaluator::process_formula(app* e, ptr_vector& todo, ptr_vector case OP_FALSE: break; case OP_EQ: - case OP_IFF: if (args[0] == args[1]) { SASSERT(v); // no-op @@ -462,8 +461,8 @@ void model_evaluator::eval_array_eq(app* e, expr* arg1, expr* arg2) { TRACE("old_spacer", tout << "array equality: " << mk_pp(e, m) << "\n";); expr_ref v1(m), v2(m); - m_model->eval(arg1, v1); - m_model->eval(arg2, v2); + v1 = (*m_model)(arg1); + v2 = (*m_model)(arg2); if (v1 == v2) { set_true(e); return; @@ -511,8 +510,8 @@ void model_evaluator::eval_array_eq(app* e, expr* arg1, expr* arg2) args2.append(store[i].size() - 1, store[i].c_ptr()); s1 = m_array.mk_select(args1.size(), args1.c_ptr()); s2 = m_array.mk_select(args2.size(), args2.c_ptr()); - m_model->eval(s1, w1); - m_model->eval(s2, w2); + w1 = (*m_model)(s1); + w2 = (*m_model)(s2); if (w1 == w2) { continue; } @@ -634,10 +633,6 @@ void model_evaluator::eval_basic(app* e) set_x(e); } break; - case OP_IFF: - VERIFY(m.is_iff(e, arg1, arg2)); - eval_eq(e, arg1, arg2); - break; case OP_XOR: VERIFY(m.is_xor(e, arg1, arg2)); eval_eq(e, arg1, arg2); @@ -734,7 +729,7 @@ void model_evaluator::eval_fmls(ptr_vector const& formulas) eval_basic(curr); } else { expr_ref vl(m); - m_model->eval(curr, vl); + vl = eval(m_model, curr); assign_value(curr, vl); } @@ -803,11 +798,10 @@ expr_ref model_evaluator::eval(const model_ref& model, func_decl* d) return result; } -expr_ref model_evaluator::eval(const model_ref& model, expr* e) -{ - expr_ref result(m); +expr_ref model_evaluator::eval(const model_ref& model, expr* e){ m_model = model.get(); - VERIFY(m_model->eval(e, result, true)); + model::scoped_model_completion _scm(m_model, true); + expr_ref result = (*m_model)(e); if (m_array.is_array(e)) { vector stores; expr_ref_vector args(m); diff --git a/src/muz/spacer/spacer_manager.cpp b/src/muz/spacer/spacer_manager.cpp index 4ad3e0d7f..856207463 100644 --- a/src/muz/spacer/spacer_manager.cpp +++ b/src/muz/spacer/spacer_manager.cpp @@ -30,6 +30,7 @@ Revision History: #include "model/model_smt2_pp.h" #include "tactic/model_converter.h" +#include "smt/smt_solver.h" namespace spacer { class collect_decls_proc { @@ -113,7 +114,7 @@ void inductive_property::to_model(model_ref& md) const } } TRACE("spacer", model_smt2_pp(tout, m, *md, 0);); - apply(const_cast(m_mc), md, 0); + apply(const_cast(m_mc), md); } expr_ref inductive_property::to_expr() const @@ -168,167 +169,30 @@ void inductive_property::display(datalog::rule_manager& rm, ptr_vector manager::get_state_suffixes() -{ - std::vector res; - res.push_back("_n"); - return res; -} - -manager::manager(unsigned max_num_contexts, ast_manager& manager) : - m(manager), - m_brwr(m), - m_mux(m, get_state_suffixes()), - m_background(m.mk_true(), m), - m_contexts(m, max_num_contexts), - m_contexts2(m, max_num_contexts), - m_contexts3(m, max_num_contexts), - m_next_unique_num(0) -{ -} -void manager::add_new_state(func_decl * s) -{ - SASSERT(s->get_arity() == 0); //we currently don't support non-constant states - decl_vector vect; +manager::manager(ast_manager& manager) : m(manager), m_mux(m) {} - SASSERT(o_index(0) == 1); //we assume this in the number of retrieved symbols - m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect); - m_o0_preds.push_back(vect[o_index(0)]); -} - -func_decl * manager::get_o_pred(func_decl* s, unsigned idx) -{ - func_decl * res = m_mux.try_get_by_prefix(s, o_index(idx)); - if (res) { return res; } - add_new_state(s); - res = m_mux.try_get_by_prefix(s, o_index(idx)); +func_decl * manager::get_o_pred(func_decl* s, unsigned idx) { + func_decl * res = m_mux.find_by_decl(s, o_index(idx)); + if (!res) { + m_mux.register_decl(s); + res = m_mux.find_by_decl(s, o_index(idx)); + } SASSERT(res); return res; } -func_decl * manager::get_n_pred(func_decl* s) -{ - func_decl * res = m_mux.try_get_by_prefix(s, n_index()); - if (res) { return res; } - add_new_state(s); - res = m_mux.try_get_by_prefix(s, n_index()); +func_decl * manager::get_n_pred(func_decl* s) { + func_decl * res = m_mux.find_by_decl(s, n_index()); + if (!res) { + m_mux.register_decl(s); + res = m_mux.find_by_decl(s, n_index()); + } SASSERT(res); return res; } -void manager::mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res) -{ - m_brwr.mk_and(mdl.size(), mdl.c_ptr(), res); -} - -void manager::mk_core_into_cube(const expr_ref_vector & core, expr_ref & res) -{ - m_brwr.mk_and(core.size(), core.c_ptr(), res); -} - -void manager::mk_cube_into_lemma(expr * cube, expr_ref & res) -{ - m_brwr.mk_not(cube, res); -} - -void manager::mk_lemma_into_cube(expr * lemma, expr_ref & res) -{ - m_brwr.mk_not(lemma, res); -} - -expr_ref manager::mk_and(unsigned sz, expr* const* exprs) -{ - expr_ref result(m); - m_brwr.mk_and(sz, exprs, result); - return result; -} - -expr_ref manager::mk_or(unsigned sz, expr* const* exprs) -{ - expr_ref result(m); - m_brwr.mk_or(sz, exprs, result); - return result; -} - -expr_ref manager::mk_not_and(expr_ref_vector const& conjs) -{ - expr_ref result(m), e(m); - expr_ref_vector es(conjs); - flatten_and(es); - for (unsigned i = 0; i < es.size(); ++i) { - m_brwr.mk_not(es[i].get(), e); - es[i] = e; - } - m_brwr.mk_or(es.size(), es.c_ptr(), result); - return result; -} - -void manager::get_or(expr* e, expr_ref_vector& result) -{ - result.push_back(e); - for (unsigned i = 0; i < result.size();) { - e = result[i].get(); - if (m.is_or(e)) { - result.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - result[i] = result.back(); - result.pop_back(); - } else { - ++i; - } - } -} - -bool manager::try_get_state_and_value_from_atom(expr * atom0, app *& state, app_ref& value) -{ - if (!is_app(atom0)) { - return false; - } - app * atom = to_app(atom0); - expr * arg1; - expr * arg2; - app * candidate_state; - app_ref candidate_value(m); - if (m.is_not(atom, arg1)) { - if (!is_app(arg1)) { - return false; - } - candidate_state = to_app(arg1); - candidate_value = m.mk_false(); - } else if (m.is_eq(atom, arg1, arg2)) { - if (!is_app(arg1) || !is_app(arg2)) { - return false; - } - if (!m_mux.is_muxed(to_app(arg1)->get_decl())) { - std::swap(arg1, arg2); - } - candidate_state = to_app(arg1); - candidate_value = to_app(arg2); - } else { - candidate_state = atom; - candidate_value = m.mk_true(); - } - if (!m_mux.is_muxed(candidate_state->get_decl())) { - return false; - } - state = candidate_state; - value = candidate_value; - return true; -} - -bool manager::try_get_state_decl_from_atom(expr * atom, func_decl *& state) -{ - app_ref dummy_value_holder(m); - app * s; - if (try_get_state_and_value_from_atom(atom, s, dummy_value_holder)) { - state = s->get_decl(); - return true; - } else { - return false; - } -} - /** * Create a new skolem constant */ @@ -340,22 +204,27 @@ app* mk_zk_const(ast_manager &m, unsigned idx, sort *s) { namespace find_zk_const_ns { struct proc { + int m_max; app_ref_vector &m_out; - proc (app_ref_vector &out) : m_out(out) {} + proc (app_ref_vector &out) : m_max(-1), m_out(out) {} void operator() (var const * n) const {} - void operator() (app *n) const { - if (is_uninterp_const(n) && - n->get_decl()->get_name().str().compare (0, 3, "sk!") == 0) { - m_out.push_back (n); + void operator() (app *n) { + int idx; + if (is_zk_const(n, idx)) { + m_out.push_back(n); + if (idx > m_max) { + m_max = idx; + } } } void operator() (quantifier const *n) const {} }; } -void find_zk_const(expr *e, app_ref_vector &res) { +int find_zk_const(expr *e, app_ref_vector &res) { find_zk_const_ns::proc p(res); for_each_expr (p, e); + return p.m_max; } namespace has_zk_const_ns { @@ -363,8 +232,8 @@ struct found {}; struct proc { void operator() (var const *n) const {} void operator() (app const *n) const { - if (is_uninterp_const(n) && - n->get_decl()->get_name().str().compare(0, 3, "sk!") == 0) { + int idx; + if (is_zk_const(n, idx)) { throw found(); } } @@ -384,4 +253,26 @@ bool has_zk_const(expr *e){ return false; } +bool is_zk_const (const app *a, int &n) { + if (!is_uninterp_const(a)) return false; + + const symbol &name = a->get_decl()->get_name(); + if (name.str().compare (0, 3, "sk!") != 0) { + return false; + } + + n = std::stoi(name.str().substr(3)); + return true; +} +bool sk_lt_proc::operator()(const app *a1, const app *a2) { + if (a1 == a2) return false; + int n1, n2; + bool z1, z2; + z1 = is_zk_const(a1, n1); + z2 = is_zk_const(a2, n2); + if (z1 && z2) return n1 < n2; + if (z1 != z2) return z1; + return ast_lt_proc()(a1, a2); +} + } diff --git a/src/muz/spacer/spacer_manager.h b/src/muz/spacer/spacer_manager.h index f2382c15d..d592a30a8 100644 --- a/src/muz/spacer/spacer_manager.h +++ b/src/muz/spacer/spacer_manager.h @@ -13,6 +13,7 @@ Abstract: Author: Krystof Hoder (t-khoder) 2011-8-25. + Arie Gurfinkel Revision History: @@ -34,12 +35,10 @@ Revision History: #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_sym_mux.h" #include "muz/spacer/spacer_farkas_learner.h" -#include "muz/spacer/spacer_smt_context_manager.h" #include "muz/base/dl_rule.h" - -namespace smt { -class context; -} +#include "solver/solver.h" +#include "solver/solver_pool.h" +namespace smt {class context;} namespace spacer { @@ -67,280 +66,74 @@ public: m_relation_info(relations) {} std::string to_string() const; - expr_ref to_expr() const; - void to_model(model_ref& md) const; - - void display(datalog::rule_manager& rm, ptr_vector const& rules, std::ostream& out) const; + void display(datalog::rule_manager& rm, + ptr_vector const& rules, + std::ostream& out) const; }; class manager { ast_manager& m; - mutable bool_rewriter m_brwr; - + // manager of multiplexed names sym_mux m_mux; - expr_ref m_background; - decl_vector m_o0_preds; - spacer::smt_context_manager m_contexts; - spacer::smt_context_manager m_contexts2; - spacer::smt_context_manager m_contexts3; - /** whenever we need an unique number, we get this one and increase */ - unsigned m_next_unique_num; - - - static std::vector get_state_suffixes(); unsigned n_index() const { return 0; } unsigned o_index(unsigned i) const { return i + 1; } - void add_new_state(func_decl * s); - public: - manager(unsigned max_num_contexts, ast_manager & manager); + manager(ast_manager & manager); ast_manager& get_manager() const { return m; } - bool_rewriter& get_brwr() const { return m_brwr; } - expr_ref mk_and(unsigned sz, expr* const* exprs); - expr_ref mk_and(expr_ref_vector const& exprs) - { - return mk_and(exprs.size(), exprs.c_ptr()); - } - expr_ref mk_and(expr* a, expr* b) - { - expr* args[2] = { a, b }; - return mk_and(2, args); - } - expr_ref mk_or(unsigned sz, expr* const* exprs); - expr_ref mk_or(expr_ref_vector const& exprs) - { - return mk_or(exprs.size(), exprs.c_ptr()); - } - - expr_ref mk_not_and(expr_ref_vector const& exprs); - - void get_or(expr* e, expr_ref_vector& result); + // management of mux names //"o" predicates stand for the old states and "n" for the new states func_decl * get_o_pred(func_decl * s, unsigned idx); func_decl * get_n_pred(func_decl * s); - /** - Marks symbol as non-model which means it will not appear in models collected by - get_state_cube_from_model function. - This is to take care of auxiliary symbols introduced by the disjunction relations - to relativize lemmas coming from disjuncts. - */ - void mark_as_non_model(func_decl * p) - { - m_mux.mark_as_non_model(p); - } - - - func_decl * const * begin_o0_preds() const { return m_o0_preds.begin(); } - func_decl * const * end_o0_preds() const { return m_o0_preds.end(); } - - bool is_state_pred(func_decl * p) const { return m_mux.is_muxed(p); } - func_decl * to_o0(func_decl * p) { return m_mux.conv(m_mux.get_primary(p), 0, o_index(0)); } - - bool is_o(func_decl * p, unsigned idx) const - { - return m_mux.has_index(p, o_index(idx)); - } - void get_o_index(func_decl* p, unsigned& idx) const - { - m_mux.try_get_index(p, idx); - SASSERT(idx != n_index()); - idx--; // m_mux has indices starting at 1 - } - bool is_o(expr* e, unsigned idx) const - { - return is_app(e) && is_o(to_app(e)->get_decl(), idx); - } - bool is_o(func_decl * p) const - { - unsigned idx; - return m_mux.try_get_index(p, idx) && idx != n_index(); - } - bool is_o(expr* e) const - { - return is_app(e) && is_o(to_app(e)->get_decl()); - } - bool is_n(func_decl * p) const - { - return m_mux.has_index(p, n_index()); - } - bool is_n(expr* e) const - { - return is_app(e) && is_n(to_app(e)->get_decl()); - } - - /** true if p should not appead in models propagates into child relations */ - bool is_non_model_sym(func_decl * p) const - { return m_mux.is_non_model_sym(p); } - - - /** true if f doesn't contain any n predicates */ - bool is_o_formula(expr * f) const - { - return !m_mux.contains(f, n_index()); - } - - /** true if f contains only o state preds of index o_idx */ - bool is_o_formula(expr * f, unsigned o_idx) const - { - return m_mux.is_homogenous_formula(f, o_index(o_idx)); - } - /** true if f doesn't contain any o predicates */ bool is_n_formula(expr * f) const - { - return m_mux.is_homogenous_formula(f, n_index()); - } + {return m_mux.is_homogenous_formula(f, n_index());} func_decl * o2n(func_decl * p, unsigned o_idx) const - { - return m_mux.conv(p, o_index(o_idx), n_index()); - } + {return m_mux.shift_decl(p, o_index(o_idx), n_index());} func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) const - { - return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx)); - } + {return m_mux.shift_decl(p, o_index(src_idx), o_index(tgt_idx));} func_decl * n2o(func_decl * p, unsigned o_idx) const - { - return m_mux.conv(p, n_index(), o_index(o_idx)); - } + {return m_mux.shift_decl(p, n_index(), o_index(o_idx));} - void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const - { m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous); } + void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, + bool homogenous = true) const + {m_mux.shift_expr(f, o_index(o_idx), n_index(), result, homogenous);} - void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous = true) const - { m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); } + void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, + bool homogenous = true) const + {m_mux.shift_expr(f, n_index(), o_index(o_idx), result, homogenous);} void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result) const - { m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous); } + {m_mux.shift_expr(result.get(), n_index(), o_index(o_idx), + result, homogenous);} - void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous = true) const - { m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous); } + void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, + unsigned tgt_idx, bool homogenous = true) const + {m_mux.shift_expr(src, o_index(src_idx), o_index(tgt_idx), + tgt, homogenous);} - /** - Return true if all state symbols which e contains are of one kind (either "n" or one of "o"). - */ - bool is_homogenous_formula(expr * e) const - { - return m_mux.is_homogenous_formula(e); - } - - /** - Collect indices used in expression. - */ - void collect_indices(expr* e, unsigned_vector& indices) const - { - m_mux.collect_indices(e, indices); - } - - /** - Collect used variables of each index. - */ - void collect_variables(expr* e, vector >& vars) const - { - m_mux.collect_variables(e, vars); - } - - /** - Return true iff both s1 and s2 are either "n" or "o" of the same index. - If one (or both) of them are not state symbol, return false. - */ - bool have_different_state_kinds(func_decl * s1, func_decl * s2) const - { - unsigned i1, i2; - return m_mux.try_get_index(s1, i1) && m_mux.try_get_index(s2, i2) && i1 != i2; - } - - /** - Increase indexes of state symbols in formula by dist. - The 'N' index becomes 'O' index with number dist-1. - */ - void formula_shift(expr * src, expr_ref & tgt, unsigned dist) const - { - SASSERT(n_index() == 0); - SASSERT(o_index(0) == 1); - m_mux.shift_formula(src, dist, tgt); - } - - void mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res); - void mk_core_into_cube(const expr_ref_vector & core, expr_ref & res); - void mk_cube_into_lemma(expr * cube, expr_ref & res); - void mk_lemma_into_cube(expr * lemma, expr_ref & res); - - /** - Remove from vec all atoms that do not have an "o" state. - The order of elements in vec may change. - An assumption is that atoms having "o" state of given index - do not have "o" states of other indexes or "n" states. - */ - void filter_o_atoms(expr_ref_vector& vec, unsigned o_idx) const - { m_mux.filter_idx(vec, o_index(o_idx)); } - void filter_n_atoms(expr_ref_vector& vec) const - { m_mux.filter_idx(vec, n_index()); } - - /** - Partition literals into o_lits and others. - */ - void partition_o_atoms(expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, - unsigned o_idx) const - { - m_mux.partition_o_idx(lits, o_lits, other, o_index(o_idx)); - } - - void filter_out_non_model_atoms(expr_ref_vector& vec) const - { m_mux.filter_non_model_lits(vec); } - - bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value); - bool try_get_state_decl_from_atom(expr * atom, func_decl *& state); - - - std::string pp_model(const model_core & mdl) const - { return m_mux.pp_model(mdl); } - - - void set_background(expr* b) { m_background = b; } - - expr* get_background() const { return m_background; } - - unsigned get_unique_num() { return m_next_unique_num++; } - - solver* mk_fresh() {return m_contexts.mk_fresh();} - smt_params& fparams() { return m_contexts.fparams(); } - solver* mk_fresh2() {return m_contexts2.mk_fresh();} - smt_params &fparams2() { return m_contexts2.fparams(); } - solver* mk_fresh3() {return m_contexts3.mk_fresh();} - smt_params &fparams3() {return m_contexts3.fparams();} - - - - void collect_statistics(statistics& st) const - { - m_contexts.collect_statistics(st); - m_contexts2.collect_statistics(st); - m_contexts3.collect_statistics(st); - } - - void reset_statistics() - { - m_contexts.reset_statistics(); - m_contexts2.reset_statistics(); - m_contexts3.reset_statistics(); - } }; +/** Skolem constants for quantified spacer */ app* mk_zk_const (ast_manager &m, unsigned idx, sort *s); -void find_zk_const(expr* e, app_ref_vector &out); +int find_zk_const(expr* e, app_ref_vector &out); +inline int find_zk_const(expr_ref_vector const &v, app_ref_vector &out) +{return find_zk_const (mk_and(v), out);} + bool has_zk_const(expr* e); +bool is_zk_const (const app *a, int &n); + +struct sk_lt_proc {bool operator()(const app* a1, const app* a2);}; + } #endif diff --git a/src/muz/spacer/spacer_matrix.cpp b/src/muz/spacer/spacer_matrix.cpp index 3cc83996c..ea9b3cb32 100644 --- a/src/muz/spacer/spacer_matrix.cpp +++ b/src/muz/spacer/spacer_matrix.cpp @@ -42,7 +42,7 @@ namespace spacer return m_num_cols; } - rational spacer_matrix::get(unsigned int i, unsigned int j) + const rational& spacer_matrix::get(unsigned int i, unsigned int j) { SASSERT(i < m_num_rows); SASSERT(j < m_num_cols); @@ -50,7 +50,7 @@ namespace spacer return m_matrix[i][j]; } - void spacer_matrix::set(unsigned int i, unsigned int j, rational v) + void spacer_matrix::set(unsigned int i, unsigned int j, const rational& v) { SASSERT(i < m_num_rows); SASSERT(j < m_num_cols); diff --git a/src/muz/spacer/spacer_matrix.h b/src/muz/spacer/spacer_matrix.h index 4fc418f2b..a93a749ae 100644 --- a/src/muz/spacer/spacer_matrix.h +++ b/src/muz/spacer/spacer_matrix.h @@ -25,13 +25,13 @@ namespace spacer { class spacer_matrix { public: - spacer_matrix(unsigned m, unsigned n); // m rows, n colums + spacer_matrix(unsigned m, unsigned n); // m rows, n columns unsigned num_rows(); unsigned num_cols(); - rational get(unsigned i, unsigned j); - void set(unsigned i, unsigned j, rational v); + const rational& get(unsigned i, unsigned j); + void set(unsigned i, unsigned j, const rational& v); unsigned perform_gaussian_elimination(); diff --git a/src/muz/spacer/spacer_mbc.cpp b/src/muz/spacer/spacer_mbc.cpp new file mode 100644 index 000000000..df0ce1cb4 --- /dev/null +++ b/src/muz/spacer/spacer_mbc.cpp @@ -0,0 +1,102 @@ +#include + +#include "muz/spacer/spacer_mbc.h" +#include "ast/rewriter/rewriter.h" +#include "ast/rewriter/rewriter_def.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/scoped_proof.h" +#include "model/model_evaluator.h" + + +namespace spacer { + +mbc::mbc(ast_manager &m) : m(m) {} + +namespace { +class mbc_rewriter_cfg : public default_rewriter_cfg { + + ast_manager &m; + const mbc::partition_map &m_pmap; + obj_map &m_subs; + model &m_mdl; + model_evaluator m_mev; + vector &m_parts; + unsigned m_current_part; + +public: + mbc_rewriter_cfg(ast_manager &m, const mbc::partition_map &pmap, + obj_map &subs, + model &mdl, vector &parts) : + m(m), m_pmap(pmap), m_subs(subs), m_mdl(mdl), m_mev(m_mdl), + m_parts(parts), m_current_part(UINT_MAX) + {m_mev.set_model_completion(true);} + + bool get_subst(expr *s, expr * & t, proof * & t_pr) { + if (!is_app(s)) return false; + unsigned part = UINT_MAX; + + // not in partition map + if (!m_pmap.find (to_app(s)->get_decl(), part)) return false; + + // first part element, remember it + if (!found_partition()) { + set_partition(part); + return false; + } + + // already in our substitution map + expr *tmp = nullptr; + if (m_subs.find(s, tmp)) { + t = tmp; + return true; + } + + // decide value based on model + expr_ref val(m); + + // eval in the model + m_mev.eval(s, val, true); + + // store decided equality (also keeps ref to s and val) + m_parts[part].push_back(m.mk_eq(s, val)); + // store substitution + m_subs.insert(s, val); + t = val; + return true; + } + + + void reset() {reset_partition();}; + void reset_partition() {m_current_part = UINT_MAX;} + unsigned partition() {return m_current_part;} + bool found_partition() {return m_current_part < UINT_MAX;} + void set_partition(unsigned v) {m_current_part = v;} +}; +} + +void mbc::operator()(const partition_map &pmap, expr_ref_vector &lits, + model &mdl, vector &res) { + scoped_no_proof _sp (m); + + obj_map subs; + mbc_rewriter_cfg cfg(m, pmap, subs, mdl, res); + rewriter_tpl rw(m, false, cfg); + th_rewriter thrw(m); + + for (auto *lit : lits) { + expr_ref new_lit(m); + rw.reset(); + rw(lit, new_lit); + thrw(new_lit); + if (cfg.found_partition()) { + SASSERT(cfg.partition() < res.size()); + res[cfg.partition()].push_back(new_lit); + } + } + + TRACE("mbc", tout << "Input: " << lits << "\n" + << "Output: \n"; + for (auto &vec : res) tout << vec << "\n==================\n";); +} + +} diff --git a/src/muz/spacer/spacer_mbc.h b/src/muz/spacer/spacer_mbc.h new file mode 100644 index 000000000..5dbf50f6d --- /dev/null +++ b/src/muz/spacer/spacer_mbc.h @@ -0,0 +1,45 @@ +/*++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + spacer_mbc.h + +Abstract: + + Model-Based Cartesian Decomposition + +Author: + + Arie Gurfinkel + +Revision History: + +--*/ + +#ifndef _SPACER_MBC_H_ +#define _SPACER_MBC_H_ + +#include "ast/ast.h" +#include "util/obj_hashtable.h" +#include "model/model.h" + +namespace spacer { + +class mbc { + ast_manager &m; +public: + mbc(ast_manager &m); + + + typedef obj_map partition_map; + + /** + \Brief Model Based Cartesian projection of lits + */ + void operator()(const partition_map &pmap, expr_ref_vector &lits, model &mdl, + vector &res); +}; + +} +#endif diff --git a/src/muz/spacer/spacer_pdr.cpp b/src/muz/spacer/spacer_pdr.cpp new file mode 100644 index 000000000..4e6b37a0a --- /dev/null +++ b/src/muz/spacer/spacer_pdr.cpp @@ -0,0 +1,375 @@ +/**++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + spacer_pdr.h + +Abstract: + + SPACER gPDR strategy implementation + +Author: + + Arie Gurfinkel + + Based on muz/pdr + +Notes: + +--*/ +#include "muz/spacer/spacer_pdr.h" +#include "muz/base/dl_context.h" +#include "muz/spacer/spacer_mbc.h" + +namespace spacer { +model_node::model_node(model_node* parent, class pob *pob): + m_pob(pob), m_parent(parent), m_next(nullptr), m_prev(nullptr), + m_orig_level(m_pob->level()), m_depth(0), + m_closed(false) { + SASSERT(m_pob); + if (m_parent) m_parent->add_child(this); +} + +void model_node::add_child(model_node* kid) { + m_children.push_back(kid); + SASSERT(level() == kid->level() + 1); + SASSERT(level() > 0); + kid->m_depth = m_depth + 1; + if (is_closed()) set_open(); +} + +unsigned model_node::index_in_parent() const { + if (!m_parent) return 0; + for (unsigned i = 0, sz = m_parent->children().size(); i < sz; ++i) { + if (this == m_parent->children().get(i)) return i; + } + UNREACHABLE(); + return 0; +} + +void model_node::check_pre_closed() { + for (auto *kid : m_children) {if (kid->is_open()) return;} + + set_pre_closed(); + model_node* p = m_parent; + while (p && p->is_1closed()) { + p->set_pre_closed(); + p = p->parent(); + } +} +void model_node::set_open() { + SASSERT(m_closed); + m_closed = false; + model_node* p = parent(); + while (p && p->is_closed()) { + p->m_closed = false; + p = p->parent(); + } +} + +void model_node::detach(model_node*& qhead) { + SASSERT(in_queue()); + SASSERT(children().empty()); + if (this == m_next) { + SASSERT(m_prev == this); + SASSERT(this == qhead); + qhead = nullptr; + } + else { + m_next->m_prev = m_prev; + m_prev->m_next = m_next; + if (this == qhead) qhead = m_next; + } + + // detach + m_prev = nullptr; + m_next = nullptr; +} + + +// insert node n after this in the queue +// requires: this is in a queue or this == n +void model_node::insert_after(model_node* n) { + SASSERT(this == n || in_queue()); + SASSERT(n); + SASSERT(!n->in_queue()); + if (this == n) { + m_next = n; + m_prev = n; + } + else { + n->m_next = m_next; + m_next->m_prev = n; + m_next = n; + n->m_prev = this; + } +} + +void model_search::reset() { + if (m_root) { + erase_children(*m_root, false); + remove_node(m_root, false); + dealloc(m_root); + m_root = nullptr; + } + m_cache.reset(); +} + +model_node* model_search::pop_front() { + model_node *res = m_qhead; + if (res) { + res->detach(m_qhead); + } + return res; +} + +void model_search::add_leaf(model_node* _n) { + model_node& n = *_n; + SASSERT(n.children().empty()); + model_nodes ns; + model_nodes& nodes = cache(n).insert_if_not_there2(n.post(), ns)->get_data().m_value; + if (nodes.contains(&n)) return; + + nodes.push_back(_n); + if (nodes.size() == 1) { + SASSERT(n.is_open()); + enqueue_leaf(n); + } + else { + n.set_pre_closed(); + } +} + +void model_search::enqueue_leaf(model_node& n) { + SASSERT(n.is_open()); + SASSERT(!n.in_queue()); + // queue is empty, initialize it with n + if (!m_qhead) { + m_qhead = &n; + m_qhead->insert_after(m_qhead); + } + // insert n after m_qhead + else if (m_bfs) { + m_qhead->insert_after(&n); + } + // insert n after m_qhead()->next() + else { + m_qhead->next()->insert_after(&n); + } +} + + + +void model_search::set_root(model_node* root) { + reset(); + m_root = root; + SASSERT(m_root); + SASSERT(m_root->children().empty()); + add_leaf(root); +} + +void model_search::backtrack_level(bool uses_level, model_node& n) { + SASSERT(m_root); + if (uses_level) {NOT_IMPLEMENTED_YET();} + if (uses_level && m_root->level() > n.level()) { + n.increase_level(); + enqueue_leaf(n); + } + else { + model_node* p = n.parent(); + if (p) { + erase_children(*p, true); + enqueue_leaf(*p); + } + } +} + +obj_map >& model_search::cache(model_node const& n) { + unsigned l = n.orig_level(); + if (l >= m_cache.size()) m_cache.resize(l + 1); + return m_cache[l]; +} + +void model_search::erase_children(model_node& n, bool backtrack) { + ptr_vector todo, nodes; + todo.append(n.children()); + // detach n from queue + if (n.in_queue()) n.detach(m_qhead); + n.reset_children(); + while (!todo.empty()) { + model_node* m = todo.back(); + todo.pop_back(); + nodes.push_back(m); + todo.append(m->children()); + remove_node(m, backtrack); + } + std::for_each(nodes.begin(), nodes.end(), delete_proc()); +} + +// removes node from the search tree and from the cache +void model_search::remove_node(model_node* _n, bool backtrack) { + model_node& n = *_n; + model_nodes& nodes = cache(n).find(n.post()); + nodes.erase(_n); + if (n.in_queue()) n.detach(m_qhead); + // TBD: siblings would also fail if n is not a goal. + if (!nodes.empty() && backtrack && + nodes[0]->children().empty() && nodes[0]->is_closed()) { + model_node* n1 = nodes[0]; + n1->set_open(); + enqueue_leaf(*n1); + } + + if (nodes.empty()) cache(n).remove(n.post()); +} + + +lbool context::gpdr_solve_core() { + scoped_watch _w_(m_solve_watch); + //if there is no query predicate, abort + if (!m_rels.find(m_query_pred, m_query)) { return l_false; } + + model_search ms(m_pdr_bfs); + unsigned lvl = 0; + unsigned max_level = m_max_level; + for (lvl = 0; lvl < max_level; ++lvl) { + checkpoint(); + IF_VERBOSE(1,verbose_stream() << "GPDR Entering level "<< lvl << "\n";); + STRACE("spacer.expand-add", tout << "\n* LEVEL " << lvl << "\n";); + m_expanded_lvl = infty_level(); + m_stats.m_max_query_lvl = lvl; + if (gpdr_check_reachability(lvl, ms)) {return l_true;} + if (lvl > 0) { + if (propagate(m_expanded_lvl, lvl, UINT_MAX)) {return l_false;} + } + } + + // communicate failure to datalog::context + if (m_context) { + m_context->set_status(datalog::BOUNDED); + } + return l_undef; +} + +bool context::gpdr_check_reachability(unsigned lvl, model_search &ms) { + pob_ref root_pob = m_query->mk_pob(nullptr, lvl, 0, m.mk_true()); + model_node *root_node = alloc(model_node, nullptr, root_pob.get()); + + ms.set_root(root_node); + pob_ref_buffer new_pobs; + + while (model_node *node = ms.pop_front()) { + IF_VERBOSE(2, verbose_stream() << "Expand node: " + << node->level() << "\n";); + new_pobs.reset(); + checkpoint(); + pred_transformer &pt = node->pt(); + + // check reachable cache + if (pt.is_must_reachable(node->pob()->post(), nullptr)) { + TRACE("spacer", + tout << "must-reachable: " << pt.head()->get_name() << " level: " + << node->level() << " depth: " << node->depth () << "\n"; + tout << mk_pp(node->pob()->post(), m) << "\n";); + + node->set_closed(); + if (node == root_node) return true; + continue; + } + + switch (expand_pob(*node->pob(), new_pobs)){ + case l_true: + node->set_closed(); + if (node == root_node) return true; + break; + case l_false: + ms.backtrack_level(false, *node); + if (node == root_node) return false; + break; + case l_undef: + SASSERT(!new_pobs.empty()); + for (auto pob : new_pobs) { + TRACE("spacer_pdr", + tout << "looking at pob at level " << pob->level() << " " + << mk_pp(pob->post(), m) << "\n";); + if (pob != node->pob()) + ms.add_leaf(alloc(model_node, node, pob)); + } + node->check_pre_closed(); + break; + } + } + + return root_node->is_closed(); +} + +bool context::gpdr_create_split_children(pob &n, const datalog::rule &r, + expr *trans, + model &mdl, + pob_ref_buffer &out) { + pred_transformer &pt = n.pt(); + ptr_vector preds; + pt.find_predecessors(r, preds); + SASSERT(preds.size() > 1); + + ptr_vector ppts; + for (auto *p : preds) ppts.push_back(&get_pred_transformer(p)); + + mbc::partition_map pmap; + for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { + func_decl *p = preds.get(i); + pred_transformer &ppt = *ppts.get(i); + for (unsigned j = 0, jsz = p->get_arity(); j < jsz; ++j) { + pmap.insert(m_pm.o2o(ppt.sig(j), 0, i), i); + } + } + + + spacer::mbc _mbc(m); + expr_ref_vector lits(m); + flatten_and(trans, lits); + vector res(preds.size(), expr_ref_vector(m)); + _mbc(pmap, lits, mdl, res); + + // pick an order to process children + unsigned_vector kid_order; + kid_order.resize(preds.size(), 0); + for (unsigned i = 0, sz = preds.size(); i < sz; ++i) kid_order[i] = i; + if (m_children_order == CO_REV_RULE) { + kid_order.reverse(); + } + else if (m_children_order == CO_RANDOM) { + shuffle(kid_order.size(), kid_order.c_ptr(), m_random); + } + + + for (unsigned i = 0, sz = res.size(); i < sz; ++i) { + unsigned j = kid_order[i]; + expr_ref post(m); + pred_transformer &ppt = *ppts.get(j); + post = mk_and(res.get(j)); + m_pm.formula_o2n(post.get(), post, j, true); + pob * k = ppt.mk_pob(&n, prev_level(n.level()), n.depth(), post); + out.push_back(k); + IF_VERBOSE (1, verbose_stream() + << "\n\tcreate_child: " << k->pt().head()->get_name() + << " (" << k->level() << ", " << k->depth() << ") " + << (k->use_farkas_generalizer() ? "FAR " : "SUB ") + << k->post()->get_id(); + verbose_stream().flush();); + TRACE ("spacer", + tout << "create-pob: " << k->pt().head()->get_name() + << " level: " << k->level() + << " depth: " << k->depth () + << " fvsz: " << k->get_free_vars_size() << "\n" + << mk_pp(k->post(), m) << "\n";); + + + } + + return true; +} + + +} // spacer diff --git a/src/muz/spacer/spacer_pdr.h b/src/muz/spacer/spacer_pdr.h new file mode 100644 index 000000000..36abb0bc9 --- /dev/null +++ b/src/muz/spacer/spacer_pdr.h @@ -0,0 +1,107 @@ +/**++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + spacer_pdr.h + +Abstract: + + SPACER gPDR strategy implementation + +Author: + + Arie Gurfinkel + + Based on muz/pdr + +Notes: + +--*/ +#ifndef _SPACER_PDR_H_ +#define _SPACER_PDR_H_ + +#include "muz/spacer/spacer_context.h" + +namespace spacer { +// structure for counter-example search. +class model_node { + pob_ref m_pob; // proof obligation + model_node* m_parent; // parent in the search tree + ptr_vector m_children; // children in the search tree + model_node* m_next; // next element of an in-place circular queue + model_node* m_prev; // prev element of an in-place circular queue + unsigned m_orig_level; // level at which this search node was created + unsigned m_depth; // + bool m_closed; // whether the pob is derivable +public: + model_node(model_node* parent, pob* pob); + void add_child(model_node* kid); + + expr *post() const { return m_pob->post(); } + unsigned level() const { return m_pob->level(); } + unsigned orig_level() const { return m_orig_level; } + unsigned depth() const { return m_depth; } + void increase_level() { m_pob->inc_level(); } + pob_ref &pob() { return m_pob; } + ptr_vector const& children() { return m_children; } + pred_transformer& pt() const { return m_pob->pt(); } + model_node* parent() const { return m_parent; } + // order in children of the parent + unsigned index_in_parent() const; + + bool is_closed() const { return m_closed; } + bool is_open() const { return !is_closed(); } + + // closed or has children and they are all closed + bool is_1closed() { + if (is_closed()) return true; + if (m_children.empty()) return false; + for (auto kid : m_children) + if (kid->is_open()) return false; + return true; + } + + void check_pre_closed(); + void set_pre_closed() { m_closed = true; } + + void set_closed() { m_closed = true; } + void set_open(); + void reset_children() { m_children.reset(); } + + /// queue + + // remove this node from the given queue + void detach(model_node*& qhead); + void insert_after(model_node* n); + model_node* next() const { return m_next; } + bool in_queue() { return m_next && m_prev; } +}; + +class model_search { + typedef ptr_vector model_nodes; + bool m_bfs; + model_node* m_root; + model_node* m_qhead; + vector > m_cache; + obj_map& cache(model_node const& n); + void erase_children(model_node& n, bool backtrack); + void remove_node(model_node* _n, bool backtrack); + +public: + model_search(bool bfs): m_bfs(bfs), m_root(nullptr), m_qhead(nullptr) {} + ~model_search() {reset();} + + void set_root(model_node* n); + + void reset(); + model_node* pop_front(); + void add_leaf(model_node* n); // add fresh node. + model_node& get_root() const { return *m_root; } + void backtrack_level(bool uses_level, model_node& n); + void remove_goal(model_node& n); + + void enqueue_leaf(model_node &n); +}; +} +#endif diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp new file mode 100644 index 000000000..dc3bbf30c --- /dev/null +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -0,0 +1,834 @@ +/*++ +Copyright (c) 2017 Arie Gurfinkel + +Module Name: + + spacer_proof_utils.cpp + +Abstract: + Utilities to traverse and manipulate proofs + +Author: + Bernhard Gleiss + Arie Gurfinkel + +Revision History: + +--*/ + +#include "util/params.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/proofs/proof_checker.h" +#include "muz/base/dl_util.h" +#include "muz/spacer/spacer_iuc_proof.h" + +#include "ast/proofs/proof_utils.h" +#include "muz/spacer/spacer_proof_utils.h" +#include "muz/spacer/spacer_util.h" + +namespace spacer { + + // arithmetic lemma recognizer + bool is_arith_lemma(ast_manager& m, proof* pr) + { + // arith lemmas: second parameter specifies exact type of lemma, + // could be "farkas", "triangle-eq", "eq-propagate", + // "assign-bounds", maybe also something else + if (pr->get_decl_kind() == PR_TH_LEMMA) { + func_decl* d = pr->get_decl(); + symbol sym; + return d->get_num_parameters() >= 1 && + d->get_parameter(0).is_symbol(sym) && + sym == "arith"; + } + return false; + } + + // farkas lemma recognizer + bool is_farkas_lemma(ast_manager& m, proof* pr) + { + if (pr->get_decl_kind() == PR_TH_LEMMA) + { + func_decl* d = pr->get_decl(); + symbol sym; + return d->get_num_parameters() >= 2 && + d->get_parameter(0).is_symbol(sym) && sym == "arith" && + d->get_parameter(1).is_symbol(sym) && sym == "farkas"; + } + return false; + } + + static bool is_assign_bounds_lemma(ast_manager &m, proof *pr) { + if (pr->get_decl_kind() == PR_TH_LEMMA) + { + func_decl* d = pr->get_decl(); + symbol sym; + return d->get_num_parameters() >= 2 && + d->get_parameter(0).is_symbol(sym) && sym == "arith" && + d->get_parameter(1).is_symbol(sym) && sym == "assign-bounds"; + } + return false; + } + + + + class linear_combinator { + struct scaled_lit { + bool is_pos; + app *lit; + rational coeff; + scaled_lit(bool is_pos, app *lit, const rational &coeff) : + is_pos(is_pos), lit(lit), coeff(coeff) {} + }; + ast_manager &m; + th_rewriter m_rw; + arith_util m_arith; + expr_ref m_sum; + bool m_is_strict; + rational m_lc; + vector m_lits; + public: + linear_combinator(ast_manager &m) : m(m), m_rw(m), m_arith(m), + m_sum(m), m_is_strict(false), + m_lc(1) {} + + void add_lit(app* lit, rational const &coeff, bool is_pos = true) { + m_lits.push_back(scaled_lit(is_pos, lit, coeff)); + } + + void normalize_coeff() { + for (auto &lit : m_lits) + m_lc = lcm(m_lc, denominator(lit.coeff)); + if (!m_lc.is_one()) { + for (auto &lit : m_lits) + lit.coeff *= m_lc; + } + } + + rational const &lc() const {return m_lc;} + + bool process_lit(scaled_lit &lit0) { + arith_util a(m); + app* lit = lit0.lit; + rational &coeff = lit0.coeff; + bool is_pos = lit0.is_pos; + + + if (m.is_not(lit)) { + lit = to_app(lit->get_arg(0)); + is_pos = !is_pos; + } + if (!m_arith.is_le(lit) && !m_arith.is_lt(lit) && + !m_arith.is_ge(lit) && !m_arith.is_gt(lit) && !m.is_eq(lit)) { + return false; + } + SASSERT(lit->get_num_args() == 2); + sort* s = m.get_sort(lit->get_arg(0)); + bool is_int = m_arith.is_int(s); + if (!is_int && m_arith.is_int_expr(lit->get_arg(0))) { + is_int = true; + s = m_arith.mk_int(); + } + + if (!is_int && is_pos && (m_arith.is_gt(lit) || m_arith.is_lt(lit))) { + m_is_strict = true; + } + if (!is_int && !is_pos && (m_arith.is_ge(lit) || m_arith.is_le(lit))) { + m_is_strict = true; + } + + + SASSERT(m_arith.is_int(s) || m_arith.is_real(s)); + expr_ref sign1(m), sign2(m), term(m); + sign1 = m_arith.mk_numeral(m.is_eq(lit)?coeff:abs(coeff), s); + sign2 = m_arith.mk_numeral(m.is_eq(lit)?-coeff:-abs(coeff), s); + if (!m_sum.get()) { + m_sum = m_arith.mk_numeral(rational(0), s); + } + + expr* a0 = lit->get_arg(0); + expr* a1 = lit->get_arg(1); + + if (is_pos && (m_arith.is_ge(lit) || m_arith.is_gt(lit))) { + std::swap(a0, a1); + } + if (!is_pos && (m_arith.is_le(lit) || m_arith.is_lt(lit))) { + std::swap(a0, a1); + } + + // + // Multiplying by coefficients over strict + // and non-strict inequalities: + // + // (a <= b) * 2 + // (a - b <= 0) * 2 + // (2a - 2b <= 0) + + // (a < b) * 2 <=> + // (a +1 <= b) * 2 <=> + // 2a + 2 <= 2b <=> + // 2a+2-2b <= 0 + + bool strict_ineq = + is_pos?(m_arith.is_gt(lit) || m_arith.is_lt(lit)):(m_arith.is_ge(lit) || m_arith.is_le(lit)); + + if (is_int && strict_ineq) { + m_sum = m_arith.mk_add(m_sum, sign1); + } + + term = m_arith.mk_mul(sign1, a0); + m_sum = m_arith.mk_add(m_sum, term); + term = m_arith.mk_mul(sign2, a1); + m_sum = m_arith.mk_add(m_sum, term); + + m_rw(m_sum); + return true; + } + + expr_ref operator()(){ + if (!m_sum) normalize_coeff(); + m_sum.reset(); + for (auto &lit : m_lits) { + if (!process_lit(lit)) return expr_ref(m); + } + return m_sum; + } + }; + +/* + * ==================================== + * methods for transforming proofs + * ==================================== + */ + + void theory_axiom_reducer::reset() { + m_cache.reset(); + m_pinned.reset(); + } + + static proof_ref mk_th_lemma(ast_manager &m, ptr_buffer const &parents, + unsigned num_params, parameter const *params) { + buffer v; + for (unsigned i = 1; i < num_params; ++i) + v.push_back(params[i]); + + SASSERT(params[0].is_symbol()); + family_id tid = m.mk_family_id(params[0].get_symbol()); + SASSERT(tid != null_family_id); + + proof_ref pf(m); + pf = m.mk_th_lemma(tid, m.mk_false(), + parents.size(), parents.c_ptr(), + v.size(), v.c_ptr()); + return pf; + } + + static bool match_mul(expr *e, expr_ref &var, expr_ref &val, arith_util &a) { + expr *e1 = nullptr, *e2 = nullptr; + if (!a.is_mul(e, e1, e2)) { + if (a.is_numeral(e)) return false; + if (!var || var == e) { + var = e; + val = a.mk_numeral(rational(1), get_sort(e)); + return true; + } + return false; + } + + if (!a.is_numeral(e1)) std::swap(e1, e2); + if (!a.is_numeral(e1)) return false; + + // if variable is given, match it as well + if (!var || var == e2) { + var = e2; + val = e1; + return true; + } + return false; + } + + static expr_ref get_coeff(expr *lit0, expr_ref &var) { + ast_manager &m = var.m(); + arith_util a(m); + + expr *lit = nullptr; + if (!m.is_not(lit0, lit)) lit = lit0; + + expr *e1 = nullptr, *e2 = nullptr; + // assume e2 is numeral and ignore it + if ((a.is_le(lit, e1, e2) || a.is_lt(lit, e1, e2) || + a.is_ge(lit, e1, e2) || a.is_gt(lit, e1, e2) || + m.is_eq(lit, e1, e2))) { + if (a.is_numeral(e1)) std::swap(e1, e2); + } + else { + e1 = lit; + } + + expr_ref val(m); + if (!a.is_add(e1)) { + if (match_mul(e1, var, val, a)) return val; + } + else { + for (auto *arg : *to_app(e1)) { + if (match_mul(arg, var, val, a)) return val; + } + } + return expr_ref(m); + } + + // convert assign-bounds lemma to a farkas lemma by adding missing coeff + // assume that missing coeff is for premise at position 0 + static proof_ref mk_fk_from_ab(ast_manager &m, + ptr_buffer const &parents, + unsigned num_params, + parameter const *params) { + SASSERT(num_params == parents.size() + 1 /* one param is missing */); + + // compute missing coefficient + linear_combinator lcb(m); + for (unsigned i = 1, sz = parents.size(); i < sz; ++i) { + app *p = to_app(m.get_fact(parents.get(i))); + rational const &r = params[i+1].get_rational(); + lcb.add_lit(p, r); + } + + TRACE("spacer.fkab", + tout << "lit0 is: " << mk_pp(m.get_fact(parents.get(0)), m) << "\n" + << "LCB is: " << lcb() << "\n";); + + expr_ref var(m), val1(m), val2(m); + val1 = get_coeff(m.get_fact(parents.get(0)), var); + val2 = get_coeff(lcb(), var); + TRACE("spacer.fkab", + tout << "var: " << var + << " val1: " << val1 << " val2: " << val2 << "\n";); + + rational rat1, rat2, coeff0; + arith_util a(m); + CTRACE("spacer.fkab", !(val1 && val2), + tout << "Failed to match variables\n";); + if (val1 && val2 && + a.is_numeral(val1, rat1) && a.is_numeral(val2, rat2)) { + coeff0 = abs(rat2/rat1); + coeff0 = coeff0 / lcb.lc(); + TRACE("spacer.fkab", tout << "coeff0: " << coeff0 << "\n";); + } + else { + IF_VERBOSE(1, verbose_stream() + << "\n\n\nFAILED TO FIND COEFFICIENT\n\n\n";); + // failed to find a coefficient + return proof_ref(m); + } + + + buffer v; + v.push_back(parameter(symbol("farkas"))); + v.push_back(parameter(coeff0)); + for (unsigned i = 2; i < num_params; ++i) + v.push_back(params[i]); + + SASSERT(params[0].is_symbol()); + family_id tid = m.mk_family_id(params[0].get_symbol()); + SASSERT(tid != null_family_id); + + proof_ref pf(m); + pf = m.mk_th_lemma(tid, m.mk_false(), + parents.size(), parents.c_ptr(), + v.size(), v.c_ptr()); + + SASSERT(is_arith_lemma(m, pf)); + + DEBUG_CODE( + proof_checker pc(m); + expr_ref_vector side(m); + ENSURE(pc.check(pf, side));); + return pf; + + } + + /// -- rewrite theory axioms into theory lemmas + proof_ref theory_axiom_reducer::reduce(proof* pr) { + proof_post_order pit(pr, m); + while (pit.hasNext()) { + proof* p = pit.next(); + + if (m.get_num_parents(p) == 0 && is_arith_lemma(m, p)) { + // we have an arith-theory-axiom and want to get rid of it + // we need to replace the axiom with + // (a) corresponding hypothesis, + // (b) a theory lemma, and + // (c) a lemma. + // Furthermore update data-structures + app *fact = to_app(m.get_fact(p)); + ptr_buffer cls; + if (m.is_or(fact)) { + for (unsigned i = 0, sz = fact->get_num_args(); i < sz; ++i) + cls.push_back(fact->get_arg(i)); + } + else + cls.push_back(fact); + + // (a) create hypothesis + ptr_buffer hyps; + for (unsigned i = 0, sz = cls.size(); i < sz; ++i) { + expr *c; + expr_ref hyp_fact(m); + if (m.is_not(cls[i], c)) + hyp_fact = c; + else + hyp_fact = m.mk_not (cls[i]); + + proof* hyp = m.mk_hypothesis(hyp_fact); + m_pinned.push_back(hyp); + hyps.push_back(hyp); + } + + // (b) Create a theory lemma + proof_ref th_lemma(m); + func_decl *d = p->get_decl(); + if (is_assign_bounds_lemma(m, p)) { + + th_lemma = mk_fk_from_ab(m, hyps, + d->get_num_parameters(), + d->get_parameters()); + } + + // fall back to th-lemma + if (!th_lemma) { + th_lemma = mk_th_lemma(m, hyps, + d->get_num_parameters(), + d->get_parameters()); + } + m_pinned.push_back(th_lemma); + SASSERT(is_arith_lemma(m, th_lemma)); + + // (c) create lemma + proof* res = m.mk_lemma(th_lemma, fact); + m_pinned.push_back(res); + m_cache.insert(p, res); + + SASSERT(m.get_fact(res) == m.get_fact(p)); + } + else { + // proof is dirty, if a sub-proof of one of its premises + // has been transformed + bool dirty = false; + + ptr_buffer args; + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + proof *pp, *tmp; + pp = m.get_parent(p, i); + VERIFY(m_cache.find(pp, tmp)); + args.push_back(tmp); + dirty |= (pp != tmp); + } + // if not dirty just use the old step + if (!dirty) m_cache.insert(p, p); + // otherwise create new proof with the corresponding proofs + // of the premises + else { + if (m.has_fact(p)) args.push_back(m.get_fact(p)); + + SASSERT(p->get_decl()->get_arity() == args.size()); + + proof* res = m.mk_app(p->get_decl(), + args.size(), (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + m_cache.insert(p, res); + } + } + } + + proof* res; + VERIFY(m_cache.find(pr,res)); + DEBUG_CODE( + proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side)); + ); + + return proof_ref(res, m); + } + +/* ------------------------------------------------------------------------- */ +/* hypothesis_reducer */ +/* ------------------------------------------------------------------------- */ + + proof_ref hypothesis_reducer::reduce(proof* pr) { + compute_hypsets(pr); + collect_units(pr); + + proof_ref res(reduce_core(pr), m); + SASSERT(res); + reset(); + + DEBUG_CODE(proof_checker pc(m); + expr_ref_vector side(m); + SASSERT(pc.check(res, side));); + return res; + } + + void hypothesis_reducer::reset() { + m_active_hyps.reset(); + m_units.reset(); + m_cache.reset(); + for (auto t : m_pinned_active_hyps) dealloc(t); + m_pinned_active_hyps.reset(); + m_pinned.reset(); + m_hyp_mark.reset(); + m_open_mark.reset(); + m_visited.reset(); + } + + void hypothesis_reducer::compute_hypsets(proof *pr) { + ptr_buffer todo; + todo.push_back(pr); + + while (!todo.empty()) { + proof* p = todo.back(); + + if (m_visited.is_marked(p)) { + todo.pop_back(); + continue; + } + + unsigned todo_sz = todo.size(); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + SASSERT(m.is_proof(p->get_arg(i))); + proof *parent = to_app(p->get_arg(i)); + + if (!m_visited.is_marked(parent)) + todo.push_back(parent); + } + if (todo.size() > todo_sz) continue; + + todo.pop_back(); + + m_visited.mark(p); + + + proof_ptr_vector* active_hyps = nullptr; + // fill both sets + if (m.is_hypothesis(p)) { + // create active_hyps-set for step p + proof_ptr_vector* active_hyps = alloc(proof_ptr_vector); + m_pinned_active_hyps.insert(active_hyps); + m_active_hyps.insert(p, active_hyps); + active_hyps->push_back(p); + m_open_mark.mark(p); + m_hyp_mark.mark(m.get_fact(p)); + continue; + } + + ast_fast_mark1 seen; + + active_hyps = alloc(proof_ptr_vector); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + proof* parent = m.get_parent(p, i); + // lemmas clear all hypotheses above them + if (m.is_lemma(p)) continue; + for (auto *x : *m_active_hyps.find(parent)) { + if (!seen.is_marked(x)) { + seen.mark(x); + active_hyps->push_back(x); + m_open_mark.mark(p); + } + } + } + if (active_hyps->empty()) { + dealloc(active_hyps); + m_active_hyps.insert(p, &m_empty_vector); + } + else { + m_pinned_active_hyps.push_back(active_hyps); + m_active_hyps.insert(p, active_hyps); + } + } + } + +// collect all units that are hyp-free and are used as hypotheses somewhere +// requires that m_active_hyps has been computed + void hypothesis_reducer::collect_units(proof* pr) { + + proof_post_order pit(pr, m); + while (pit.hasNext()) { + proof* p = pit.next(); + if (!m.is_hypothesis(p)) { + // collect units that are hyp-free and are used as + // hypotheses in the proof pr + if (!m_open_mark.is_marked(p) && m.has_fact(p) && + m_hyp_mark.is_marked(m.get_fact(p))) + m_units.insert(m.get_fact(p), p); + } + } + } + +/** + \brief returns true if p is an ancestor of q +*/ + bool hypothesis_reducer::is_ancestor(proof *p, proof *q) { + if (p == q) return true; + ptr_vector todo; + todo.push_back(q); + + expr_mark visited; + while (!todo.empty()) { + proof *cur; + cur = todo.back(); + todo.pop_back(); + + if (visited.is_marked(cur)) continue; + + if (cur == p) return true; + visited.mark(cur); + + for (unsigned i = 0, sz = m.get_num_parents(cur); i < sz; ++i) { + todo.push_back(m.get_parent(cur, i)); + } + } + return false; + } + + proof* hypothesis_reducer::reduce_core(proof* pf) { + SASSERT(m.is_false(m.get_fact(pf))); + + proof *res = NULL; + + ptr_vector todo; + todo.push_back(pf); + ptr_buffer args; + bool dirty = false; + + while (true) { + proof *p, *tmp, *pp; + unsigned todo_sz; + + p = todo.back(); + if (m_cache.find(p, tmp)) { + todo.pop_back(); + continue; + } + + dirty = false; + args.reset(); + todo_sz = todo.size(); + for (unsigned i = 0, sz = m.get_num_parents(p); i < sz; ++i) { + pp = m.get_parent(p, i); + if (m_cache.find(pp, tmp)) { + args.push_back(tmp); + dirty |= pp != tmp; + } else { + todo.push_back(pp); + } + } + + if (todo_sz < todo.size()) continue; + + todo.pop_back(); + + // transform the current proof node + + if (m.is_hypothesis(p)) { + // if possible, replace a hypothesis by a unit derivation + if (m_units.find(m.get_fact(p), tmp)) { + // use already transformed proof of the unit if it is available + proof* proof_of_unit; + if (!m_cache.find(tmp, proof_of_unit)) { + proof_of_unit = tmp; + } + + // make sure hypsets for the unit are computed + // AG: is this needed? + compute_hypsets(proof_of_unit); + + // if the transformation doesn't create a cycle, perform it + if (!is_ancestor(p, proof_of_unit)) { + res = proof_of_unit; + } + else { + // -- failed to transform the proof, perhaps bad + // -- choice of the proof of unit + res = p; + } + } + else { + // -- no unit found to replace the hypothesis + res = p; + } + } + + else if (!dirty) {res = p;} + + else if (m.is_lemma(p)) { + // lemma: reduce the premise; remove reduced consequences + // from conclusion + SASSERT(args.size() == 1); + res = mk_lemma_core(args[0], m.get_fact(p)); + // -- re-compute hypsets + compute_hypsets(res); + } + else if (m.is_unit_resolution(p)) { + // unit: reduce untis; reduce the first premise; rebuild + // unit resolution + res = mk_unit_resolution_core(p, args); + // -- re-compute hypsets + compute_hypsets(res); + } + else { + res = mk_proof_core(p, args); + // -- re-compute hypsets + compute_hypsets(res); + } + + SASSERT(res); + m_cache.insert(p, res); + + // bail out as soon as found a sub-proof of false + if (!m_open_mark.is_marked(res) && m.has_fact(res) && m.is_false(m.get_fact(res))) + return res; + } + UNREACHABLE(); + return nullptr; + } + + proof* hypothesis_reducer::mk_lemma_core(proof* premise, expr *fact) { + SASSERT(m.is_false(m.get_fact(premise))); + SASSERT(m_active_hyps.contains(premise)); + + proof_ptr_vector* active_hyps = m_active_hyps.find(premise); + + // if there is no active hypothesis return the premise + if (!m_open_mark.is_marked(premise)) { + // XXX just in case premise might go away + m_pinned.push_back(premise); + return premise; + } + + // add some stability + std::stable_sort(active_hyps->begin(), active_hyps->end(), ast_lt_proc()); + // otherwise, build a disjunction of the negated active hypotheses + // and add a lemma proof step + expr_ref_buffer args(m); + for (auto hyp : *active_hyps) { + expr *hyp_fact, *t; + hyp_fact = m.get_fact(hyp); + if (m.is_not(hyp_fact, t)) + args.push_back(t); + else + args.push_back(m.mk_not(hyp_fact)); + } + + expr_ref lemma(m); + lemma = mk_or(m, args.size(), args.c_ptr()); + + proof* res; + res = m.mk_lemma(premise, lemma); + m_pinned.push_back(res); + return res; + } + + proof* hypothesis_reducer::mk_unit_resolution_core(proof *ures, + ptr_buffer& args) { + // if any literal is false, we don't need a unit resolution step + // This can be the case due to some previous transformations + for (unsigned i = 1, sz = args.size(); i < sz; ++i) { + if (m.is_false(m.get_fact(args[i]))) { + // XXX pin just in case + m_pinned.push_back(args[i]); + return args[i]; + } + } + + proof* arg0 = args[0]; + app *fact0 = to_app(m.get_fact(arg0)); + + + ptr_buffer pf_args; + ptr_buffer pf_fact; + pf_args.push_back(arg0); + + // compute literals to be resolved + ptr_buffer lits; + + // fact0 is a literal whenever the original resolution was a + // binary resolution to an empty clause + if (m.get_num_parents(ures) == 2 && m.is_false(m.get_fact(ures))) { + lits.push_back(fact0); + } + // fact0 is a literal unless it is a dijsunction + else if (!m.is_or(fact0)) { + lits.push_back(fact0); + } + // fact0 is a literal only if it appears as a literal in the + // original resolution + else { + lits.reset(); + app* ures_fact = to_app(m.get_fact(m.get_parent(ures, 0))); + for (unsigned i = 0, sz = ures_fact->get_num_args(); i < sz; ++i) { + if (ures_fact->get_arg(i) == fact0) { + lits.push_back(fact0); + break; + } + } + if (lits.empty()) { + lits.append(fact0->get_num_args(), fact0->get_args()); + } + + } + + // -- find all literals that are resolved on + for (unsigned i = 0, sz = lits.size(); i < sz; ++i) { + bool found = false; + for (unsigned j = 1; j < args.size(); ++j) { + if (m.is_complement(lits.get(i), m.get_fact(args[j]))) { + found = true; + pf_args.push_back(args[j]); + break; + } + } + if (!found) {pf_fact.push_back(lits.get(i));} + } + + // unit resolution got reduced to noop + if (pf_args.size() == 1) { + // XXX pin just in case + m_pinned.push_back(arg0); + + return arg0; + } + + // make unit resolution proof step + // expr_ref tmp(m); + // tmp = mk_or(m, pf_fact.size(), pf_fact.c_ptr()); + // proof* res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr(), tmp); + proof *res = m.mk_unit_resolution(pf_args.size(), pf_args.c_ptr()); + m_pinned.push_back(res); + + return res; + } + + proof* hypothesis_reducer::mk_proof_core(proof* old, ptr_buffer& args) { + // if any of the literals are false, we don't need a step + for (unsigned i = 0; i < args.size(); ++i) { + if (m.is_false(m.get_fact(args[i]))) { + // XXX just in case + m_pinned.push_back(args[i]); + return args[i]; + } + } + + // otherwise build step + // BUG: I guess this doesn't work with quantifiers (since they are no apps) + args.push_back(to_app(m.get_fact(old))); + + SASSERT(old->get_decl()->get_arity() == args.size()); + + proof* res = m.mk_app(old->get_decl(), args.size(), + (expr * const*)args.c_ptr()); + m_pinned.push_back(res); + return res; + } + +}; diff --git a/src/muz/spacer/spacer_proof_utils.h b/src/muz/spacer/spacer_proof_utils.h new file mode 100644 index 000000000..ead85f885 --- /dev/null +++ b/src/muz/spacer/spacer_proof_utils.h @@ -0,0 +1,105 @@ +/*++ +Copyright (c) 2017 Arie Gurfinkel + +Module Name: + + spacer_proof_utils.cpp + +Abstract: + Utilities to traverse and manipulate proofs + +Author: + Bernhard Gleiss + Arie Gurfinkel + +Revision History: + +--*/ + +#ifndef _SPACER_PROOF_UTILS_H_ +#define _SPACER_PROOF_UTILS_H_ +#include "ast/ast.h" + +namespace spacer { + +bool is_arith_lemma(ast_manager& m, proof* pr); +bool is_farkas_lemma(ast_manager& m, proof* pr); + +/// rewrites theory axioms into theory lemmas +class theory_axiom_reducer { +public: + theory_axiom_reducer(ast_manager& m) : m(m), m_pinned(m) {} + + // reduce theory axioms and return transformed proof + proof_ref reduce(proof* pr); + +private: + ast_manager &m; + + // tracking all created expressions + expr_ref_vector m_pinned; + + // maps each proof of a clause to the transformed subproof of + // that clause + obj_map m_cache; + + void reset(); +}; + +/// reduces the number of hypotheses in a proof +class hypothesis_reducer +{ +public: + hypothesis_reducer(ast_manager &m) : m(m), m_pinned(m) {} + ~hypothesis_reducer() {reset();} + + // reduce hypothesis and return transformed proof + proof_ref reduce(proof* pf); + +private: + typedef ptr_vector proof_ptr_vector; + + ast_manager &m; + + proof_ptr_vector m_empty_vector; + + // created expressions + expr_ref_vector m_pinned; + + // created sets of active hypothesis + ptr_vector m_pinned_active_hyps; + + // maps a proof to the transformed proof + obj_map m_cache; + + // maps a unit literal to its derivation + obj_map m_units; + + // maps a proof node to the set of its active (i.e., in scope) hypotheses + obj_map m_active_hyps; + + /// marks if an expression is ever used as a hypothesis in a proof + expr_mark m_hyp_mark; + /// marks a proof as open, i.e., has a non-discharged hypothesis as ancestor + expr_mark m_open_mark; + expr_mark m_visited; + + void reset(); + + /// true if p is an ancestor of q + bool is_ancestor(proof *p, proof *q); + // compute active_hyps and parent_hyps for a given proof node and + // all its ancestors + void compute_hypsets(proof* pr); + // compute m_units + void collect_units(proof* pr); + + // -- rewrite proof to reduce number of hypotheses used + proof* reduce_core(proof* pf); + + proof* mk_lemma_core(proof *pf, expr *fact); + proof* mk_unit_resolution_core(proof* ures, ptr_buffer& args); + proof* mk_proof_core(proof* old, ptr_buffer& args); +}; +} +#endif diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 19c5c3aa3..7bd21a1b5 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -35,18 +35,20 @@ Revision History: #include "muz/spacer/spacer_farkas_learner.h" #include "muz/spacer/spacer_prop_solver.h" -#include "muz/base/fixedpoint_params.hpp" +#include "model/model_evaluator.h" +#include "muz/base/fp_params.hpp" namespace spacer { -prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& name) : - m(pm.get_manager()), - m_pm(pm), +prop_solver::prop_solver(ast_manager &m, + solver *solver0, solver *solver1, + fp_params const& p, symbol const& name) : + m(m), m_name(name), - m_ctx(NULL), + m_ctx(nullptr), m_pos_level_atoms(m), m_neg_level_atoms(m), - m_core(0), + m_core(nullptr), m_subset_based_core(false), m_uses_level(infty_level()), m_delta_level(false), @@ -54,17 +56,21 @@ prop_solver::prop_solver(manager& pm, fixedpoint_params const& p, symbol const& m_use_push_bg(p.spacer_keep_proxy()) { - m_solvers[0] = pm.mk_fresh(); - m_fparams[0] = &pm.fparams(); + m_solvers[0] = solver0; + m_solvers[1] = solver1; - m_solvers[1] = pm.mk_fresh2(); - m_fparams[1] = &pm.fparams2(); - - m_contexts[0] = alloc(spacer::itp_solver, *(m_solvers[0]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_split_farkas_literals()); - m_contexts[1] = alloc(spacer::itp_solver, *(m_solvers[1]), p.spacer_new_unsat_core(), p.spacer_minimize_unsat_core(), p.spacer_farkas_optimized(), p.spacer_farkas_a_const(), p.spacer_split_farkas_literals()); - - for (unsigned i = 0; i < 2; ++i) - { m_contexts[i]->assert_expr(m_pm.get_background()); } + m_contexts[0] = alloc(spacer::iuc_solver, *(m_solvers[0]), + p.spacer_iuc(), + p.spacer_iuc_arith(), + p.spacer_iuc_print_farkas_stats(), + p.spacer_iuc_old_hyp_reducer(), + p.spacer_iuc_split_farkas_literals()); + m_contexts[1] = alloc(spacer::iuc_solver, *(m_solvers[1]), + p.spacer_iuc(), + p.spacer_iuc_arith(), + p.spacer_iuc_print_farkas_stats(), + p.spacer_iuc_old_hyp_reducer(), + p.spacer_iuc_split_farkas_literals()); } void prop_solver::add_level() @@ -72,7 +78,7 @@ void prop_solver::add_level() unsigned idx = level_cnt(); std::stringstream name; name << m_name << "#level_" << idx; - func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, 0, m.mk_bool_sort()); + func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, nullptr, m.mk_bool_sort()); m_level_preds.push_back(lev_pred); app_ref pos_la(m.mk_const(lev_pred), m); @@ -119,6 +125,8 @@ void prop_solver::assert_expr(expr * form) void prop_solver::assert_expr(expr * form, unsigned level) { + if (is_infty_level(level)) {assert_expr(form);return;} + ensure_level(level); app * lev_atom = m_pos_level_atoms[level].get(); app_ref lform(m.mk_or(form, lev_atom), m); @@ -126,18 +134,109 @@ void prop_solver::assert_expr(expr * form, unsigned level) } +/// Local model guided maxsmt +lbool prop_solver::mss(expr_ref_vector &hard, expr_ref_vector &soft) { + // replace expressions by assumption literals + iuc_solver::scoped_mk_proxy _p_(*m_ctx, hard); + unsigned hard_sz = hard.size(); + + lbool res = m_ctx->check_sat(hard.size(), hard.c_ptr()); + // bail out if hard constraints are not sat, or if there are no + // soft constraints + if (res != l_true || soft.empty()) {return res;} + + // the main loop + + model_ref mdl; + m_ctx->get_model(mdl); + + // don't proxy soft literals. Assume that they are propositional. + hard.append(soft); + soft.reset(); + + + // hard is divided into 4 regions + // x < hard_sz ---> hard constraints + // hard_sz <= x < i ---> sat soft constraints + // i <= x < j ---> backbones (unsat soft constraints) + // j <= x < hard.size() ---> unprocessed soft constraints + unsigned i, j; + i = hard_sz; + j = hard_sz; + + while (j < hard.size()) { + model_evaluator mev(*mdl); + + // move all true soft constraints to [hard_sz, i) + for (unsigned k = j; k < hard.size(); ++k) { + expr_ref e(m); + e = hard.get(k); + if (!mev.is_false(e) /* true or unset */) { + expr_ref tmp(m); + tmp = hard.get(i); + hard[i] = e; + if (i < j) { + // tmp is a backbone, put it at j + if (j == k) {hard[j] = tmp;} + else /* j < k */ { + e = hard.get(j); + hard[j] = tmp; + hard[k] = e; + } + j++; + } + else { + // there are no backbone literals + hard[k] = tmp; + j++; + } + i++; + } + } + + // done with the model. Reset to avoid confusion in debugging + mdl.reset(); + + // -- grow the set of backbone literals + for (;j < hard.size(); ++j) { + res = m_ctx->check_sat(j+1, hard.c_ptr()); + if (res == l_false) { + // -- flip non-true literal to be false + hard[j] = mk_not(m, hard.get(j)); + } + else if (res == l_true) { + // -- get the model for the next iteration of the outer loop + m_ctx->get_model(mdl); + break; + } + else if (res == l_undef) { + // -- conservatively bail out + hard.resize(hard_sz); + return l_undef; + } + } + } + + // move sat soft constraints to the output vector + for (unsigned k = i; k < j; ++k) { soft.push_back(hard.get(k)); } + // cleanup hard constraints + hard.resize(hard_sz); + return l_true; +} + /// Poor man's maxsat. No guarantees of maximum solution /// Runs maxsat loop on m_ctx Returns l_false if hard is unsat, /// otherwise reduces soft such that hard & soft is sat. -lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) +lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, + vector const & clauses) { // replace expressions by assumption literals - itp_solver::scoped_mk_proxy _p_(*m_ctx, hard); + iuc_solver::scoped_mk_proxy _p_(*m_ctx, hard); unsigned hard_sz = hard.size(); // assume soft constraints are propositional literals (no need to proxy) hard.append(soft); - lbool res = m_ctx->check_sat(hard.size(), hard.c_ptr()); + lbool res = m_ctx->check_sat_cc(hard, clauses); // if hard constraints alone are unsat or there are no soft // constraints, we are done if (res != l_false || soft.empty()) { return res; } @@ -146,7 +245,7 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) soft.reset(); expr_ref saved(m); - ptr_vector core; + expr_ref_vector core(m); m_ctx->get_unsat_core(core); // while there are soft constraints @@ -171,7 +270,7 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) } // check that the NEW constraints became sat - res = m_ctx->check_sat(hard.size(), hard.c_ptr()); + res = m_ctx->check_sat_cc(hard, clauses); if (res != l_false) { break; } // still unsat, update the core and repeat core.reset(); @@ -189,17 +288,21 @@ lbool prop_solver::maxsmt(expr_ref_vector &hard, expr_ref_vector &soft) return res; } -lbool prop_solver::internal_check_assumptions( - expr_ref_vector& hard_atoms, - expr_ref_vector& soft_atoms) +lbool prop_solver::internal_check_assumptions(expr_ref_vector &hard_atoms, + expr_ref_vector &soft_atoms, + vector const & clauses) { // XXX Turn model generation if m_model != 0 SASSERT(m_ctx); - SASSERT(m_ctx_fparams); - flet _model(m_ctx_fparams->m_model, m_model != 0); + + params_ref p; + if (m_model != nullptr) { + p.set_bool("produce_models", true); + m_ctx->updt_params(p); + } if (m_in_level) { assert_level_atoms(m_current_level); } - lbool result = maxsmt(hard_atoms, soft_atoms); + lbool result = maxsmt(hard_atoms, soft_atoms, clauses); if (result != l_false && m_model) { m_ctx->get_model(*m_model); } SASSERT(result != l_false || soft_atoms.empty()); @@ -226,15 +329,22 @@ lbool prop_solver::internal_check_assumptions( } if (result == l_false && m_core && m.proofs_enabled() && !m_subset_based_core) { - TRACE("spacer", tout << "theory core\n";); + TRACE("spacer", tout << "Using IUC core\n";); m_core->reset(); - m_ctx->get_itp_core(*m_core); + m_ctx->get_iuc(*m_core); } else if (result == l_false && m_core) { m_core->reset(); m_ctx->get_unsat_core(*m_core); // manually undo proxies because maxsmt() call above manually adds proxies + // AG: don't think this is needed. maxsmt() undoes the proxies already m_ctx->undo_proxies(*m_core); } + + if (m_model != nullptr) { + p.set_bool("produce_models", false); + m_ctx->updt_params(p); + } + return result; } @@ -242,9 +352,11 @@ lbool prop_solver::internal_check_assumptions( lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, expr_ref_vector& soft, + const expr_ref_vector &clause, unsigned num_bg, expr * const * bg, unsigned solver_id) { + expr_ref cls(m); // current clients expect that flattening of HARD is // done implicitly during check_assumptions expr_ref_vector hard(m); @@ -252,12 +364,11 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, flatten_and(hard); m_ctx = m_contexts [solver_id == 0 ? 0 : 0 /* 1 */].get(); - m_ctx_fparams = m_fparams [solver_id == 0 ? 0 : 0 /* 1 */]; // can be disabled if use_push_bg == true // solver::scoped_push _s_(*m_ctx); - if (!m_use_push_bg) { m_ctx->push(); } - itp_solver::scoped_bg _b_(*m_ctx); + if (!m_use_push_bg) {m_ctx->push();} + iuc_solver::scoped_bg _b_(*m_ctx); for (unsigned i = 0; i < num_bg; ++i) if (m_use_push_bg) { m_ctx->push_bg(bg [i]); } @@ -265,7 +376,9 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, unsigned soft_sz = soft.size(); (void) soft_sz; - lbool res = internal_check_assumptions(hard, soft); + vector clauses; + if (!clause.empty()) clauses.push_back(clause); + lbool res = internal_check_assumptions(hard, soft, clauses); if (!m_use_push_bg) { m_ctx->pop(1); } TRACE("psolve_verbose", @@ -280,8 +393,8 @@ lbool prop_solver::check_assumptions(const expr_ref_vector & _hard, SASSERT(soft_sz >= soft.size()); // -- reset all parameters - m_core = 0; - m_model = 0; + m_core = nullptr; + m_model = nullptr; m_subset_based_core = false; return res; } diff --git a/src/muz/spacer/spacer_prop_solver.h b/src/muz/spacer/spacer_prop_solver.h index 1ddec91c6..1db65dc3e 100644 --- a/src/muz/spacer/spacer_prop_solver.h +++ b/src/muz/spacer/spacer_prop_solver.h @@ -29,25 +29,23 @@ Revision History: #include "smt/smt_kernel.h" #include "util/util.h" #include "util/vector.h" -#include "muz/spacer/spacer_manager.h" -#include "muz/spacer/spacer_smt_context_manager.h" -#include "muz/spacer/spacer_itp_solver.h" +#include "solver/solver.h" +#include "muz/spacer/spacer_iuc_solver.h" +#include "muz/spacer/spacer_util.h" -struct fixedpoint_params; +struct fp_params; namespace spacer { +typedef ptr_vector decl_vector; class prop_solver { private: ast_manager& m; - manager& m_pm; symbol m_name; - smt_params* m_fparams[2]; - solver* m_solvers[2]; - scoped_ptr m_contexts[2]; - itp_solver * m_ctx; - smt_params * m_ctx_fparams; + ref m_solvers[2]; + scoped_ptr m_contexts[2]; + iuc_solver * m_ctx; decl_vector m_level_preds; app_ref_vector m_pos_level_atoms; // atoms used to identify level app_ref_vector m_neg_level_atoms; // @@ -68,13 +66,17 @@ private: void ensure_level(unsigned lvl); lbool internal_check_assumptions(expr_ref_vector &hard, - expr_ref_vector &soft); + expr_ref_vector &soft, + vector const & clause); - lbool maxsmt(expr_ref_vector &hard, expr_ref_vector &soft); + lbool maxsmt(expr_ref_vector &hard, expr_ref_vector &soft, + vector const & clauses); + lbool mss(expr_ref_vector &hard, expr_ref_vector &soft); public: - prop_solver(spacer::manager& pm, fixedpoint_params const& p, symbol const& name); + prop_solver(ast_manager &m, solver *solver0, solver* solver1, + fp_params const& p, symbol const& name); void set_core(expr_ref_vector* core) { m_core = core; } @@ -91,13 +93,21 @@ public: void assert_expr(expr * form); void assert_expr(expr * form, unsigned level); + void assert_exprs(const expr_ref_vector &fmls) { + for (auto *f : fmls) assert_expr(f); + } + void assert_exprs(const expr_ref_vector &fmls, unsigned level) { + for (auto *f : fmls) assert_expr(f, level); + } + /** * check assumptions with a background formula */ lbool check_assumptions(const expr_ref_vector & hard, expr_ref_vector & soft, + const expr_ref_vector &clause, unsigned num_bg = 0, - expr * const *bg = NULL, + expr * const *bg = nullptr, unsigned solver_id = 0); void collect_statistics(statistics& st) const; @@ -136,7 +146,22 @@ public: ~scoped_delta_level() {m_delta = false;} }; + class scoped_weakness { + public: + solver *sol; + scoped_weakness(prop_solver &ps, unsigned solver_id, unsigned weakness) + : sol(nullptr) { + sol = ps.m_solvers[solver_id == 0 ? 0 : 0 /* 1 */].get(); + if (!sol) return; + sol->push_params(); + params_ref p; + p.set_bool("arith.ignore_int", weakness < 1); + p.set_bool("array.weak", weakness < 2); + sol->updt_params(p); + } + ~scoped_weakness() {if (sol) {sol->pop_params();}} + }; }; } diff --git a/src/muz/spacer/spacer_qe_project.cpp b/src/muz/spacer/spacer_qe_project.cpp index 9f4f4e4fe..ed5a0215c 100644 --- a/src/muz/spacer/spacer_qe_project.cpp +++ b/src/muz/spacer/spacer_qe_project.cpp @@ -41,7 +41,7 @@ Revision History: #include "muz/spacer/spacer_mev_array.h" #include "muz/spacer/spacer_qe_project.h" -namespace +namespace spacer_qe { bool is_partial_eq (app* a); @@ -186,18 +186,18 @@ bool is_partial_eq (app* a) { } -namespace qe { +namespace spacer_qe { class is_relevant_default : public i_expr_pred { public: - bool operator()(expr* e) { + bool operator()(expr* e) override { return true; } }; - class mk_atom_default : public i_nnf_atom { + class mk_atom_default : public qe::i_nnf_atom { public: - virtual void operator()(expr* e, bool pol, expr_ref& result) { + void operator()(expr* e, bool pol, expr_ref& result) override { if (pol) result = e; else result = result.get_manager().mk_not(e); } @@ -378,7 +378,7 @@ namespace qe { rational r; cx = mk_mul (c, m_var->x()); cxt = mk_add (cx, t); - VERIFY(mdl.eval(cxt, val, true)); + val = mdl(cxt); VERIFY(a.is_numeral(val, r)); SASSERT (r > rational::zero () || r < rational::zero ()); if (r > rational::zero ()) { @@ -464,8 +464,7 @@ namespace qe { m_strict.reset(); m_eq.reset (); - expr_ref var_val (m); - VERIFY (mdl.eval (m_var->x(), var_val, true)); + expr_ref var_val = mdl(m_var->x()); unsigned eq_idx = lits.size (); for (unsigned i = 0; i < lits.size(); ++i) { @@ -492,7 +491,7 @@ namespace qe { rational r; cx = mk_mul (c, m_var->x()); cxt = mk_add (cx, t); - VERIFY(mdl.eval(cxt, val, true)); + val = mdl(cxt); VERIFY(a.is_numeral(val, r)); if (is_eq) { @@ -595,7 +594,7 @@ namespace qe { // c*x + t = 0 <=> x = -t/c expr_ref eq_term (mk_mul (-(rational::one ()/m_coeffs[eq_idx]), m_terms.get (eq_idx)), m); m_rw (eq_term); - map.insert (m_var->x (), eq_term, 0); + map.insert (m_var->x (), eq_term, nullptr); TRACE ("qe", tout << "Using equality term: " << mk_pp (eq_term, m) << "\n"; ); @@ -680,7 +679,7 @@ namespace qe { } else { new_lit = m.mk_true (); } - map.insert (m_lits.get (i), new_lit, 0); + map.insert (m_lits.get (i), new_lit, nullptr); TRACE ("qe", tout << "Old literal: " << mk_pp (m_lits.get (i), m) << "\n"; tout << "New literal: " << mk_pp (new_lit, m) << "\n"; @@ -722,7 +721,7 @@ namespace qe { } else { new_lit = m.mk_true (); } - map.insert (m_lits.get (i), new_lit, 0); + map.insert (m_lits.get (i), new_lit, nullptr); TRACE ("qe", tout << "Old literal: " << mk_pp (m_lits.get (i), m) << "\n"; tout << "New literal: " << mk_pp (new_lit, m) << "\n"; @@ -738,7 +737,7 @@ namespace qe { rational r; cx = mk_mul (m_coeffs[max_t], m_var->x()); cxt = mk_add (cx, m_terms.get (max_t)); - VERIFY(mdl.eval(cxt, val, true)); + val = mdl(cxt); VERIFY(a.is_numeral(val, r)); // get the offset from the smallest/largest possible value for x @@ -796,13 +795,13 @@ namespace qe { // evaluate x in mdl rational r_x; - VERIFY(mdl.eval(m_var->x (), val, true)); + val = mdl(m_var->x ()); VERIFY(a.is_numeral (val, r_x)); for (unsigned i = 0; i < m_terms.size(); ++i) { rational const& ac = m_coeffs[i]; if (!m_eq[i] && ac.is_pos() == do_pos) { - VERIFY(mdl.eval(m_terms.get (i), val, true)); + val = mdl(m_terms.get (i)); VERIFY(a.is_numeral(val, r)); r /= abs(ac); // skip the literal if false in the model @@ -932,7 +931,7 @@ namespace qe { if (!all_done) continue; // all args so far have been processed // get the correct arg to use - proof* pr = 0; expr* new_arg = 0; + proof* pr = nullptr; expr* new_arg = nullptr; factored_terms.get (old_arg, new_arg, pr); if (new_arg) { // changed @@ -955,8 +954,7 @@ namespace qe { new_var = m.mk_fresh_const ("mod_var", d->get_range ()); eqs.push_back (m.mk_eq (new_var, new_term)); // obtain value of new_term in mdl - expr_ref val (m); - mdl.eval (new_term, val, true); + expr_ref val = mdl(new_term); // use the variable from now on new_term = new_var; changed = true; @@ -965,7 +963,7 @@ namespace qe { mdl.register_decl (new_var->get_decl (), val); } if (changed) { - factored_terms.insert (e, new_term, 0); + factored_terms.insert (e, new_term, nullptr); } done.mark (e, true); todo.pop_back (); @@ -973,7 +971,7 @@ namespace qe { } // mk new fml - proof* pr = 0; expr* new_fml = 0; + proof* pr = nullptr; expr* new_fml = nullptr; factored_terms.get (fml, new_fml, pr); if (new_fml) { fml = new_fml; @@ -994,9 +992,9 @@ namespace qe { * the divisibility atom is a special mod term ((t1-t2) % num == 0) */ void mod2div (expr_ref& fml, expr_map& map) { - expr* new_fml = 0; + expr* new_fml = nullptr; - proof *pr = 0; + proof *pr = nullptr; map.get (fml, new_fml, pr); if (new_fml) { fml = new_fml; @@ -1065,7 +1063,7 @@ namespace qe { new_fml = m.mk_app (a->get_decl (), children.size (), children.c_ptr ()); } - map.insert (fml, new_fml, 0); + map.insert (fml, new_fml, nullptr); fml = new_fml; } @@ -1133,7 +1131,7 @@ namespace qe { z); } } - map.insert (m_lits.get (i), new_lit, 0); + map.insert (m_lits.get (i), new_lit, nullptr); TRACE ("qe", tout << "Old literal: " << mk_pp (m_lits.get (i), m) << "\n"; tout << "New literal: " << mk_pp (new_lit, m) << "\n"; @@ -1145,7 +1143,7 @@ namespace qe { expr_substitution sub (m); // literals for (unsigned i = 0; i < lits.size (); i++) { - expr* new_lit = 0; proof* pr = 0; + expr* new_lit = nullptr; proof* pr = nullptr; app* old_lit = lits.get (i); map.get (old_lit, new_lit, pr); if (new_lit) { @@ -1157,7 +1155,7 @@ namespace qe { } } // substitute for x, if any - expr* x_term = 0; proof* pr = 0; + expr* x_term = nullptr; proof* pr = nullptr; map.get (m_var->x (), x_term, pr); if (x_term) { sub.insert (m_var->x (), x_term); @@ -1292,9 +1290,9 @@ namespace qe { model_evaluator_array_util m_mev; void reset_v () { - m_v = 0; + m_v = nullptr; m_has_stores_v.reset (); - m_subst_term_v = 0; + m_subst_term_v = nullptr; m_true_sub_v.reset (); m_false_sub_v.reset (); m_aux_lits_v.reset (); @@ -1302,7 +1300,7 @@ namespace qe { } void reset () { - M = 0; + M = nullptr; reset_v (); m_aux_vars.reset (); } @@ -1393,7 +1391,7 @@ namespace qe { todo.push_back (to_app (arg)); } else if (all_done) { // all done so far.. - expr* arg_new = 0; proof* pr; + expr* arg_new = nullptr; proof* pr; sel_cache.get (arg, arg_new, pr); if (!arg_new) { arg_new = arg; @@ -1413,7 +1411,7 @@ namespace qe { app_ref val_const (m.mk_fresh_const ("sel", val_sort), m); m_aux_vars.push_back (val_const); // extend M to include val_const - expr_ref val (m); + expr_ref val(m); m_mev.eval (*M, a_new, val); M->register_decl (val_const->get_decl (), val); // add equality @@ -1423,12 +1421,12 @@ namespace qe { } if (a != a_new) { - sel_cache.insert (a, a_new, 0); + sel_cache.insert (a, a_new, nullptr); pinned.push_back (a_new); } done.mark (a, true); } - expr* res = 0; proof* pr; + expr* res = nullptr; proof* pr; sel_cache.get (fml, res, pr); if (res) { fml = to_app (res); @@ -1478,7 +1476,7 @@ namespace qe { void find_subst_term (app* eq) { app_ref p_exp (m); - mk_peq (eq->get_arg (0), eq->get_arg (1), 0, 0, p_exp); + mk_peq (eq->get_arg (0), eq->get_arg (1), 0, nullptr, p_exp); bool subst_eq_found = false; while (true) { TRACE ("qe", @@ -1672,7 +1670,7 @@ namespace qe { expr* rhs = eq->get_arg (1); bool lhs_has_v = (lhs == m_v || m_has_stores_v.is_marked (lhs)); bool rhs_has_v = (rhs == m_v || m_has_stores_v.is_marked (rhs)); - app* store = 0; + app* store = nullptr; SASSERT (lhs_has_v || rhs_has_v); @@ -1829,7 +1827,7 @@ namespace qe { m_cache.reset (); m_pinned.reset (); m_idx_lits.reset (); - M = 0; + M = nullptr; m_arr_test.reset (); m_has_stores.reset (); m_reduce_all_selects = false; @@ -1864,7 +1862,7 @@ namespace qe { bool reduce (expr_ref& e) { if (!is_app (e)) return true; - expr *r = 0; + expr *r = nullptr; if (m_cache.find (e, r)) { e = r; return true; @@ -1882,7 +1880,7 @@ namespace qe { for (unsigned i = 0; i < a->get_num_args (); ++i) { expr *arg = a->get_arg (i); - expr *narg = 0; + expr *narg = nullptr; if (!is_app (arg)) args.push_back (arg); else if (m_cache.find (arg, narg)) { @@ -2025,7 +2023,7 @@ namespace qe { m_idx_vals.reset (); m_sel_consts.reset (); m_idx_lits.reset (); - M = 0; + M = nullptr; m_sub.reset (); m_arr_test.reset (); } @@ -2254,7 +2252,7 @@ namespace qe { void arith_project(model& mdl, app_ref_vector& vars, expr_ref& fml) { ast_manager& m = vars.get_manager(); arith_project_util ap(m); - atom_set pos_lits, neg_lits; + qe::atom_set pos_lits, neg_lits; is_relevant_default is_relevant; mk_atom_default mk_atom; get_nnf (fml, is_relevant, mk_atom, pos_lits, neg_lits); @@ -2264,7 +2262,7 @@ namespace qe { void arith_project(model& mdl, app_ref_vector& vars, expr_ref& fml, expr_map& map) { ast_manager& m = vars.get_manager(); arith_project_util ap(m); - atom_set pos_lits, neg_lits; + qe::atom_set pos_lits, neg_lits; is_relevant_default is_relevant; mk_atom_default mk_atom; get_nnf (fml, is_relevant, mk_atom, pos_lits, neg_lits); diff --git a/src/muz/spacer/spacer_qe_project.h b/src/muz/spacer/spacer_qe_project.h index b923dc3c7..65848f8f3 100644 --- a/src/muz/spacer/spacer_qe_project.h +++ b/src/muz/spacer/spacer_qe_project.h @@ -23,7 +23,7 @@ Notes: #include "model/model.h" #include "ast/expr_map.h" -namespace qe { +namespace spacer_qe { /** Loos-Weispfenning model-based projection for a basic conjunction. Lits is a vector of literals. diff --git a/src/muz/spacer/spacer_quant_generalizer.cpp b/src/muz/spacer/spacer_quant_generalizer.cpp new file mode 100644 index 000000000..f66fe7b29 --- /dev/null +++ b/src/muz/spacer/spacer_quant_generalizer.cpp @@ -0,0 +1,671 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation and Arie Gurfinkel + +Module Name: + + spacer_quant_generalizer.cpp + +Abstract: + + Quantified lemma generalizer. + +Author: + + + Yakir Vizel + Arie Gurfinkel + +Revision History: + +--*/ + + +#include "muz/spacer/spacer_context.h" +#include "muz/spacer/spacer_generalizers.h" +#include "muz/spacer/spacer_manager.h" +#include "ast/expr_abstract.h" +#include "ast/rewriter/var_subst.h" +#include "ast/for_each_expr.h" +#include "ast/rewriter/factor_equivs.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/substitution/matcher.h" +#include "ast/expr_functors.h" + +#include "muz/spacer/spacer_sem_matcher.h" + +using namespace spacer; + +namespace { +struct index_lt_proc : public std::binary_function { + arith_util m_arith; + index_lt_proc(ast_manager &m) : m_arith(m) {} + bool operator() (app *a, app *b) { + // XXX This order is a bit strange. + // XXX It does the job in our current application, but only because + // XXX we assume that we only compare expressions of the form (B + k), + // XXX where B is fixed and k is a number. + // XXX Might be better to specialize just for that specific use case. + rational val1, val2; + bool is_num1 = m_arith.is_numeral(a, val1); + bool is_num2 = m_arith.is_numeral(b, val2); + + if (is_num1 && is_num2) { + return val1 < val2; + } + else if (is_num1 != is_num2) { + return is_num1; + } + + is_num1 = false; + is_num2 = false; + // compare the first numeric argument of a to first numeric argument of b + // if available + for (unsigned i = 0, sz = a->get_num_args(); !is_num1 && i < sz; ++i) { + is_num1 = m_arith.is_numeral (a->get_arg(i), val1); + } + for (unsigned i = 0, sz = b->get_num_args(); !is_num2 && i < sz; ++i) { + is_num2 = m_arith.is_numeral(b->get_arg(i), val2); + } + + if (is_num1 && is_num2) { + return val1 < val2; + } + else if (is_num1 != is_num2) { + return is_num1; + } + else { + return a->get_id() < b->get_id(); + } + + } +}; + +} + +namespace spacer { + +lemma_quantifier_generalizer::lemma_quantifier_generalizer(context &ctx, + bool normalize_cube) : + lemma_generalizer(ctx), m(ctx.get_ast_manager()), m_arith(m), m_cube(m), + m_normalize_cube(normalize_cube), m_offset(0) {} + +void lemma_quantifier_generalizer::collect_statistics(statistics &st) const +{ + st.update("time.spacer.solve.reach.gen.quant", m_st.watch.get_seconds()); + st.update("quantifier gen", m_st.count); + st.update("quantifier gen failures", m_st.num_failures); +} + +/** + Finds candidates terms to be existentially abstracted. + A term t is a candidate if + (a) t is ground + + (b) t appears in an expression of the form (select A t) for some array A + + (c) t appears in an expression of the form (select A (+ t v)) + where v is ground + + The goal is to pick candidates that might result in a lemma in the + essentially uninterpreted fragment of FOL or its extensions. + */ +void lemma_quantifier_generalizer::find_candidates(expr *e, + app_ref_vector &candidates) { + if (!contains_selects(e, m)) return; + + app_ref_vector indices(m); + get_select_indices(e, indices, m); + + app_ref_vector extra(m); + expr_sparse_mark marked_args; + + // Make sure not to try and quantify already-quantified indices + for (unsigned idx=0, sz = indices.size(); idx < sz; idx++) { + // skip expressions that already contain a quantified variable + if (has_zk_const(indices.get(idx))) { + continue; + } + + app *index = indices.get(idx); + TRACE ("spacer_qgen", tout << "Candidate: "<< mk_pp(index, m) + << " in " << mk_pp(e, m) << "\n";); + extra.push_back(index); + if (m_arith.is_add(index)) { + for (expr * arg : *index) { + if (!is_app(arg) || marked_args.is_marked(arg)) {continue;} + marked_args.mark(arg); + candidates.push_back (to_app(arg)); + } + } + } + + std::sort(candidates.c_ptr(), candidates.c_ptr() + candidates.size(), + index_lt_proc(m)); + // keep actual select indecies in the order found at the back of + // candidate list. There is no particular reason for this order + candidates.append(extra); +} + + +/// returns true if expression e contains a sub-expression of the form (select A idx) where +/// idx contains exactly one skolem from zks. Returns idx and the skolem +bool lemma_quantifier_generalizer::match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk) { + if (zks.size() != 1) return false; + contains_app has_zk(m, zks.get(0)); + + if (!contains_selects(e, m)) return false; + app_ref_vector indicies(m); + get_select_indices(e, indicies, m); + if (indicies.size() > 2) return false; + + unsigned i=0; + if (indicies.size() == 1) { + if (!has_zk(indicies.get(0))) return false; + } + else { + if (has_zk(indicies.get(0)) && !has_zk(indicies.get(1))) + i = 0; + else if (!has_zk(indicies.get(0)) && has_zk(indicies.get(1))) + i = 1; + else if (!has_zk(indicies.get(0)) && !has_zk(indicies.get(1))) + return false; + } + + idx = indicies.get(i); + sk = zks.get(0); + return true; +} + +namespace { +expr* times_minus_one(expr *e, arith_util &arith) { + expr *r; + if (arith.is_times_minus_one (e, r)) { return r; } + + return arith.mk_mul(arith.mk_numeral(rational(-1), arith.is_int(get_sort(e))), e); +} +} + +/** Attempts to rewrite a cube so that quantified variable appears as + a top level argument of select-term + + Find sub-expression of the form (select A (+ sk!0 t)) and replaces + (+ sk!0 t) --> sk!0 and sk!0 --> (+ sk!0 (* (- 1) t)) + + Current implementation is an ugly hack for one special case +*/ +void lemma_quantifier_generalizer::cleanup(expr_ref_vector &cube, app_ref_vector const &zks, expr_ref &bind) { + if (zks.size() != 1) return; + + arith_util arith(m); + expr *idx = nullptr; + app *sk = nullptr; + expr_ref rep(m); + + for (expr *e : cube) { + if (match_sk_idx(e, zks, idx, sk)) { + CTRACE("spacer_qgen", idx != sk, + tout << "Possible cleanup of " << mk_pp(idx, m) << " in " + << mk_pp(e, m) << " on " << mk_pp(sk, m) << "\n";); + + if (!arith.is_add(idx)) continue; + app *a = to_app(idx); + bool found = false; + expr_ref_vector kids(m); + expr_ref_vector kids_bind(m); + for (expr* arg : *a) { + if (arg == sk) { + found = true; + kids.push_back(arg); + kids_bind.push_back(bind); + } + else { + kids.push_back (times_minus_one(arg, arith)); + kids_bind.push_back (times_minus_one(arg, arith)); + } + } + if (!found) continue; + + rep = arith.mk_add(kids.size(), kids.c_ptr()); + bind = arith.mk_add(kids_bind.size(), kids_bind.c_ptr()); + TRACE("spacer_qgen", + tout << "replace " << mk_pp(idx, m) << " with " << mk_pp(rep, m) << "\n";); + break; + } + } + + if (rep) { + expr_safe_replace rw(m); + rw.insert(sk, rep); + rw.insert(idx, sk); + rw(cube); + TRACE("spacer_qgen", + tout << "Cleaned cube to: " << mk_and(cube) << "\n";); + } +} + +/** + Create an abstract cube by abstracting a given term with a given variable. + On return, + gnd_cube contains all ground literals from m_cube + abs_cube contains all newly quantified literals from m_cube + lb contains an expression determining the lower bound on the variable + ub contains an expression determining the upper bound on the variable + + Conjunction of gnd_cube and abs_cube is the new quantified cube + + lb and ub are null if no bound was found + */ +void lemma_quantifier_generalizer::mk_abs_cube(lemma_ref &lemma, app *term, var *var, + expr_ref_vector &gnd_cube, + expr_ref_vector &abs_cube, + expr *&lb, expr *&ub, unsigned &stride) { + + // create an abstraction function that maps candidate term to variables + expr_safe_replace sub(m); + // term -> var + sub.insert(term , var); + rational val; + if (m_arith.is_numeral(term, val)) { + bool is_int = val.is_int(); + expr_ref minus_one(m); + minus_one = m_arith.mk_numeral(rational(-1), is_int); + + // term+1 -> var+1 if term is a number + sub.insert( + m_arith.mk_numeral(val + 1, is_int), + m_arith.mk_add(var, m_arith.mk_numeral(rational(1), is_int))); + // -term-1 -> -1*var + -1 if term is a number + sub.insert( + m_arith.mk_numeral(-1*val + -1, is_int), + m_arith.mk_add (m_arith.mk_mul (minus_one, var), minus_one)); + } + + lb = nullptr; + ub = nullptr; + + for (expr *lit : m_cube) { + expr_ref abs_lit(m); + sub (lit, abs_lit); + if (lit == abs_lit) { + gnd_cube.push_back(lit); + } + else { + expr *e1, *e2; + // generalize v=num into v>=num + if (m.is_eq(abs_lit, e1, e2) && (e1 == var || e2 == var)) { + if (m_arith.is_numeral(e1)) { + abs_lit = m_arith.mk_ge (var, e1); + } else if (m_arith.is_numeral(e2)) { + abs_lit = m_arith.mk_ge(var, e2); + } + } + abs_cube.push_back(abs_lit); + if (contains_selects(abs_lit, m)) { + expr_ref_vector pob_cube(m); + flatten_and(lemma->get_pob()->post(), pob_cube); + find_stride(pob_cube, abs_lit, stride); + } + + if (!lb && is_lb(var, abs_lit)) { + lb = abs_lit; + } + else if (!ub && is_ub(var, abs_lit)) { + ub = abs_lit; + } + } + } +} + +// -- returns true if e is an upper bound for var +bool lemma_quantifier_generalizer::is_ub(var *var, expr *e) { + expr *e1, *e2; + // var <= e2 + if ((m_arith.is_le (e, e1, e2) || m_arith.is_lt(e, e1, e2)) && var == e1) { + return true; + } + // e1 >= var + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && var == e2) { + return true; + } + + // t <= -1*var + if ((m_arith.is_le (e, e1, e2) || m_arith.is_lt(e, e1, e2)) + && m_arith.is_times_minus_one(e2, e2) && e2 == var) { + return true; + } + // -1*var >= t + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_times_minus_one(e1, e1) && e1 == var) { + return true; + } + // ! (var >= e2) + if (m.is_not (e, e1) && is_lb(var, e1)) { + return true; + } + // var + t1 <= t2 + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_add(e1)) { + app *a = to_app(e1); + for (expr* arg : *a) { + if (arg == var) return true; + } + } + // t1 <= t2 + -1*var + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_add(e2)) { + app *a = to_app(e2); + for (expr* arg : *a) { + if (m_arith.is_times_minus_one(arg, arg) && arg == var) + return true; + } + } + // t1 >= t2 + var + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_add(e2)) { + app *a = to_app(e2); + for (expr * arg : *a) { + if (arg == var) return true; + } + } + // -1*var + t1 >= t2 + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_add(e1)) { + app *a = to_app(e1); + for (expr * arg : *a) { + if (m_arith.is_times_minus_one(arg, arg) && arg == var) + return true; + } + } + return false; +} + +// -- returns true if e is a lower bound for var +bool lemma_quantifier_generalizer::is_lb(var *var, expr *e) { + expr *e1, *e2; + // var >= e2 + if ((m_arith.is_ge (e, e1, e2) || m_arith.is_gt(e, e1, e2)) && var == e1) { + return true; + } + // e1 <= var + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && var == e2) { + return true; + } + // t >= -1*var + if ((m_arith.is_ge (e, e1, e2) || m_arith.is_gt(e, e1, e2)) + && m_arith.is_times_minus_one(e2, e2) && e2 == var) { + return true; + } + // -1*var <= t + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_times_minus_one(e1, e1) && e1 == var) { + return true; + } + // ! (var <= e2) + if (m.is_not (e, e1) && is_ub(var, e1)) { + return true; + } + // var + t1 >= t2 + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_add(e1)) { + app *a = to_app(e1); + for (expr * arg : *a) { + if (arg == var) return true; + } + } + // t1 >= t2 + -1*var + if ((m_arith.is_ge(e, e1, e2) || m_arith.is_gt(e, e1, e2)) && + m_arith.is_add(e2)) { + app *a = to_app(e2); + for (expr * arg : *a) { + if (m_arith.is_times_minus_one(arg, arg) && arg == var) + return true; + } + } + // t1 <= t2 + var + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_add(e2)) { + app *a = to_app(e2); + for (expr * arg : *a) { + if (arg == var) return true; + } + } + // -1*var + t1 <= t2 + if ((m_arith.is_le(e, e1, e2) || m_arith.is_lt(e, e1, e2)) && + m_arith.is_add(e1)) { + app *a = to_app(e1); + for (expr * arg : *a) { + if (m_arith.is_times_minus_one(arg, arg) && arg == var) + return true; + } + } + + return false; +} + +bool lemma_quantifier_generalizer::generalize (lemma_ref &lemma, app *term) { + + expr *lb = nullptr, *ub = nullptr; + unsigned stride = 1; + expr_ref_vector gnd_cube(m); + expr_ref_vector abs_cube(m); + + var_ref var(m); + var = m.mk_var (m_offset, get_sort(term)); + + mk_abs_cube(lemma, term, var, gnd_cube, abs_cube, lb, ub, stride); + if (abs_cube.empty()) {return false;} + + TRACE("spacer_qgen", + tout << "abs_cube is: " << mk_and(abs_cube) << "\n"; + tout << "lb = "; + if (lb) tout << mk_pp(lb, m); else tout << "none"; + tout << "\n"; + tout << "ub = "; + if (ub) tout << mk_pp(ub, m); else tout << "none"; + tout << "\n";); + + if (!lb && !ub) + return false; + + // -- guess lower or upper bound if missing + if (!lb) { + abs_cube.push_back (m_arith.mk_ge (var, term)); + lb = abs_cube.back(); + } + if (!ub) { + abs_cube.push_back (m_arith.mk_lt(var, term)); + ub = abs_cube.back(); + } + + rational init; + expr_ref constant(m); + if (is_var(to_app(lb)->get_arg(0))) + constant = to_app(lb)->get_arg(1); + else + constant = to_app(lb)->get_arg(0); + + if (stride > 1 && m_arith.is_numeral(constant, init)) { + unsigned mod = init.get_unsigned() % stride; + TRACE("spacer_qgen", + tout << "mod=" << mod << " init=" << init << " stride=" << stride << "\n"; + tout.flush();); + abs_cube.push_back(m.mk_eq( + m_arith.mk_mod(var, m_arith.mk_numeral(rational(stride), true)), + m_arith.mk_numeral(rational(mod), true))); + } + + // skolemize + expr_ref gnd(m); + app_ref_vector zks(m); + ground_expr(mk_and(abs_cube), gnd, zks); + flatten_and(gnd, gnd_cube); + + TRACE("spacer_qgen", + tout << "New CUBE is: " << gnd_cube << "\n";); + + // check if the result is a true lemma + unsigned uses_level = 0; + pred_transformer &pt = lemma->get_pob()->pt(); + if (pt.check_inductive(lemma->level(), gnd_cube, uses_level, lemma->weakness())) { + TRACE("spacer_qgen", + tout << "Quantifier Generalization Succeeded!\n" + << "New CUBE is: " << gnd_cube << "\n";); + SASSERT(zks.size() >= static_cast(m_offset)); + + // lift quantified variables to top of select + expr_ref ext_bind(m); + ext_bind = term; + cleanup(gnd_cube, zks, ext_bind); + + // XXX better do that check before changing bind in cleanup() + // XXX Or not because substitution might introduce _n variable into bind + if (m_ctx.get_manager().is_n_formula(ext_bind)) { + // XXX this creates an instance, but not necessarily the needed one + + // XXX This is sound because any instance of + // XXX universal quantifier is sound + + // XXX needs better long term solution. leave + // comment here for the future + m_ctx.get_manager().formula_n2o(ext_bind, ext_bind, 0); + } + + lemma->update_cube(lemma->get_pob(), gnd_cube); + lemma->set_level(uses_level); + + SASSERT(var->get_idx() < zks.size()); + SASSERT(is_app(ext_bind)); + lemma->add_skolem(zks.get(var->get_idx()), to_app(ext_bind)); + return true; + } + + return false; +} + +bool lemma_quantifier_generalizer::find_stride(expr_ref_vector &c, expr_ref &pattern, unsigned &stride) { + expr_ref tmp(m); + tmp = mk_and(c); + normalize(tmp, tmp, false, true); + c.reset(); + flatten_and(tmp, c); + + app_ref_vector indices(m); + get_select_indices(pattern, indices, m); + + // TODO + if (indices.size() > 1) + return false; + + app *p_index = indices.get(0); + if (is_var(p_index)) return false; + + std::vector instances; + for (expr* lit : c) { + + if (!contains_selects(lit, m)) + continue; + + indices.reset(); + get_select_indices(lit, indices, m); + + // TODO: + if (indices.size() > 1) + continue; + + app *candidate = indices.get(0); + + unsigned size = p_index->get_num_args(); + unsigned matched = 0; + for (unsigned p=0; p < size; p++) { + expr *arg = p_index->get_arg(p); + if (is_var(arg)) { + rational val; + if (p < candidate->get_num_args() && + m_arith.is_numeral(candidate->get_arg(p), val) && + val.is_unsigned()) { + instances.push_back(val.get_unsigned()); + } + } + else { + for (expr* cand : *candidate) { + if (cand == arg) { + matched++; + break; + } + } + } + } + + if (matched < size - 1) + continue; + + if (candidate->get_num_args() == matched) + instances.push_back(0); + + TRACE("spacer_qgen", + tout << "Match succeeded!\n";); + } + + if (instances.size() <= 1) + return false; + + std::sort(instances.begin(), instances.end()); + + stride = instances[1]-instances[0]; + TRACE("spacer_qgen", tout << "Index Stride is: " << stride << "\n";); + + return true; +} + +void lemma_quantifier_generalizer::operator()(lemma_ref &lemma) { + if (lemma->get_cube().empty()) return; + if (!lemma->has_pob()) return; + + m_st.count++; + scoped_watch _w_(m_st.watch); + + TRACE("spacer_qgen", + tout << "initial cube: " << mk_and(lemma->get_cube()) << "\n";); + + // setup the cube + m_cube.reset(); + m_cube.append(lemma->get_cube()); + + if (m_normalize_cube) { + // -- re-normalize the cube + expr_ref c(m); + c = mk_and(m_cube); + normalize(c, c, false, true); + m_cube.reset(); + flatten_and(c, m_cube); + TRACE("spacer_qgen", + tout << "normalized cube:\n" << mk_and(m_cube) << "\n";); + } + + // first unused free variable + m_offset = lemma->get_pob()->get_free_vars_size(); + + // for every literal, find a candidate term to abstract + for (unsigned i=0; i < m_cube.size(); i++) { + expr *r = m_cube.get(i); + + // generate candidates for abstraction + app_ref_vector candidates(m); + find_candidates(r, candidates); + if (candidates.empty()) continue; + + // for every candidate + for (unsigned arg=0, sz = candidates.size(); arg < sz; arg++) { + if (generalize (lemma, candidates.get(arg))) { + return; + } + else { + ++m_st.num_failures; + } + } + } +} + + + +} diff --git a/src/muz/spacer/spacer_sat_answer.cpp b/src/muz/spacer/spacer_sat_answer.cpp new file mode 100644 index 000000000..30241230f --- /dev/null +++ b/src/muz/spacer/spacer_sat_answer.cpp @@ -0,0 +1,181 @@ +#include "muz/spacer/spacer_sat_answer.h" +#include "muz/base/dl_context.h" +#include "muz/base/dl_rule.h" + +#include "smt/smt_solver.h" + +namespace spacer { + +struct ground_sat_answer_op::frame { + reach_fact *m_rf; + pred_transformer &m_pt; + expr_ref_vector m_gnd_subst; + expr_ref m_gnd_eq; + expr_ref m_fact; + unsigned m_visit; + expr_ref_vector m_kids; + + frame(reach_fact *rf, pred_transformer &pt, const expr_ref_vector &gnd_subst) : + m_rf(rf), m_pt(pt), + m_gnd_subst(gnd_subst), + m_gnd_eq(pt.get_ast_manager()), + m_fact(pt.get_ast_manager()), + m_visit(0), + m_kids(pt.get_ast_manager()) { + + ast_manager &m = pt.get_ast_manager(); + spacer::manager &pm = pt.get_manager(); + + m_fact = m.mk_app(head(), m_gnd_subst.size(), m_gnd_subst.c_ptr()); + if (pt.head()->get_arity() == 0) + m_gnd_eq = m.mk_true(); + else { + SASSERT(m_gnd_subst.size() == pt.head()->get_arity()); + for (unsigned i = 0, sz = pt.sig_size(); i < sz; ++i) { + m_gnd_eq = m.mk_eq(m.mk_const(pm.o2n(pt.sig(i), 0)), + m_gnd_subst.get(i)); + } + } + } + + func_decl* head() {return m_pt.head();} + expr* fact() {return m_fact;} + const datalog::rule &rule() {return m_rf->get_rule();} + pred_transformer &pt() {return m_pt;} +}; + +ground_sat_answer_op::ground_sat_answer_op(context &ctx) : + m_ctx(ctx), m(m_ctx.get_ast_manager()), m_pm(m_ctx.get_manager()), + m_pinned(m) { + m_solver = mk_smt_solver(m, params_ref::get_empty(), symbol::null); +} + +proof_ref ground_sat_answer_op::operator()(pred_transformer &query) { + + + vector todo, new_todo; + + // -- find substitution for a query if query is not nullary + expr_ref_vector qsubst(m); + if (query.head()->get_arity() > 0) { + solver::scoped_push _s_(*m_solver); + m_solver->assert_expr(query.get_last_rf()->get()); + lbool res = m_solver->check_sat(0, nullptr); + (void)res; + SASSERT(res == l_true); + model_ref mdl; + m_solver->get_model(mdl); + model::scoped_model_completion _scm(mdl, true); + for (unsigned i = 0, sz = query.sig_size(); i < sz; ++i) { + expr_ref arg(m), val(m); + arg = m.mk_const(m_pm.o2n(query.sig(i), 0)); + val = (*mdl)(arg); + qsubst.push_back(val); + } + } + + todo.push_back(frame(query.get_last_rf(), query, qsubst)); + expr_ref root_fact(m); + root_fact = todo.back().fact(); + + while (!todo.empty()) { + frame &curr = todo.back(); + if (m_cache.contains(curr.fact())) { + todo.pop_back(); + continue; + } + + if (curr.m_visit == 0) { + new_todo.reset(); + mk_children(curr, new_todo); + curr.m_visit = 1; + // curr becomes invalid + todo.append(new_todo); + } + else { + proof* pf = mk_proof_step(curr); + m_pinned.push_back(curr.fact()); + m_cache.insert(curr.fact(), pf); + todo.pop_back(); + } + } + return proof_ref(m_cache.find(root_fact), m); +} + + +void ground_sat_answer_op::mk_children(frame &fr, vector &todo) { + const datalog::rule &r = fr.rule(); + ptr_vector preds; + fr.pt().find_predecessors(r, preds); + + if (preds.empty()) return; + + const reach_fact_ref_vector &kid_rfs = fr.m_rf->get_justifications(); + solver::scoped_push _s_(*m_solver); + m_solver->assert_expr(fr.m_gnd_eq); + unsigned ut_sz = r.get_uninterpreted_tail_size(); + for (unsigned i = 0; i < ut_sz; ++i) { + expr_ref f(m); + m_pm.formula_n2o(kid_rfs.get(i)->get(), f, i); + m_solver->assert_expr(f); + } + m_solver->assert_expr(fr.pt().transition()); + m_solver->assert_expr(fr.pt().rule2tag(&r)); + + lbool res = m_solver->check_sat(0, nullptr); + (void)res; + VERIFY(res == l_true); + + model_ref mdl; + m_solver->get_model(mdl); + expr_ref_vector subst(m); + for (unsigned i = 0, sz = preds.size(); i < sz; ++i) { + subst.reset(); + mk_child_subst_from_model(preds.get(i), i, mdl, subst); + todo.push_back(frame(kid_rfs.get(i), + m_ctx.get_pred_transformer(preds.get(i)), subst)); + fr.m_kids.push_back(todo.back().fact()); + } +} + + +void ground_sat_answer_op::mk_child_subst_from_model(func_decl *pred, + unsigned j, model_ref &mdl, + expr_ref_vector &subst) { + + model::scoped_model_completion _scm(mdl, true); + pred_transformer &pt = m_ctx.get_pred_transformer(pred); + for (unsigned i = 0, sz = pt.sig_size(); i < sz; ++i) { + expr_ref arg(m), val(m); + arg = m.mk_const(m_pm.o2o(pt.sig(i), 0, j)); + val = (*mdl)(arg); + subst.push_back(val); + } +} + +proof *ground_sat_answer_op::mk_proof_step(frame &fr) { + svector> positions; + vector substs; + + proof_ref_vector premises(m); + datalog::rule_manager &rm = m_ctx.get_datalog_context().get_rule_manager(); + expr_ref rule_fml(m); + rm.to_formula(fr.rule(), rule_fml); + // premises.push_back(fr.rule().get_proof()); + premises.push_back(m.mk_asserted(rule_fml)); + for (auto &k : fr.m_kids) {premises.push_back(m_cache.find(k));} + + for (unsigned i = 0; i < premises.size(); i++) { + positions.push_back(std::make_pair(0,i)); + } + for (unsigned i = 0; i <= premises.size(); i++) { + substs.push_back(expr_ref_vector(m)); + } + m_pinned.push_back(m.mk_hyper_resolve(premises.size(), + premises.c_ptr(), + fr.fact(), + positions, substs)); + return to_app(m_pinned.back()); +} + +} diff --git a/src/muz/spacer/spacer_sat_answer.h b/src/muz/spacer/spacer_sat_answer.h new file mode 100644 index 000000000..6cfd3b14c --- /dev/null +++ b/src/muz/spacer/spacer_sat_answer.h @@ -0,0 +1,55 @@ +/*++ +Copyright (c) 2018 Arie Gurfinkel + +Module Name: + + spacer_sat_answer.h + +Abstract: + + Compute refutation proof for CHC + +Author: + + Arie Gurfinkel + +Revision History: + +--*/ + +#ifndef _SPACER_SAT_ANSWER_H_ +#define _SPACER_SAT_ANSWER_H_ + +#include "muz/spacer/spacer_context.h" +#include "ast/ast.h" +#include "util/obj_hashtable.h" +#include "model/model.h" +#include "solver/solver.h" + +namespace spacer { + +class ground_sat_answer_op { + context &m_ctx; + ast_manager &m; + manager &m_pm; + + expr_ref_vector m_pinned; + obj_map m_cache; + + ref m_solver; + + struct frame; + + proof *mk_proof_step(frame &fr); + void mk_children(frame &fr, vector &todo); + void mk_child_subst_from_model(func_decl *pred, unsigned i, + model_ref &mdl, expr_ref_vector &subst); + +public: + ground_sat_answer_op(context &ctx); + + proof_ref operator() (pred_transformer &query); +}; +} + +#endif diff --git a/src/muz/spacer/spacer_sem_matcher.cpp b/src/muz/spacer/spacer_sem_matcher.cpp new file mode 100644 index 000000000..f3c2af8a9 --- /dev/null +++ b/src/muz/spacer/spacer_sem_matcher.cpp @@ -0,0 +1,147 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation and Arie Gurfinkel + +Module Name: + + sem_matcher.cpp + +Abstract: + + + +Author: + + Leonardo de Moura (leonardo) 2008-02-02. + Arie Gurfinkel + +Revision History: + +--*/ +#include "muz/spacer/spacer_sem_matcher.h" + +namespace spacer { + +sem_matcher::sem_matcher(ast_manager &man) : m(man), m_arith(m), m_pinned(m) {} + +bool sem_matcher::match_var (var *v, expr *e) { + expr_offset r; + if (m_subst->find(v, 0, r)) { + if (!m.are_equal(r.get_expr(), e)) { + return false; + } + } + else { + m_subst->insert(v, 0, expr_offset(e, 1)); + } + return true; +} +bool sem_matcher::operator()(expr * e1, expr * e2, substitution & s, bool &pos) { + reset(); + m_subst = &s; + m_todo.push_back(expr_pair(e1, e2)); + + // true on the first run through the loop + bool top = true; + pos = true; + while (!m_todo.empty()) { + expr_pair const & p = m_todo.back(); + + if (is_var(p.first)) { + if (!match_var(to_var(p.first), p.second)) { + return false; + } + m_todo.pop_back(); + top = false; + continue; + } + + + if (is_var(p.second)) + return false; + if (!is_app(p.first)) + return false; + if (!is_app(p.second)) + return false; + + app * n1 = to_app(p.first); + app * n2 = to_app(p.second); + + expr *t = nullptr; + + // strip negation + if (top && n1->get_decl() != n2->get_decl()) { + if (m.is_not(n1, t) && !m.is_not(n2) && is_app(t) && + to_app(t)->get_decl() == n2->get_decl()) { + pos = false; + n1 = to_app(e1); + } + else if (!m.is_not(n1) && m.is_not(n2, t) && is_app(t) && + to_app(t)->get_decl() == n1->get_decl()) { + pos = false; + n2 = to_app(t); + } + } + top = false; + + if (n1->get_decl() != n2->get_decl()) { + expr *e1 = nullptr, *e2 = nullptr; + rational val1, val2; + + // x<=y == !(x>y) + if (m_arith.is_le(n1) && m.is_not(n2, t) && m_arith.is_gt(t)) { + n2 = to_app(t); + } + else if (m_arith.is_le(n2) && m.is_not(n1, t) && m_arith.is_gt(t)) { + n1 = to_app(t); + } + // x>=y == !(xget_num_args(); + if (num_args1 != n2->get_num_args()) + return false; + + m_todo.pop_back(); + + if (num_args1 == 0) + continue; + + unsigned j = num_args1; + while (j > 0) { + --j; + m_todo.push_back(expr_pair(n1->get_arg(j), n2->get_arg(j))); + } + } + return true; +} + +void sem_matcher::reset() { + m_todo.reset(); + m_pinned.reset(); +} +} diff --git a/src/muz/spacer/spacer_sem_matcher.h b/src/muz/spacer/spacer_sem_matcher.h new file mode 100644 index 000000000..7d169166e --- /dev/null +++ b/src/muz/spacer/spacer_sem_matcher.h @@ -0,0 +1,69 @@ +/*++ +Copyright (c) 2006 Microsoft Corporation and Arie Gurfinkel + +Module Name: + + sem_matcher.h + +Abstract: + + Semantic matcher + +Author: + + Leonardo de Moura (leonardo) 2008-02-02. + Arie Gurfinkel + +Revision History: + +--*/ +#ifndef SPACER_SEM_MATCHER_H_ +#define SPACER_SEM_MATCHER_H_ + +#include "ast/substitution/substitution.h" +#include "ast/arith_decl_plugin.h" +#include "util/hashtable.h" + +namespace spacer { +/** + \brief Functor for matching expressions. +*/ +class sem_matcher { + typedef std::pair expr_pair; + typedef pair_hash, obj_ptr_hash > expr_pair_hash; + typedef hashtable > cache; + + ast_manager &m; + arith_util m_arith; + expr_ref_vector m_pinned; + substitution * m_subst; + svector m_todo; + + void reset(); + + bool match_var(var *v, expr *e); +public: + sem_matcher(ast_manager &man); + + /** + \brief Return true if e2 is an instance of e1. + In case of success (result is true), it will store the substitution that makes e1 equals to e2 into s. + Sets pos to true if the match is positive and to false if it is negative (i.e., e1 equals !e2) + + For example: + 1) e1 = f(g(x), x), e2 = f(g(h(a)), h(a)) + The result is true, and s will contain x -> h(a) + + 2) e1 = f(a, x) e2 = f(x, a) + The result is false. + + 3) e1 = f(x, x) e2 = f(y, a) + The result is false + + 4) e1 = f(x, y) e2 = f(h(z), a) + The result is true, and s contains x->h(z) and y->a + */ + bool operator()(expr * e1, expr * e2, substitution & s, bool &pos); +}; +} +#endif /* SPACER_SEM_MATCHER_H_ */ diff --git a/src/muz/spacer/spacer_smt_context_manager.cpp b/src/muz/spacer/spacer_smt_context_manager.cpp deleted file mode 100644 index e6ab2c883..000000000 --- a/src/muz/spacer/spacer_smt_context_manager.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - spacer_smt_context_manager.cpp - -Abstract: - - Manager of smt contexts - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-26. - Arie Gurfinkel -Revision History: - ---*/ - - -#include "ast/ast_pp.h" -#include "ast/ast_pp_util.h" -#include "ast/ast_smt_pp.h" - -#include "smt/smt_context.h" -#include "smt/params/smt_params.h" - -#include "muz/spacer/spacer_util.h" -#include "muz/spacer/spacer_smt_context_manager.h" -namespace spacer { - - - - -smt_context_manager::smt_context_manager(ast_manager &m, - unsigned max_num_contexts, - const params_ref &p) : - m_fparams(p), - m(m), - m_max_num_contexts(max_num_contexts), - m_num_contexts(0) { m_stats.reset();} - - -smt_context_manager::~smt_context_manager() -{ - std::for_each(m_solvers.begin(), m_solvers.end(), - delete_proc()); -} - -virtual_solver* smt_context_manager::mk_fresh() -{ - ++m_num_contexts; - virtual_solver_factory *solver_factory = 0; - - if (m_max_num_contexts == 0 || m_solvers.size() < m_max_num_contexts) { - m_solvers.push_back(alloc(spacer::virtual_solver_factory, m, m_fparams)); - solver_factory = m_solvers.back(); - } else - { solver_factory = m_solvers[(m_num_contexts - 1) % m_max_num_contexts]; } - - return solver_factory->mk_solver(); -} - -void smt_context_manager::collect_statistics(statistics& st) const -{ - for (unsigned i = 0; i < m_solvers.size(); ++i) { - m_solvers[i]->collect_statistics(st); - } -} - -void smt_context_manager::reset_statistics() -{ - for (unsigned i = 0; i < m_solvers.size(); ++i) { - m_solvers[i]->reset_statistics(); - } -} - - -}; diff --git a/src/muz/spacer/spacer_smt_context_manager.h b/src/muz/spacer/spacer_smt_context_manager.h deleted file mode 100644 index 0df9bc77b..000000000 --- a/src/muz/spacer/spacer_smt_context_manager.h +++ /dev/null @@ -1,68 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - spacer_smt_context_manager.h - -Abstract: - - Manager of smt contexts - -Author: - - Nikolaj Bjorner (nbjorner) 2011-11-26. - Arie Gurfinkel -Revision History: - ---*/ - -#ifndef _SPACER_SMT_CONTEXT_MANAGER_H_ -#define _SPACER_SMT_CONTEXT_MANAGER_H_ - -#include "util/stopwatch.h" - -#include "smt/smt_kernel.h" -#include "muz/base/dl_util.h" -#include "muz/spacer/spacer_virtual_solver.h" - -namespace spacer { - -class smt_context_manager { - - struct stats { - unsigned m_num_smt_checks; - unsigned m_num_sat_smt_checks; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - smt_params m_fparams; - ast_manager& m; - unsigned m_max_num_contexts; - ptr_vector m_solvers; - unsigned m_num_contexts; - - - stats m_stats; - stopwatch m_check_watch; - stopwatch m_check_sat_watch; - -public: - smt_context_manager(ast_manager& m, unsigned max_num_contexts = 1, - const params_ref &p = params_ref::get_empty()); - - ~smt_context_manager(); - virtual_solver* mk_fresh(); - - void collect_statistics(statistics& st) const; - void reset_statistics(); - - void updt_params(params_ref const &p) { m_fparams.updt_params(p); } - smt_params& fparams() {return m_fparams;} - -}; - -}; - -#endif diff --git a/src/muz/spacer/spacer_sym_mux.cpp b/src/muz/spacer/spacer_sym_mux.cpp index ee1d3726d..cfe0908d6 100644 --- a/src/muz/spacer/spacer_sym_mux.cpp +++ b/src/muz/spacer/spacer_sym_mux.cpp @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2011 Microsoft Corporation +Copyright (c) 2018 Arie Gurfinkel and Microsoft Corporation Module Name: @@ -7,10 +7,12 @@ Module Name: Abstract: - A symbol multiplexer that helps with having multiple versions of each of a set of symbols. + A symbol multiplexer that helps with having multiple versions of + each of a set of symbols. Author: + Arie Gurfinkel Krystof Hoder (t-khoder) 2011-9-8. Revision History: @@ -22,345 +24,123 @@ Revision History: #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" -#include "model/model.h" - #include "muz/spacer/spacer_util.h" #include "muz/spacer/spacer_sym_mux.h" using namespace spacer; -sym_mux::sym_mux(ast_manager & m, const std::vector & suffixes) - : m(m), m_ref_holder(m), m_next_sym_suffix_idx(0), m_suffixes(suffixes) -{ - for (std::string const& s : m_suffixes) { - m_used_suffixes.insert(symbol(s.c_str())); +sym_mux::sym_mux(ast_manager & m) : m(m) {} +sym_mux::~sym_mux() { + for (auto &entry : m_entries) { + dealloc(entry.m_value); } } -std::string sym_mux::get_suffix(unsigned i) const -{ - while (m_suffixes.size() <= i) { - std::string new_suffix; - symbol new_syffix_sym; - do { - std::stringstream stm; - stm << '_' << m_next_sym_suffix_idx; - m_next_sym_suffix_idx++; - new_suffix = stm.str(); - new_syffix_sym = symbol(new_suffix.c_str()); - } while (m_used_suffixes.contains(new_syffix_sym)); - m_used_suffixes.insert(new_syffix_sym); - m_suffixes.push_back(new_suffix); - } - return m_suffixes[i]; +func_decl_ref sym_mux::mk_variant(func_decl *fdecl, unsigned i) const { + func_decl_ref v(m); + std::string name = fdecl->get_name().str(); + std::string suffix = "_"; + suffix += i == 0 ? "n" : std::to_string(i - 1); + name += suffix; + v = m.mk_func_decl(symbol(name.c_str()), fdecl->get_arity(), + fdecl->get_domain(), fdecl->get_range()); + return v; } -void sym_mux::create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range, - unsigned tuple_length, decl_vector & tuple) -{ - SASSERT(tuple_length > 0); - while (tuple.size() < tuple_length) { - tuple.push_back(0); - } - SASSERT(tuple.size() == tuple_length); - std::string pre = prefix->get_name().str(); - for (unsigned i = 0; i < tuple_length; i++) { +void sym_mux::register_decl(func_decl *fdecl) { + sym_mux_entry *entry = alloc(sym_mux_entry, m); + entry->m_main = fdecl; + entry->m_variants.push_back(mk_variant(fdecl, 0)); + entry->m_variants.push_back(mk_variant(fdecl, 1)); - if (tuple[i] != 0) { - SASSERT(tuple[i]->get_arity() == arity); - SASSERT(tuple[i]->get_range() == range); - //domain should match as well, but we won't bother checking an array equality - } else { - std::string name = pre + get_suffix(i); - tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, domain, range); - } - m_ref_holder.push_back(tuple[i]); - m_sym2idx.insert(tuple[i], i); - m_sym2prim.insert(tuple[i], tuple[0]); - } - - m_prim2all.insert(tuple[0], tuple); - m_prefix2prim.insert(prefix, tuple[0]); - m_prim2prefix.insert(tuple[0], prefix); - m_prim_preds.push_back(tuple[0]); - m_ref_holder.push_back(prefix); + m_entries.insert(fdecl, entry); + m_muxes.insert(entry->m_variants.get(0), std::make_pair(entry, 0)); + m_muxes.insert(entry->m_variants.get(1), std::make_pair(entry, 1)); } - -void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) const -{ - SASSERT(m_prim2all.contains(prim)); - decl_vector& tuple = m_prim2all.find_core(prim)->get_data().m_value; - SASSERT(tuple[0] == prim); - - if (sz <= tuple.size()) { return; } - - func_decl * prefix; - TRUSTME(m_prim2prefix.find(prim, prefix)); - std::string prefix_name = prefix->get_name().bare_str(); - for (unsigned i = tuple.size(); i < sz; ++i) { - std::string name = prefix_name + get_suffix(i); - func_decl * new_sym = m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(), - prefix->get_domain(), prefix->get_range()); - - tuple.push_back(new_sym); - m_ref_holder.push_back(new_sym); - m_sym2idx.insert(new_sym, i); - m_sym2prim.insert(new_sym, prim); +void sym_mux::ensure_capacity(sym_mux_entry &entry, unsigned sz) const { + while (entry.m_variants.size() < sz) { + unsigned idx = entry.m_variants.size(); + entry.m_variants.push_back (mk_variant(entry.m_main, idx)); + m_muxes.insert(entry.m_variants.back(), std::make_pair(&entry, idx)); } } -func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const -{ - if (src_idx == tgt_idx) { return sym; } - func_decl * prim = (src_idx == 0) ? sym : get_primary(sym); - if (tgt_idx > src_idx) { - ensure_tuple_size(prim, tgt_idx + 1); - } - decl_vector & sym_vect = m_prim2all.find_core(prim)->get_data().m_value; - SASSERT(sym_vect[src_idx] == sym); - return sym_vect[tgt_idx]; +bool sym_mux::find_idx(func_decl * sym, unsigned & idx) const { + std::pair entry; + if (m_muxes.find(sym, entry)) {idx = entry.second; return true;} + return false; } - -func_decl * sym_mux::get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, - unsigned arity, sort * const * domain, sort * range) -{ - func_decl * prim = try_get_primary_by_prefix(prefix); - if (prim) { - SASSERT(prim->get_arity() == arity); - SASSERT(prim->get_range() == range); - //domain should match as well, but we won't bother checking an array equality - - return conv(prim, 0, idx); +func_decl * sym_mux::find_by_decl(func_decl* fdecl, unsigned idx) const { + sym_mux_entry *entry = nullptr; + if (m_entries.find(fdecl, entry)) { + ensure_capacity(*entry, idx+1); + return entry->m_variants.get(idx); } - - decl_vector syms; - create_tuple(prefix, arity, domain, range, idx + 1, syms); - return syms[idx]; + return nullptr; } -bool sym_mux::is_muxed_lit(expr * e, unsigned idx) const -{ - if (!is_app(e)) { return false; } - app * a = to_app(e); - if (m.is_not(a) && is_app(a->get_arg(0))) { - a = to_app(a->get_arg(0)); +func_decl * sym_mux::shift_decl(func_decl * decl, + unsigned src_idx, unsigned tgt_idx) const { + std::pair entry; + if (m_muxes.find(decl, entry)) { + SASSERT(entry.second == src_idx); + ensure_capacity(*entry.first, tgt_idx + 1); + return entry.first->m_variants.get(tgt_idx); } - return is_muxed(a->get_decl()); + UNREACHABLE(); + return nullptr; } +namespace { +struct formula_checker { + formula_checker(const sym_mux & parent, unsigned idx) : + m_parent(parent), m_idx(idx), m_found(false) {} -struct sym_mux::formula_checker { - formula_checker(const sym_mux & parent, bool all, unsigned idx) : - m_parent(parent), m_all(all), m_idx(idx), - m_found_what_needed(false) - { - } - - void operator()(expr * e) - { - if (m_found_what_needed || !is_app(e)) { return; } + void operator()(expr * e) { + if (m_found || !is_app(e)) { return; } func_decl * sym = to_app(e)->get_decl(); unsigned sym_idx; - if (!m_parent.try_get_index(sym, sym_idx)) { return; } + if (!m_parent.find_idx(sym, sym_idx)) { return; } bool have_idx = sym_idx == m_idx; - - if (m_all ? (!have_idx) : have_idx) { - m_found_what_needed = true; - } - + m_found = !have_idx; } - bool all_have_idx() const - { - SASSERT(m_all); //we were looking for the queried property - return !m_found_what_needed; - } - - bool some_with_idx() const - { - SASSERT(!m_all); //we were looking for the queried property - return m_found_what_needed; - } + bool all_have_idx() const {return !m_found;} private: const sym_mux & m_parent; - bool m_all; unsigned m_idx; - - /** - If we check whether all muxed symbols are of given index, we look for - counter-examples, checking whether form contains a muxed symbol of an index, - we look for symbol of index m_idx. - */ - bool m_found_what_needed; + bool m_found; }; - -bool sym_mux::contains(expr * e, unsigned idx) const -{ - formula_checker chck(*this, false, idx); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return chck.some_with_idx(); } -bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const -{ - formula_checker chck(*this, true, idx); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return chck.all_have_idx(); +bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const { + expr_mark visited; + formula_checker fck(*this, idx); + for_each_expr(fck, visited, e); + return fck.all_have_idx(); } -bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const -{ - expr * const * begin = vect.c_ptr(); - expr * const * end = begin + vect.size(); - for (expr * const * it = begin; it != end; it++) { - if (!is_homogenous_formula(*it, idx)) { - return false; - } - } - return true; -} - -class sym_mux::index_collector { - sym_mux const& m_parent; - svector m_indices; -public: - index_collector(sym_mux const& s): - m_parent(s) {} - - void operator()(expr * e) - { - if (is_app(e)) { - func_decl * sym = to_app(e)->get_decl(); - unsigned idx; - if (m_parent.try_get_index(sym, idx)) { - SASSERT(idx > 0); - --idx; - if (m_indices.size() <= idx) { - m_indices.resize(idx + 1, false); - } - m_indices[idx] = true; - } - } - } - - void extract(unsigned_vector& indices) - { - for (unsigned i = 0; i < m_indices.size(); ++i) { - if (m_indices[i]) { - indices.push_back(i); - } - } - } -}; - - - -void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const -{ - indices.reset(); - index_collector collector(*this); - for_each_expr(collector, m_visited, e); - m_visited.reset(); - collector.extract(indices); -} - -class sym_mux::variable_collector { - sym_mux const& m_parent; - vector >& m_vars; -public: - variable_collector(sym_mux const& s, vector >& vars): - m_parent(s), m_vars(vars) {} - - void operator()(expr * e) - { - if (is_app(e)) { - func_decl * sym = to_app(e)->get_decl(); - unsigned idx; - if (m_parent.try_get_index(sym, idx)) { - SASSERT(idx > 0); - --idx; - if (m_vars.size() <= idx) { - m_vars.resize(idx + 1, ptr_vector()); - } - m_vars[idx].push_back(to_app(e)); - } - } - } -}; - -void sym_mux::collect_variables(expr* e, vector >& vars) const -{ - vars.reset(); - variable_collector collector(*this, vars); - for_each_expr(collector, m_visited, e); - m_visited.reset(); -} - -class sym_mux::hmg_checker { - const sym_mux & m_parent; - - bool m_found_idx; - unsigned m_idx; - bool m_multiple_indexes; - -public: - hmg_checker(const sym_mux & parent) : - m_parent(parent), m_found_idx(false), m_multiple_indexes(false) - { - } - - void operator()(expr * e) - { - if (m_multiple_indexes || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - unsigned sym_idx; - if (!m_parent.try_get_index(sym, sym_idx)) { return; } - - if (!m_found_idx) { - m_found_idx = true; - m_idx = sym_idx; - return; - } - if (m_idx == sym_idx) { return; } - m_multiple_indexes = true; - } - - bool has_multiple_indexes() const - { - return m_multiple_indexes; - } -}; - -bool sym_mux::is_homogenous_formula(expr * e) const -{ - hmg_checker chck(*this); - for_each_expr(chck, m_visited, e); - m_visited.reset(); - return !chck.has_multiple_indexes(); -} - - -struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg { +namespace { +struct conv_rewriter_cfg : public default_rewriter_cfg { private: ast_manager & m; const sym_mux & m_parent; unsigned m_from_idx; unsigned m_to_idx; bool m_homogenous; + expr_ref_vector m_pinned; public: - conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous) + conv_rewriter_cfg(const sym_mux & parent, unsigned from_idx, + unsigned to_idx, bool homogenous) : m(parent.get_manager()), m_parent(parent), m_from_idx(from_idx), m_to_idx(to_idx), - m_homogenous(homogenous) {} + m_homogenous(homogenous), m_pinned(m) {(void) m_homogenous;} bool get_subst(expr * s, expr * & t, proof * & t_pr) { @@ -368,241 +148,23 @@ public: app * a = to_app(s); func_decl * sym = a->get_decl(); if (!m_parent.has_index(sym, m_from_idx)) { - (void) m_homogenous; SASSERT(!m_homogenous || !m_parent.is_muxed(sym)); return false; } - func_decl * tgt = m_parent.conv(sym, m_from_idx, m_to_idx); - + func_decl * tgt = m_parent.shift_decl(sym, m_from_idx, m_to_idx); t = m.mk_app(tgt, a->get_args()); + m_pinned.push_back(t); return true; } }; - -void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous) const -{ - if (src_idx == tgt_idx) { - res = f; - return; - } - conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous); - rewriter_tpl rwr(m, false, r_cfg); - rwr(f, res); } -struct sym_mux::shifting_rewriter_cfg : public default_rewriter_cfg { -private: - ast_manager & m; - const sym_mux & m_parent; - int m_shift; -public: - shifting_rewriter_cfg(const sym_mux & parent, int shift) - : m(parent.get_manager()), - m_parent(parent), - m_shift(shift) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) - { - if (!is_app(s)) { return false; } - app * a = to_app(s); - func_decl * sym = a->get_decl(); - - unsigned idx; - if (!m_parent.try_get_index(sym, idx)) { - return false; - } - SASSERT(static_cast(idx) + m_shift >= 0); - func_decl * tgt = m_parent.conv(sym, idx, idx + m_shift); - t = m.mk_app(tgt, a->get_args()); - return true; - } -}; - -void sym_mux::shift_formula(expr * f, int dist, expr_ref & res) const -{ - if (dist == 0) { - res = f; - return; - } - shifting_rewriter_cfg r_cfg(*this, dist); - rewriter_tpl rwr(m, false, r_cfg); - rwr(f, res); -} - -void sym_mux::conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, - expr_ref_vector & res) const -{ - res.reset(); - expr * const * begin = vect.c_ptr(); - expr * const * end = begin + vect.size(); - for (expr * const * it = begin; it != end; it++) { - expr_ref converted(m); - conv_formula(*it, src_idx, tgt_idx, converted); - res.push_back(converted); +void sym_mux::shift_expr(expr * f, unsigned src_idx, unsigned tgt_idx, + expr_ref & res, bool homogenous) const { + if (src_idx == tgt_idx) {res = f;} + else { + conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous); + rewriter_tpl rwr(m, false, r_cfg); + rwr(f, res); } } - -void sym_mux::filter_idx(expr_ref_vector & vect, unsigned idx) const -{ - unsigned i = 0; - while (i < vect.size()) { - expr* e = vect[i].get(); - if (contains(e, idx) && is_homogenous_formula(e, idx)) { - i++; - } else { - //we don't allow mixing states inside vector elements - SASSERT(!contains(e, idx)); - vect[i] = vect.back(); - vect.pop_back(); - } - } -} - -void sym_mux::partition_o_idx( - expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, unsigned idx) const -{ - - for (unsigned i = 0; i < lits.size(); ++i) { - if (contains(lits[i], idx) && is_homogenous_formula(lits[i], idx)) { - o_lits.push_back(lits[i]); - } else { - other.push_back(lits[i]); - } - } -} - - - -class sym_mux::nonmodel_sym_checker { - const sym_mux & m_parent; - - bool m_found; -public: - nonmodel_sym_checker(const sym_mux & parent) : - m_parent(parent), m_found(false) - { - } - - void operator()(expr * e) - { - if (m_found || !is_app(e)) { return; } - - func_decl * sym = to_app(e)->get_decl(); - - if (m_parent.is_non_model_sym(sym)) { - m_found = true; - } - } - - bool found() const - { - return m_found; - } -}; - -bool sym_mux::has_nonmodel_symbol(expr * e) const -{ - nonmodel_sym_checker chck(*this); - for_each_expr(chck, e); - return chck.found(); -} - -void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const -{ - unsigned i = 0; - while (i < vect.size()) { - if (!has_nonmodel_symbol(vect[i].get())) { - i++; - continue; - } - vect[i] = vect.back(); - vect.pop_back(); - } -} - -class sym_mux::decl_idx_comparator { - const sym_mux & m_parent; -public: - decl_idx_comparator(const sym_mux & parent) - : m_parent(parent) - { } - - bool operator()(func_decl * sym1, func_decl * sym2) - { - unsigned idx1, idx2; - if (!m_parent.try_get_index(sym1, idx1)) { idx1 = UINT_MAX; } - if (!m_parent.try_get_index(sym2, idx2)) { idx2 = UINT_MAX; } - - if (idx1 != idx2) { return idx1 < idx2; } - return lt(sym1->get_name(), sym2->get_name()); - } -}; - -std::string sym_mux::pp_model(const model_core & mdl) const -{ - decl_vector consts; - unsigned sz = mdl.get_num_constants(); - for (unsigned i = 0; i < sz; i++) { - func_decl * d = mdl.get_constant(i); - consts.push_back(d); - } - - std::sort(consts.begin(), consts.end(), decl_idx_comparator(*this)); - - std::stringstream res; - - decl_vector::iterator end = consts.end(); - for (decl_vector::iterator it = consts.begin(); it != end; it++) { - func_decl * d = *it; - std::string name = d->get_name().str(); - const char * arrow = " -> "; - res << name << arrow; - unsigned indent = static_cast(name.length() + strlen(arrow)); - res << mk_pp(mdl.get_const_interp(d), m, indent) << "\n"; - - if (it + 1 != end) { - unsigned idx1, idx2; - if (!try_get_index(*it, idx1)) { idx1 = UINT_MAX; } - if (!try_get_index(*(it + 1), idx2)) { idx2 = UINT_MAX; } - if (idx1 != idx2) { res << "\n"; } - } - } - return res.str(); -} - - -#if 0 - -class sym_mux::index_renamer_cfg : public default_rewriter_cfg { - const sym_mux & m_parent; - unsigned m_idx; - -public: - index_renamer_cfg(const sym_mux & p, unsigned idx) : m_parent(p), m_idx(idx) {} - - bool get_subst(expr * s, expr * & t, proof * & t_pr) - { - if (!is_app(s)) { return false; } - app * a = to_app(s); - if (a->get_family_id() != null_family_id) { - return false; - } - func_decl * sym = a->get_decl(); - unsigned idx; - if (!m_parent.try_get_index(sym, idx)) { - return false; - } - if (m_idx == idx) { - return false; - } - ast_manager& m = m_parent.get_manager(); - symbol name = symbol((sym->get_name().str() + "!").c_str()); - func_decl * tgt = m.mk_func_decl(name, sym->get_arity(), sym->get_domain(), sym->get_range()); - t = m.mk_app(tgt, a->get_num_args(), a->get_args()); - return true; - } -}; - -#endif diff --git a/src/muz/spacer/spacer_sym_mux.h b/src/muz/spacer/spacer_sym_mux.h index 892542557..5690370bd 100644 --- a/src/muz/spacer/spacer_sym_mux.h +++ b/src/muz/spacer/spacer_sym_mux.h @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2011 Microsoft Corporation +Copyright (c) 2018 Arie Gurfinkel and Microsoft Corporation Module Name: @@ -7,10 +7,12 @@ Module Name: Abstract: - A symbol multiplexer that helps with having multiple versions of each of a set of symbols. + A symbol multiplexer that helps with having multiple versions of + each of a set of symbols. Author: + Arie Gurfinkel Krystof Hoder (t-khoder) 2011-9-8. Revision History: @@ -20,236 +22,72 @@ Revision History: #ifndef _SYM_MUX_H_ #define _SYM_MUX_H_ +#include + #include "ast/ast.h" #include "util/map.h" #include "util/vector.h" -#include - -class model_core; namespace spacer { class sym_mux { -public: - typedef ptr_vector app_vector; - typedef ptr_vector decl_vector; private: - typedef obj_map sym2u; - typedef obj_map sym2dv; - typedef obj_map sym2sym; - typedef obj_map sym2pred; - typedef hashtable symbols; + class sym_mux_entry { + public: + func_decl_ref m_main; + func_decl_ref_vector m_variants; + sym_mux_entry(ast_manager &m) : m_main(m), m_variants(m) {}; + }; - ast_manager & m; - mutable ast_ref_vector m_ref_holder; - mutable expr_mark m_visited; + typedef obj_map decl2entry_map; + typedef obj_map > mux2entry_map; - mutable unsigned m_next_sym_suffix_idx; - mutable symbols m_used_suffixes; - /** Here we have default suffixes for each of the variants */ - mutable std::vector m_suffixes; + ast_manager &m; + mutable decl2entry_map m_entries; + mutable mux2entry_map m_muxes; + func_decl_ref mk_variant(func_decl *fdecl, unsigned i) const; + void ensure_capacity(sym_mux_entry &entry, unsigned sz) const; - /** - Primary symbol is the 0-th variant. This member maps from primary symbol - to vector of all its variants (including the primary variant). - */ - sym2dv m_prim2all; - - /** - For each symbol contains its variant index - */ - mutable sym2u m_sym2idx; - /** - For each symbol contains its primary variant - */ - mutable sym2sym m_sym2prim; - - /** - Maps prefixes passed to the create_tuple to - the primary symbol created from it. - */ - sym2pred m_prefix2prim; - - /** - Maps pripary symbols to prefixes that were used to create them. - */ - sym2sym m_prim2prefix; - - decl_vector m_prim_preds; - - obj_hashtable m_non_model_syms; - - struct formula_checker; - struct conv_rewriter_cfg; - struct shifting_rewriter_cfg; - class decl_idx_comparator; - class hmg_checker; - class nonmodel_sym_checker; - class index_renamer_cfg; - class index_collector; - class variable_collector; - - std::string get_suffix(unsigned i) const; - void ensure_tuple_size(func_decl * prim, unsigned sz) const; - - expr_ref isolate_o_idx(expr* e, unsigned idx) const; public: - sym_mux(ast_manager & m, const std::vector & suffixes); - + sym_mux(ast_manager & m); + ~sym_mux(); ast_manager & get_manager() const { return m; } - bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); } - - bool try_get_index(func_decl * sym, unsigned & idx) const - { - return m_sym2idx.find(sym, idx); - } - + void register_decl(func_decl *fdecl); + bool find_idx(func_decl * sym, unsigned & idx) const; bool has_index(func_decl * sym, unsigned idx) const - { - unsigned actual_idx; - return try_get_index(sym, actual_idx) && idx == actual_idx; - } + {unsigned v; return find_idx(sym, v) && idx == v;} - /** Return primary symbol. sym must be muxed. */ - func_decl * get_primary(func_decl * sym) const - { - func_decl * prim; - TRUSTME(m_sym2prim.find(sym, prim)); - return prim; - } + bool is_muxed(func_decl *fdecl) const {return m_muxes.contains(fdecl);} /** - Return primary symbol created from prefix, or 0 if the prefix was never used. + \brief Return symbol created from prefix, or 0 if the prefix + was never used. */ - func_decl * try_get_primary_by_prefix(func_decl* prefix) const - { - func_decl * res; - if(!m_prefix2prim.find(prefix, res)) { - return 0; - } - return res; - } + func_decl * find_by_decl(func_decl* fdecl, unsigned idx) const; /** - Return symbol created from prefix, or 0 if the prefix was never used. - */ - func_decl * try_get_by_prefix(func_decl* prefix, unsigned idx) const - { - func_decl * prim = try_get_primary_by_prefix(prefix); - if(!prim) { - return 0; - } - return conv(prim, 0, idx); - } - - /** - Marks symbol as non-model which means it will not appear in models collected by - get_muxed_cube_from_model function. - This is to take care of auxiliary symbols introduced by the disjunction relations - to relativize lemmas coming from disjuncts. - */ - void mark_as_non_model(func_decl * sym) - { - SASSERT(is_muxed(sym)); - m_non_model_syms.insert(get_primary(sym)); - } - - func_decl * get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx, - unsigned arity, sort * const * domain, sort * range); - - - - bool is_muxed_lit(expr * e, unsigned idx) const; - - bool is_non_model_sym(func_decl * s) const - { - return is_muxed(s) && m_non_model_syms.contains(get_primary(s)); - } - - /** - Create a multiplexed tuple of propositional constants. - Symbols may be suplied in the tuple vector, - those beyond the size of the array and those with corresponding positions - assigned to zero will be created using prefix. - Tuple length must be at least one. - */ - void create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range, - unsigned tuple_length, decl_vector & tuple); - - /** - Return true if the only multiplexed symbols which e contains are of index idx. + \brief Return true if the only multiplexed symbols which e contains are + of index idx. */ bool is_homogenous_formula(expr * e, unsigned idx) const; - bool is_homogenous(const expr_ref_vector & vect, unsigned idx) const; - - /** - Return true if all multiplexed symbols which e contains are of one index. - */ - bool is_homogenous_formula(expr * e) const; - - /** - Return true if expression e contains a muxed symbol of index idx. - */ - bool contains(expr * e, unsigned idx) const; - - /** - Collect indices used in expression. - */ - void collect_indices(expr* e, unsigned_vector& indices) const; - - /** - Collect used variables of each index. - */ - void collect_variables(expr* e, vector >& vars) const; - - /** - Convert symbol sym which has to be of src_idx variant into variant tgt_idx. - */ - func_decl * conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const; /** - Convert src_idx symbols in formula f variant into tgt_idx. - If homogenous is true, formula cannot contain symbols of other variants. + \brief Convert symbol sym which has to be of src_idx variant + into variant tgt_idx. */ - void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous = true) const; - void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx, - expr_ref_vector & res) const; + func_decl * shift_decl(func_decl * sym, unsigned src_idx, unsigned tgt_idx) const; /** - Shifts the muxed symbols in f by dist. Dist can be negative, but it should never shift - symbol index to a negative value. + \brief Convert src_idx symbols in formula f variant into + tgt_idx. If homogenous is true, formula cannot contain symbols + of other variants. */ - void shift_formula(expr * f, int dist, expr_ref & res) const; + void shift_expr(expr * f, unsigned src_idx, unsigned tgt_idx, + expr_ref & res, bool homogenous = true) const; - /** - Remove from vect literals (atoms or negations of atoms) of symbols - that contain multiplexed symbols with indexes other than idx. - Each of the literals can contain only symbols multiplexed with one index - (this trivially holds if the literals are propositional). - - Order of elements in vect may be modified by this function - */ - void filter_idx(expr_ref_vector & vect, unsigned idx) const; - - /** - Partition literals into o_literals and others. - */ - void partition_o_idx(expr_ref_vector const& lits, - expr_ref_vector& o_lits, - expr_ref_vector& other, unsigned idx) const; - - bool has_nonmodel_symbol(expr * e) const; - void filter_non_model_lits(expr_ref_vector & vect) const; - - func_decl * const * begin_prim_preds() const { return m_prim_preds.begin(); } - func_decl * const * end_prim_preds() const { return m_prim_preds.end(); } - - void get_muxed_cube_from_model(const model_core & model, expr_ref_vector & res) const; - - std::string pp_model(const model_core & mdl) const; }; } diff --git a/src/muz/spacer/spacer_unsat_core_learner.cpp b/src/muz/spacer/spacer_unsat_core_learner.cpp index d478b1e8f..730ef1629 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.cpp +++ b/src/muz/spacer/spacer_unsat_core_learner.cpp @@ -17,111 +17,46 @@ Revision History: --*/ #include +#include "ast/for_each_expr.h" +#include "ast/proofs/proof_utils.h" #include "muz/spacer/spacer_unsat_core_learner.h" #include "muz/spacer/spacer_unsat_core_plugin.h" -#include "ast/for_each_expr.h" - -namespace spacer -{ +#include "muz/spacer/spacer_iuc_proof.h" +#include "muz/spacer/spacer_util.h" -unsat_core_learner::~unsat_core_learner() -{ +namespace spacer { + +unsat_core_learner::~unsat_core_learner() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); - } -void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) -{ +void unsat_core_learner::register_plugin(unsat_core_plugin* plugin) { m_plugins.push_back(plugin); } -void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, expr_ref_vector& unsat_core) -{ - // transform proof in order to get a proof which is better suited for unsat-core-extraction - proof_ref pr(root, m); +void unsat_core_learner::compute_unsat_core(expr_ref_vector& unsat_core) { + proof_post_order it(m_pr.get(), m); + while (it.hasNext()) { + proof* curr = it.next(); - reduce_hypotheses(pr); - STRACE("spacer.unsat_core_learner", - verbose_stream() << "Reduced proof:\n" << mk_ismt2_pp(pr, m) << "\n"; - ); + bool done = is_closed(curr); + if (done) continue; - // compute symbols occuring in B - collect_symbols_b(asserted_b); - - // traverse proof - proof_post_order it(root, m); - while (it.hasNext()) - { - proof* currentNode = it.next(); - - if (m.get_num_parents(currentNode) == 0) - { - switch(currentNode->get_decl_kind()) - { - - case PR_ASSERTED: // currentNode is an axiom - { - if (asserted_b.contains(m.get_fact(currentNode))) - { - m_b_mark.mark(currentNode, true); - } - else - { - m_a_mark.mark(currentNode, true); - } - break; - } - // currentNode is a hypothesis: - case PR_HYPOTHESIS: - { - m_h_mark.mark(currentNode, true); - break; - } - default: - { - break; - } - } - } - else - { - // collect from parents whether derivation of current node contains A-axioms, B-axioms and hypothesis - bool need_to_mark_a = false; - bool need_to_mark_b = false; - bool need_to_mark_h = false; - bool need_to_mark_closed = true; - - for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) - { - SASSERT(m.is_proof(currentNode->get_arg(i))); - proof* premise = to_app(currentNode->get_arg(i)); - - need_to_mark_a = need_to_mark_a || m_a_mark.is_marked(premise); - need_to_mark_b = need_to_mark_b || m_b_mark.is_marked(premise); - need_to_mark_h = need_to_mark_h || m_h_mark.is_marked(premise); - need_to_mark_closed = need_to_mark_closed && (!m_b_mark.is_marked(premise) || m_closed.is_marked(premise)); - } - - // if current node is application of lemma, we know that all hypothesis are removed - if(currentNode->get_decl_kind() == PR_LEMMA) - { - need_to_mark_h = false; - } - - // save results - m_a_mark.mark(currentNode, need_to_mark_a); - m_b_mark.mark(currentNode, need_to_mark_b); - m_h_mark.mark(currentNode, need_to_mark_h); - m_closed.mark(currentNode, need_to_mark_closed); + if (m.get_num_parents(curr) > 0) { + done = true; + for (proof* p : m.get_parents(curr)) done &= !is_b_open(p); + set_closed(curr, done); } - // we have now collected all necessary information, so we can visit the node - // if the node mixes A-reasoning and B-reasoning and contains non-closed premises - if (m_a_mark.is_marked(currentNode) && m_b_mark.is_marked(currentNode) && !m_closed.is_marked(currentNode)) - { - compute_partial_core(currentNode); // then we need to compute a partial core - // SASSERT(!(m_a_mark.is_marked(currentNode) && m_b_mark.is_marked(currentNode)) || m_closed.is_marked(currentNode)); TODO: doesn't hold anymore if we do the mincut-thing! + // we have now collected all necessary information, + // so we can visit the node + // if the node mixes A-reasoning and B-reasoning + // and contains non-closed premises + if (!done) { + if (is_a(curr) && is_b(curr)) { + compute_partial_core(curr); + } } } @@ -130,226 +65,36 @@ void unsat_core_learner::compute_unsat_core(proof *root, expr_set& asserted_b, e // TODO: remove duplicates from unsat core? - bool debug_proof = false; - if(debug_proof) - { - // print proof for debugging - verbose_stream() << "\n\nProof:\n"; - std::unordered_map id_to_small_id; - unsigned counter = 0; - - proof_post_order it2(root, m); - while (it2.hasNext()) - { - proof* currentNode = it2.next(); - - SASSERT(id_to_small_id.find(currentNode->get_id()) == id_to_small_id.end()); - id_to_small_id.insert(std::make_pair(currentNode->get_id(), counter)); - - verbose_stream() << counter << " "; - verbose_stream() << "["; - if (is_a_marked(currentNode)) - { - verbose_stream() << "a"; - } - if (is_b_marked(currentNode)) - { - verbose_stream() << "b"; - } - if (is_h_marked(currentNode)) - { - verbose_stream() << "h"; - } - if (is_closed(currentNode)) - { - verbose_stream() << "c"; - } - verbose_stream() << "] "; - - if (m.get_num_parents(currentNode) == 0) - { - switch (currentNode->get_decl_kind()) - { - case PR_ASSERTED: - verbose_stream() << "asserted"; - break; - case PR_HYPOTHESIS: - verbose_stream() << "hypothesis"; - break; - default: - verbose_stream() << "unknown axiom-type"; - break; - } - } - else - { - if (currentNode->get_decl_kind() == PR_LEMMA) - { - verbose_stream() << "lemma"; - } - else if (currentNode->get_decl_kind() == PR_TH_LEMMA) - { - verbose_stream() << "th_lemma"; - func_decl* d = currentNode->get_decl(); - symbol sym; - if (d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas") - { - verbose_stream() << "(farkas)"; - } - else - { - verbose_stream() << "(other)"; - } - } - else - { - verbose_stream() << "step"; - } - verbose_stream() << " from "; - for (int i = m.get_num_parents(currentNode) - 1; i >= 0 ; --i) - { - proof* premise = to_app(currentNode->get_arg(i)); - unsigned premise_small_id = id_to_small_id[premise->get_id()]; - if (i > 0) - { - verbose_stream() << premise_small_id << ", "; - } - else - { - verbose_stream() << premise_small_id; - } - - } - } - if (currentNode->get_decl_kind() == PR_TH_LEMMA || (is_a_marked(currentNode) && is_b_marked(currentNode)) || is_h_marked(currentNode) || (!is_a_marked(currentNode) && !is_b_marked(currentNode))) - { - verbose_stream() << std::endl; - } - else - { - verbose_stream() << ": " << mk_pp(m.get_fact(currentNode), m) << std::endl; - } - ++counter; - } - } // move all lemmas into vector - for (expr* const* it = m_unsat_core.begin(); it != m_unsat_core.end(); ++it) - { - unsat_core.push_back(*it); + for (expr* e : m_unsat_core) { + unsat_core.push_back(e); } } -void unsat_core_learner::compute_partial_core(proof* step) -{ - for (unsat_core_plugin** it=m_plugins.begin(), **end = m_plugins.end (); it != end && !m_closed.is_marked(step); ++it) - { - unsat_core_plugin* plugin = *it; +void unsat_core_learner::compute_partial_core(proof* step) { + for (unsat_core_plugin* plugin : m_plugins) { + if (is_closed(step)) break; plugin->compute_partial_core(step); } } -void unsat_core_learner::finalize() -{ - for (unsat_core_plugin** it=m_plugins.begin(); it != m_plugins.end(); ++it) - { - unsat_core_plugin* plugin = *it; +void unsat_core_learner::finalize() { + for (unsat_core_plugin* plugin : m_plugins) { plugin->finalize(); } } - -bool unsat_core_learner::is_a_marked(proof* p) -{ - return m_a_mark.is_marked(p); -} -bool unsat_core_learner::is_b_marked(proof* p) -{ - return m_b_mark.is_marked(p); -} -bool unsat_core_learner::is_h_marked(proof* p) -{ - return m_h_mark.is_marked(p); -} -bool unsat_core_learner::is_closed(proof*p) -{ +bool unsat_core_learner::is_closed(proof* p) { return m_closed.is_marked(p); } -void unsat_core_learner::set_closed(proof* p, bool value) -{ + +void unsat_core_learner::set_closed(proof* p, bool value) { m_closed.mark(p, value); } - void unsat_core_learner::add_lemma_to_core(expr* lemma) - { - m_unsat_core.push_back(lemma); - } - -class collect_pure_proc { - func_decl_set& m_symbs; -public: - collect_pure_proc(func_decl_set& s):m_symbs(s) {} - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - m_symbs.insert(a->get_decl()); - } - } - void operator()(var*) {} - void operator()(quantifier*) {} -}; - -void unsat_core_learner::collect_symbols_b(expr_set axioms_b) -{ - expr_mark visited; - collect_pure_proc proc(m_symbols_b); - for (expr_set::iterator it = axioms_b.begin(); it != axioms_b.end(); ++it) - { - for_each_expr(proc, visited, *it); - } +void unsat_core_learner::add_lemma_to_core(expr* lemma) { + m_unsat_core.push_back(lemma); } -class is_pure_expr_proc { - func_decl_set const& m_symbs; - array_util m_au; -public: - struct non_pure {}; - - is_pure_expr_proc(func_decl_set const& s, ast_manager& m): - m_symbs(s), - m_au (m) - {} - - void operator()(app* a) { - if (a->get_family_id() == null_family_id) { - if (!m_symbs.contains(a->get_decl())) { - throw non_pure(); - } - } - else if (a->get_family_id () == m_au.get_family_id () && - a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) { - throw non_pure(); - } - } - void operator()(var*) {} - void operator()(quantifier*) {} -}; - -bool unsat_core_learner::only_contains_symbols_b(expr* expr) const -{ - is_pure_expr_proc proc(m_symbols_b, m); - try { - for_each_expr(proc, expr); - } - catch (is_pure_expr_proc::non_pure) - { - return false; - } - return true; -} - - - } diff --git a/src/muz/spacer/spacer_unsat_core_learner.h b/src/muz/spacer/spacer_unsat_core_learner.h index a7c9f6aa7..ead430ca0 100644 --- a/src/muz/spacer/spacer_unsat_core_learner.h +++ b/src/muz/spacer/spacer_unsat_core_learner.h @@ -6,7 +6,8 @@ Module Name: spacer_unsat_core_learner.h Abstract: - itp cores + + itp cores Author: Bernhard Gleiss @@ -20,21 +21,43 @@ Revision History: #include "ast/ast.h" #include "muz/spacer/spacer_util.h" -#include "ast/proofs/proof_utils.h" +#include "muz/spacer/spacer_proof_utils.h" +#include "muz/spacer/spacer_iuc_proof.h" namespace spacer { class unsat_core_plugin; - class unsat_core_learner - { + class iuc_proof; + class unsat_core_learner { typedef obj_hashtable expr_set; + ast_manager& m; + iuc_proof& m_pr; + + ptr_vector m_plugins; + ast_mark m_closed; + + expr_ref_vector m_unsat_core; + public: - unsat_core_learner(ast_manager& m) : m(m), m_unsat_core(m) {}; + unsat_core_learner(ast_manager& m, iuc_proof& pr) : + m(m), m_pr(pr), m_unsat_core(m) {}; virtual ~unsat_core_learner(); - ast_manager& m; + ast_manager& get_manager() {return m;} + + + bool is_a(proof *pr) { + // AG: treat hypotheses as A + // AG: assume that all B-hyp have been eliminated + // AG: this is not yet true in case of arithmetic eq_prop + return m_pr.is_a_marked(pr) || is_h(pr); + } + bool is_b(proof *pr) {return m_pr.is_b_marked(pr);} + bool is_h(proof *pr) {return m_pr.is_h_marked(pr);} + bool is_b_pure(proof *pr) { return m_pr.is_b_pure(pr);} + bool is_b_open(proof *p) {return m_pr.is_b_marked(p) && !is_closed (p);} /* * register a plugin for computation of partial unsat cores @@ -45,51 +68,24 @@ namespace spacer { /* * compute unsat core using the registered unsat-core-plugins */ - void compute_unsat_core(proof* root, expr_set& asserted_b, expr_ref_vector& unsat_core); + void compute_unsat_core(expr_ref_vector& unsat_core); /* * getter/setter methods for data structures exposed to plugins - * the following invariants can be assumed and need to be maintained by the plugins: - * - a node is a-marked iff it is derived using at least one asserted proof step from A. - * - a node is b-marked iff its derivation contains no asserted proof steps from A and - * no hypothesis (with the additional complication that lemmas conceptually remove hypothesis) - * - a node is h-marked, iff it is derived using at least one hypothesis + * the following invariant can be assumed and need to be maintained by the plugins: * - a node is closed, iff it has already been interpolated, i.e. its contribution is * already covered by the unsat-core. */ - bool is_a_marked(proof* p); - bool is_b_marked(proof* p); - bool is_h_marked(proof* p); bool is_closed(proof* p); void set_closed(proof* p, bool value); + /* * adds a lemma to the unsat core */ void add_lemma_to_core(expr* lemma); - /* - * helper method, which can be used by plugins - * returns true iff all symbols of expr occur in some b-asserted formula. - * must only be called after a call to collect_symbols_b. - */ - bool only_contains_symbols_b(expr* expr) const; - bool is_b_pure (proof *p) - {return !is_h_marked (p) && only_contains_symbols_b (m.get_fact (p));} - bool is_b_open (proof *p) - { return is_b_marked (p) && !is_closed (p); } - private: - ptr_vector m_plugins; - func_decl_set m_symbols_b; // symbols, which occur in any b-asserted formula - void collect_symbols_b(expr_set axioms_b); - - ast_mark m_a_mark; - ast_mark m_b_mark; - ast_mark m_h_mark; - ast_mark m_closed; - - expr_ref_vector m_unsat_core; // collects the lemmas of the unsat-core, will at the end be inserted into unsat_core. /* * computes partial core for step by delegating computation to plugins @@ -101,7 +97,6 @@ namespace spacer { */ void finalize(); }; - } #endif diff --git a/src/muz/spacer/spacer_unsat_core_plugin.cpp b/src/muz/spacer/spacer_unsat_core_plugin.cpp index af9230713..aeb509c2e 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.cpp +++ b/src/muz/spacer/spacer_unsat_core_plugin.cpp @@ -6,7 +6,7 @@ Module Name: spacer_unsat_core_plugin.cpp Abstract: - plugin for itp cores + plugin for itp cores Author: Bernhard Gleiss @@ -30,311 +30,248 @@ Revision History: #include "muz/spacer/spacer_matrix.h" #include "muz/spacer/spacer_unsat_core_plugin.h" #include "muz/spacer/spacer_unsat_core_learner.h" +#include "muz/spacer/spacer_iuc_proof.h" -namespace spacer -{ +namespace spacer { + unsat_core_plugin::unsat_core_plugin(unsat_core_learner& ctx): + m(ctx.get_manager()), m_ctx(ctx) {}; -void unsat_core_plugin_lemma::compute_partial_core(proof* step) -{ - SASSERT(m_learner.is_a_marked(step)); - SASSERT(m_learner.is_b_marked(step)); + void unsat_core_plugin_lemma::compute_partial_core(proof* step) { + SASSERT(m_ctx.is_a(step)); + SASSERT(m_ctx.is_b(step)); - for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof* premise = to_app(step->get_arg(i)); + for (auto* p : m.get_parents(step)) { + if (m_ctx.is_b_open (p)) { + // by IH, premises that are AB marked are already closed + SASSERT(!m_ctx.is_a(p)); + add_lowest_split_to_core(p); + } + } + m_ctx.set_closed(step, true); + } - if (m_learner.is_b_open (premise)) - { - // by IH, premises that are AB marked are already closed - SASSERT(!m_learner.is_a_marked(premise)); - add_lowest_split_to_core(premise); + void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const { + SASSERT(m_ctx.is_b_open(step)); + + ptr_buffer todo; + todo.push_back(step); + + while (!todo.empty()) { + proof* pf = todo.back(); + todo.pop_back(); + + // if current step hasn't been processed, + if (!m_ctx.is_closed(pf)) { + m_ctx.set_closed(pf, true); + // the step is b-marked and not closed. + // by I.H. the step must be already visited + // so if it is also a-marked, it must be closed + SASSERT(m_ctx.is_b(pf)); + SASSERT(!m_ctx.is_a(pf)); + + // the current step needs to be interpolated: + expr* fact = m.get_fact(pf); + // if we trust the current step and we are able to use it + if (m_ctx.is_b_pure (pf) && (m.is_asserted(pf) || is_literal(m, fact))) { + // just add it to the core + m_ctx.add_lemma_to_core(fact); + } + // otherwise recurse on premises + else { + for (proof* premise : m.get_parents(pf)) + if (m_ctx.is_b_open(premise)) + todo.push_back(premise); + } + } } } - m_learner.set_closed(step, true); -} -void unsat_core_plugin_lemma::add_lowest_split_to_core(proof* step) const -{ - SASSERT(m_learner.is_b_open(step)); - ast_manager &m = m_learner.m; - ptr_vector todo; - todo.push_back(step); + /*** + * FARKAS + */ + void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) { + SASSERT(m_ctx.is_a(step)); + SASSERT(m_ctx.is_b(step)); + // XXX this assertion should be true so there is no need to check for it + SASSERT (!m_ctx.is_closed (step)); + func_decl* d = step->get_decl(); + symbol sym; + TRACE("spacer.farkas", + tout << "looking at: " << mk_pp(step, m) << "\n";); + if (!m_ctx.is_closed(step) && is_farkas_lemma(m, step)) { + // weaker check : d->get_num_parameters() >= m.get_num_parents(step) + 2 - while (!todo.empty()) - { - proof* pf = todo.back(); - todo.pop_back(); + SASSERT(d->get_num_parameters() == m.get_num_parents(step) + 2); + SASSERT(m.has_fact(step)); - // if current step hasn't been processed, - if (!m_learner.is_closed(pf)) - { - m_learner.set_closed(pf, true); - // the step is b-marked and not closed. - // by I.H. the step must be already visited - // so if it is also a-marked, it must be closed - SASSERT(m_learner.is_b_marked(pf)); - SASSERT(!m_learner.is_a_marked(pf)); + coeff_lits_t coeff_lits; + expr_ref_vector pinned(m); - // the current step needs to be interpolated: - expr* fact = m_learner.m.get_fact(pf); - // if we trust the current step and we are able to use it - if (m_learner.is_b_pure (pf) && - (m.is_asserted(pf) || is_literal(m, fact))) - { - // just add it to the core - m_learner.add_lemma_to_core(fact); - } - // otherwise recurse on premises - else - { - for (unsigned i = 0, sz = m_learner.m.get_num_parents(pf); - i < sz; ++i) - { - SASSERT(m_learner.m.is_proof(pf->get_arg(i))); - proof* premise = m.get_parent (pf, i); - if (m_learner.is_b_open(premise)) { - todo.push_back(premise); + /* The farkas lemma represents a subproof starting from premise(-set)s A, BNP and BP(ure) and + * ending in a disjunction D. We need to compute the contribution of BP, i.e. a formula, which + * is entailed by BP and together with A and BNP entails D. + * + * Let Fark(F) be the farkas coefficient for F. We can use the fact that + * (A*Fark(A) + BNP*Fark(BNP) + BP*Fark(BP) + (neg D)*Fark(D)) => false. (E1) + * We further have that A+B => C implies (A \land B) => C. (E2) + * + * Alternative 1: + * From (E1) immediately get that BP*Fark(BP) is a solution. + * + * Alternative 2: + * We can rewrite (E2) to rewrite (E1) to + * (BP*Fark(BP)) => (neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D))) (E3) + * and since we can derive (A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) from + * A, BNP and D, we also know that it is inconsisent. Therefore + * neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) is a solution. + * + * Finally we also need the following workaround: + * 1) Although we know from theory, that the Farkas coefficients are always nonnegative, + * the Farkas coefficients provided by arith_core are sometimes negative (must be a bug) + * as workaround we take the absolute value of the provided coefficients. + */ + parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient + + TRACE("spacer.farkas", + tout << "Farkas input: "<< "\n"; + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * prem = m.get_parent(step, i); + rational coef = params[i].get_rational(); + bool b_pure = m_ctx.is_b_pure (prem); + tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; + } + ); + + bool done = true; + + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * premise = m.get_parent(step, i); + + if (m_ctx.is_b_open (premise)) { + SASSERT(!m_ctx.is_a(premise)); + + if (m_ctx.is_b_pure (premise)) { + if (!m_use_constant_from_a) { + rational coefficient = params[i].get_rational(); + coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); + } + } + else { + // -- mixed premise, won't be able to close this proof step + done = false; + + if (m_use_constant_from_a) { + rational coefficient = params[i].get_rational(); + coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); + } + } + } + else { + if (m_use_constant_from_a) { + rational coefficient = params[i].get_rational(); + coeff_lits.push_back(std::make_pair(abs(coefficient), (app*)m.get_fact(premise))); } } } - } - } -} + // TBD: factor into another method + if (m_use_constant_from_a) { + params += m.get_num_parents(step); // point to the first Farkas coefficient, which corresponds to a formula in the conclusion - -void unsat_core_plugin_farkas_lemma::compute_partial_core(proof* step) -{ - ast_manager &m = m_learner.m; - SASSERT(m_learner.is_a_marked(step)); - SASSERT(m_learner.is_b_marked(step)); - // XXX this assertion should be true so there is no need to check for it - SASSERT (!m_learner.is_closed (step)); - func_decl* d = step->get_decl(); - symbol sym; - if(!m_learner.is_closed(step) && // if step is not already interpolated - step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma - d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas" && - d->get_num_parameters() >= m_learner.m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients - { - SASSERT(m_learner.m.has_fact(step)); - - ptr_vector literals; - vector coefficients; - - /* The farkas lemma represents a subproof starting from premise(-set)s A, BNP and BP(ure) and - * ending in a disjunction D. We need to compute the contribution of BP, i.e. a formula, which - * is entailed by BP and together with A and BNP entails D. - * - * Let Fark(F) be the farkas coefficient for F. We can use the fact that - * (A*Fark(A) + BNP*Fark(BNP) + BP*Fark(BP) + (neg D)*Fark(D)) => false. (E1) - * We further have that A+B => C implies (A \land B) => C. (E2) - * - * Alternative 1: - * From (E1) immediately get that BP*Fark(BP) is a solution. - * - * Alternative 2: - * We can rewrite (E2) to rewrite (E1) to - * (BP*Fark(BP)) => (neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D))) (E3) - * and since we can derive (A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) from - * A, BNP and D, we also know that it is inconsisent. Therefore - * neg(A*Fark(A) + BNP*Fark(BNP) + (neg D)*Fark(D)) is a solution. - * - * Finally we also need the following workaround: - * 1) Although we know from theory, that the Farkas coefficients are always nonnegative, - * the Farkas coefficients provided by arith_core are sometimes negative (must be a bug) - * as workaround we take the absolute value of the provided coefficients. - */ - parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient - - STRACE("spacer.farkas", - verbose_stream() << "Farkas input: "<< "\n"; - for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof *prem = m.get_parent (step, i); - - rational coef; - VERIFY(params[i].is_rational(coef)); - - bool b_pure = m_learner.is_b_pure (prem); - verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m_learner.m.get_fact(prem), m_learner.m) << "\n"; - } - ); - - bool can_be_closed = true; - - for(unsigned i = 0; i < m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof * premise = m.get_parent (step, i); - - if (m_learner.is_b_open (premise)) - { - SASSERT(!m_learner.is_a_marked(premise)); - - if (m_learner.is_b_pure (step)) - { - if (!m_use_constant_from_a) - { - rational coefficient; - VERIFY(params[i].is_rational(coefficient)); - literals.push_back(to_app(m_learner.m.get_fact(premise))); - coefficients.push_back(abs(coefficient)); + // the conclusion can either be a single formula or a disjunction of several formulas, we have to deal with both situations + if (m.get_num_parents(step) + 2 < d->get_num_parameters()) { + unsigned num_args = 1; + expr* conclusion = m.get_fact(step); + expr* const* args = &conclusion; + if (m.is_or(conclusion)) { + app* _or = to_app(conclusion); + num_args = _or->get_num_args(); + args = _or->get_args(); } - } - else - { - can_be_closed = false; + SASSERT(m.get_num_parents(step) + 2 + num_args == d->get_num_parameters()); - if (m_use_constant_from_a) - { - rational coefficient; - VERIFY(params[i].is_rational(coefficient)); - literals.push_back(to_app(m_learner.m.get_fact(premise))); - coefficients.push_back(abs(coefficient)); + bool_rewriter brw(m); + for (unsigned i = 0; i < num_args; ++i) { + expr* premise = args[i]; + + expr_ref negatedPremise(m); + brw.mk_not(premise, negatedPremise); + pinned.push_back(negatedPremise); + rational coefficient = params[i].get_rational(); + coeff_lits.push_back(std::make_pair(abs(coefficient), to_app(negatedPremise))); } } } - else - { - if (m_use_constant_from_a) - { - rational coefficient; - VERIFY(params[i].is_rational(coefficient)); - literals.push_back(to_app(m_learner.m.get_fact(premise))); - coefficients.push_back(abs(coefficient)); - } - } - } - if (m_use_constant_from_a) - { - params += m_learner.m.get_num_parents(step); // point to the first Farkas coefficient, which corresponds to a formula in the conclusion - - // the conclusion can either be a single formula or a disjunction of several formulas, we have to deal with both situations - if (m_learner.m.get_num_parents(step) + 2 < d->get_num_parameters()) - { - unsigned num_args = 1; - expr* conclusion = m_learner.m.get_fact(step); - expr* const* args = &conclusion; - if (m_learner.m.is_or(conclusion)) - { - app* _or = to_app(conclusion); - num_args = _or->get_num_args(); - args = _or->get_args(); - } - SASSERT(m_learner.m.get_num_parents(step) + 2 + num_args == d->get_num_parameters()); - - bool_rewriter brw(m_learner.m); - for (unsigned i = 0; i < num_args; ++i) - { - expr* premise = args[i]; - - expr_ref negatedPremise(m_learner.m); - brw.mk_not(premise, negatedPremise); - literals.push_back(to_app(negatedPremise)); - - rational coefficient; - VERIFY(params[i].is_rational(coefficient)); - coefficients.push_back(abs(coefficient)); - } - } - } - - // only if all b-premises can be used directly, add the farkas core and close the step - if (can_be_closed) - { - m_learner.set_closed(step, true); - - expr_ref res(m_learner.m); - compute_linear_combination(coefficients, literals, res); - - m_learner.add_lemma_to_core(res); + // only if all b-premises can be used directly, add the farkas core and close the step + // AG: this decision needs to be re-evaluated. If the proof cannot be closed, literals above + // AG: it will go into the core. However, it does not mean that this literal should/could not be added. + m_ctx.set_closed(step, done); + expr_ref res = compute_linear_combination(coeff_lits); + TRACE("spacer.farkas", tout << "Farkas core: " << res << "\n";); + m_ctx.add_lemma_to_core(res); } } -} -void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector& coefficients, const ptr_vector& literals, expr_ref& res) -{ - SASSERT(literals.size() == coefficients.size()); + expr_ref unsat_core_plugin_farkas_lemma::compute_linear_combination(const coeff_lits_t& coeff_lits) + { - ast_manager& m = res.get_manager(); - smt::farkas_util util(m); - if (m_use_constant_from_a) - { - util.set_split_literals (m_split_literals); // small optimization: if flag m_split_literals is set, then preserve diff constraints + smt::farkas_util util(m); + if (m_use_constant_from_a) { + util.set_split_literals (m_split_literals); // small optimization: if flag m_split_literals is set, then preserve diff constraints + } + for (auto& p : coeff_lits) { + util.add(p.first, p.second); + } + if (m_use_constant_from_a) { + return util.get(); + } + else { + return expr_ref(mk_not(m, util.get()), m); + } } - for(unsigned i = 0; i < literals.size(); ++i) - { - util.add(coefficients[i], literals[i]); - } - if (m_use_constant_from_a) - { - res = util.get(); - } - else - { - expr_ref negated_linear_combination = util.get(); - res = mk_not(m, negated_linear_combination); - } -} void unsat_core_plugin_farkas_lemma_optimized::compute_partial_core(proof* step) { - SASSERT(m_learner.is_a_marked(step)); - SASSERT(m_learner.is_b_marked(step)); + SASSERT(m_ctx.is_a(step)); + SASSERT(m_ctx.is_b(step)); func_decl* d = step->get_decl(); symbol sym; - if(!m_learner.is_closed(step) && // if step is not already interpolated - step->get_decl_kind() == PR_TH_LEMMA && // and step is a Farkas lemma - d->get_num_parameters() >= 2 && // the Farkas coefficients are saved in the parameters of step - d->get_parameter(0).is_symbol(sym) && sym == "arith" && // the first two parameters are "arith", "farkas", - d->get_parameter(1).is_symbol(sym) && sym == "farkas" && - d->get_num_parameters() >= m_learner.m.get_num_parents(step) + 2) // the following parameters are the Farkas coefficients - { - SASSERT(m_learner.m.has_fact(step)); + if (!m_ctx.is_closed(step) && // if step is not already interpolated + is_farkas_lemma(m, step)) { + SASSERT(d->get_num_parameters() == m.get_num_parents(step) + 2); + SASSERT(m.has_fact(step)); - vector > linear_combination; // collects all summands of the linear combination + coeff_lits_t linear_combination; // collects all summands of the linear combination parameter const* params = d->get_parameters() + 2; // point to the first Farkas coefficient - STRACE("spacer.farkas", - verbose_stream() << "Farkas input: "<< "\n"; - for (unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof *prem = m.get_parent (step, i); - - rational coef; - VERIFY(params[i].is_rational(coef)); - - bool b_pure = m_learner.is_b_pure (prem); - verbose_stream() << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m_learner.m.get_fact(prem), m_learner.m) << "\n"; - } - ); + TRACE("spacer.farkas", + tout << "Farkas input: "<< "\n"; + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * prem = m.get_parent(step, i); + rational coef = params[i].get_rational(); + bool b_pure = m_ctx.is_b_pure (prem); + tout << (b_pure?"B":"A") << " " << coef << " " << mk_pp(m.get_fact(prem), m) << "\n"; + } + ); bool can_be_closed = true; - for(unsigned i = 0; i < m_learner.m.get_num_parents(step); ++i) - { - SASSERT(m_learner.m.is_proof(step->get_arg(i))); - proof * premise = m.get_parent (step, i); + for (unsigned i = 0; i < m.get_num_parents(step); ++i) { + proof * premise = m.get_parent(step, i); - if (m_learner.is_b_marked(premise) && !m_learner.is_closed(premise)) + if (m_ctx.is_b(premise) && !m_ctx.is_closed(premise)) { - SASSERT(!m_learner.is_a_marked(premise)); + SASSERT(!m_ctx.is_a(premise)); - if (m_learner.only_contains_symbols_b(m_learner.m.get_fact(premise)) && !m_learner.is_h_marked(premise)) + if (m_ctx.is_b_pure(premise)) { - rational coefficient; - VERIFY(params[i].is_rational(coefficient)); - linear_combination.push_back(std::make_pair(to_app(m_learner.m.get_fact(premise)), abs(coefficient))); + rational coefficient = params[i].get_rational(); + linear_combination.push_back + (std::make_pair(abs(coefficient), to_app(m.get_fact(premise)))); } else { @@ -346,7 +283,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector& pair1, const std::pair& pair2) const + inline bool operator() (const std::pair& pair1, const std::pair& pair2) const { - return (pair1.first->get_id() < pair2.first->get_id()); + return (pair1.second->get_id() < pair2.second->get_id()); } }; void unsat_core_plugin_farkas_lemma_optimized::finalize() { - if(m_linear_combinations.empty()) + if (m_linear_combinations.empty()) { return; } DEBUG_CODE( for (auto& linear_combination : m_linear_combinations) { - SASSERT(linear_combination.size() > 0); - }); - - // 1. construct ordered basis - ptr_vector ordered_basis; - obj_map map; - unsigned counter = 0; - for (const auto& linear_combination : m_linear_combinations) - { - for (const auto& pair : linear_combination) - { - if (!map.contains(pair.first)) - { - ordered_basis.push_back(pair.first); - map.insert(pair.first, counter++); - } - } - } - - // 2. populate matrix - spacer_matrix matrix(m_linear_combinations.size(), ordered_basis.size()); - - for (unsigned i=0; i < m_linear_combinations.size(); ++i) - { - auto linear_combination = m_linear_combinations[i]; - for (const auto& pair : linear_combination) - { - matrix.set(i, map[pair.first], pair.second); - } - } - - // 3. perform gaussian elimination - unsigned i = matrix.perform_gaussian_elimination(); - - // 4. extract linear combinations from matrix and add result to core - for (unsigned k=0; k < i; k++)// i points to the row after the last row which is non-zero - { - ptr_vector literals; - vector coefficients; - for (unsigned l=0; l < matrix.num_cols(); ++l) - { - if (!matrix.get(k,l).is_zero()) - { - literals.push_back(ordered_basis[l]); - coefficients.push_back(matrix.get(k,l)); - } - } - SASSERT(literals.size() > 0); - expr_ref linear_combination(m); - compute_linear_combination(coefficients, literals, linear_combination); - - m_learner.add_lemma_to_core(linear_combination); - } - - } - - void unsat_core_plugin_farkas_lemma_optimized::compute_linear_combination(const vector& coefficients, const ptr_vector& literals, expr_ref& res) - { - SASSERT(literals.size() == coefficients.size()); - - ast_manager& m = res.get_manager(); - smt::farkas_util util(m); - for(unsigned i = 0; i < literals.size(); ++i) - { - util.add(coefficients[i], literals[i]); - } - expr_ref negated_linear_combination = util.get(); - SASSERT(m.is_not(negated_linear_combination)); - res = mk_not(m, negated_linear_combination); //TODO: rewrite the get-method to return nonnegated stuff? - } - - - void unsat_core_plugin_farkas_lemma_bounded::finalize() { - if (m_linear_combinations.empty()) { - return; - } - DEBUG_CODE( - for (auto& linear_combination : m_linear_combinations) { - SASSERT(linear_combination.size() > 0); + SASSERT(!linear_combination.empty()); }); // 1. construct ordered basis @@ -458,9 +317,72 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector ordered_basis; + obj_map map; + unsigned counter = 0; + for (const auto& linear_combination : m_linear_combinations) { + for (const auto& pair : linear_combination) { + if (!map.contains(pair.second)) { + ordered_basis.push_back(pair.second); + map.insert(pair.second, counter++); } } } @@ -471,7 +393,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector coeffs; - for (unsigned i=0; i < matrix.num_rows(); ++i) { + for (unsigned i = 0; i < matrix.num_rows(); ++i) { coeffs.push_back(expr_ref_vector(m)); } vector bounded_vectors; - for (unsigned j=0; j < matrix.num_cols(); ++j) - { + for (unsigned j = 0; j < matrix.num_cols(); ++j) { bounded_vectors.push_back(expr_ref_vector(m)); } // 4. find smallest n using guess and check algorithm - for(unsigned n = 1; true; ++n) + for (unsigned n = 1; true; ++n) { params_ref p; p.set_bool("model", true); - scoped_ptr s = mk_smt_solver(m, p, symbol::null); // TODO: incremental version? + solver_ref s = mk_smt_solver(m, p, symbol::null); // TODO: incremental version? // add new variables w_in, - for (unsigned i=0; i < matrix.num_rows(); ++i) - { + for (unsigned i = 0; i < matrix.num_rows(); ++i) { std::string name = "w_" + std::to_string(i) + std::to_string(n); - - func_decl_ref decl(m); - decl = m.mk_func_decl(symbol(name.c_str()), 0, (sort*const*)0, util.mk_int()); - coeffs[i].push_back(m.mk_const(decl)); + coeffs[i].push_back(m.mk_const(name, util.mk_int())); } // we need s_jn - for (unsigned j=0; j < matrix.num_cols(); ++j) - { + for (unsigned j = 0; j < matrix.num_cols(); ++j) { std::string name = "s_" + std::to_string(j) + std::to_string(n); - - func_decl_ref decl(m); - decl = m.mk_func_decl(symbol(name.c_str()), 0, (sort*const*)0, util.mk_int()); - - expr_ref s_jn(m); - s_jn = m.mk_const(decl); - - bounded_vectors[j].push_back(s_jn); + bounded_vectors[j].push_back(m.mk_const(name, util.mk_int())); } // assert bounds for all s_jn - for (unsigned l=0; l < n; ++l) { - for (unsigned j=0; j < matrix.num_cols(); ++j) { + for (unsigned l = 0; l < n; ++l) { + for (unsigned j = 0; j < matrix.num_cols(); ++j) { expr* s_jn = bounded_vectors[j][l].get(); - expr_ref lb(util.mk_le(util.mk_int(0), s_jn), m); expr_ref ub(util.mk_le(s_jn, util.mk_int(1)), m); s->assert_expr(lb); s->assert_expr(ub); } } - - // assert: forall i,j: a_ij = sum_k w_ik * s_jk - for (unsigned i=0; i < matrix.num_rows(); ++i) - { - for (unsigned j=0; j < matrix.num_cols(); ++j) - { - SASSERT(matrix.get(i, j).is_int()); - app_ref a_ij(util.mk_numeral(matrix.get(i,j), true),m); - app_ref sum(m); - sum = util.mk_int(0); - for (unsigned k=0; k < n; ++k) - { + // assert: forall i,j: a_ij = sum_k w_ik * s_jk + for (unsigned i = 0; i < matrix.num_rows(); ++i) { + for (unsigned j = 0; j < matrix.num_cols(); ++j) { + SASSERT(matrix.get(i, j).is_int()); + app_ref a_ij(util.mk_numeral(matrix.get(i,j), true), m); + + app_ref sum(util.mk_int(0), m); + for (unsigned k = 0; k < n; ++k) { sum = util.mk_add(sum, util.mk_mul(coeffs[i][k].get(), bounded_vectors[j][k].get())); - } - expr_ref eq(m.mk_eq(a_ij, sum),m); - s->assert_expr(eq); } + expr_ref eq(m.mk_eq(a_ij, sum), m); + s->assert_expr(eq); } + } // check result - lbool res = s->check_sat(0,0); + lbool res = s->check_sat(0, nullptr); // if sat extract model and add corresponding linear combinations to core if (res == lbool::l_true) { model_ref model; s->get_model(model); - - for (unsigned k=0; k < n; ++k) { - ptr_vector literals; - vector coefficients; - for (unsigned j=0; j < matrix.num_cols(); ++j) { + + for (unsigned k = 0; k < n; ++k) { + coeff_lits_t coeff_lits; + for (unsigned j = 0; j < matrix.num_cols(); ++j) { expr_ref evaluation(m); - - model.get()->eval(bounded_vectors[j][k].get(), evaluation, false); + + evaluation = (*model)(bounded_vectors[j][k].get()); if (!util.is_zero(evaluation)) { - literals.push_back(ordered_basis[j]); - coefficients.push_back(rational(1)); + coeff_lits.push_back(std::make_pair(rational(1), ordered_basis[j])); } } - SASSERT(!literals.empty()); // since then previous outer loop would have found solution already - expr_ref linear_combination(m); - compute_linear_combination(coefficients, literals, linear_combination); - - m_learner.add_lemma_to_core(linear_combination); + SASSERT(!coeff_lits.empty()); // since then previous outer loop would have found solution already + expr_ref linear_combination = compute_linear_combination(coeff_lits); + + m_ctx.add_lemma_to_core(linear_combination); } return; } } } - unsat_core_plugin_min_cut::unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner), m(m){} + unsat_core_plugin_min_cut::unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner) {} /* * traverses proof rooted in step and constructs graph, which corresponds to the proof-DAG, with the following differences: @@ -603,10 +504,10 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector todo; - SASSERT(m_learner.is_a_marked(step)); - SASSERT(m_learner.is_b_marked(step)); + SASSERT(m_ctx.is_a(step)); + SASSERT(m_ctx.is_b(step)); SASSERT(m.get_num_parents(step) > 0); - SASSERT(!m_learner.is_closed(step)); + SASSERT(!m_ctx.is_closed(step)); todo.push_back(step); while (!todo.empty()) @@ -615,36 +516,30 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector& todo) { bool is_sink = true; - ast_manager &m = m_learner.m; - ptr_vector todo_subproof; + ptr_buffer todo_subproof; - for (unsigned i = 0, sz = m.get_num_parents(step); i < sz; ++i) - { - proof* premise = m.get_parent (step, i); - { - if (m_learner.is_b_marked(premise)) - { - todo_subproof.push_back(premise); - } + for (proof* premise : m.get_parents(step)) { + if (m_ctx.is_b(premise)) { + todo_subproof.push_back(premise); } } while (!todo_subproof.empty()) @@ -653,21 +548,21 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vectorget_arg(i))); - proof* premise = m.get_parent (current, i); + for (proof* premise : m.get_parents(current)) { todo_subproof.push_back(premise); } } @@ -707,6 +599,8 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector j (only relevant if i is the supersource)) + if (!(i == nullptr && m_connected_to_s.is_marked(j))) + { + m_min_cut.add_edge(node_i, node_j, 1); + } + + if (i == nullptr) + { + m_connected_to_s.mark(j, true); + } } /* * computes min-cut on the graph constructed by compute_partial_core-method * and adds the corresponding lemmas to the core */ - void unsat_core_plugin_min_cut::finalize() - { + void unsat_core_plugin_min_cut::finalize() { unsigned_vector cut_nodes; m_min_cut.compute_min_cut(cut_nodes); - for (unsigned cut_node : cut_nodes) - { - m_learner.add_lemma_to_core(m_node_to_formula[cut_node]); - } - } + for (unsigned cut_node : cut_nodes) { + m_ctx.add_lemma_to_core(m_node_to_formula[cut_node]); + } + } } diff --git a/src/muz/spacer/spacer_unsat_core_plugin.h b/src/muz/spacer/spacer_unsat_core_plugin.h index 96d03140c..746f21b19 100644 --- a/src/muz/spacer/spacer_unsat_core_plugin.h +++ b/src/muz/spacer/spacer_unsat_core_plugin.h @@ -23,84 +23,73 @@ Revision History: namespace spacer { -class unsat_core_learner; + class unsat_core_learner; -class unsat_core_plugin { - -public: - unsat_core_plugin(unsat_core_learner& learner) : m_learner(learner){}; - virtual ~unsat_core_plugin(){}; - virtual void compute_partial_core(proof* step) = 0; - virtual void finalize(){}; - - unsat_core_learner& m_learner; -}; - - -class unsat_core_plugin_lemma : public unsat_core_plugin { - -public: - unsat_core_plugin_lemma(unsat_core_learner& learner) : unsat_core_plugin(learner){}; - - virtual void compute_partial_core(proof* step) override; - -private: - void add_lowest_split_to_core(proof* step) const; -}; - - -class unsat_core_plugin_farkas_lemma : public unsat_core_plugin { - -public: - unsat_core_plugin_farkas_lemma(unsat_core_learner& learner, bool split_literals, bool use_constant_from_a=true) : unsat_core_plugin(learner), m_split_literals(split_literals), m_use_constant_from_a(use_constant_from_a) {}; - - virtual void compute_partial_core(proof* step) override; - -private: - bool m_split_literals; - bool m_use_constant_from_a; - /* - * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res - */ - void compute_linear_combination(const vector& coefficients, const ptr_vector& literals, expr_ref& res); -}; - - class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin { - - public: - unsat_core_plugin_farkas_lemma_optimized(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner), m(m) {}; - - virtual void compute_partial_core(proof* step) override; - virtual void finalize() override; - + class unsat_core_plugin { protected: - vector > > m_linear_combinations; + typedef vector> coeff_lits_t; ast_manager& m; + public: + unsat_core_plugin(unsat_core_learner& learner); + virtual ~unsat_core_plugin() {}; + virtual void compute_partial_core(proof* step) = 0; + virtual void finalize(){}; + + unsat_core_learner& m_ctx; + }; + + class unsat_core_plugin_lemma : public unsat_core_plugin { + public: + unsat_core_plugin_lemma(unsat_core_learner& learner) : unsat_core_plugin(learner){}; + void compute_partial_core(proof* step) override; + private: + void add_lowest_split_to_core(proof* step) const; + }; + + class unsat_core_plugin_farkas_lemma : public unsat_core_plugin { + public: + unsat_core_plugin_farkas_lemma(unsat_core_learner& learner, + bool split_literals, + bool use_constant_from_a=true) : + unsat_core_plugin(learner), + m_split_literals(split_literals), + m_use_constant_from_a(use_constant_from_a) {}; + void compute_partial_core(proof* step) override; + private: + bool m_split_literals; + bool m_use_constant_from_a; /* * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res */ - void compute_linear_combination(const vector& coefficients, const ptr_vector& literals, expr_ref& res); + expr_ref compute_linear_combination(const coeff_lits_t& coeff_lits); + }; + + class unsat_core_plugin_farkas_lemma_optimized : public unsat_core_plugin { + public: + unsat_core_plugin_farkas_lemma_optimized(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin(learner) {}; + void compute_partial_core(proof* step) override; + void finalize() override; + protected: + vector m_linear_combinations; + /* + * compute linear combination of literals 'literals' having coefficients 'coefficients' and save result in res + */ + expr_ref compute_linear_combination(const coeff_lits_t& coeff_lits); }; class unsat_core_plugin_farkas_lemma_bounded : public unsat_core_plugin_farkas_lemma_optimized { - public: unsat_core_plugin_farkas_lemma_bounded(unsat_core_learner& learner, ast_manager& m) : unsat_core_plugin_farkas_lemma_optimized(learner, m) {}; - - virtual void finalize() override; + void finalize() override; }; class unsat_core_plugin_min_cut : public unsat_core_plugin { - public: unsat_core_plugin_min_cut(unsat_core_learner& learner, ast_manager& m); - - virtual void compute_partial_core(proof* step) override; - virtual void finalize() override; + void compute_partial_core(proof* step) override; + void finalize() override; private: - ast_manager& m; - ast_mark m_visited; // saves for each node i whether the subproof with root i has already been added to the min-cut-problem obj_map m_proof_to_node_minus; // maps proof-steps to the corresponding minus-nodes (the ones which are closer to source) obj_map m_proof_to_node_plus; // maps proof-steps to the corresponding plus-nodes (the ones which are closer to sink) @@ -108,6 +97,7 @@ private: void add_edge(proof* i, proof* j); vector m_node_to_formula; // maps each node to the corresponding formula in the original proof + ast_mark m_connected_to_s; // remember which nodes have already been connected to the supersource, in order to avoid multiple edges. min_cut m_min_cut; }; diff --git a/src/muz/spacer/spacer_util.cpp b/src/muz/spacer/spacer_util.cpp index 83042cd6d..aaa203c5e 100644 --- a/src/muz/spacer/spacer_util.cpp +++ b/src/muz/spacer/spacer_util.cpp @@ -9,6 +9,7 @@ Abstract: Utility functions for SPACER. + Author: Krystof Hoder (t-khoder) 2011-8-19. @@ -63,442 +64,73 @@ Notes: #include "tactic/arith/propagate_ineqs_tactic.h" #include "tactic/arith/arith_bounds_tactic.h" -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" +#include "qe/qe_term_graph.h" namespace spacer { - ///////////////////////// - // model_evaluator_util - // - - model_evaluator_util::model_evaluator_util(ast_manager& m) : - m(m), m_mev(nullptr) { - reset (nullptr); + void subst_vars(ast_manager& m, + app_ref_vector const& vars, model& mdl, expr_ref& fml) { + model::scoped_model_completion _sc_(mdl, true); + expr_safe_replace sub(m); + for (app * v : vars) sub.insert (v, mdl(v)); + sub(fml); } - model_evaluator_util::~model_evaluator_util() {reset (NULL);} + void to_mbp_benchmark(std::ostream &out, expr* fml, const app_ref_vector &vars) { + ast_manager &m = vars.m(); + ast_pp_util pp(m); + pp.collect(fml); + pp.display_decls(out); + out << "(define-fun mbp_benchmark_fml () Bool\n "; + out << mk_pp(fml, m) << ")\n\n"; - void model_evaluator_util::reset(model* model) { - if (m_mev) { - dealloc(m_mev); - m_mev = NULL; - } - m_model = model; - if (!m_model) { return; } - m_mev = alloc(model_evaluator, *m_model); - } - - bool model_evaluator_util::eval(expr *e, expr_ref &result, bool model_completion) { - m_mev->set_model_completion (model_completion); - try { - m_mev->operator() (e, result); - return true; - } - catch (model_evaluator_exception &ex) { - (void)ex; - TRACE("spacer_model_evaluator", tout << ex.msg () << "\n";); - return false; - } - } - - bool model_evaluator_util::eval(const expr_ref_vector &v, - expr_ref& res, bool model_completion) { - expr_ref e(m); - e = mk_and (v); - return eval(e, res, model_completion); - } - - - bool model_evaluator_util::is_true(const expr_ref_vector &v) { - expr_ref res(m); - return eval (v, res, false) && m.is_true (res); - } - - bool model_evaluator_util::is_false(expr *x) { - expr_ref res(m); - return eval(x, res, false) && m.is_false (res); + out << "(push)\n" + << "(assert mbp_benchmark_fml)\n" + << "(check-sat)\n" + << "(mbp mbp_benchmark_fml ("; + for (auto v : vars) {out << mk_pp(v, m) << " ";} + out << "))\n" + << "(pop)\n" + << "(exit)\n"; } - bool model_evaluator_util::is_true(expr *x) { - expr_ref res(m); - return eval(x, res, false) && m.is_true (res); - } - - void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) { - ast_manager& m = fml.get_manager(); - expr_ref_vector conjs(m); - flatten_and(fml, conjs); - obj_map diseqs; - expr* n, *lhs, *rhs; - for (unsigned i = 0; i < conjs.size(); ++i) { - if (m.is_not(conjs[i].get(), n) && m.is_eq(n, lhs, rhs)) { - if (!m.is_value(rhs)) { - std::swap(lhs, rhs); - } - if (!m.is_value(rhs)) { - continue; - } - diseqs.insert_if_not_there2(lhs, 0)->get_data().m_value++; - } - } - expr_substitution sub(m); - - unsigned orig_size = conjs.size(); - unsigned num_deleted = 0; - expr_ref val(m), tmp(m); - proof_ref pr(m); - pr = m.mk_asserted(m.mk_true()); - for (auto const& kv : diseqs) { - if (kv.m_value >= threshold) { - model.eval(kv.m_key, val); - sub.insert(kv.m_key, val, pr); - conjs.push_back(m.mk_eq(kv.m_key, val)); - num_deleted += kv.m_value; - } - } - if (orig_size < conjs.size()) { - scoped_ptr rep = mk_expr_simp_replacer(m); - rep->set_substitution(&sub); - for (unsigned i = 0; i < orig_size; ++i) { - tmp = conjs[i].get(); - (*rep)(tmp); - if (m.is_true(tmp)) { - conjs[i] = conjs.back(); - SASSERT(orig_size <= conjs.size()); - conjs.pop_back(); - SASSERT(orig_size <= 1 + conjs.size()); - if (i + 1 == orig_size) { - // no-op. - } - else if (orig_size <= conjs.size()) { - // no-op - } - else { - SASSERT(orig_size == 1 + conjs.size()); - --orig_size; - --i; - } - } - else { - conjs[i] = tmp; - } - } - IF_VERBOSE(2, verbose_stream() << "Deleted " << num_deleted << " disequalities " << conjs.size() << " conjuncts\n";); - } - fml = m.mk_and(conjs.size(), conjs.c_ptr()); - } - - // - // (f (if c1 (if c2 e1 e2) e3) b c) -> - // (if c1 (if c2 (f e1 b c) - - class ite_hoister { - ast_manager& m; - public: - ite_hoister(ast_manager& m): m(m) {} - - br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { - if (m.is_ite(f)) { - return BR_FAILED; - } - for (unsigned i = 0; i < num_args; ++i) { - expr* c, *t, *e; - if (!m.is_bool(args[i]) && m.is_ite(args[i], c, t, e)) { - expr_ref e1(m), e2(m); - ptr_vector args1(num_args, args); - args1[i] = t; - e1 = m.mk_app(f, num_args, args1.c_ptr()); - if (t == e) { - result = e1; - return BR_REWRITE1; - } - args1[i] = e; - e2 = m.mk_app(f, num_args, args1.c_ptr()); - result = m.mk_app(f, num_args, args); - result = m.mk_ite(c, e1, e2); - return BR_REWRITE3; - } - } - return BR_FAILED; - } - }; - - struct ite_hoister_cfg: public default_rewriter_cfg { - ite_hoister m_r; - bool rewrite_patterns() const { return false; } - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - return m_r.mk_app_core(f, num, args, result); - } - ite_hoister_cfg(ast_manager & m, params_ref const & p):m_r(m) {} - }; - - class ite_hoister_star : public rewriter_tpl { - ite_hoister_cfg m_cfg; - public: - ite_hoister_star(ast_manager & m, params_ref const & p): - rewriter_tpl(m, false, m_cfg), - m_cfg(m, p) {} - }; - - void hoist_non_bool_if(expr_ref& fml) { - ast_manager& m = fml.get_manager(); - scoped_no_proof _sp(m); + void qe_project_z3 (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + model & mdl, bool reduce_all_selects, bool use_native_mbp, + bool dont_sub) { params_ref p; - ite_hoister_star ite_rw(m, p); - expr_ref tmp(m); - ite_rw(fml, tmp); - fml = tmp; - } + p.set_bool("reduce_all_selects", reduce_all_selects); + p.set_bool("dont_sub", dont_sub); - class test_diff_logic { - ast_manager& m; - arith_util a; - bv_util bv; - bool m_is_dl; - bool m_test_for_utvpi; - - bool is_numeric(expr* e) const { - if (a.is_numeral(e)) { - return true; - } - expr* cond, *th, *el; - if (m.is_ite(e, cond, th, el)) { - return is_numeric(th) && is_numeric(el); - } - return false; - } - - bool is_arith_expr(expr *e) const { - return is_app(e) && a.get_family_id() == to_app(e)->get_family_id(); - } - - bool is_offset(expr* e) const { - if (a.is_numeral(e)) { - return true; - } - expr* cond, *th, *el, *e1, *e2; - if (m.is_ite(e, cond, th, el)) { - return is_offset(th) && is_offset(el); - } - // recognize offsets. - if (a.is_add(e, e1, e2)) { - if (is_numeric(e1)) { - return is_offset(e2); - } - if (is_numeric(e2)) { - return is_offset(e1); - } - return false; - } - if (m_test_for_utvpi) { - if (a.is_mul(e, e1, e2)) { - if (is_minus_one(e1)) { - return is_offset(e2); - } - if (is_minus_one(e2)) { - return is_offset(e1); - } - } - } - return !is_arith_expr(e); - } - - bool is_minus_one(expr const * e) const { - rational r; - return a.is_numeral(e, r) && r.is_minus_one(); - } - - bool test_ineq(expr* e) const { - SASSERT(a.is_le(e) || a.is_ge(e) || m.is_eq(e)); - SASSERT(to_app(e)->get_num_args() == 2); - expr * lhs = to_app(e)->get_arg(0); - expr * rhs = to_app(e)->get_arg(1); - if (is_offset(lhs) && is_offset(rhs)) - { return true; } - if (!is_numeric(rhs)) - { std::swap(lhs, rhs); } - if (!is_numeric(rhs)) - { return false; } - // lhs can be 'x' or '(+ x (* -1 y))' - if (is_offset(lhs)) - { return true; } - expr* arg1, *arg2; - if (!a.is_add(lhs, arg1, arg2)) - { return false; } - // x - if (m_test_for_utvpi) { - return is_offset(arg1) && is_offset(arg2); - } - if (is_arith_expr(arg1)) - { std::swap(arg1, arg2); } - if (is_arith_expr(arg1)) - { return false; } - // arg2: (* -1 y) - expr* m1, *m2; - if (!a.is_mul(arg2, m1, m2)) - { return false; } - return is_minus_one(m1) && is_offset(m2); - } - - bool test_eq(expr* e) const { - expr* lhs, *rhs; - VERIFY(m.is_eq(e, lhs, rhs)); - if (!a.is_int_real(lhs)) { - return true; - } - if (a.is_numeral(lhs) || a.is_numeral(rhs)) { - return test_ineq(e); - } - return - test_term(lhs) && - test_term(rhs) && - !a.is_mul(lhs) && - !a.is_mul(rhs); - } - - bool test_term(expr* e) const { - if (m.is_bool(e)) { - return true; - } - if (a.is_numeral(e)) { - return true; - } - if (is_offset(e)) { - return true; - } - expr* lhs, *rhs; - if (a.is_add(e, lhs, rhs)) { - if (!a.is_numeral(lhs)) { - std::swap(lhs, rhs); - } - return a.is_numeral(lhs) && is_offset(rhs); - } - if (a.is_mul(e, lhs, rhs)) { - return is_minus_one(lhs) || is_minus_one(rhs); - } - return false; - } - - bool is_non_arith_or_basic(expr* e) - { - if (!is_app(e)) { - return false; - } - family_id fid = to_app(e)->get_family_id(); - - if (fid == null_family_id && - !m.is_bool(e) && - to_app(e)->get_num_args() > 0) { - return true; - } - return - fid != m.get_basic_family_id() && - fid != null_family_id && - fid != a.get_family_id() && - fid != bv.get_family_id(); - } - - public: - test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {} - - void test_for_utvpi() { m_test_for_utvpi = true; } - - void operator()(expr* e) - { - if (!m_is_dl) { - return; - } - if (a.is_le(e) || a.is_ge(e)) { - m_is_dl = test_ineq(e); - } else if (m.is_eq(e)) { - m_is_dl = test_eq(e); - } else if (is_non_arith_or_basic(e)) { - m_is_dl = false; - } else if (is_app(e)) { - app* a = to_app(e); - for (unsigned i = 0; m_is_dl && i < a->get_num_args(); ++i) { - m_is_dl = test_term(a->get_arg(i)); - } - } - - if (!m_is_dl) { - char const* msg = "non-diff: "; - if (m_test_for_utvpi) { - msg = "non-utvpi: "; - } - IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";); - } - } - - bool is_dl() const { return m_is_dl; } - }; - -bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) -{ - test_diff_logic test(m); - expr_fast_mark1 mark; - for (unsigned i = 0; i < num_fmls; ++i) { - quick_for_each_expr(test, mark, fmls[i]); - } - return test.is_dl(); - } - -bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) -{ - test_diff_logic test(m); - test.test_for_utvpi(); - expr_fast_mark1 mark; - for (unsigned i = 0; i < num_fmls; ++i) { - quick_for_each_expr(test, mark, fmls[i]); - } - return test.is_dl(); - } - - - void subst_vars (ast_manager& m, app_ref_vector const& vars, - model* M, expr_ref& fml) -{ - expr_safe_replace sub (m); - model_evaluator_util mev (m); - mev.set_model(*M); - for (unsigned i = 0; i < vars.size (); i++) { - app* v = vars.get (i); - expr_ref val (m); - VERIFY(mev.eval (v, val, true)); - sub.insert (v, val); - } - sub (fml); + qe::mbp mbp(m, p); + mbp.spacer(vars, mdl, fml); } /* * eliminate simple equalities using qe_lite * then, MBP for Booleans (substitute), reals (based on LW), ints (based on Cooper), and arrays */ -void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, - const model_ref& M, bool reduce_all_selects, bool use_native_mbp, - bool dont_sub) -{ + void qe_project_spacer (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + model& mdl, bool reduce_all_selects, bool use_native_mbp, + bool dont_sub) { th_rewriter rw (m); TRACE ("spacer_mbp", - tout << "Before projection:\n"; - tout << mk_pp (fml, m) << "\n"; - tout << "Vars:\n"; - for (unsigned i = 0; i < vars.size(); ++i) { - tout << mk_pp(vars.get (i), m) << "\n"; - } - ); + tout << "Before projection:\n"; + tout << fml << "\n"; + tout << "Vars:\n" << vars;); { - // Ensure that top-level AND of fml is flat - expr_ref_vector flat(m); - flatten_and (fml, flat); - if (flat.size () == 1) - { fml = flat.get(0); } - else if (flat.size () > 1) - { fml = m.mk_and(flat.size(), flat.c_ptr()); } + // Ensure that top-level AND of fml is flat + expr_ref_vector flat(m); + flatten_and (fml, flat); + fml = mk_and(flat); } + + // uncomment for benchmarks + //to_mbp_benchmark(verbose_stream(), fml, vars); + app_ref_vector arith_vars (m); app_ref_vector array_vars (m); array_util arr_u (m); @@ -507,56 +139,45 @@ void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, expr_ref bval (m); while (true) { - params_ref p; - qe_lite qe(m, p, false); + params_ref p; + qe_lite qe(m, p, false); qe (vars, fml); rw (fml); TRACE ("spacer_mbp", - tout << "After qe_lite:\n"; - tout << mk_pp (fml, m) << "\n"; - tout << "Vars:\n"; - for (unsigned i = 0; i < vars.size(); ++i) { - tout << mk_pp(vars.get (i), m) << "\n"; - } - ); + tout << "After qe_lite:\n"; + tout << mk_pp (fml, m) << "\n"; + tout << "Vars:\n" << vars;); + SASSERT (!m.is_false (fml)); - bool has_bool_vars = false; // sort out vars into bools, arith (int/real), and arrays - for (unsigned i = 0; i < vars.size (); i++) { - if (m.is_bool (vars.get (i))) { - // obtain the interpretation of the ith var using model completion - VERIFY (M->eval (vars.get (i), bval, true)); - bool_sub.insert (vars.get (i), bval); - has_bool_vars = true; - } else if (arr_u.is_array(vars.get(i))) { - array_vars.push_back (vars.get (i)); - } else { - SASSERT (ari_u.is_int (vars.get (i)) || ari_u.is_real (vars.get (i))); - arith_vars.push_back (vars.get (i)); + for (app* v : vars) { + if (m.is_bool (v)) { + // obtain the interpretation of the ith var + // using model completion + model::scoped_model_completion _sc_(mdl, true); + bool_sub.insert (v, mdl(v)); + } else if (arr_u.is_array(v)) { + array_vars.push_back(v); + } else { + SASSERT (ari_u.is_int(v) || ari_u.is_real(v)); + arith_vars.push_back(v); } } // substitute Booleans - if (has_bool_vars) { - bool_sub (fml); + if (!bool_sub.empty()) { + bool_sub(fml); // -- bool_sub is not simplifying rw (fml); - SASSERT (!m.is_false (fml)); - TRACE ("spacer_mbp", - tout << "Projected Booleans:\n" << mk_pp (fml, m) << "\n"; - ); - bool_sub.reset (); + SASSERT(!m.is_false (fml)); + TRACE("spacer_mbp", tout << "Projected Booleans:\n" << fml << "\n"; ); + bool_sub.reset(); } - TRACE ("spacer_mbp", - tout << "Array vars:\n"; - for (unsigned i = 0; i < array_vars.size (); ++i) { - tout << mk_pp (array_vars.get (i), m) << "\n"; - } - ); + TRACE ("spacer_mbp", tout << "Array vars:\n"; tout << array_vars;); vars.reset (); @@ -565,70 +186,51 @@ void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, scoped_no_proof _sp (m); // -- local rewriter that is aware of current proof mode th_rewriter srw(m); - qe::array_project (*M.get (), array_vars, fml, vars, reduce_all_selects); + spacer_qe::array_project (mdl, array_vars, fml, vars, reduce_all_selects); SASSERT (array_vars.empty ()); srw (fml); SASSERT (!m.is_false (fml)); } TRACE ("spacer_mbp", - tout << "extended model:\n"; - model_pp (tout, *M); - tout << "Auxiliary variables of index and value sorts:\n"; - for (unsigned i = 0; i < vars.size (); i++) { - tout << mk_pp (vars.get (i), m) << "\n"; - } - ); + tout << "extended model:\n"; + model_pp (tout, mdl); + tout << "Auxiliary variables of index and value sorts:\n"; + tout << vars;); - if (vars.empty()) { break; } + if (vars.empty()) { break; } } // project reals and ints if (!arith_vars.empty ()) { - TRACE ("spacer_mbp", - tout << "Arith vars:\n"; - for (unsigned i = 0; i < arith_vars.size (); ++i) { - tout << mk_pp (arith_vars.get (i), m) << "\n"; - } - ); - - // XXX Does not seem to have an effect - // qe_lite qe(m); - // qe (arith_vars, fml); - // TRACE ("spacer_mbp", - // tout << "After second qelite: " << - // mk_pp (fml, m) << "\n";); + TRACE ("spacer_mbp", tout << "Arith vars:\n" << arith_vars;); if (use_native_mbp) { qe::mbp mbp (m); expr_ref_vector fmls(m); flatten_and (fml, fmls); - mbp (true, arith_vars, *M.get (), fmls); - fml = mk_and (fmls); - SASSERT (arith_vars.empty ()); + mbp (true, arith_vars, mdl, fmls); + fml = mk_and(fmls); + SASSERT(arith_vars.empty ()); } else { scoped_no_proof _sp (m); - qe::arith_project (*M.get (), arith_vars, fml); - } + spacer_qe::arith_project (mdl, arith_vars, fml); + } TRACE ("spacer_mbp", - tout << "Projected arith vars:\n" << mk_pp (fml, m) << "\n"; - tout << "Remaining arith vars:\n"; - for (unsigned i = 0; i < arith_vars.size (); i++) { - tout << mk_pp (arith_vars.get (i), m) << "\n"; - } - ); + tout << "Projected arith vars:\n" << fml << "\n"; + tout << "Remaining arith vars:\n" << arith_vars << "\n";); SASSERT (!m.is_false (fml)); } if (!arith_vars.empty ()) { - mbqi_project (*M.get(), arith_vars, fml); + mbqi_project (mdl, arith_vars, fml); } // substitute any remaining arith vars if (!dont_sub && !arith_vars.empty ()) { - subst_vars (m, arith_vars, M.get(), fml); + subst_vars (m, arith_vars, mdl, fml); TRACE ("spacer_mbp", tout << "After substituting remaining arith vars:\n"; tout << mk_pp (fml, m) << "\n"; @@ -638,16 +240,15 @@ void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, } DEBUG_CODE ( - model_evaluator_util mev (m); - expr_ref v(m); - mev.set_model(*M.get()); - SASSERT (mev.eval (fml, v, false)); - SASSERT (m.is_true (v)); + model_evaluator mev(mdl); + mev.set_model_completion(false); + SASSERT(mev.is_true(fml)); ); vars.reset (); - if (dont_sub && !arith_vars.empty ()) - { vars.append(arith_vars); } + if (dont_sub && !arith_vars.empty ()) { + vars.append(arith_vars); + } } @@ -655,18 +256,28 @@ void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, ptr_vector const& acc, unsigned j, func_decl* f, - expr* c) -{ + expr* c) + { if (is_app(c) && to_app(c)->get_decl() == f) { return to_app(c)->get_arg(j); - } else { + } else { return m.mk_app(acc[j], c); } } -void expand_literals(ast_manager &m, expr_ref_vector& conjs) -{ - if (conjs.empty()) { return; } + void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + model &mdl, bool reduce_all_selects, bool use_native_mbp, + bool dont_sub) { + if (use_native_mbp) + qe_project_z3(m, vars, fml, mdl, + reduce_all_selects, use_native_mbp, dont_sub); + else + qe_project_spacer(m, vars, fml, mdl, + reduce_all_selects, use_native_mbp, dont_sub); + } + + void expand_literals(ast_manager &m, expr_ref_vector& conjs) { + if (conjs.empty()) { return; } arith_util arith(m); datatype_util dt(m); bv_util bv(m); @@ -674,11 +285,7 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs) rational r; unsigned bv_size; - TRACE("spacer_expand", - tout << "begin expand\n"; - for (unsigned i = 0; i < conjs.size(); ++i) { - tout << mk_pp(conjs[i].get(), m) << "\n"; - }); + TRACE("spacer_expand", tout << "begin expand\n" << conjs << "\n";); for (unsigned i = 0; i < conjs.size(); ++i) { expr* e = conjs[i].get(); @@ -686,26 +293,25 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs) conjs[i] = arith.mk_le(e1,e2); if (i+1 == conjs.size()) { conjs.push_back(arith.mk_ge(e1, e2)); - } else { + } else { conjs.push_back(conjs[i+1].get()); conjs[i+1] = arith.mk_ge(e1, e2); } ++i; - } else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) || - (m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))){ + } else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) || + (m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))) { func_decl* f = to_app(val)->get_decl(); - func_decl* r = dt.get_constructor_recognizer(f); + func_decl* r = dt.get_constructor_is(f); conjs[i] = m.mk_app(r, c); ptr_vector const& acc = *dt.get_constructor_accessors(f); for (unsigned j = 0; j < acc.size(); ++j) { conjs.push_back(m.mk_eq(apply_accessor(m, acc, j, f, c), to_app(val)->get_arg(j))); } - } else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) || - (m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) { + } else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) || + (m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) { rational two(2); for (unsigned j = 0; j < bv_size; ++j) { parameter p(j); - //expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c); expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c)); if ((r % two).is_zero()) { e = m.mk_not(e); @@ -713,410 +319,416 @@ void expand_literals(ast_manager &m, expr_ref_vector& conjs) r = div(r, two); if (j == 0) { conjs[i] = e; - } else { + } else { conjs.push_back(e); } } } } - TRACE("spacer_expand", - tout << "end expand\n"; - for (unsigned i = 0; i < conjs.size(); ++i) { - tout << mk_pp(conjs[i].get(), m) << "\n"; - }); + TRACE("spacer_expand", tout << "end expand\n" << conjs << "\n";); } namespace { -class implicant_picker { - model_evaluator_util &m_mev; + class implicant_picker { + model &m_model; ast_manager &m; arith_util m_arith; expr_ref_vector m_todo; expr_mark m_visited; + // add literal to the implicant + // applies lightweight normalization + void add_literal(expr *e, expr_ref_vector &out) { + SASSERT(m.is_bool(e)); - void add_literal (expr *e, expr_ref_vector &out) - { - SASSERT (m.is_bool (e)); + expr_ref res(m), v(m); + v = m_model(e); + // the literal must have a value + SASSERT(m.is_true(v) || m.is_false(v)); - expr_ref res (m), v(m); - m_mev.eval (e, v, false); - SASSERT (m.is_true (v) || m.is_false (v)); + res = m.is_false(v) ? m.mk_not(e) : e; - res = m.is_false (v) ? m.mk_not (e) : e; - - if (m.is_distinct (res)) { - // -- (distinct a b) == (not (= a b)) + if (m.is_distinct(res)) { + // --(distinct a b) == (not (= a b)) if (to_app(res)->get_num_args() == 2) { - res = m.mk_eq (to_app(res)->get_arg(0), to_app(res)->get_arg(1)); - res = m.mk_not (res); + res = m.mk_eq(to_app(res)->get_arg(0), + to_app(res)->get_arg(1)); + res = m.mk_not(res); } } - expr *nres, *f1, *f2; + expr *nres = nullptr, *f1 = nullptr, *f2 = nullptr; if (m.is_not(res, nres)) { - // -- (not (xor a b)) == (= a b) + // --(not (xor a b)) == (= a b) if (m.is_xor(nres, f1, f2)) - { res = m.mk_eq(f1, f2); } - + res = m.mk_eq(f1, f2); // -- split arithmetic inequality - else if (m.is_eq (nres, f1, f2) && m_arith.is_int_real (f1)) { - expr_ref u(m); - u = m_arith.mk_lt(f1, f2); - if (m_mev.eval (u, v, false) && m.is_true (v)) - { res = u; } - else - { res = m_arith.mk_lt(f2, f1); } + else if (m.is_eq(nres, f1, f2) && m_arith.is_int_real(f1)) { + res = m_arith.mk_lt(f1, f2); + if (!m_model.is_true(res)) + res = m_arith.mk_lt(f2, f1); } } - if (!m_mev.is_true (res)) - { verbose_stream() << "Bad literal: " << mk_pp(res, m) << "\n"; } - SASSERT (m_mev.is_true (res)); - out.push_back (res); + if (!m_model.is_true(res)) { + verbose_stream() << "Bad literal: " << res << "\n"; + } + SASSERT(m_model.is_true(res)); + out.push_back(res); } - void process_app(app *a, expr_ref_vector &out) - { - if (m_visited.is_marked(a)) { return; } - SASSERT (m.is_bool (a)); + void process_app(app *a, expr_ref_vector &out) { + if (m_visited.is_marked(a)) return; + SASSERT(m.is_bool(a)); expr_ref v(m); - m_mev.eval (a, v, false); + v = m_model(a); + bool is_true = m.is_true(v); - if (!m.is_true(v) && !m.is_false(v)) { return; } + if (!is_true && !m.is_false(v)) return; - expr *na, *f1, *f2, *f3; + expr *na = nullptr, *f1 = nullptr, *f2 = nullptr, *f3 = nullptr; - if (a->get_family_id() != m.get_basic_family_id()) - { add_literal(a, out); } - else if (is_uninterp_const(a)) - { add_literal(a, out); } - else if (m.is_not(a, na) && m.is_not(na, na)) - { m_todo.push_back(na); } - else if (m.is_not(a, na)) - { m_todo.push_back(na); } + SASSERT(!m.is_false(a)); + if (m.is_true(a)) { + // noop + } + else if (a->get_family_id() != m.get_basic_family_id()) { + add_literal(a, out); + } + else if (is_uninterp_const(a)) { + add_literal(a, out); + } + else if (m.is_not(a, na)) { + m_todo.push_back(na); + } else if (m.is_distinct(a)) { - if (m.is_false(v)) - m_todo.push_back - (qe::project_plugin::pick_equality(m, *m_mev.get_model(), a)); - else if (a->get_num_args() == 2) - { add_literal(a, out); } - else + if (!is_true) { + f1 = qe::project_plugin::pick_equality(m, m_model, a); + m_todo.push_back(f1); + } + else if (a->get_num_args() == 2) { + add_literal(a, out); + } + else { m_todo.push_back(m.mk_distinct_expanded(a->get_num_args(), a->get_args())); - } else if (m.is_and(a)) { - if (m.is_true(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } - else if (m.is_false(v)) { - for (unsigned i = 0, sz = a->get_num_args (); i < sz; ++i) { - if (m_mev.is_false(a->get_arg(i))) { - m_todo.push_back(a->get_arg(i)); + } + } + else if (m.is_and(a)) { + if (is_true) { + m_todo.append(a->get_num_args(), a->get_args()); + } + else { + for (expr* e : *a) { + if (m_model.is_false(e)) { + m_todo.push_back(e); break; } } } - } else if (m.is_or(a)) { - if (m.is_false(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } - else if (m.is_true(v)) { - for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) { - if (m_mev.is_true(a->get_arg(i))) { - m_todo.push_back(a->get_arg(i)); + } + else if (m.is_or(a)) { + if (!is_true) + m_todo.append(a->get_num_args(), a->get_args()); + else { + for (expr * e : *a) { + if (m_model.is_true(e)) { + m_todo.push_back(e); break; } } } - } else if (m.is_iff(a, f1, f2) || m.is_eq(a, f1, f2) || - (m.is_true(v) && m.is_not(a, na) && m.is_xor (na, f1, f2))) { + } + else if (m.is_eq(a, f1, f2) || + (is_true && m.is_not(a, na) && m.is_xor(na, f1, f2))) { if (!m.are_equal(f1, f2) && !m.are_distinct(f1, f2)) { if (m.is_bool(f1) && (!is_uninterp_const(f1) || !is_uninterp_const(f2))) - { m_todo.append(a->get_num_args(), a->get_args()); } + m_todo.append(a->get_num_args(), a->get_args()); else - { add_literal(a, out); } + add_literal(a, out); } - } else if (m.is_ite(a, f1, f2, f3)) { - if (m.are_equal(f2, f3)) { m_todo.push_back(f2); } - else if (m_mev.is_true (f2) && m_mev.is_true (f3)) { - m_todo.push_back(f2); - m_todo.push_back(f3); - } else if (m_mev.is_false(f2) && m_mev.is_false(f3)) { - m_todo.push_back(f2); - m_todo.push_back(f3); - } else if (m_mev.is_true(f1)) { - m_todo.push_back(f1); - m_todo.push_back(f2); - } else if (m_mev.is_false(f1)) { - m_todo.push_back(f1); - m_todo.push_back(f3); + } + else if (m.is_ite(a, f1, f2, f3)) { + if (m.are_equal(f2, f3)) { + m_todo.push_back(f2); } - } else if (m.is_xor(a, f1, f2)) - { m_todo.append(a->get_num_args(), a->get_args()); } + else if (m_model.is_true(f2) && m_model.is_true(f3)) { + m_todo.push_back(f2); + m_todo.push_back(f3); + } + else if (m_model.is_false(f2) && m_model.is_false(f3)) { + m_todo.push_back(f2); + m_todo.push_back(f3); + } + else if (m_model.is_true(f1)) { + m_todo.push_back(f1); + m_todo.push_back(f2); + } + else if (m_model.is_false(f1)) { + m_todo.push_back(f1); + m_todo.push_back(f3); + } + } + else if (m.is_xor(a, f1, f2)) { + m_todo.append(a->get_num_args(), a->get_args()); + } else if (m.is_implies(a, f1, f2)) { - if (m.is_true (v)) { - if (m_mev.is_true(f2)) { m_todo.push_back(f2); } - else if (m_mev.is_false(f1)) { m_todo.push_back(f1); } - } else if (m.is_false(v)) - { m_todo.append(a->get_num_args(), a->get_args()); } - } else if (m.is_true(a) || m.is_false(a)) { /* nothing */ } + if (is_true) { + if (m_model.is_true(f2)) + m_todo.push_back(f2); + else if (m_model.is_false(f1)) + m_todo.push_back(f1); + } + else + m_todo.append(a->get_num_args(), a->get_args()); + } else { - verbose_stream () << "Unexpected expression: " - << mk_pp(a, m) << "\n"; + IF_VERBOSE(0, + verbose_stream() << "Unexpected expression: " + << mk_pp(a, m) << "\n"); UNREACHABLE(); } } - void pick_literals(expr *e, expr_ref_vector &out) - { + void pick_literals(expr *e, expr_ref_vector &out) { SASSERT(m_todo.empty()); - if (m_visited.is_marked(e)) { return; } - SASSERT(is_app(e)); + if (m_visited.is_marked(e) || !is_app(e)) return; m_todo.push_back(e); do { - app *a = to_app(m_todo.back()); + e = m_todo.back(); + if (!is_app(e)) continue; + app * a = to_app(e); m_todo.pop_back(); process_app(a, out); m_visited.mark(a, true); - } while (!m_todo.empty()); + } + while (!m_todo.empty()); } - bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) - { + bool pick_implicant(const expr_ref_vector &in, expr_ref_vector &out) { m_visited.reset(); - expr_ref e(m); - e = mk_and (in); - bool is_true = m_mev.is_true (e); + bool is_true = m_model.is_true(in); - for (unsigned i = 0, sz = in.size (); i < sz; ++i) { - if (is_true || m_mev.is_true(in.get(i))) - { pick_literals(in.get(i), out); } + for (expr* e : in) { + if (is_true || m_model.is_true(e)) { + pick_literals(e, out); + } } - - m_visited.reset (); + m_visited.reset(); return is_true; } public: - implicant_picker (model_evaluator_util &mev) : - m_mev (mev), m (m_mev.get_ast_manager ()), m_arith(m), m_todo(m) {} - void operator() (expr_ref_vector &in, expr_ref_vector& out) - {pick_implicant (in, out);} + implicant_picker(model &mdl) : + m_model(mdl), m(m_model.get_manager()), m_arith(m), m_todo(m) {} + + void operator()(expr_ref_vector &in, expr_ref_vector& out) { + model::scoped_model_completion _sc_(m_model, false); + pick_implicant(in, out); + } }; - } - - void compute_implicant_literals (model_evaluator_util &mev, expr_ref_vector &formula, - expr_ref_vector &res) - { - // XXX what is the point of flattening? - flatten_and (formula); - if (formula.empty()) { return; } - - implicant_picker ipick (mev); - ipick (formula, res); - } - -void simplify_bounds_old(expr_ref_vector& cube) { - ast_manager& m = cube.m(); - - scoped_no_proof _no_pf_(m); - goal_ref g(alloc(goal, m, false, false, false)); - - for (unsigned i = 0; i < cube.size(); ++i) { - g->assert_expr(cube.get(i)); - } - - expr_ref tmp(m); - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref core(m); - goal_ref_buffer result; - tactic_ref simplifier = mk_arith_bounds_tactic(m); - (*simplifier)(g, result, mc, pc, core); - SASSERT(result.size() == 1); - goal* r = result[0]; - - cube.reset(); - for (unsigned i = 0; i < r->size(); ++i) { - cube.push_back(r->form(i)); - } } -void simplify_bounds_new (expr_ref_vector &cube) { - ast_manager &m = cube.m(); + void compute_implicant_literals(model &mdl, + expr_ref_vector &formula, + expr_ref_vector &res) { + // flatten the formula and remove all trivial literals + // TBD: not clear why there is a dependence on it(other than + // not handling of Boolean constants by implicant_picker), however, + // it was a source of a problem on a benchmark + flatten_and(formula); + if (formula.empty()) {return;} - scoped_no_proof _no_pf_(m); - - goal_ref g(alloc(goal, m, false, false, false)); - for (unsigned i = 0, sz = cube.size(); i < sz; ++i) { - g->assert_expr(cube.get(i)); + implicant_picker ipick(mdl); + ipick(formula, res); } - model_converter_ref mc; - proof_converter_ref pc; - expr_dependency_ref dep(m); - goal_ref_buffer goals; - tactic_ref prop_values = mk_propagate_values_tactic(m); - tactic_ref prop_bounds = mk_propagate_ineqs_tactic(m); - tactic_ref t = and_then(prop_values.get(), prop_bounds.get()); + void simplify_bounds_old(expr_ref_vector& cube) { + ast_manager& m = cube.m(); + scoped_no_proof _no_pf_(m); + goal_ref g(alloc(goal, m, false, false, false)); + for (expr* c : cube) + g->assert_expr(c); - (*t)(g, goals, mc, pc, dep); - SASSERT(goals.size() == 1); - - g = goals[0]; - cube.reset(); - for (unsigned i = 0; i < g->size(); ++i) { - cube.push_back(g->form(i)); + goal_ref_buffer result; + tactic_ref simplifier = mk_arith_bounds_tactic(m); + (*simplifier)(g, result); + SASSERT(result.size() == 1); + goal* r = result[0]; + cube.reset(); + for (unsigned i = 0; i < r->size(); ++i) { + cube.push_back(r->form(i)); + } } -} -void simplify_bounds(expr_ref_vector &cube) { - simplify_bounds_new(cube); -} + void simplify_bounds_new(expr_ref_vector &cube) { + ast_manager &m = cube.m(); + scoped_no_proof _no_pf_(m); + goal_ref g(alloc(goal, m, false, false, false)); + for (expr* c : cube) + g->assert_expr(c); -/// Adhoc rewriting of arithmetic expressions -struct adhoc_rewriter_cfg : public default_rewriter_cfg { - ast_manager &m; - arith_util m_util; + goal_ref_buffer goals; + tactic_ref prop_values = mk_propagate_values_tactic(m); + tactic_ref prop_bounds = mk_propagate_ineqs_tactic(m); + tactic_ref t = and_then(prop_values.get(), prop_bounds.get()); - adhoc_rewriter_cfg (ast_manager &manager) : m(manager), m_util(m) {} + (*t)(g, goals); + SASSERT(goals.size() == 1); - bool is_le(func_decl const * n) const - { return is_decl_of(n, m_util.get_family_id (), OP_LE); } - bool is_ge(func_decl const * n) const - { return is_decl_of(n, m_util.get_family_id (), OP_GE); } + g = goals[0]; + cube.reset(); + for (unsigned i = 0; i < g->size(); ++i) { + cube.push_back(g->form(i)); + } + } - br_status reduce_app (func_decl * f, unsigned num, expr * const * args, - expr_ref & result, proof_ref & result_pr) - { + void simplify_bounds(expr_ref_vector &cube) { + simplify_bounds_new(cube); + } + + /// Adhoc rewriting of arithmetic expressions + struct adhoc_rewriter_cfg : public default_rewriter_cfg { + ast_manager &m; + arith_util m_util; + + adhoc_rewriter_cfg(ast_manager &manager) : m(manager), m_util(m) {} + + bool is_le(func_decl const * n) const { return m_util.is_le(n); } + bool is_ge(func_decl const * n) const { return m_util.is_ge(n); } + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, + expr_ref & result, proof_ref & result_pr) { expr * e; - br_status st = BR_FAILED; - if (is_le(f)) { - st = mk_le_core (args[0], args[1], result); - } else if (is_ge(f)) { - st = mk_ge_core (args[0], args[1], result); - } else if (m.is_not(f)) { - if (m.is_not (args[0], e)) { - result = e; - st = BR_DONE; + if (is_le(f)) + return mk_le_core(args[0], args[1], result); + if (is_ge(f)) + return mk_ge_core(args[0], args[1], result); + if (m.is_not(f) && m.is_not(args[0], e)) { + result = e; + return BR_DONE; + } + return BR_FAILED; + } + + br_status mk_le_core(expr *arg1, expr * arg2, expr_ref & result) { + // t <= -1 ==> t < 0 ==> !(t >= 0) + if (m_util.is_int(arg1) && m_util.is_minus_one(arg2)) { + result = m.mk_not(m_util.mk_ge(arg1, mk_zero())); + return BR_DONE; + } + return BR_FAILED; + } + br_status mk_ge_core(expr * arg1, expr * arg2, expr_ref & result) { + // t >= 1 ==> t > 0 ==> !(t <= 0) + if (m_util.is_int(arg1) && is_one(arg2)) { + + result = m.mk_not(m_util.mk_le(arg1, mk_zero())); + return BR_DONE; + } + return BR_FAILED; + } + expr * mk_zero() {return m_util.mk_numeral(rational(0), true);} + bool is_one(expr const * n) const { + rational val; return m_util.is_numeral(n, val) && val.is_one(); + } + }; + + void normalize(expr *e, expr_ref &out, + bool use_simplify_bounds, + bool use_factor_eqs) + { + + params_ref params; + // arith_rewriter + params.set_bool("sort_sums", true); + params.set_bool("gcd_rounding", true); + params.set_bool("arith_lhs", true); + // poly_rewriter + params.set_bool("som", true); + params.set_bool("flat", true); + + // apply rewriter + th_rewriter rw(out.m(), params); + rw(e, out); + + adhoc_rewriter_cfg adhoc_cfg(out.m()); + rewriter_tpl adhoc_rw(out.m(), false, adhoc_cfg); + adhoc_rw(out.get(), out); + + if (out.m().is_and(out)) { + expr_ref_vector v(out.m()); + flatten_and(out, v); + + if (v.size() > 1) { + // sort arguments of the top-level and + std::stable_sort(v.c_ptr(), v.c_ptr() + v.size(), ast_lt_proc()); + + if (use_simplify_bounds) { + // remove redundant inequalities + simplify_bounds(v); } + if (use_factor_eqs) { + // -- refactor equivalence classes and choose a representative + qe::term_graph egraph(out.m()); + egraph.add_lits(v); + v.reset(); + egraph.to_lits(v); + } + + TRACE("spacer_normalize", + tout << "Normalized:\n" + << out << "\n" + << "to\n" + << mk_and(v) << "\n";); + TRACE("spacer_normalize", + qe::term_graph egraph(out.m()); + for (expr* e : v) egraph.add_lit(to_app(e)); + tout << "Reduced app:\n" + << mk_pp(egraph.to_app(), out.m()) << "\n";); + out = mk_and(v); } - - return st; - } - - br_status mk_le_core (expr *arg1, expr * arg2, expr_ref & result) - { - // t <= -1 ==> t < 0 ==> ! (t >= 0) - if (m_util.is_int (arg1) && m_util.is_minus_one (arg2)) { - result = m.mk_not (m_util.mk_ge (arg1, mk_zero ())); - return BR_DONE; - } - return BR_FAILED; - } - br_status mk_ge_core (expr * arg1, expr * arg2, expr_ref & result) - { - // t >= 1 ==> t > 0 ==> ! (t <= 0) - if (m_util.is_int (arg1) && is_one (arg2)) { - - result = m.mk_not (m_util.mk_le (arg1, mk_zero ())); - return BR_DONE; - } - return BR_FAILED; - } - expr * mk_zero () {return m_util.mk_numeral (rational (0), true);} - bool is_one (expr const * n) const - {rational val; return m_util.is_numeral (n, val) && val.is_one ();} -}; - -void normalize (expr *e, expr_ref &out, - bool use_simplify_bounds, - bool use_factor_eqs) -{ - - params_ref params; - // arith_rewriter - params.set_bool ("sort_sums", true); - params.set_bool ("gcd_rounding", true); - params.set_bool ("arith_lhs", true); - // poly_rewriter - params.set_bool ("som", true); - params.set_bool ("flat", true); - - // apply rewriter - th_rewriter rw(out.m(), params); - rw (e, out); - - adhoc_rewriter_cfg adhoc_cfg(out.m ()); - rewriter_tpl adhoc_rw (out.m (), false, adhoc_cfg); - adhoc_rw (out.get (), out); - - if (out.m().is_and(out)) { - expr_ref_vector v(out.m()); - flatten_and (out, v); - - if (v.size() > 1) { - // sort arguments of the top-level and - std::stable_sort (v.c_ptr(), v.c_ptr () + v.size (), ast_lt_proc()); - - if (use_simplify_bounds) { - // remove redundant inequalities - simplify_bounds (v); - } - if (use_factor_eqs) { - // pick non-constant value representative for - // equivalence classes - expr_equiv_class eq_classes(out.m()); - factor_eqs(v, eq_classes); - equiv_to_expr(eq_classes, v); - } - - out = mk_and (v); } } -} -// rewrite term such that the pretty printing is easier to read -struct adhoc_rewriter_rpp : public default_rewriter_cfg { - ast_manager &m; - arith_util m_arith; + // rewrite term such that the pretty printing is easier to read + struct adhoc_rewriter_rpp : public default_rewriter_cfg { + ast_manager &m; + arith_util m_arith; - adhoc_rewriter_rpp (ast_manager &manager) : m(manager), m_arith(m) {} + adhoc_rewriter_rpp(ast_manager &manager) : m(manager), m_arith(m) {} - bool is_le(func_decl const * n) const - { return is_decl_of(n, m_arith.get_family_id (), OP_LE); } - bool is_ge(func_decl const * n) const - { return is_decl_of(n, m_arith.get_family_id (), OP_GE); } - bool is_lt(func_decl const * n) const - { return is_decl_of(n, m_arith.get_family_id (), OP_LT); } - bool is_gt(func_decl const * n) const - { return is_decl_of(n, m_arith.get_family_id (), OP_GT); } - bool is_zero (expr const * n) const - {rational val; return m_arith.is_numeral(n, val) && val.is_zero();} + bool is_le(func_decl const * n) const { return m_arith.is_le(n); } + bool is_ge(func_decl const * n) const { return m_arith.is_ge(n); } + bool is_lt(func_decl const * n) const { return m_arith.is_lt(n); } + bool is_gt(func_decl const * n) const { return m_arith.is_gt(n); } + bool is_zero(expr const * n) const {rational val; return m_arith.is_numeral(n, val) && val.is_zero();} - br_status reduce_app (func_decl * f, unsigned num, expr * const * args, - expr_ref & result, proof_ref & result_pr) + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, + expr_ref & result, proof_ref & result_pr) { br_status st = BR_FAILED; expr *e1, *e2, *e3, *e4; - // rewrites (= (+ A (* -1 B)) 0) into (= A B) - if (m.is_eq (f) && is_zero (args [1]) && - m_arith.is_add (args[0], e1, e2) && - m_arith.is_mul (e2, e3, e4) && m_arith.is_minus_one (e3)) { - result = m.mk_eq (e1, e4); + // rewrites(=(+ A(* -1 B)) 0) into(= A B) + if (m.is_eq(f) && is_zero(args [1]) && + m_arith.is_add(args[0], e1, e2) && + m_arith.is_mul(e2, e3, e4) && m_arith.is_minus_one(e3)) { + result = m.mk_eq(e1, e4); return BR_DONE; } // simplify normalized leq, where right side is different from 0 - // rewrites (<= (+ A (* -1 B)) C) into (<= A B+C) + // rewrites(<=(+ A(* -1 B)) C) into(<= A B+C) else if ((is_le(f) || is_lt(f) || is_ge(f) || is_gt(f)) && - m_arith.is_add (args[0], e1, e2) && - m_arith.is_mul (e2, e3, e4) && m_arith.is_minus_one (e3)) { + m_arith.is_add(args[0], e1, e2) && + m_arith.is_mul(e2, e3, e4) && m_arith.is_minus_one(e3)) { expr_ref rhs(m); - rhs = is_zero (args[1]) ? e4 : m_arith.mk_add(e4, args[1]); + rhs = is_zero(args[1]) ? e4 : m_arith.mk_add(e4, args[1]); if (is_le(f)) { result = m_arith.mk_le(e1, rhs); @@ -1134,7 +746,7 @@ struct adhoc_rewriter_rpp : public default_rewriter_cfg { { UNREACHABLE(); } } // simplify negation of ordering predicate - else if (m.is_not (f)) { + else if (m.is_not(f)) { if (m_arith.is_lt(args[0], e1, e2)) { result = m_arith.mk_ge(e1, e2); st = BR_DONE; @@ -1151,228 +763,175 @@ struct adhoc_rewriter_rpp : public default_rewriter_cfg { } return st; } + }; -}; -mk_epp::mk_epp(ast *t, ast_manager &m, unsigned indent, - unsigned num_vars, char const * var_prefix) : - mk_pp (t, m, m_epp_params, indent, num_vars, var_prefix), m_epp_expr(m) { - m_epp_params.set_uint("min_alias_size", UINT_MAX); - m_epp_params.set_uint("max_depth", UINT_MAX); + mk_epp::mk_epp(ast *t, ast_manager &m, unsigned indent, + unsigned num_vars, char const * var_prefix) : + mk_pp(t, m, m_epp_params, indent, num_vars, var_prefix), m_epp_expr(m) { + m_epp_params.set_uint("min_alias_size", UINT_MAX); + m_epp_params.set_uint("max_depth", UINT_MAX); - if (is_expr (m_ast)) { - rw(to_expr(m_ast), m_epp_expr); - m_ast = m_epp_expr; - } -} - -void mk_epp::rw(expr *e, expr_ref &out) -{ - adhoc_rewriter_rpp cfg(out.m()); - rewriter_tpl arw(out.m(), false, cfg); - arw(e, out); -} - - void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars) - { - expr_free_vars fv; - ast_manager &m = out.get_manager (); - fv (e); - if (vars.size () < fv.size ()) - { vars.resize(fv.size()); } - for (unsigned i = 0, sz = fv.size (); i < sz; ++i) { - SASSERT (fv[i]); - std::string str = "zk!" + datalog::to_string(sz - 1 - i); - vars [i] = m.mk_const (symbol(str.c_str()), fv [i]); - } - var_subst vs(m); - vs (e, vars.size (), (expr**) vars.c_ptr (), out); + if (is_expr(m_ast)) { + rw(to_expr(m_ast), m_epp_expr); + m_ast = m_epp_expr; + } } + void mk_epp::rw(expr *e, expr_ref &out) { + adhoc_rewriter_rpp cfg(out.m()); + rewriter_tpl arw(out.m(), false, cfg); + arw(e, out); + } + + void ground_expr(expr *e, expr_ref &out, app_ref_vector &vars) { + expr_free_vars fv; + ast_manager &m = out.get_manager(); + + fv(e); + if (vars.size() < fv.size()) { + vars.resize(fv.size()); + } + for (unsigned i = 0, sz = fv.size(); i < sz; ++i) { + sort *s = fv[i] ? fv[i] : m.mk_bool_sort(); + vars[i] = mk_zk_const(m, i, s); + var_subst vs(m, false); + vs(e, vars.size(),(expr * *) vars.c_ptr(), out); + } + } struct index_term_finder { - ast_manager &m; - array_util m_array; - app_ref m_var; + ast_manager &m; + array_util m_array; + app_ref m_var; expr_ref_vector &m_res; - index_term_finder (ast_manager &mgr, app* v, expr_ref_vector &res) : m(mgr), m_array (m), m_var (v, m), m_res (res) {} - void operator() (var *n) {} - void operator() (quantifier *n) {} - void operator() (app *n) - { - expr *e1, *e2; - if (m_array.is_select (n) && n->get_arg (1) != m_var) { - m_res.push_back (n->get_arg (1)); - } else if (m.is_eq(n, e1, e2)) { - if (e1 == m_var) { m_res.push_back(e2); } - else if (e2 == m_var) { m_res.push_back(e1); } + index_term_finder(ast_manager &mgr, app* v, expr_ref_vector &res) : m(mgr), m_array(m), m_var(v, m), m_res(res) {} + void operator()(var *n) {} + void operator()(quantifier *n) {} + void operator()(app *n) { + if (m_array.is_select(n) || m.is_eq(n)) { + unsigned i = 0; + for (expr * arg : *n) { + if ((m.is_eq(n) || i > 0) && m_var != arg) m_res.push_back(arg); + ++i; + } } } }; - bool mbqi_project_var (model_evaluator_util &mev, app* var, expr_ref &fml) - { - ast_manager &m = fml.get_manager (); + bool mbqi_project_var(model &mdl, app* var, expr_ref &fml) { + ast_manager &m = fml.get_manager(); + model::scoped_model_completion _sc_(mdl, false); expr_ref val(m); - mev.eval (var, val, false); + val = mdl(var); - TRACE ("mbqi_project_verbose", - tout << "MBQI: var: " << mk_pp (var, m) << "\n" - << "fml: " << mk_pp (fml, m) << "\n";); - expr_ref_vector terms (m); - index_term_finder finder (m, var, terms); - for_each_expr (finder, fml); + TRACE("mbqi_project_verbose", + tout << "MBQI: var: " << mk_pp(var, m) << "\n" + << "fml: " << fml << "\n";); + expr_ref_vector terms(m); + index_term_finder finder(m, var, terms); + for_each_expr(finder, fml); + TRACE("mbqi_project_verbose", tout << "terms:\n" << terms << "\n";); - TRACE ("mbqi_project_verbose", - tout << "terms:\n"; - for (unsigned i = 0, e = terms.size (); i < e; ++i) - tout << i << ": " << mk_pp (terms.get (i), m) << "\n"; - ); + for (expr * term : terms) { + expr_ref tval(m); + tval = mdl(term); - for (unsigned i = 0, e = terms.size(); i < e; ++i) { - expr* term = terms.get (i); - expr_ref tval (m); - mev.eval (term, tval, false); - - TRACE ("mbqi_project_verbose", - tout << "term: " << mk_pp (term, m) - << " tval: " << mk_pp (tval, m) - << " val: " << mk_pp (val, m) << "\n";); + TRACE("mbqi_project_verbose", + tout << "term: " << mk_pp(term, m) + << " tval: " << tval << " val: " << val << "\n";); // -- if the term does not contain an occurrence of var // -- and is in the same equivalence class in the model - if (tval == val && !occurs (var, term)) { - TRACE ("mbqi_project", - tout << "MBQI: replacing " << mk_pp (var, m) << " with " << mk_pp (term, m) << "\n";); + if (tval == val && !occurs(var, term)) { + TRACE("mbqi_project", + tout << "MBQI: replacing " << mk_pp(var, m) + << " with " << mk_pp(term, m) << "\n";); expr_safe_replace sub(m); - sub.insert (var, term); - sub (fml); + sub.insert(var, term); + sub(fml); return true; } } - TRACE ("mbqi_project", - tout << "MBQI: failed to eliminate " << mk_pp (var, m) << " from " << mk_pp (fml, m) << "\n";); + TRACE("mbqi_project", + tout << "MBQI: failed to eliminate " << mk_pp(var, m) + << " from " << fml << "\n";); return false; } - void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml) - { - ast_manager &m = fml.get_manager (); - model_evaluator_util mev(m); - mev.set_model (M); + void mbqi_project(model &mdl, app_ref_vector &vars, expr_ref &fml) { + ast_manager &m = fml.get_manager(); expr_ref tmp(m); + model::scoped_model_completion _sc_(mdl, false); // -- evaluate to initialize mev cache - mev.eval (fml, tmp, false); - tmp.reset (); + tmp = mdl(fml); + tmp.reset(); - for (unsigned idx = 0; idx < vars.size (); ) { - if (mbqi_project_var (mev, vars.get (idx), fml)) { - vars[idx] = vars.back (); - vars.pop_back (); - } else { - idx++; - } + unsigned j = 0; + for (app* v : vars) + if (!mbqi_project_var(mdl, v, fml)) + vars[j++] = v; + vars.shrink(j); + } + + struct found {}; + struct check_select { + array_util a; + check_select(ast_manager& m): a(m) {} + void operator()(expr* n) {} + void operator()(app* n) { if (a.is_select(n)) throw found(); } + }; + + bool contains_selects(expr* fml, ast_manager& m) { + check_select cs(m); + try { + for_each_expr(cs, fml); + return false; + } + catch(found) { + return true; } } -bool contains_selects(expr* fml, ast_manager& m) -{ - array_util a_util(m); - if (!is_app(fml)) { return false; } - ast_mark done; - ptr_vector todo; - todo.push_back (to_app (fml)); - while (!todo.empty ()) { - app* a = todo.back (); - if (done.is_marked (a)) { - todo.pop_back (); - continue; - } - unsigned num_args = a->get_num_args (); - bool all_done = true; - for (unsigned i = 0; i < num_args; i++) { - expr* arg = a->get_arg (i); - if (!done.is_marked (arg) && is_app (arg)) { - todo.push_back (to_app (arg)); - all_done = false; - } - } - if (!all_done) { continue; } - todo.pop_back (); - if (a_util.is_select (a)) - { return true; } - done.mark (a, true); + struct collect_indices { + app_ref_vector& m_indices; + array_util a; + collect_indices(app_ref_vector& indices): m_indices(indices), a(indices.get_manager()) {} + void operator()(expr* n) {} + void operator()(app* n) { + if (a.is_select(n)) + for (unsigned i = 1; i < n->get_num_args(); ++i) + if (is_app(n->get_arg(i))) + m_indices.push_back(to_app(n->get_arg(i))); } - return false; + }; + + void get_select_indices(expr* fml, app_ref_vector &indices, ast_manager& m) { + collect_indices ci(indices); + for_each_expr(ci, fml); } -void get_select_indices(expr* fml, app_ref_vector& indices, ast_manager& m) -{ - array_util a_util(m); - if (!is_app(fml)) { return; } - ast_mark done; - ptr_vector todo; - todo.push_back (to_app (fml)); - while (!todo.empty ()) { - app* a = todo.back (); - if (done.is_marked (a)) { - todo.pop_back (); - continue; - } - unsigned num_args = a->get_num_args (); - bool all_done = true; - for (unsigned i = 0; i < num_args; i++) { - expr* arg = a->get_arg (i); - if (!done.is_marked (arg) && is_app (arg)) { - todo.push_back (to_app (arg)); - all_done = false; - } - } - if (!all_done) { continue; } - todo.pop_back (); - if (a_util.is_select (a)) { - SASSERT(a->get_num_args() == 2); - indices.push_back(to_app(a->get_arg(1))); - } - done.mark (a, true); + struct collect_decls { + app_ref_vector& m_decls; + std::string& prefix; + collect_decls(app_ref_vector& decls, std::string& p): m_decls(decls), prefix(p) {} + void operator()(expr* n) {} + void operator()(app* n) { + if (n->get_decl()->get_name().str().find(prefix) != std::string::npos) + m_decls.push_back(n); } + }; + + void find_decls(expr* fml, app_ref_vector& decls, std::string& prefix) { + collect_decls cd(decls, prefix); + for_each_expr(cd, fml); } -void find_decls(expr* fml, app_ref_vector& decls, std::string& prefix) -{ - if (!is_app(fml)) { return; } - ast_mark done; - ptr_vector todo; - todo.push_back (to_app (fml)); - while (!todo.empty ()) { - app* a = todo.back (); - if (done.is_marked (a)) { - todo.pop_back (); - continue; - } - unsigned num_args = a->get_num_args (); - bool all_done = true; - for (unsigned i = 0; i < num_args; i++) { - expr* arg = a->get_arg (i); - if (!done.is_marked (arg) && is_app (arg)) { - todo.push_back (to_app (arg)); - all_done = false; - } - } - if (!all_done) { continue; } - todo.pop_back (); - if (a->get_decl()->get_name().str().find(prefix) != std::string::npos) - { decls.push_back(a); } - done.mark (a, true); - } - return; } -} template class rewriter_tpl; template class rewriter_tpl; -template class rewriter_tpl; diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index 7fb17329e..9912769c5 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -40,133 +40,102 @@ Revision History: class model; class model_core; -class model_evaluator; namespace spacer { -inline unsigned infty_level () {return UINT_MAX;} - -inline bool is_infty_level(unsigned lvl) -{ return lvl == infty_level (); } - -inline unsigned next_level(unsigned lvl) -{ return is_infty_level(lvl)?lvl:(lvl+1); } - -inline unsigned prev_level (unsigned lvl) -{ - if(is_infty_level(lvl)) { return infty_level(); } - if(lvl == 0) { return 0; } - return lvl -1; -} - -struct pp_level { - unsigned m_level; - pp_level(unsigned l): m_level(l) {} -}; - -inline std::ostream& operator<<(std::ostream& out, pp_level const& p) -{ - if (is_infty_level(p.m_level)) { - return out << "oo"; - } else { - return out << p.m_level; + inline unsigned infty_level () { + return UINT_MAX; } -} + + inline bool is_infty_level(unsigned lvl) { + return lvl == infty_level (); + } + + inline unsigned next_level(unsigned lvl) { + return is_infty_level(lvl)?lvl:(lvl+1); + } + + inline unsigned prev_level (unsigned lvl) { + if (is_infty_level(lvl)) return infty_level(); + if (lvl == 0) return 0; + return lvl - 1; + } + + struct pp_level { + unsigned m_level; + pp_level(unsigned l): m_level(l) {} + }; + + inline std::ostream& operator<<(std::ostream& out, pp_level const& p) { + if (is_infty_level(p.m_level)) { + return out << "oo"; + } else { + return out << p.m_level; + } + } + + typedef ptr_vector app_vector; + typedef ptr_vector decl_vector; + typedef obj_hashtable func_decl_set; + + /** + \brief hoist non-boolean if expressions. + */ + + void to_mbp_benchmark(std::ostream &out, const expr* fml, const app_ref_vector &vars); + // TBD: deprecate by qe::mbp + /** + * do the following in sequence + * 1. use qe_lite to cheaply eliminate vars + * 2. for remaining boolean vars, substitute using M + * 3. use MBP for remaining array and arith variables + * 4. for any remaining arith variables, substitute using M + */ + void qe_project (ast_manager& m, app_ref_vector& vars, + expr_ref& fml, model &mdl, + bool reduce_all_selects=false, + bool native_mbp=false, + bool dont_sub=false); + // deprecate + void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, + model_ref& M, expr_map& map); -typedef ptr_vector app_vector; -typedef ptr_vector decl_vector; -typedef obj_hashtable func_decl_set; + // TBD: sort out + void expand_literals(ast_manager &m, expr_ref_vector& conjs); + void compute_implicant_literals(model &mdl, + expr_ref_vector &formula, + expr_ref_vector &res); + void simplify_bounds (expr_ref_vector &lemmas); + void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false); + /** + * Ground expression by replacing all free variables by skolem + * constants. On return, out is the resulting expression, and vars is + * a map from variable ids to corresponding skolem constants. + */ + void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars); -class model_evaluator_util { - ast_manager& m; - model_ref m_model; - model_evaluator* m_mev; + void mbqi_project(model &mdl, app_ref_vector &vars, expr_ref &fml); - /// initialize with a given model. All previous state is lost. model can be NULL - void reset (model *model); -public: - model_evaluator_util(ast_manager& m); - ~model_evaluator_util(); + bool contains_selects (expr* fml, ast_manager& m); + void get_select_indices (expr* fml, app_ref_vector& indices, ast_manager& m); - void set_model(model &model) {reset (&model);} - model_ref &get_model() {return m_model;} - ast_manager& get_ast_manager() const {return m;} - -public: - bool is_true (const expr_ref_vector &v); - bool is_false(expr* x); - bool is_true(expr* x); - - bool eval (const expr_ref_vector &v, expr_ref &result, bool model_completion); - /// evaluates an expression - bool eval (expr *e, expr_ref &result, bool model_completion); - // expr_ref eval(expr* e, bool complete=true); -}; - - -/** - \brief replace variables that are used in many disequalities by - an equality using the model. - - Assumption: the model satisfies the conjunctions. -*/ -void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml); - -/** - \brief hoist non-boolean if expressions. -*/ -void hoist_non_bool_if(expr_ref& fml); - -bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); - -bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls); - -/** - * do the following in sequence - * 1. use qe_lite to cheaply eliminate vars - * 2. for remaining boolean vars, substitute using M - * 3. use MBP for remaining array and arith variables - * 4. for any remaining arith variables, substitute using M - */ -void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, - const model_ref& M, bool reduce_all_selects=false, bool native_mbp=false, - bool dont_sub=false); - -void qe_project (ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref& M, expr_map& map); - -void expand_literals(ast_manager &m, expr_ref_vector& conjs); -void compute_implicant_literals (model_evaluator_util &mev, - expr_ref_vector &formula, expr_ref_vector &res); -void simplify_bounds (expr_ref_vector &lemmas); -void normalize(expr *e, expr_ref &out, bool use_simplify_bounds = true, bool factor_eqs = false); - -/** ground expression by replacing all free variables by skolem constants */ -void ground_expr (expr *e, expr_ref &out, app_ref_vector &vars); - - -void mbqi_project (model &M, app_ref_vector &vars, expr_ref &fml); - -bool contains_selects (expr* fml, ast_manager& m); -void get_select_indices (expr* fml, app_ref_vector& indices, ast_manager& m); - -void find_decls (expr* fml, app_ref_vector& decls, std::string& prefix); - -/** extended pretty-printer - * used for debugging - * disables aliasing of common sub-expressions -*/ -struct mk_epp : public mk_pp { - params_ref m_epp_params; - expr_ref m_epp_expr; - mk_epp(ast *t, ast_manager &m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = 0); - void rw(expr *e, expr_ref &out); - -}; + void find_decls (expr* fml, app_ref_vector& decls, std::string& prefix); + /** + * extended pretty-printer + * used for debugging + * disables aliasing of common sub-expressions + */ + struct mk_epp : public mk_pp { + params_ref m_epp_params; + expr_ref m_epp_expr; + mk_epp(ast *t, ast_manager &m, unsigned indent = 0, unsigned num_vars = 0, char const * var_prefix = nullptr); + void rw(expr *e, expr_ref &out); + }; } #endif diff --git a/src/muz/spacer/spacer_virtual_solver.cpp b/src/muz/spacer/spacer_virtual_solver.cpp deleted file mode 100644 index 938e8cb94..000000000 --- a/src/muz/spacer/spacer_virtual_solver.cpp +++ /dev/null @@ -1,360 +0,0 @@ -/** -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_virtual_solver.cpp - -Abstract: - - multi-solver view of a single smt::kernel - -Author: - - Arie Gurfinkel - -Notes: - ---*/ - -#include "muz/spacer/spacer_virtual_solver.h" -#include "ast/ast_util.h" -#include "ast/ast_pp_util.h" -#include "muz/spacer/spacer_util.h" -#include "ast/rewriter/bool_rewriter.h" - -#include "ast/proofs/proof_checker.h" -#include "ast/proofs/proof_utils.h" - -#include "ast/scoped_proof.h" - -namespace spacer { -virtual_solver::virtual_solver(virtual_solver_factory &factory, - smt::kernel &context, app* pred) : - solver_na2as(context.m()), - m_factory(factory), - m(context.m()), - m_context(context), - m_pred(pred, m), - m_virtual(!m.is_true(pred)), - m_assertions(m), - m_head(0), - m_flat(m), - m_pushed(false), - m_in_delay_scope(false), - m_dump_benchmarks(factory.fparams().m_dump_benchmarks), - m_dump_counter(0), - m_proof(m) -{ - // -- insert m_pred->true background assumption this will not - // -- change m_context, but will add m_pred to - // -- the private field solver_na2as::m_assumptions - if (m_virtual) - { solver_na2as::assert_expr(m.mk_true(), m_pred); } -} - -virtual_solver::~virtual_solver() -{ - SASSERT(!m_pushed || get_scope_level() > 0); - if (m_pushed) { pop(get_scope_level()); } - - if (m_virtual) { - m_pred = m.mk_not(m_pred); - m_context.assert_expr(m_pred); - } -} - -namespace { - - -// TBD: move to ast/proofs/elim_aux_assertions - - -} - -proof *virtual_solver::get_proof() -{ - scoped_watch _t_(m_factory.m_proof_watch); - - if (!m_proof.get()) { - elim_aux_assertions pc(m_pred); - m_proof = m_context.get_proof(); - pc(m, m_proof.get(), m_proof); - } - return m_proof.get(); -} - -bool virtual_solver::is_aux_predicate(expr *p) -{return is_app(p) && to_app(p) == m_pred.get();} - -lbool virtual_solver::check_sat_core(unsigned num_assumptions, - expr *const * assumptions) -{ - SASSERT(!m_pushed || get_scope_level() > 0); - m_proof.reset(); - scoped_watch _t_(m_factory.m_check_watch); - m_factory.m_stats.m_num_smt_checks++; - - stopwatch sw; - sw.start(); - internalize_assertions(); - if (false) { - std::stringstream file_name; - file_name << "virt_solver"; - if (m_virtual) { file_name << "_" << m_pred->get_decl()->get_name(); } - file_name << "_" << (m_dump_counter++) << ".smt2"; - - verbose_stream() << "Dumping SMT2 benchmark: " << file_name.str() << "\n"; - - std::ofstream out(file_name.str().c_str()); - - to_smt2_benchmark(out, m_context, num_assumptions, assumptions, - "virt_solver"); - - out << "(exit)\n"; - out.close(); - } - lbool res = m_context.check(num_assumptions, assumptions); - sw.stop(); - if (res == l_true) { - m_factory.m_check_sat_watch.add(sw); - m_factory.m_stats.m_num_sat_smt_checks++; - } else if (res == l_undef) { - m_factory.m_check_undef_watch.add(sw); - m_factory.m_stats.m_num_undef_smt_checks++; - } - set_status(res); - - if (m_dump_benchmarks && - sw.get_seconds() >= m_factory.fparams().m_dump_min_time) { - std::stringstream file_name; - file_name << "virt_solver"; - if (m_virtual) { file_name << "_" << m_pred->get_decl()->get_name(); } - file_name << "_" << (m_dump_counter++) << ".smt2"; - - std::ofstream out(file_name.str().c_str()); - - - out << "(set-info :status "; - if (res == l_true) { out << "sat"; } - else if (res == l_false) { out << "unsat"; } - else { out << "unknown"; } - out << ")\n"; - - to_smt2_benchmark(out, m_context, num_assumptions, assumptions, - "virt_solver"); - - out << "(exit)\n"; - ::statistics st; - m_context.collect_statistics(st); - st.update("time", sw.get_seconds()); - st.display_smt2(out); - - out.close(); - - if (m_factory.fparams().m_dump_recheck) { - scoped_no_proof _no_proof_(m); - smt_params p; - stopwatch sw2; - smt::kernel kernel(m, p); - for (unsigned i = 0, sz = m_context.size(); i < sz; ++i) - { kernel.assert_expr(m_context.get_formula(i)); } - sw2.start(); - kernel.check(num_assumptions, assumptions); - sw2.stop(); - verbose_stream() << file_name.str() << " :orig " - << sw.get_seconds() << " :new " << sw2.get_seconds(); - } - } - - - return res; -} - -void virtual_solver::push_core() -{ - SASSERT(!m_pushed || get_scope_level() > 0); - if (m_in_delay_scope) { - // second push - internalize_assertions(); - m_context.push(); - m_pushed = true; - m_in_delay_scope = false; - } - - if (!m_pushed) { m_in_delay_scope = true; } - else { - SASSERT(m_pushed); - SASSERT(!m_in_delay_scope); - m_context.push(); - } -} -void virtual_solver::pop_core(unsigned n) { - SASSERT(!m_pushed || get_scope_level() > 0); - if (m_pushed) { - SASSERT(!m_in_delay_scope); - m_context.pop(n); - m_pushed = get_scope_level() - n > 0; - } - else { - m_in_delay_scope = get_scope_level() - n > 0; - } -} - -void virtual_solver::get_unsat_core(ptr_vector &r) -{ - for (unsigned i = 0, sz = m_context.get_unsat_core_size(); i < sz; ++i) { - expr *core = m_context.get_unsat_core_expr(i); - if (is_aux_predicate(core)) { continue; } - r.push_back(core); - } -} - -void virtual_solver::assert_expr(expr *e) -{ - SASSERT(!m_pushed || get_scope_level() > 0); - if (m.is_true(e)) { return; } - if (m_in_delay_scope) { - internalize_assertions(); - m_context.push(); - m_pushed = true; - m_in_delay_scope = false; - } - - if (m_pushed) - { m_context.assert_expr(e); } - else { - m_flat.push_back(e); - flatten_and(m_flat); - m_assertions.append(m_flat); - m_flat.reset(); - } -} -void virtual_solver::internalize_assertions() -{ - SASSERT(!m_pushed || m_head == m_assertions.size()); - for (unsigned sz = m_assertions.size(); m_head < sz; ++m_head) { - expr_ref f(m); - f = m.mk_implies(m_pred, (m_assertions.get(m_head))); - m_context.assert_expr(f); - } -} -void virtual_solver::refresh() -{ - SASSERT(!m_pushed); - m_head = 0; -} - -void virtual_solver::reset() -{ - SASSERT(!m_pushed); - m_head = 0; - m_assertions.reset(); - m_factory.refresh(); -} - -void virtual_solver::get_labels(svector &r) -{ - r.reset(); - buffer tmp; - m_context.get_relevant_labels(0, tmp); - r.append(tmp.size(), tmp.c_ptr()); -} - -solver* virtual_solver::translate(ast_manager& m, params_ref const& p) -{ - UNREACHABLE(); - return 0; -} -void virtual_solver::updt_params(params_ref const &p) -{ m_factory.updt_params(p); } -void virtual_solver::collect_param_descrs(param_descrs &r) -{ m_factory.collect_param_descrs(r); } -void virtual_solver::set_produce_models(bool f) -{ m_factory.set_produce_models(f); } -bool virtual_solver::get_produce_models() -{return m_factory.get_produce_models(); } -smt_params &virtual_solver::fparams() -{return m_factory.fparams();} - -void virtual_solver::to_smt2_benchmark(std::ostream &out, - smt::kernel &context, - unsigned num_assumptions, - expr * const * assumptions, - char const * name, - symbol const &logic, - char const * status, - char const * attributes) -{ - ast_pp_util pp(m); - expr_ref_vector asserts(m); - - - for (unsigned i = 0, sz = context.size(); i < sz; ++i) { - asserts.push_back(context.get_formula(i)); - pp.collect(asserts.back()); - } - pp.collect(num_assumptions, assumptions); - pp.display_decls(out); - pp.display_asserts(out, asserts); - out << "(check-sat "; - for (unsigned i = 0; i < num_assumptions; ++i) - { out << mk_pp(assumptions[i], m) << " "; } - out << ")\n"; -} - - -virtual_solver_factory::virtual_solver_factory(ast_manager &mgr, smt_params &fparams) : - m_fparams(fparams), m(mgr), m_context(m, m_fparams) -{ - m_stats.reset(); -} - -virtual_solver* virtual_solver_factory::mk_solver() -{ - std::stringstream name; - name << "vsolver#" << m_solvers.size(); - app_ref pred(m); - pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()); - SASSERT(m_context.get_scope_level() == 0); - m_solvers.push_back(alloc(virtual_solver, *this, m_context, pred)); - return m_solvers.back(); -} - -void virtual_solver_factory::collect_statistics(statistics &st) const -{ - m_context.collect_statistics(st); - st.update("time.virtual_solver.smt.total", m_check_watch.get_seconds()); - st.update("time.virtual_solver.smt.total.sat", m_check_sat_watch.get_seconds()); - st.update("time.virtual_solver.smt.total.undef", m_check_undef_watch.get_seconds()); - st.update("time.virtual_solver.proof", m_proof_watch.get_seconds()); - st.update("virtual_solver.checks", m_stats.m_num_smt_checks); - st.update("virtual_solver.checks.sat", m_stats.m_num_sat_smt_checks); - st.update("virtual_solver.checks.undef", m_stats.m_num_undef_smt_checks); -} -void virtual_solver_factory::reset_statistics() -{ - m_context.reset_statistics(); - m_stats.reset(); - m_check_sat_watch.reset(); - m_check_undef_watch.reset(); - m_check_watch.reset(); - m_proof_watch.reset(); -} - -void virtual_solver_factory::refresh() -{ - m_context.reset(); - for (unsigned i = 0, e = m_solvers.size(); i < e; ++i) - { m_solvers [i]->refresh(); } -} - -virtual_solver_factory::~virtual_solver_factory() -{ - for (unsigned i = 0, e = m_solvers.size(); i < e; ++i) - { dealloc(m_solvers [i]); } -} - - - -} diff --git a/src/muz/spacer/spacer_virtual_solver.h b/src/muz/spacer/spacer_virtual_solver.h deleted file mode 100644 index fed64c589..000000000 --- a/src/muz/spacer/spacer_virtual_solver.h +++ /dev/null @@ -1,154 +0,0 @@ -/** -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - spacer_virtual_solver.h - -Abstract: - - multi-solver view of a single smt::kernel - -Author: - - Arie Gurfinkel - -Notes: - ---*/ -#ifndef SPACER_VIRTUAL_SOLVER_H_ -#define SPACER_VIRTUAL_SOLVER_H_ -#include"ast/ast.h" -#include"util/params.h" -#include"solver/solver_na2as.h" -#include"smt/smt_kernel.h" -#include"smt/params/smt_params.h" -#include"util/stopwatch.h" -namespace spacer { -class virtual_solver_factory; - -class virtual_solver : public solver_na2as { - friend class virtual_solver_factory; - -private: - virtual_solver_factory &m_factory; - ast_manager &m; - smt::kernel &m_context; - app_ref m_pred; - - bool m_virtual; - expr_ref_vector m_assertions; - unsigned m_head; - // temporary to flatten conjunction - expr_ref_vector m_flat; - - bool m_pushed; - bool m_in_delay_scope; - bool m_dump_benchmarks; - unsigned m_dump_counter; - - proof_ref m_proof; - - virtual_solver(virtual_solver_factory &factory, smt::kernel &context, app* pred); - - bool is_aux_predicate(expr *p); - void internalize_assertions(); - void to_smt2_benchmark(std::ostream &out, - smt::kernel &context, - unsigned num_assumptions, - expr * const * assumptions, - char const * name = "benchmarks", - symbol const &logic = symbol::null, - char const * status = "unknown", - char const * attributes = ""); - - void refresh(); - -public: - virtual ~virtual_solver(); - virtual unsigned get_num_assumptions() const - { - unsigned sz = solver_na2as::get_num_assumptions(); - return m_virtual ? sz - 1 : sz; - } - virtual expr* get_assumption(unsigned idx) const - { - if(m_virtual) { idx++; } - return solver_na2as::get_assumption(idx); - } - - virtual void get_unsat_core(ptr_vector &r); - virtual void assert_expr(expr *e); - virtual void collect_statistics(statistics &st) const {} - virtual void get_model(model_ref &m) {m_context.get_model(m);} - virtual proof* get_proof(); - virtual std::string reason_unknown() const - {return m_context.last_failure_as_string();} - virtual void set_reason_unknown(char const *msg) - {m_context.set_reason_unknown(msg);} - virtual ast_manager& get_manager() const {return m;} - virtual void get_labels(svector &r); - virtual void set_produce_models(bool f); - virtual bool get_produce_models(); - virtual smt_params &fparams(); - virtual void reset(); - virtual void set_progress_callback(progress_callback *callback) - {UNREACHABLE();} - - virtual solver *translate(ast_manager &m, params_ref const &p); - - virtual void updt_params(params_ref const &p); - virtual void collect_param_descrs(param_descrs &r); - - -protected: - virtual lbool check_sat_core(unsigned num_assumptions, expr *const * assumptions); - virtual void push_core(); - virtual void pop_core(unsigned n); -}; - -/// multi-solver abstraction on top of a single smt::kernel -class virtual_solver_factory { - friend class virtual_solver; -private: - smt_params &m_fparams; - ast_manager &m; - smt::kernel m_context; - /// solvers managed by this factory - ptr_vector m_solvers; - - struct stats { - unsigned m_num_smt_checks; - unsigned m_num_sat_smt_checks; - unsigned m_num_undef_smt_checks; - stats() { reset(); } - void reset() { memset(this, 0, sizeof(*this)); } - }; - - stats m_stats; - stopwatch m_check_watch; - stopwatch m_check_sat_watch; - stopwatch m_check_undef_watch; - stopwatch m_proof_watch; - - - void refresh(); - - smt_params &fparams() { return m_fparams; } - -public: - virtual_solver_factory(ast_manager &mgr, smt_params &fparams); - virtual ~virtual_solver_factory(); - virtual_solver* mk_solver(); - void collect_statistics(statistics &st) const; - void reset_statistics(); - void updt_params(params_ref const &p) { m_fparams.updt_params(p); } - void collect_param_descrs(param_descrs &r) { /* empty */ } - void set_produce_models(bool f) { m_fparams.m_model = f; } - bool get_produce_models() { return m_fparams.m_model; } -}; - -} - - -#endif diff --git a/src/muz/tab/tab_context.cpp b/src/muz/tab/tab_context.cpp index 8809c0dc7..7200b3522 100644 --- a/src/muz/tab/tab_context.cpp +++ b/src/muz/tab/tab_context.cpp @@ -30,7 +30,7 @@ Revision History: #include "ast/for_each_expr.h" #include "ast/substitution/matcher.h" #include "ast/scoped_proof.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "ast/ast_util.h" namespace tb { @@ -693,13 +693,12 @@ namespace tb { m_solver.assert_expr(postcond); lbool is_sat = m_solver.check(); if (is_sat == l_true) { - expr_ref tmp(m); expr* n; model_ref mdl; m_solver.get_model(mdl); for (unsigned i = 0; i < fmls.size(); ++i) { n = fmls[i].get(); - if (mdl->eval(n, tmp) && m.is_false(tmp)) { + if (mdl->is_false(n)) { m_refs.push_back(normalize(n)); m_sat_lits.insert(m_refs.back()); } @@ -1129,7 +1128,7 @@ namespace tb { } else { change = true; - m_rename.push_back(0); + m_rename.push_back(nullptr); } } if (change) { @@ -1602,7 +1601,7 @@ namespace datalog { pc.invert(); prs.push_back(m.mk_asserted(root)); - pc(m, 1, prs.c_ptr(), pr); + pr = pc(m, 1, prs.c_ptr()); return pr; } diff --git a/src/muz/tab/tab_context.h b/src/muz/tab/tab_context.h index 329234524..84d6e2420 100644 --- a/src/muz/tab/tab_context.h +++ b/src/muz/tab/tab_context.h @@ -32,13 +32,13 @@ namespace datalog { imp* m_imp; public: tab(context& ctx); - ~tab(); - virtual lbool query(expr* query); - virtual void cleanup(); - virtual void reset_statistics(); - virtual void collect_statistics(statistics& st) const; - virtual void display_certificate(std::ostream& out) const; - virtual expr_ref get_answer(); + ~tab() override; + lbool query(expr* query) override; + void cleanup() override; + void reset_statistics() override; + void collect_statistics(statistics& st) const override; + void display_certificate(std::ostream& out) const override; + expr_ref get_answer() override; }; }; diff --git a/src/muz/transforms/dl_mk_array_blast.cpp b/src/muz/transforms/dl_mk_array_blast.cpp index 8fe3f0e43..34e739bc3 100644 --- a/src/muz/transforms/dl_mk_array_blast.cpp +++ b/src/muz/transforms/dl_mk_array_blast.cpp @@ -42,7 +42,7 @@ namespace datalog { } bool mk_array_blast::is_store_def(expr* e, expr*& x, expr*& y) { - if (m.is_iff(e, x, y) || m.is_eq(e, x, y)) { + if (m.is_eq(e, x, y)) { if (!a.is_store(y)) { std::swap(x,y); } @@ -176,7 +176,7 @@ namespace datalog { if (m_defs.find(e1, v)) { cache.insert(e, v); } - else if (!insert_def(r, e1, 0)) { + else if (!insert_def(r, e1, nullptr)) { return false; } else { @@ -320,7 +320,7 @@ namespace datalog { rule_set * mk_array_blast::operator()(rule_set const & source) { if (!m_ctx.array_blast ()) { - return 0; + return nullptr; } rule_set* rules = alloc(rule_set, m_ctx); rules->inherit_predicates(source); @@ -331,7 +331,7 @@ namespace datalog { } if (!change) { dealloc(rules); - rules = 0; + rules = nullptr; } return rules; } diff --git a/src/muz/transforms/dl_mk_array_blast.h b/src/muz/transforms/dl_mk_array_blast.h index e7b951168..ba2f826dd 100644 --- a/src/muz/transforms/dl_mk_array_blast.h +++ b/src/muz/transforms/dl_mk_array_blast.h @@ -66,9 +66,9 @@ namespace datalog { */ mk_array_blast(context & ctx, unsigned priority); - virtual ~mk_array_blast(); + ~mk_array_blast() override; - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; diff --git a/src/muz/transforms/dl_mk_array_eq_rewrite.cpp b/src/muz/transforms/dl_mk_array_eq_rewrite.cpp index c33cecda9..c8a3d4357 100644 --- a/src/muz/transforms/dl_mk_array_eq_rewrite.cpp +++ b/src/muz/transforms/dl_mk_array_eq_rewrite.cpp @@ -20,9 +20,9 @@ Revision History: #include "ast/expr_abstract.h" #include "muz/base/dl_context.h" #include "muz/base/dl_context.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "muz/transforms/dl_mk_array_eq_rewrite.h" -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" namespace datalog { diff --git a/src/muz/transforms/dl_mk_array_eq_rewrite.h b/src/muz/transforms/dl_mk_array_eq_rewrite.h index 166c01142..298697998 100644 --- a/src/muz/transforms/dl_mk_array_eq_rewrite.h +++ b/src/muz/transforms/dl_mk_array_eq_rewrite.h @@ -40,8 +40,8 @@ namespace datalog { public: mk_array_eq_rewrite(context & ctx, unsigned priority); - rule_set * operator()(rule_set const & source); - virtual ~mk_array_eq_rewrite(){} + rule_set * operator()(rule_set const & source) override; + ~mk_array_eq_rewrite() override{} }; diff --git a/src/muz/transforms/dl_mk_array_instantiation.cpp b/src/muz/transforms/dl_mk_array_instantiation.cpp index 362d83865..6a8f0ce81 100644 --- a/src/muz/transforms/dl_mk_array_instantiation.cpp +++ b/src/muz/transforms/dl_mk_array_instantiation.cpp @@ -23,7 +23,7 @@ Revision History: #include "muz/base/dl_context.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { diff --git a/src/muz/transforms/dl_mk_array_instantiation.h b/src/muz/transforms/dl_mk_array_instantiation.h index b87f679fc..51a818a3f 100644 --- a/src/muz/transforms/dl_mk_array_instantiation.h +++ b/src/muz/transforms/dl_mk_array_instantiation.h @@ -26,7 +26,7 @@ Implementation: 1) Dealing with multiple quantifiers -> The options fixedpoint.xform.instantiate_arrays.nb_quantifier gives the number of quantifiers per array. - 2) Inforcing the instantiation -> We suggest an option (enforce_instantiation) to enforce this abstraction. This transforms + 2) Enforcing the instantiation -> We suggest an option (enforce_instantiation) to enforce this abstraction. This transforms P(a) into P(i, a[i]). This enforces the solver to limit the space search at the cost of imprecise results. This option corresponds to fixedpoint.xform.instantiate_arrays.enforce @@ -70,7 +70,7 @@ Revision History: #define DL_MK_ARRAY_INSTANTIATION_H_ -#include "ast/factor_equivs.h" +#include "ast/rewriter/factor_equivs.h" #include "muz/base/dl_rule_transformer.h" namespace datalog { @@ -112,8 +112,8 @@ namespace datalog { expr_ref_vector getId(app*old_pred, const expr_ref_vector& new_args); public: mk_array_instantiation(context & ctx, unsigned priority); - rule_set * operator()(rule_set const & source); - virtual ~mk_array_instantiation(){} + rule_set * operator()(rule_set const & source) override; + ~mk_array_instantiation() override{} }; diff --git a/src/muz/transforms/dl_mk_backwards.h b/src/muz/transforms/dl_mk_backwards.h index ca441fd0a..344da2afb 100644 --- a/src/muz/transforms/dl_mk_backwards.h +++ b/src/muz/transforms/dl_mk_backwards.h @@ -28,8 +28,8 @@ namespace datalog { context& m_ctx; public: mk_backwards(context & ctx, unsigned priority = 33000); - ~mk_backwards(); - rule_set * operator()(rule_set const & source); + ~mk_backwards() override; + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_bit_blast.cpp b/src/muz/transforms/dl_mk_bit_blast.cpp index 3c8045e34..992b8f51b 100644 --- a/src/muz/transforms/dl_mk_bit_blast.cpp +++ b/src/muz/transforms/dl_mk_bit_blast.cpp @@ -22,9 +22,9 @@ Revision History: #include "ast/rewriter/rewriter_def.h" #include "ast/ast_pp.h" #include "ast/rewriter/expr_safe_replace.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "muz/transforms/dl_mk_interp_tail_simplifier.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" #include "ast/scoped_proof.h" #include "model/model_v2_pp.h" @@ -32,9 +32,9 @@ namespace datalog { // // P(v) :- Q(extract[1:1]v ++ 0), R(1 ++ extract[0:0]v). - // -> + // -> // P(bv(x,y)) :- Q(bv(x,0)), R(bv(1,y)) . - // + // // Introduce P_bv: // P_bv(x,y) :- Q_bv(x,0), R_bv(1,y) // P(bv(x,y)) :- P_bv(x,y) @@ -51,7 +51,7 @@ namespace datalog { bit_blast_model_converter(ast_manager& m): m(m), m_bv(m), - m_old_funcs(m), + m_old_funcs(m), m_new_funcs(m) {} void insert(func_decl* old_f, func_decl* new_f) { @@ -59,17 +59,21 @@ namespace datalog { m_new_funcs.push_back(new_f); } - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { return alloc(bit_blast_model_converter, m); } - virtual void operator()(model_ref & model) { + void get_units(obj_map& units) override {} + + void display(std::ostream& out) override { out << "(bit-blast-model-converter)\n"; } + + void operator()(model_ref & model) override { for (unsigned i = 0; i < m_new_funcs.size(); ++i) { func_decl* p = m_new_funcs[i].get(); func_decl* q = m_old_funcs[i].get(); func_interp* f = model->get_func_interp(p); if (!f) continue; - expr_ref body(m); + expr_ref body(m); unsigned arity_q = q->get_arity(); TRACE("dl", model_v2_pp(tout, *model); @@ -83,10 +87,10 @@ namespace datalog { if (f) { body = f->get_interp(); SASSERT(!f->is_partial()); - SASSERT(body); + SASSERT(body); } else { - body = m.mk_false(); + body = m.mk_false(); } unsigned idx = 0; expr_ref arg(m), proj(m); @@ -100,18 +104,18 @@ namespace datalog { for (unsigned k = 0; k < sz; ++k) { parameter p(k); proj = m.mk_app(m_bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &t); - sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); + sub.insert(m.mk_var(idx++, m.mk_bool_sort()), proj); } } else { sub.insert(m.mk_var(idx++, s), arg); } } - sub(body); + sub(body); g->set_else(body); model->register_decl(q, g); - } - } + } + } }; class expand_mkbv_cfg : public default_rewriter_cfg { @@ -130,16 +134,16 @@ namespace datalog { public: expand_mkbv_cfg(context& ctx): - m_context(ctx), + m_context(ctx), m(ctx.get_manager()), m_util(m), - m_args(m), + m_args(m), m_f_vars(m), m_g_vars(m), m_old_funcs(m), m_new_funcs(m), - m_src(0), - m_dst(0) + m_src(nullptr), + m_dst(nullptr) {} ~expand_mkbv_cfg() {} @@ -148,8 +152,8 @@ namespace datalog { void set_dst(rule_set* dst) { m_dst = dst; } func_decl_ref_vector const& old_funcs() const { return m_old_funcs; } func_decl_ref_vector const& new_funcs() const { return m_new_funcs; } - - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (num == 0) { if (m_src->is_output_predicate(f)) m_dst->set_output_predicate(f); @@ -161,9 +165,9 @@ namespace datalog { return BR_FAILED; } - // + // // f(mk_bv(args),...) - // + // m_args.reset(); m_g_vars.reset(); m_f_vars.reset(); @@ -186,10 +190,10 @@ namespace datalog { m_g_vars.push_back(m_f_vars.back()); } } - func_decl* g = 0; - + func_decl* g = nullptr; + if (!m_pred2blast.find(f, g)) { - + ptr_vector domain; for (unsigned i = 0; i < m_args.size(); ++i) { domain.push_back(m.get_sort(m_args[i].get())); @@ -202,7 +206,7 @@ namespace datalog { m_dst->inherit_predicate(*m_src, f, g); } result = m.mk_app(g, m_args.size(), m_args.c_ptr()); - result_pr = 0; + result_pr = nullptr; return BR_DONE; } }; @@ -258,16 +262,16 @@ namespace datalog { m_params.set_bool("blast_quant", true); m_blaster.updt_params(m_params); } - + rule_set * operator()(rule_set const & source) { // TODO pc if (!m_context.xform_bit_blast()) { - return 0; + return nullptr; } rule_manager& rm = m_context.get_rule_manager(); unsigned sz = source.get_num_rules(); - expr_ref fml(m); - rule_set * result = alloc(rule_set, m_context); + expr_ref fml(m); + rule_set * result = alloc(rule_set, m_context); m_rewriter.m_cfg.set_src(&source); m_rewriter.m_cfg.set_dst(result); for (unsigned i = 0; !m_context.canceled() && i < sz; ++i) { @@ -295,19 +299,19 @@ namespace datalog { if (!source.contains(*I)) result->set_output_predicate(*I); } - - if (m_context.get_model_converter()) { - filter_model_converter* fmc = alloc(filter_model_converter, m); + + if (m_context.get_model_converter()) { + generic_model_converter* fmc = alloc(generic_model_converter, m, "dl_mk_bit_blast"); bit_blast_model_converter* bvmc = alloc(bit_blast_model_converter, m); func_decl_ref_vector const& old_funcs = m_rewriter.m_cfg.old_funcs(); func_decl_ref_vector const& new_funcs = m_rewriter.m_cfg.new_funcs(); for (unsigned i = 0; i < old_funcs.size(); ++i) { - fmc->insert(new_funcs[i]); + fmc->hide(new_funcs[i]); bvmc->insert(old_funcs[i], new_funcs[i]); } m_context.add_model_converter(concat(bvmc, fmc)); } - + return result; } }; @@ -322,6 +326,6 @@ namespace datalog { rule_set * mk_bit_blast::operator()(rule_set const & source) { return (*m_impl)(source); - } + } }; diff --git a/src/muz/transforms/dl_mk_bit_blast.h b/src/muz/transforms/dl_mk_bit_blast.h index 31df57567..a8be254a6 100644 --- a/src/muz/transforms/dl_mk_bit_blast.h +++ b/src/muz/transforms/dl_mk_bit_blast.h @@ -28,8 +28,8 @@ namespace datalog { impl* m_impl; public: mk_bit_blast(context & ctx, unsigned priority = 35000); - ~mk_bit_blast(); - rule_set * operator()(rule_set const & source); + ~mk_bit_blast() override; + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_coalesce.h b/src/muz/transforms/dl_mk_coalesce.h index 47d702fbb..7721d7556 100644 --- a/src/muz/transforms/dl_mk_coalesce.h +++ b/src/muz/transforms/dl_mk_coalesce.h @@ -52,7 +52,7 @@ namespace datalog { */ mk_coalesce(context & ctx); - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_coi_filter.cpp b/src/muz/transforms/dl_mk_coi_filter.cpp index 3ea0e305a..e37444c5e 100644 --- a/src/muz/transforms/dl_mk_coi_filter.cpp +++ b/src/muz/transforms/dl_mk_coi_filter.cpp @@ -21,6 +21,7 @@ Author: #include "muz/dataflow/dataflow.h" #include "muz/dataflow/reachability.h" #include "ast/ast_pp.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_util.h" #include "tactic/extension_model_converter.h" @@ -45,7 +46,7 @@ namespace datalog { for (unsigned i = 0; i < r->get_uninterpreted_tail_size(); ++i) { func_decl* decl_i = r->get_decl(i); if (m_context.has_facts(decl_i)) { - return 0; + return nullptr; } bool reachable = engine.get_fact(decl_i).is_reachable(); @@ -79,6 +80,10 @@ namespace datalog { } if (contained) { if (new_tail) { + for (unsigned i = r->get_uninterpreted_tail_size(); i < r->get_tail_size(); ++i) { + m_new_tail.push_back(r->get_tail(i)); + m_new_tail_neg.push_back(false); + } rule* new_r = m_context.get_rule_manager().mk(r->get_head(), m_new_tail.size(), m_new_tail.c_ptr(), m_new_tail_neg.c_ptr(), symbol::null, false); res->add_rule(new_r); @@ -89,7 +94,7 @@ namespace datalog { } if (res->get_num_rules() == source.get_num_rules()) { TRACE("dl", tout << "No transformation\n";); - res = 0; + res = nullptr; } else { res->close(); @@ -97,14 +102,14 @@ namespace datalog { // set to false each unreached predicate if (res && m_context.get_model_converter()) { - extension_model_converter* mc0 = alloc(extension_model_converter, m); + generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl_coi"); for (auto const& kv : engine) { if (!kv.m_value.is_reachable()) { - mc0->insert(kv.m_key, m.mk_false()); + mc0->add(kv.m_key, m.mk_false()); } } for (func_decl* f : unreachable) { - mc0->insert(f, m.mk_false()); + mc0->add(f, m.mk_false()); } m_context.add_model_converter(mc0); } @@ -130,10 +135,10 @@ namespace datalog { if (res->get_num_rules() == source.get_num_rules()) { TRACE("dl", tout << "No transformation\n";); - res = 0; + res = nullptr; } if (res && m_context.get_model_converter()) { - extension_model_converter* mc0 = alloc(extension_model_converter, m); + generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl_coi"); for (func_decl* f : pruned_preds) { const rule_vector& rules = source.get_predicate_rules(f); expr_ref_vector fmls(m); @@ -148,7 +153,9 @@ namespace datalog { } fmls.push_back(mk_and(conj)); } - mc0->insert(f, mk_or(fmls)); + expr_ref fml(m); + fml = m.mk_or(fmls.size(), fmls.c_ptr()); + mc0->add(f, fml); } m_context.add_model_converter(mc0); } diff --git a/src/muz/transforms/dl_mk_coi_filter.h b/src/muz/transforms/dl_mk_coi_filter.h index c03308b6a..4fe7033bd 100644 --- a/src/muz/transforms/dl_mk_coi_filter.h +++ b/src/muz/transforms/dl_mk_coi_filter.h @@ -42,7 +42,7 @@ namespace datalog { m(ctx.get_manager()), m_context(ctx) {} - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; } diff --git a/src/muz/transforms/dl_mk_filter_rules.cpp b/src/muz/transforms/dl_mk_filter_rules.cpp index a8c13fc17..b5dfb84ec 100644 --- a/src/muz/transforms/dl_mk_filter_rules.cpp +++ b/src/muz/transforms/dl_mk_filter_rules.cpp @@ -29,7 +29,7 @@ namespace datalog { m_context(ctx), m(ctx.get_manager()), rm(ctx.get_rule_manager()), - m_result(0), + m_result(nullptr), m_pinned(m) { } @@ -89,7 +89,7 @@ namespace datalog { app_ref filter_head(m); filter_head = m.mk_app(filter_decl, key->filter_args.size(), key->filter_args.c_ptr()); app * filter_tail = key->new_pred; - rule * filter_rule = m_context.get_rule_manager().mk(filter_head, 1, &filter_tail, (const bool *)0); + rule * filter_rule = m_context.get_rule_manager().mk(filter_head, 1, &filter_tail, (const bool *)nullptr); filter_rule->set_accounting_parent_object(m_context, m_current); m_result->add_rule(filter_rule); m_context.get_rule_manager().mk_rule_asserted_proof(*filter_rule); @@ -161,7 +161,7 @@ namespace datalog { } if(!m_modified) { dealloc(m_result); - return static_cast(0); + return static_cast(nullptr); } m_result->inherit_predicates(source); return m_result; diff --git a/src/muz/transforms/dl_mk_filter_rules.h b/src/muz/transforms/dl_mk_filter_rules.h index b81921d59..3e357dc4e 100644 --- a/src/muz/transforms/dl_mk_filter_rules.h +++ b/src/muz/transforms/dl_mk_filter_rules.h @@ -73,11 +73,11 @@ namespace datalog { public: mk_filter_rules(context & ctx); - ~mk_filter_rules(); + ~mk_filter_rules() override; /** \brief Return a new rule set where only filter rules contain atoms with repeated variables and/or values. */ - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp index 8421a99ba..c1abd7bef 100644 --- a/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.cpp @@ -27,7 +27,7 @@ Revision History: #include "muz/transforms/dl_mk_interp_tail_simplifier.h" #include "ast/ast_util.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { // ----------------------------------- @@ -46,7 +46,7 @@ namespace datalog { bool mk_interp_tail_simplifier::rule_substitution::unify(expr * e1, expr * e2) { SASSERT(m_rule); - //we need to apply the current substitution in order to ensure the unifier + //we need to apply the current substitution in order to ensure the unifier //works in an incremental way expr_ref e1_s(m); expr_ref e2_s(m); @@ -222,20 +222,20 @@ namespace datalog { */ app * detect_equivalence(const arg_pair& p1, const arg_pair& p2, bool inside_disjunction) { - if (m.is_not(p1.first)==m.is_not(p2.first)) { return 0; } - if (m.is_not(p1.second)==m.is_not(p2.second)) { return 0; } + if (m.is_not(p1.first)==m.is_not(p2.first)) { return nullptr; } + if (m.is_not(p1.second)==m.is_not(p2.second)) { return nullptr; } - expr * first_bare = 0; - if (m.is_not(p1.first, first_bare) && p2.first!=first_bare) { return 0; } - if (m.is_not(p2.first, first_bare) && p1.first!=first_bare) { return 0; } + expr * first_bare = nullptr; + if (m.is_not(p1.first, first_bare) && p2.first!=first_bare) { return nullptr; } + if (m.is_not(p2.first, first_bare) && p1.first!=first_bare) { return nullptr; } SASSERT(first_bare); - expr * second_bare = 0; - if (m.is_not(p1.second, second_bare) && p2.second!=second_bare) { return 0; } - if (m.is_not(p2.second, second_bare) && p1.second!=second_bare) { return 0; } + expr * second_bare = nullptr; + if (m.is_not(p1.second, second_bare) && p2.second!=second_bare) { return nullptr; } + if (m.is_not(p2.second, second_bare) && p1.second!=second_bare) { return nullptr; } SASSERT(second_bare); - if (!m.is_bool(first_bare) || !m.is_bool(second_bare)) { return 0; } + if (!m.is_bool(first_bare) || !m.is_bool(second_bare)) { return nullptr; } //both negations are in the same pair bool negs_together = m.is_not(p1.first)==m.is_not(p1.second); @@ -261,14 +261,14 @@ namespace datalog { arg_pair new_ap; if (match_arg_pair(e, new_ap, inside_disjunction)) { - app * neq = 0; + app * neq = nullptr; if (have_pair) { neq = detect_equivalence(ap, new_ap, inside_disjunction); } if (neq) { have_pair = false; v[prev_pair_idx] = neq; - + read_idx++; continue; } @@ -294,7 +294,7 @@ namespace datalog { //bool detect_same_variable_conj_pairs - br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { if (m.is_not(f) && (m.is_and(args[0]) || m.is_or(args[0]))) { @@ -307,15 +307,15 @@ namespace datalog { m_app_args.push_back(tmp); } if (m.is_and(args[0])) { - result = mk_or(m_app_args); + result = mk_or(m_app_args); } else { - result = mk_and(m_app_args); + result = mk_and(m_app_args); } return BR_REWRITE2; } - if (!m.is_and(f) && !m.is_or(f)) { - return BR_FAILED; + if (!m.is_and(f) && !m.is_or(f)) { + return BR_FAILED; } if (num == 0) { if (m.is_and(f)) { @@ -375,7 +375,7 @@ namespace datalog { m_simp(ctx.get_rewriter()), a(m), m_rule_subst(ctx), - m_tail(m), + m_tail(m), m_itail_members(m), m_conj(m) { m_cfg = alloc(normalizer_cfg, m); @@ -386,7 +386,7 @@ namespace datalog { dealloc(m_rw); dealloc(m_cfg); } - + void mk_interp_tail_simplifier::simplify_expr(app * a, expr_ref& res) { @@ -537,7 +537,7 @@ namespace datalog { simplify_expr(itail.get(), simp_res); modified |= itail.get() != simp_res.get(); - + if (m.is_false(simp_res)) { TRACE("dl", r->display(m_context, tout << "rule is infeasible\n");); return false; @@ -568,7 +568,7 @@ namespace datalog { rule_ref pro_var_eq_result(m_context.get_rule_manager()); if (propagate_variable_equivalences(res, pro_var_eq_result)) { - SASSERT(rule_counter().get_max_rule_var(*r.get())==0 || + SASSERT(rule_counter().get_max_rule_var(*r.get())==0 || rule_counter().get_max_rule_var(*r.get()) > rule_counter().get_max_rule_var(*pro_var_eq_result.get())); r = pro_var_eq_result; goto start; @@ -601,21 +601,20 @@ namespace datalog { rule_set * mk_interp_tail_simplifier::operator()(rule_set const & source) { if (source.get_num_rules() == 0) { - return 0; + return nullptr; } rule_set * res = alloc(rule_set, m_context); if (transform_rules(source, *res)) { res->inherit_predicates(source); - TRACE("dl", - source.display(tout); + TRACE("dl", + source.display(tout); res->display(tout);); } else { dealloc(res); - res = 0; + res = nullptr; } return res; } - -}; +}; diff --git a/src/muz/transforms/dl_mk_interp_tail_simplifier.h b/src/muz/transforms/dl_mk_interp_tail_simplifier.h index a8a7393cd..0d4c65d11 100644 --- a/src/muz/transforms/dl_mk_interp_tail_simplifier.h +++ b/src/muz/transforms/dl_mk_interp_tail_simplifier.h @@ -44,7 +44,7 @@ namespace datalog { void apply(app * a, app_ref& res); public: rule_substitution(context & ctx) - : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_head(m), m_tail(m), m_rule(0) {} + : m(ctx.get_manager()), m_context(ctx), m_subst(m), m_unif(m), m_head(m), m_tail(m), m_rule(nullptr) {} /** Reset substitution and get it ready for working with rule r. @@ -53,7 +53,7 @@ namespace datalog { */ void reset(rule * r); - /** Reset subtitution and unify tail tgt_idx of the target rule and the head of the src rule */ + /** Reset substitution and unify tail tgt_idx of the target rule and the head of the src rule */ bool unify(expr * e1, expr * e2); void get_result(rule_ref & res); @@ -90,7 +90,7 @@ namespace datalog { bool transform_rules(const rule_set & orig, rule_set & tgt); public: mk_interp_tail_simplifier(context & ctx, unsigned priority=40000); - virtual ~mk_interp_tail_simplifier(); + ~mk_interp_tail_simplifier() override; /**If rule should be retained, assign transformed version to res and return true; if rule can be deleted, return false. @@ -100,7 +100,7 @@ namespace datalog { */ bool transform_rule(rule * r, rule_ref& res); - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_karr_invariants.cpp b/src/muz/transforms/dl_mk_karr_invariants.cpp index 48219eeb9..fd14538c5 100644 --- a/src/muz/transforms/dl_mk_karr_invariants.cpp +++ b/src/muz/transforms/dl_mk_karr_invariants.cpp @@ -111,7 +111,7 @@ namespace datalog { add_invariant_model_converter(ast_manager& m): m(m), a(m), m_funcs(m), m_invs(m) {} - virtual ~add_invariant_model_converter() { } + ~add_invariant_model_converter() override { } void add(func_decl* p, expr* inv) { if (!m.is_true(inv)) { @@ -120,7 +120,9 @@ namespace datalog { } } - virtual void operator()(model_ref & mr) { + void get_units(obj_map& units) override {} + + void operator()(model_ref & mr) override { for (unsigned i = 0; i < m_funcs.size(); ++i) { func_decl* p = m_funcs[i].get(); func_interp* f = mr->get_func_interp(p); @@ -142,7 +144,7 @@ namespace datalog { } } - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { add_invariant_model_converter* mc = alloc(add_invariant_model_converter, m); for (unsigned i = 0; i < m_funcs.size(); ++i) { mc->add(translator(m_funcs[i].get()), m_invs[i].get()); @@ -150,6 +152,10 @@ namespace datalog { return mc; } + void display(std::ostream& out) override { + out << "(add-invariant-model-converter)\n"; + } + private: void mk_body(matrix const& M, expr_ref& body) { expr_ref_vector conj(m); @@ -191,13 +197,13 @@ namespace datalog { rule_set * mk_karr_invariants::operator()(rule_set const & source) { if (!m_ctx.karr()) { - return 0; + return nullptr; } rule_set::iterator it = source.begin(), end = source.end(); for (; it != end; ++it) { rule const& r = **it; if (r.has_negation()) { - return 0; + return nullptr; } } mk_loop_counter lc(m_ctx); @@ -209,7 +215,7 @@ namespace datalog { get_invariants(*src_loop); if (m.canceled()) { - return 0; + return nullptr; } // figure out whether to update same rules as used for saturation. @@ -248,7 +254,7 @@ namespace datalog { func_decl* p = dit->m_key; expr_ref fml = rctx.try_get_formula(p); if (fml && !m.is_true(fml)) { - expr* inv = 0; + expr* inv = nullptr; if (m_fun2inv.find(p, inv)) { fml = m.mk_and(inv, fml); } @@ -270,7 +276,7 @@ namespace datalog { rule_set::decl2rules::iterator gend = src.end_grouped_rules(); for (; git != gend; ++git) { func_decl* p = git->m_key; - expr* fml = 0; + expr* fml = nullptr; if (m_fun2inv.find(p, fml)) { kmc->add(p, fml); } @@ -292,7 +298,7 @@ namespace datalog { } for (unsigned i = 0; i < utsz; ++i) { func_decl* q = r.get_decl(i); - expr* fml = 0; + expr* fml = nullptr; if (m_fun2inv.find(q, fml)) { expr_safe_replace rep(m); for (unsigned j = 0; j < q->get_arity(); ++j) { @@ -306,7 +312,7 @@ namespace datalog { } rule* new_rule = &r; if (tail.size() != tsz) { - new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), 0, r.name()); + new_rule = rm.mk(r.get_head(), tail.size(), tail.c_ptr(), nullptr, r.name()); } rules.add_rule(new_rule); rm.mk_rule_rewrite_proof(r, *new_rule); // should be weakening rule. diff --git a/src/muz/transforms/dl_mk_karr_invariants.h b/src/muz/transforms/dl_mk_karr_invariants.h index b31f1f8d9..cf021efda 100644 --- a/src/muz/transforms/dl_mk_karr_invariants.h +++ b/src/muz/transforms/dl_mk_karr_invariants.h @@ -65,9 +65,9 @@ namespace datalog { public: mk_karr_invariants(context & ctx, unsigned priority); - virtual ~mk_karr_invariants(); + ~mk_karr_invariants() override; - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; diff --git a/src/muz/transforms/dl_mk_loop_counter.cpp b/src/muz/transforms/dl_mk_loop_counter.cpp index 62568640c..1aea7be45 100644 --- a/src/muz/transforms/dl_mk_loop_counter.cpp +++ b/src/muz/transforms/dl_mk_loop_counter.cpp @@ -56,7 +56,7 @@ namespace datalog { app_ref mk_loop_counter::del_arg(app* fn) { expr_ref_vector args(m); - func_decl* old_fn = 0, *new_fn = fn->get_decl(); + func_decl* old_fn = nullptr, *new_fn = fn->get_decl(); SASSERT(fn->get_num_args() > 0); args.append(fn->get_num_args()-1, fn->get_args()); VERIFY (m_new2old.find(new_fn, old_fn)); diff --git a/src/muz/transforms/dl_mk_loop_counter.h b/src/muz/transforms/dl_mk_loop_counter.h index fb4b6d704..cf79dc218 100644 --- a/src/muz/transforms/dl_mk_loop_counter.h +++ b/src/muz/transforms/dl_mk_loop_counter.h @@ -36,9 +36,9 @@ namespace datalog { app_ref del_arg(app* fn); public: mk_loop_counter(context & ctx, unsigned priority = 33000); - ~mk_loop_counter(); + ~mk_loop_counter() override; - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; func_decl* get_old(func_decl* f) const { return m_new2old.find(f); } diff --git a/src/muz/transforms/dl_mk_magic_sets.cpp b/src/muz/transforms/dl_mk_magic_sets.cpp index 15a4c1093..0de37a4ce 100644 --- a/src/muz/transforms/dl_mk_magic_sets.cpp +++ b/src/muz/transforms/dl_mk_magic_sets.cpp @@ -129,9 +129,9 @@ namespace datalog { SASSERT(m.is_bool(old_pred->get_range())); adornment_desc adn(old_pred); adn.m_adornment.populate(lit, bound_vars); - adornment_map::entry * e = m_adorned_preds.insert_if_not_there2(adn, 0); + adornment_map::entry * e = m_adorned_preds.insert_if_not_there2(adn, nullptr); func_decl * new_pred = e->get_data().m_value; - if (new_pred==0) { + if (new_pred==nullptr) { std::string suffix = "ad_"+adn.m_adornment.to_string(); new_pred = m_context.mk_fresh_head_predicate( old_pred->get_name(), symbol(suffix.c_str()), @@ -163,7 +163,7 @@ namespace datalog { pred2pred::obj_map_entry * e = m_magic_preds.insert_if_not_there2(l_pred, 0); func_decl * mag_pred = e->get_data().m_value; - if (mag_pred==0) { + if (mag_pred==nullptr) { unsigned mag_arity = bound_args.size(); ptr_vector mag_domain; @@ -264,7 +264,7 @@ namespace datalog { } - func_decl * new_head_pred = 0; + func_decl * new_head_pred = nullptr; VERIFY( m_adorned_preds.find(adornment_desc(head->get_decl(), head_adornment), new_head_pred) ); app * new_head = m.mk_app(new_head_pred, head->get_args()); @@ -301,14 +301,14 @@ namespace datalog { app * tail[] = {lit, mag_lit}; - rule * r = m_context.get_rule_manager().mk(adn_lit, 2, tail, 0); + rule * r = m_context.get_rule_manager().mk(adn_lit, 2, tail, nullptr); result.add_rule(r); } rule_set * mk_magic_sets::operator()(rule_set const & source) { if (!m_context.magic_sets_for_queries()) { - return 0; + return nullptr; } SASSERT(source.contains(m_goal)); SASSERT(source.get_predicate_rules(m_goal).size() == 1); @@ -372,10 +372,10 @@ namespace datalog { app * adn_goal_head = adorn_literal(goal_head, empty_var_idx_set); app * mag_goal_head = create_magic_literal(adn_goal_head); SASSERT(mag_goal_head->is_ground()); - rule * mag_goal_rule = m_context.get_rule_manager().mk(mag_goal_head, 0, 0, 0); + rule * mag_goal_rule = m_context.get_rule_manager().mk(mag_goal_head, 0, nullptr, nullptr); result->add_rule(mag_goal_rule); - rule * back_to_goal_rule = m_context.get_rule_manager().mk(goal_head, 1, &adn_goal_head, 0); + rule * back_to_goal_rule = m_context.get_rule_manager().mk(goal_head, 1, &adn_goal_head, nullptr); result->add_rule(back_to_goal_rule); return result; } diff --git a/src/muz/transforms/dl_mk_magic_sets.h b/src/muz/transforms/dl_mk_magic_sets.h index 73b5e94f6..dc3ad03b7 100644 --- a/src/muz/transforms/dl_mk_magic_sets.h +++ b/src/muz/transforms/dl_mk_magic_sets.h @@ -126,7 +126,7 @@ namespace datalog { */ mk_magic_sets(context & ctx, func_decl* goal); - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_magic_symbolic.cpp b/src/muz/transforms/dl_mk_magic_symbolic.cpp index 2edc57375..0e2e4991a 100644 --- a/src/muz/transforms/dl_mk_magic_symbolic.cpp +++ b/src/muz/transforms/dl_mk_magic_symbolic.cpp @@ -68,7 +68,7 @@ namespace datalog { rule_set * mk_magic_symbolic::operator()(rule_set const & source) { if (!m_ctx.magic()) { - return 0; + return nullptr; } context& ctx = source.get_context(); rule_manager& rm = source.get_rule_manager(); @@ -98,7 +98,7 @@ namespace datalog { result->add_rule(new_rule); if (source.is_output_predicate(r.get_decl())) { result->set_output_predicate(new_rule->get_decl()); - new_rule = rm.mk(mk_query(r.get_head()), 0, 0, 0, r.name(), true); + new_rule = rm.mk(mk_query(r.get_head()), 0, nullptr, nullptr, r.name(), true); result->add_rule(new_rule); } diff --git a/src/muz/transforms/dl_mk_magic_symbolic.h b/src/muz/transforms/dl_mk_magic_symbolic.h index 9c51a5287..451b2c46f 100644 --- a/src/muz/transforms/dl_mk_magic_symbolic.h +++ b/src/muz/transforms/dl_mk_magic_symbolic.h @@ -30,8 +30,8 @@ namespace datalog { app_ref mk_query(app* q); public: mk_magic_symbolic(context & ctx, unsigned priority = 33037); - ~mk_magic_symbolic(); - rule_set * operator()(rule_set const & source); + ~mk_magic_symbolic() override; + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp index b171aaa7c..dafb0ddd5 100644 --- a/src/muz/transforms/dl_mk_quantifier_abstraction.cpp +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.cpp @@ -11,7 +11,7 @@ Abstract: Author: - Ken McMillan + Ken McMillan Andrey Rybalchenko Nikolaj Bjorner (nbjorner) 2013-04-02 @@ -23,13 +23,12 @@ Revision History: #include "muz/base/dl_context.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/expr_abstract.h" -#include "muz/base/fixedpoint_params.hpp" namespace datalog { - // model converter: + // model converter: // Given model for P^(x, y, i, a[i]) // create model: P(x,y,a) == forall i . P^(x,y,i,a[i]) // requires substitution and list of bound variables. @@ -47,12 +46,16 @@ namespace datalog { qa_model_converter(ast_manager& m): m(m), m_old_funcs(m), m_new_funcs(m) {} - virtual ~qa_model_converter() {} + ~qa_model_converter() override {} - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { return alloc(qa_model_converter, m); } + void display(std::ostream& out) override { display_add(out, m); } + + void get_units(obj_map& units) override { units.reset(); } + void insert(func_decl* old_p, func_decl* new_p, expr_ref_vector& sub, sort_ref_vector& sorts, svector const& bound) { m_old_funcs.push_back(old_p); m_new_funcs.push_back(new_p); @@ -61,7 +64,7 @@ namespace datalog { m_sorts.push_back(sorts); } - virtual void operator()(model_ref & old_model) { + void operator()(model_ref & old_model) override { model_ref new_model = alloc(model, m); for (unsigned i = 0; i < m_new_funcs.size(); ++i) { func_decl* p = m_new_funcs[i].get(); @@ -70,7 +73,7 @@ namespace datalog { sort_ref_vector const& sorts = m_sorts[i]; svector const& is_bound = m_bound[i]; func_interp* f = old_model->get_func_interp(p); - expr_ref body(m); + expr_ref body(m); unsigned arity_q = q->get_arity(); SASSERT(0 < p->get_arity()); func_interp* g = alloc(func_interp, m, arity_q); @@ -78,15 +81,19 @@ namespace datalog { if (f) { body = f->get_interp(); SASSERT(!f->is_partial()); - SASSERT(body); + SASSERT(body); } else { - body = m.mk_false(); + expr_ref_vector args(m); + for (unsigned i = 0; i < p->get_arity(); ++i) { + args.push_back(m.mk_var(i, p->get_domain(i))); + } + body = m.mk_app(p, args.size(), args.c_ptr()); } // Create quantifier wrapper around body. TRACE("dl", tout << mk_pp(body, m) << "\n";); - // 1. replace variables by the compound terms from + // 1. replace variables by the compound terms from // the original predicate. expr_safe_replace rep(m); for (unsigned i = 0; i < sub.size(); ++i) { @@ -113,7 +120,7 @@ namespace datalog { _free.push_back(consts.back()); } } - rep(body); + rep(body); rep.reset(); TRACE("dl", tout << mk_pp(body, m) << "\n";); @@ -122,18 +129,18 @@ namespace datalog { body = m.mk_forall(names.size(), bound_sorts.c_ptr(), names.c_ptr(), body); TRACE("dl", tout << mk_pp(body, m) << "\n";); - // 4. replace remaining constants by variables. + // 4. replace remaining constants by variables. for (unsigned i = 0; i < _free.size(); ++i) { rep.insert(_free[i].get(), m.mk_var(i, m.get_sort(_free[i].get()))); } - rep(body); + rep(body); g->set_else(body); TRACE("dl", tout << mk_pp(body, m) << "\n";); new_model->register_decl(q, g); - } + } old_model = new_model; - } + } }; mk_quantifier_abstraction::mk_quantifier_abstraction( @@ -143,17 +150,17 @@ namespace datalog { m_ctx(ctx), a(m), m_refs(m), - m_mc(NULL){ + m_mc(nullptr) { } - mk_quantifier_abstraction::~mk_quantifier_abstraction() { + mk_quantifier_abstraction::~mk_quantifier_abstraction() { } func_decl* mk_quantifier_abstraction::declare_pred(rule_set const& rules, rule_set& dst, func_decl* old_p) { if (rules.is_output_predicate(old_p)) { dst.inherit_predicate(rules, old_p, old_p); - return 0; + return nullptr; } unsigned sz = old_p->get_arity(); @@ -164,13 +171,13 @@ namespace datalog { } } if (num_arrays == 0) { - return 0; + return nullptr; } - func_decl* new_p = 0; + func_decl* new_p = nullptr; if (!m_old2new.find(old_p, new_p)) { expr_ref_vector sub(m), vars(m); - svector bound; + svector bound; sort_ref_vector domain(m), sorts(m); expr_ref arg(m); for (unsigned i = 0; i < sz; ++i) { @@ -200,7 +207,7 @@ namespace datalog { bound.push_back(false); sub.push_back(arg); sorts.push_back(s0); - } + } SASSERT(old_p->get_range() == m.mk_bool_sort()); new_p = m.mk_func_decl(old_p->get_name(), domain.size(), domain.c_ptr(), old_p->get_range()); m_refs.push_back(new_p); @@ -234,12 +241,12 @@ namespace datalog { } args.push_back(arg); } - TRACE("dl", + TRACE("dl", tout << mk_pp(new_p, m) << "\n"; for (unsigned i = 0; i < args.size(); ++i) { tout << mk_pp(args[i].get(), m) << "\n"; }); - return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); + return app_ref(m.mk_app(new_p, args.size(), args.c_ptr()), m); } app_ref mk_quantifier_abstraction::mk_tail(rule_set const& rules, rule_set& dst, app* p) { @@ -264,7 +271,7 @@ namespace datalog { for (unsigned i = 0; i < sz; ++i) { arg = ps->get_arg(i); sort* s = m.get_sort(arg); - bool is_pattern = false; + bool is_pattern = false; while (a.is_array(s)) { is_pattern = true; unsigned arity = get_array_arity(s); @@ -281,7 +288,7 @@ namespace datalog { } args.push_back(arg); } - expr* pat = 0; + expr* pat = nullptr; expr_ref pattern(m); pattern = m.mk_pattern(pats.size(), pats.c_ptr()); pat = pattern.get(); @@ -296,18 +303,18 @@ namespace datalog { ptr_vector args2; args2.push_back(arg); args2.append(num_args, args); - return a.mk_select(args2.size(), args2.c_ptr()); + return a.mk_select(args2.size(), args2.c_ptr()); } - + rule_set * mk_quantifier_abstraction::operator()(rule_set const & source) { if (!m_ctx.quantify_arrays()) { - return 0; + return nullptr; } unsigned sz = source.get_num_rules(); for (unsigned i = 0; i < sz; ++i) { rule& r = *source.get_rule(i); if (r.has_negation()) { - return 0; + return nullptr; } } @@ -326,10 +333,10 @@ namespace datalog { } rule_set * result = alloc(rule_set, m_ctx); - for (unsigned i = 0; i < sz; ++i) { + for (unsigned i = 0; i < sz; ++i) { tail.reset(); rule & r = *source.get_rule(i); - TRACE("dl", r.display(m_ctx, tout); ); + TRACE("dl", r.display(m_ctx, tout); ); unsigned cnt = vc.get_max_rule_var(r)+1; unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); @@ -344,24 +351,22 @@ namespace datalog { proof_ref pr(m); rm.mk_rule(fml, pr, *result, r.name()); TRACE("dl", result->last()->display(m_ctx, tout);); - } - + } + // proof converter: proofs are not necessarily preserved using this transformation. if (m_old2new.empty()) { dealloc(result); dealloc(m_mc); - result = 0; + result = nullptr; } else { m_ctx.add_model_converter(m_mc); } - m_mc = 0; + m_mc = nullptr; return result; } }; - - diff --git a/src/muz/transforms/dl_mk_quantifier_abstraction.h b/src/muz/transforms/dl_mk_quantifier_abstraction.h index 593e458b9..83d0e975d 100644 --- a/src/muz/transforms/dl_mk_quantifier_abstraction.h +++ b/src/muz/transforms/dl_mk_quantifier_abstraction.h @@ -51,9 +51,9 @@ namespace datalog { public: mk_quantifier_abstraction(context & ctx, unsigned priority); - virtual ~mk_quantifier_abstraction(); + ~mk_quantifier_abstraction() override; - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp index 8986bf506..9f6302e05 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.cpp +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.cpp @@ -139,7 +139,7 @@ namespace datalog { return; } expr* arg = pat->get_arg(i); - ptr_vector* terms = 0; + ptr_vector* terms = nullptr; if (m_funs.find(to_app(arg)->get_decl(), terms)) { for (unsigned k = 0; k < terms->size(); ++k) { @@ -180,12 +180,12 @@ namespace datalog { } m_terms[n] = e; visited.mark(e); - if (m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) { + if (m.is_eq(e, e1, e2)) { m_uf.merge(e1->get_id(), e2->get_id()); } if (is_app(e)) { app* ap = to_app(e); - ptr_vector* terms = 0; + ptr_vector* terms = nullptr; if (!m_funs.find(ap->get_decl(), terms)) { terms = alloc(ptr_vector); m_funs.insert(ap->get_decl(), terms); @@ -250,7 +250,7 @@ namespace datalog { rule_set * mk_quantifier_instantiation::operator()(rule_set const & source) { if (!m_ctx.instantiate_quantifiers()) { - return 0; + return nullptr; } bool has_quantifiers = false; unsigned sz = source.get_num_rules(); @@ -259,11 +259,11 @@ namespace datalog { rule& r = *source.get_rule(i); has_quantifiers = has_quantifiers || rm.has_quantifiers(r); if (r.has_negation()) { - return 0; + return nullptr; } } if (!has_quantifiers) { - return 0; + return nullptr; } expr_ref_vector conjs(m); @@ -291,7 +291,7 @@ namespace datalog { } else { dealloc(result); - result = 0; + result = nullptr; } return result; } diff --git a/src/muz/transforms/dl_mk_quantifier_instantiation.h b/src/muz/transforms/dl_mk_quantifier_instantiation.h index 4f1626ec6..f8e12f712 100644 --- a/src/muz/transforms/dl_mk_quantifier_instantiation.h +++ b/src/muz/transforms/dl_mk_quantifier_instantiation.h @@ -61,9 +61,9 @@ namespace datalog { public: mk_quantifier_instantiation(context & ctx, unsigned priority); - virtual ~mk_quantifier_instantiation(); + ~mk_quantifier_instantiation() override; - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; diff --git a/src/muz/transforms/dl_mk_rule_inliner.cpp b/src/muz/transforms/dl_mk_rule_inliner.cpp index f1a4eb32b..317f13ff8 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.cpp +++ b/src/muz/transforms/dl_mk_rule_inliner.cpp @@ -18,7 +18,7 @@ Revision History: Added linear_inline 2012-9-10 (nbjorner) Disable inliner for quantified rules 2012-10-31 (nbjorner) - + Notes: Resolution transformation (resolve): @@ -27,7 +27,7 @@ Resolution transformation (resolve): -------------------------------------------------- P(x) :- R(z), phi(x,y), psi(y,z) - Proof converter: + Proof converter: replace assumption (*) by rule and upper assumptions. @@ -37,9 +37,9 @@ Subsumption transformation (remove rule): P(x) :- Q(y), phi(x,y) Rules --------------------------------- Rules - - - Model converter: + + + Model converter: P(x) := P(x) or (exists y . Q(y) & phi(x,y)) @@ -52,7 +52,7 @@ Subsumption transformation (remove rule): #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_mk_rule_inliner.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { @@ -67,15 +67,15 @@ namespace datalog { unsigned var_cnt = std::max(vc.get_max_rule_var(tgt), vc.get_max_rule_var(src))+1; m_subst.reset(); m_subst.reserve(2, var_cnt); - + m_ready = m_unif(tgt.get_tail(tgt_idx), src.get_head(), m_subst); if (m_ready) { m_deltas[0] = 0; m_deltas[1] = var_cnt; - TRACE("dl", - output_predicate(m_context, src.get_head(), tout << "unify rules "); - output_predicate(m_context, tgt.get_head(), tout << "\n"); + TRACE("dl", + output_predicate(m_context, src.get_head(), tout << "unify rules "); + output_predicate(m_context, tgt.get_head(), tout << "\n"); tout << "\n";); } return m_ready; @@ -90,7 +90,7 @@ namespace datalog { } void rule_unifier::apply( - rule const& r, bool is_tgt, unsigned skipped_index, + rule const& r, bool is_tgt, unsigned skipped_index, app_ref_vector& res, svector& res_neg) { unsigned rule_len = r.get_tail_size(); for (unsigned i = 0; i < rule_len; i++) { @@ -127,7 +127,7 @@ namespace datalog { ); if (m_normalize) { - m_rm.fix_unbound_vars(res, true); + m_rm.fix_unbound_vars(res, true); if (m_interp_simplifier.transform_rule(res.get(), simpl_rule)) { res = simpl_rule; return true; @@ -150,8 +150,8 @@ namespace datalog { for (unsigned i = 0; i < sorts.size(); ++i) { v = m.mk_var(i, sorts[i]); m_subst.apply(2, m_deltas, expr_offset(v, is_tgt?0:1), w); - result.push_back(w); - } + result.push_back(w); + } return result; } @@ -184,7 +184,7 @@ namespace datalog { expr_ref_vector s2 = m_unifier.get_rule_subst(src, false); datalog::resolve_rule(m_rm, tgt, src, tail_index, s1, s2, *res.get()); } - return true; + return true; } else { TRACE("dl", res->display(m_context, tout << "interpreted tail is unsat\n");); @@ -209,9 +209,7 @@ namespace datalog { rel->collect_non_empty_predicates(m_preds_with_facts); } - rule_set::iterator rend = orig.end(); - for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { - rule * r = *rit; + for (rule * r : orig) { func_decl * head_pred = r->get_decl(); m_head_pred_ctr.inc(head_pred); @@ -242,12 +240,12 @@ namespace datalog { return false; } - // - // these conditions are optional, they avoid possible exponential increase + // + // these conditions are optional, they avoid possible exponential increase // in the size of the problem - // + // - return + return //m_head_pred_non_empty_tails_ctr.get(pred)<=1 m_head_pred_ctr.get(pred) <= 1 || (m_tail_pred_ctr.get(pred) <= 1 && m_head_pred_ctr.get(pred) <= 4) @@ -255,12 +253,10 @@ namespace datalog { } /** Caller has to dealloc the returned object */ - rule_set * mk_rule_inliner::create_allowed_rule_set(rule_set const & orig) + rule_set * mk_rule_inliner::create_allowed_rule_set(rule_set const & orig) { rule_set * res = alloc(rule_set, m_context); - unsigned rcnt = orig.get_num_rules(); - for (unsigned i=0; iget_decl())) { res->add_rule(r); } @@ -272,7 +268,7 @@ namespace datalog { /** Try to make the set of inlined predicates acyclic by forbidding inlining of one - predicate from each strongly connected component. Return true if we did forbide some + predicate from each strongly connected component. Return true if we did forbide some predicate, and false if the set of rules is already acyclic. */ bool mk_rule_inliner::forbid_preds_from_cycles(rule_set const & r) @@ -280,16 +276,14 @@ namespace datalog { SASSERT(r.is_closed()); bool something_forbidden = false; - + const rule_stratifier::comp_vector& comps = r.get_stratifier().get_strats(); - rule_stratifier::comp_vector::const_iterator cend = comps.end(); - for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) { - rule_stratifier::item_set * stratum = *it; - if (stratum->size()==1) { + for (rule_stratifier::item_set * stratum : comps) { + if (stratum->size() == 1) { continue; } - SASSERT(stratum->size()>1); + SASSERT(stratum->size() > 1); func_decl * first_stratum_pred = *stratum->begin(); //we're trying to break cycles by removing one predicate from each of them @@ -299,17 +293,15 @@ namespace datalog { return something_forbidden; } - bool mk_rule_inliner::forbid_multiple_multipliers(const rule_set & orig, + bool mk_rule_inliner::forbid_multiple_multipliers(const rule_set & orig, rule_set const & proposed_inlined_rules) { bool something_forbidden = false; - const rule_stratifier::comp_vector& comps = + const rule_stratifier::comp_vector& comps = proposed_inlined_rules.get_stratifier().get_strats(); - rule_stratifier::comp_vector::const_iterator cend = comps.end(); - for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) { - rule_stratifier::item_set * stratum = *it; + for (rule_stratifier::item_set * stratum : comps) { SASSERT(stratum->size()==1); func_decl * head_pred = *stratum->begin(); @@ -318,10 +310,7 @@ namespace datalog { bool is_multi_occurrence_pred = m_tail_pred_ctr.get(head_pred)>1; const rule_vector& pred_rules = proposed_inlined_rules.get_predicate_rules(head_pred); - rule_vector::const_iterator iend = pred_rules.end(); - for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) { - rule * r = *iit; - + for (rule * r : pred_rules) { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; tiget_decl(ti); @@ -343,7 +332,7 @@ namespace datalog { } else { is_multi_head_pred = true; - m_head_pred_ctr.get(head_pred) = + m_head_pred_ctr.get(head_pred) = m_head_pred_ctr.get(head_pred)*tail_pred_head_cnt; } } @@ -390,7 +379,7 @@ namespace datalog { void mk_rule_inliner::plan_inlining(rule_set const & orig) { count_pred_occurrences(orig); - + scoped_ptr candidate_inlined_set = create_allowed_rule_set(orig); while (forbid_preds_from_cycles(*candidate_inlined_set)) { candidate_inlined_set = create_allowed_rule_set(orig); @@ -405,28 +394,22 @@ namespace datalog { // now we start filling in the set of the inlined rules in a topological order, // so that we inline rules into other rules - SASSERT(m_inlined_rules.get_num_rules()==0); + SASSERT(m_inlined_rules.get_num_rules() == 0); const rule_stratifier::comp_vector& comps = candidate_inlined_set->get_stratifier().get_strats(); - rule_stratifier::comp_vector::const_iterator cend = comps.end(); - for (rule_stratifier::comp_vector::const_iterator it = comps.begin(); it!=cend; ++it) { - rule_stratifier::item_set * stratum = *it; - SASSERT(stratum->size()==1); + for (rule_stratifier::item_set * stratum : comps) { + SASSERT(stratum->size() == 1); func_decl * pred = *stratum->begin(); - - const rule_vector& pred_rules = candidate_inlined_set->get_predicate_rules(pred); - rule_vector::const_iterator iend = pred_rules.end(); - for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) { - transform_rule(orig, *iit, m_inlined_rules); + for (rule * r : candidate_inlined_set->get_predicate_rules(pred)) { + transform_rule(orig, r, m_inlined_rules); } } TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; ); - for (unsigned i = 0; i < m_inlined_rules.get_num_rules(); ++i) { - rule* r = m_inlined_rules.get_rule(i); - datalog::del_rule(m_mc, *r, true); + for (rule * r : m_inlined_rules) { + datalog::del_rule(m_mc, *r, false); } } @@ -439,9 +422,7 @@ namespace datalog { rule_ref r(todo.back(), m_rm); todo.pop_back(); unsigned pt_len = r->get_positive_tail_size(); - unsigned i = 0; - for (; i < pt_len && !inlining_allowed(orig, r->get_decl(i)); ++i) {}; SASSERT(!has_quantifier(*r.get())); @@ -455,9 +436,7 @@ namespace datalog { func_decl * pred = r->get_decl(i); const rule_vector& pred_rules = m_inlined_rules.get_predicate_rules(pred); - rule_vector::const_iterator iend = pred_rules.end(); - for (rule_vector::const_iterator iit = pred_rules.begin(); iit!=iend; ++iit) { - rule * inl_rule = *iit; + for (rule * inl_rule : pred_rules) { rule_ref inl_result(m_rm); if (try_to_inline_rule(*r.get(), *inl_rule, i, inl_result)) { todo.push_back(inl_result); @@ -475,13 +454,12 @@ namespace datalog { bool something_done = false; - rule_set::iterator rend = orig.end(); - for (rule_set::iterator rit = orig.begin(); rit!=rend; ++rit) { - rule_ref r(*rit, m_rm); + for (rule* rl : orig) { + rule_ref r(rl, m_rm); func_decl * pred = r->get_decl(); - // if inlining is allowed, then we are eliminating - // this relation through inlining, + // if inlining is allowed, then we are eliminating + // this relation through inlining, // so we don't add its rules to the result something_done |= !inlining_allowed(orig, pred) && transform_rule(orig, r, tgt); @@ -494,33 +472,28 @@ namespace datalog { } } } - + return something_done; } /** Check whether rule r is oriented in a particular ordering. This is to avoid infinite cycle of inlining in the eager inliner. - + Out ordering is lexicographic, comparing atoms first on stratum they are in, then on arity and then on ast ID of their func_decl. */ bool mk_rule_inliner::is_oriented_rewriter(rule * r, rule_stratifier const& strat) { func_decl * head_pred = r->get_decl(); unsigned head_strat = strat.get_predicate_strat(head_pred); - unsigned head_arity = head_pred->get_arity(); - - unsigned pt_len = r->get_positive_tail_size(); - for (unsigned ti=0; tiget_decl(ti); - unsigned pred_strat = strat.get_predicate_strat(pred); - SASSERT(pred_strat<=head_strat); + SASSERT(pred_strat <= head_strat); - if (pred_strat==head_strat) { + if (pred_strat == head_strat) { if (pred->get_arity()>head_arity || (pred->get_arity()==head_arity && pred->get_id()>=head_pred->get_id()) ) { return false; @@ -543,43 +516,43 @@ namespace datalog { unsigned pt_len = r->get_positive_tail_size(); for (unsigned ti = 0; ti < pt_len; ++ti) { - + func_decl * pred = r->get_decl(ti); if (pred == head_pred || m_preds_with_facts.contains(pred)) { continue; } const rule_vector& pred_rules = rules.get_predicate_rules(pred); - rule * inlining_candidate = 0; + rule * inlining_candidate = nullptr; unsigned rule_cnt = pred_rules.size(); if (rule_cnt == 0) { - inlining_candidate = 0; + inlining_candidate = nullptr; } else if (rule_cnt == 1) { inlining_candidate = pred_rules[0]; } else { - inlining_candidate = 0; - + inlining_candidate = nullptr; + for (unsigned ri = 0; ri < rule_cnt; ++ri) { rule * pred_rule = pred_rules[ri]; if (!m_unifier.unify_rules(*r, ti, *pred_rule)) { //we skip rules which don't unify with the tail atom continue; } - if (inlining_candidate != 0) { - // We have two rules that can be inlined into the current + if (inlining_candidate != nullptr) { + // We have two rules that can be inlined into the current // tail predicate. In this situation we don't do inlinning - // on this tail atom, as we don't want the overall number + // on this tail atom, as we don't want the overall number // of rules to increase. goto process_next_tail; } inlining_candidate = pred_rule; } } - if (inlining_candidate == 0) { + if (inlining_candidate == nullptr) { // nothing unifies with the tail atom, therefore the rule is unsatisfiable // (we can say this because relation pred doesn't have any ground facts either) - res = 0; + res = nullptr; datalog::del_rule(m_mc, *r, false); return true; } @@ -591,7 +564,7 @@ namespace datalog { } if (!try_to_inline_rule(*r, *inlining_candidate, ti, res)) { datalog::del_rule(m_mc, *r, false); - res = 0; + res = nullptr; } return true; @@ -635,14 +608,14 @@ namespace datalog { P(1,x) :- P(1,z), phi(x,y), psi(y,z) - whenever P(0,x) is not unifiable with the + whenever P(0,x) is not unifiable with the body of the rule where it appears (P(1,z)) - and P(0,x) is unifiable with at most one (?) + and P(0,x) is unifiable with at most one (?) other rule (and it does not occur negatively). */ bool mk_rule_inliner::visitor::operator()(expr* e) { m_unifiers.append(m_positions.find(e)); - TRACE("dl", + TRACE("dl", tout << "unifier: " << (m_unifiers.empty()?0:m_unifiers.back()); tout << " num unifiers: " << m_unifiers.size(); tout << " num positions: " << m_positions.find(e).size() << "\n"; @@ -667,7 +640,7 @@ namespace datalog { } unsigned_vector const& mk_rule_inliner::visitor::del_position(expr* e, unsigned j) { - obj_map::obj_map_entry * et = m_positions.find_core(e); + obj_map::obj_map_entry * et = m_positions.find_core(e); SASSERT(et && et->get_data().m_value.contains(j)); et->get_data().m_value.erase(j); return et->get_data().m_value; @@ -681,7 +654,7 @@ namespace datalog { m_head_visitor.add_position(head, i); m_head_index.insert(head); m_pinned.push_back(r); - + if (source.is_output_predicate(headd) || m_preds_with_facts.contains(headd)) { can_remove.set(i, false); @@ -694,10 +667,10 @@ namespace datalog { m_tail_visitor.add_position(tail, i); m_tail_index.insert(tail); } - bool can_exp = + bool can_exp = tl_sz == 1 - && r->get_positive_tail_size() == 1 - && !m_preds_with_facts.contains(r->get_decl(0)) + && r->get_positive_tail_size() == 1 + && !m_preds_with_facts.contains(r->get_decl(0)) && !source.is_output_predicate(r->get_decl(0)); can_expand.set(i, can_exp); } @@ -709,14 +682,14 @@ namespace datalog { for (unsigned j = 0; j < tl_sz; ++j) { app* tail = r->get_tail(j); m_tail_visitor.del_position(tail, i); - } + } } #define PRT(_x_) ((_x_)?"T":"F") bool mk_rule_inliner::inline_linear(scoped_ptr& rules) { - bool done_something = false; + bool done_something = false; unsigned sz = rules->get_num_rules(); m_head_visitor.reset(sz); @@ -731,7 +704,7 @@ namespace datalog { acc.push_back(rules->get_rule(i)); } - // set up unification index. + // set up unification index. svector& can_remove = m_head_visitor.can_remove(); svector& can_expand = m_head_visitor.can_expand(); @@ -756,7 +729,7 @@ namespace datalog { svector valid; valid.reset(); - valid.resize(sz, true); + valid.resize(sz, true); bool allow_branching = m_context.get_params().xform_inline_linear_branch(); @@ -765,9 +738,9 @@ namespace datalog { while (true) { rule_ref r(acc[i].get(), m_rm); - + TRACE("dl", r->display(m_context, tout << "processing: " << i << "\n");); - + if (!valid.get(i)) { TRACE("dl", tout << "invalid: " << i << "\n";); break; @@ -789,9 +762,9 @@ namespace datalog { TRACE("dl", tout << PRT(can_remove.get(j)) << " " << PRT(valid.get(j)) << " " << PRT(i != j) << "\n";); break; } - + rule* r2 = acc[j].get(); - + // check that the head of r2 only unifies with this single body position. TRACE("dl", output_predicate(m_context, r2->get_head(), tout << "unify head: "); tout << "\n";); m_tail_visitor.reset(); @@ -803,7 +776,7 @@ namespace datalog { TRACE("dl", tout << "too many tails " << num_tail_unifiers << "\n";); break; } - + rule_ref rl_res(m_rm); if (!try_to_inline_rule(*r.get(), *r2, 0, rl_res)) { TRACE("dl", r->display(m_context, tout << "inlining failed\n"); r2->display(m_context, tout); ); @@ -814,12 +787,12 @@ namespace datalog { del_rule(r, i); add_rule(*rules, rl_res.get(), i); - + r = rl_res; acc[i] = r.get(); can_expand.set(i, can_expand.get(j)); - + if (num_tail_unifiers == 1) { TRACE("dl", tout << "setting invalid: " << j << "\n";); valid.set(j, false); @@ -842,26 +815,22 @@ namespace datalog { res->inherit_predicates(*rules); TRACE("dl", res->display(tout);); rules = res.detach(); - } + } return done_something; } rule_set * mk_rule_inliner::operator()(rule_set const & source) { bool something_done = false; - ref hsmc; + ref hsmc; if (source.get_num_rules() == 0) { - return 0; + return nullptr; } - rule_set::iterator end = source.end(); - for (rule_set::iterator it = source.begin(); it != end; ++ it) { - if (has_quantifier(**it)) { - return 0; - } - } - + for (rule const* r : source) + if (has_quantifier(*r)) + return nullptr; if (m_context.get_model_converter()) { hsmc = alloc(horn_subsume_model_converter, m); @@ -872,15 +841,15 @@ namespace datalog { if (m_context.get_params().xform_inline_eager()) { TRACE("dl", source.display(tout << "before eager inlining\n");); - plan_inlining(source); - something_done = transform_rules(source, *res); + plan_inlining(source); + something_done = transform_rules(source, *res); VERIFY(res->close()); //this transformation doesn't break the negation stratification // try eager inlining if (do_eager_inlining(res)) { something_done = true; - } + } TRACE("dl", res->display(tout << "after eager inlining\n");); - } + } if (something_done) { res->inherit_predicates(source); } @@ -893,7 +862,7 @@ namespace datalog { } if (!something_done) { - res = 0; + res = nullptr; } else { m_context.add_model_converter(hsmc.get()); @@ -901,6 +870,5 @@ namespace datalog { return res.detach(); } - -}; +}; diff --git a/src/muz/transforms/dl_mk_rule_inliner.h b/src/muz/transforms/dl_mk_rule_inliner.h index 55b9b9487..9146343fa 100644 --- a/src/muz/transforms/dl_mk_rule_inliner.h +++ b/src/muz/transforms/dl_mk_rule_inliner.h @@ -45,7 +45,7 @@ namespace datalog { : m(ctx.get_manager()), m_rm(ctx.get_rule_manager()), m_context(ctx), m_interp_simplifier(ctx), m_subst(m), m_unif(m), m_ready(false), m_normalize(true) {} - /** Reset subtitution and unify tail tgt_idx of the target rule and the head of the src rule */ + /** Reset substitution and unify tail tgt_idx of the target rule and the head of the src rule */ bool unify_rules(rule const& tgt, unsigned tgt_idx, rule const& src); /** @@ -89,7 +89,7 @@ namespace datalog { obj_map m_positions; public: visitor(context& c, substitution & s): st_visitor(s), m_context(c) { (void) m_context; } - virtual bool operator()(expr* e); + bool operator()(expr* e) override; void reset() { m_unifiers.reset(); } void reset(unsigned sz); svector& can_remove() { return m_can_remove; } @@ -186,7 +186,7 @@ namespace datalog { m_simp(m_context.get_rewriter()), m_pinned(m_rm), m_inlined_rules(m_context), - m_mc(0), + m_mc(nullptr), m_unifier(ctx), m_head_index(m), m_tail_index(m), @@ -194,9 +194,9 @@ namespace datalog { m_head_visitor(ctx, m_subst), m_tail_visitor(ctx, m_subst) {} - virtual ~mk_rule_inliner() { } + ~mk_rule_inliner() override { } - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_scale.cpp b/src/muz/transforms/dl_mk_scale.cpp index 5bc10b957..9ceaeeab3 100644 --- a/src/muz/transforms/dl_mk_scale.cpp +++ b/src/muz/transforms/dl_mk_scale.cpp @@ -18,7 +18,7 @@ Revision History: #include "muz/transforms/dl_mk_scale.h" #include "muz/base/dl_context.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { @@ -30,21 +30,21 @@ namespace datalog { public: scale_model_converter(ast_manager& m): m(m), m_trail(m), a(m) {} - virtual ~scale_model_converter() {} + ~scale_model_converter() override {} void add_new2old(func_decl* new_f, func_decl* old_f) { m_trail.push_back(old_f); m_trail.push_back(new_f); m_new2old.insert(new_f, old_f); } - - virtual void operator()(model_ref& md) { + + void get_units(obj_map& units) override { units.reset(); } + + void operator()(model_ref& md) override { model_ref old_model = alloc(model, m); - obj_map::iterator it = m_new2old.begin(); - obj_map::iterator end = m_new2old.end(); - for (; it != end; ++it) { - func_decl* old_p = it->m_value; - func_decl* new_p = it->m_key; + for (auto const& kv : m_new2old) { + func_decl* old_p = kv.m_value; + func_decl* new_p = kv.m_key; func_interp* old_fi = alloc(func_interp, m, old_p->get_arity()); if (new_p->get_arity() == 0) { @@ -74,7 +74,7 @@ namespace datalog { old_model->register_decl(old_p, old_fi); } } - + // register values that have not been scaled. unsigned sz = md->get_num_constants(); for (unsigned i = 0; i < sz; ++i) { @@ -95,10 +95,13 @@ namespace datalog { //TRACE("dl", model_smt2_pp(tout, m, *md, 0); ); } - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { UNREACHABLE(); - return 0; + return nullptr; } + + void display(std::ostream& out) override { out << "(scale-model-converter)\n"; } + }; @@ -108,15 +111,15 @@ namespace datalog { m_ctx(ctx), a(m), m_trail(m), - m_eqs(m) { + m_eqs(m) { } - mk_scale::~mk_scale() { + mk_scale::~mk_scale() { } - + rule_set * mk_scale::operator()(rule_set const & source) { if (!m_ctx.scale()) { - return 0; + return nullptr; } rule_manager& rm = source.get_rule_manager(); rule_set * result = alloc(rule_set, m_ctx); @@ -132,7 +135,7 @@ namespace datalog { } m_mc = smc.get(); - for (unsigned i = 0; i < sz; ++i) { + for (unsigned i = 0; i < sz; ++i) { rule & r = *source.get_rule(i); unsigned utsz = r.get_uninterpreted_tail_size(); unsigned tsz = r.get_tail_size(); @@ -154,10 +157,10 @@ namespace datalog { tail.push_back(a.mk_gt(m.mk_var(num_vars, a.mk_real()), a.mk_numeral(rational(0), false))); neg.resize(tail.size(), false); new_rule = rm.mk(new_pred, tail.size(), tail.c_ptr(), neg.c_ptr(), r.name(), true); - result->add_rule(new_rule); + result->add_rule(new_rule); if (source.is_output_predicate(r.get_decl())) { result->set_output_predicate(new_rule->get_decl()); - } + } } TRACE("dl", result->display(tout);); if (m_mc) { @@ -224,7 +227,7 @@ namespace datalog { a.is_lt(e) || a.is_gt(e)) { expr_ref_vector args(m); for (unsigned i = 0; i < ap->get_num_args(); ++i) { - args.push_back(linearize(sigma_idx, ap->get_arg(i))); + args.push_back(linearize(sigma_idx, ap->get_arg(i))); } result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr()); } diff --git a/src/muz/transforms/dl_mk_scale.h b/src/muz/transforms/dl_mk_scale.h index 10a5ea8b2..94090ec93 100644 --- a/src/muz/transforms/dl_mk_scale.h +++ b/src/muz/transforms/dl_mk_scale.h @@ -7,7 +7,7 @@ Module Name: Abstract: - Add scale factor to linear (Real) arithemetic Horn clauses. + Add scale factor to linear (Real) arithmetic Horn clauses. The transformation replaces occurrences of isolated constants by a scale multiplied to each constant. @@ -43,8 +43,8 @@ namespace datalog { app_ref mk_constraint(unsigned num_vars, app* q); public: mk_scale(context & ctx, unsigned priority = 33039); - virtual ~mk_scale(); - rule_set * operator()(rule_set const & source); + ~mk_scale() override; + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_separate_negated_tails.cpp b/src/muz/transforms/dl_mk_separate_negated_tails.cpp index 3da759f9b..c50aba827 100644 --- a/src/muz/transforms/dl_mk_separate_negated_tails.cpp +++ b/src/muz/transforms/dl_mk_separate_negated_tails.cpp @@ -127,7 +127,7 @@ namespace datalog { } } if (!has_new_rule) { - return 0; + return nullptr; } else { result->inherit_predicates(src); diff --git a/src/muz/transforms/dl_mk_separate_negated_tails.h b/src/muz/transforms/dl_mk_separate_negated_tails.h index a5cdda003..aae90b2db 100644 --- a/src/muz/transforms/dl_mk_separate_negated_tails.h +++ b/src/muz/transforms/dl_mk_separate_negated_tails.h @@ -51,7 +51,7 @@ namespace datalog { public: mk_separate_negated_tails(context& ctx, unsigned priority = 21000); - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; } diff --git a/src/muz/transforms/dl_mk_slice.cpp b/src/muz/transforms/dl_mk_slice.cpp index dc11935e3..8f3d5c220 100644 --- a/src/muz/transforms/dl_mk_slice.cpp +++ b/src/muz/transforms/dl_mk_slice.cpp @@ -155,8 +155,8 @@ namespace datalog { } bool translate_asserted(proof* p) { - expr* fact = 0; - rule* r = 0; + expr* fact = nullptr; + rule* r = nullptr; if (!m.is_asserted(p, fact)) { return false; } @@ -215,7 +215,7 @@ namespace datalog { proof* p1_new = m_new_proof.find(p1); expr* fact1 = m.get_fact(p1); TRACE("dl", tout << "fact1: " << mk_pp(fact1, m) << "\n";); - rule* orig1 = 0; + rule* orig1 = nullptr; if (!m_sliceform2rule.find(fact1, orig1)) { return false; } @@ -271,18 +271,21 @@ namespace datalog { m_renaming.insert(orig_rule, unsigned_vector(sz, renaming)); } - virtual void operator()(ast_manager& m, unsigned num_source, proof * const * source, proof_ref & result) { + proof_ref operator()(ast_manager& m, unsigned num_source, proof * const * source) override { SASSERT(num_source == 1); - result = source[0]; + proof_ref result(source[0], m); init_form2rule(); translate_proof(result); + return result; } - virtual proof_converter * translate(ast_translation & translator) { + proof_converter * translate(ast_translation & translator) override { UNREACHABLE(); // this would require implementing translation for the dl_context. - return 0; + return nullptr; } + + void display(std::ostream& out) override { out << "(slice-proof-converter)\n"; } }; class mk_slice::slice_model_converter : public model_converter { @@ -305,7 +308,9 @@ namespace datalog { m_sliceable.insert(f, bv); } - virtual void operator()(model_ref & md) { + void get_units(obj_map& units) override {} + + void operator()(model_ref & md) override { if (m_slice2old.empty()) { return; } @@ -391,11 +396,13 @@ namespace datalog { TRACE("dl", model_smt2_pp(tout, m, *md, 0); ); } - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { UNREACHABLE(); - return 0; + return nullptr; } + void display(std::ostream& out) override { out << "(slice-model-converter)\n"; } + }; mk_slice::mk_slice(context & ctx): @@ -405,8 +412,8 @@ namespace datalog { rm(ctx.get_rule_manager()), m_solved_vars(m), m_pinned(m), - m_pc(0), - m_mc(0) + m_pc(nullptr), + m_mc(nullptr) {} @@ -790,7 +797,7 @@ namespace datalog { tail.push_back(to_app(e)); } - new_rule = rm.mk(head.get(), tail.size(), tail.c_ptr(), (const bool*) 0, r.name()); + new_rule = rm.mk(head.get(), tail.size(), tail.c_ptr(), (const bool*) nullptr, r.name()); rm.fix_unbound_vars(new_rule, false); @@ -805,7 +812,7 @@ namespace datalog { dst.add_rule(new_rule.get()); if (m_pc) { - m_pc->insert(&r, new_rule.get(), 0, 0); + m_pc->insert(&r, new_rule.get(), 0, nullptr); } } @@ -819,7 +826,7 @@ namespace datalog { rule_manager& rm = m_ctx.get_rule_manager(); for (unsigned i = 0; i < src.get_num_rules(); ++i) { if (rm.has_quantifiers(*src.get_rule(i))) { - return 0; + return nullptr; } } ref spc; @@ -839,7 +846,7 @@ namespace datalog { if (m_predicates.empty()) { // nothing could be sliced. dealloc(result); - return 0; + return nullptr; } TRACE("dl", display(tout);); update_rules(src, *result); diff --git a/src/muz/transforms/dl_mk_slice.h b/src/muz/transforms/dl_mk_slice.h index 4a7d4a81a..0bc58c95a 100644 --- a/src/muz/transforms/dl_mk_slice.h +++ b/src/muz/transforms/dl_mk_slice.h @@ -100,9 +100,9 @@ namespace datalog { */ mk_slice(context & ctx); - virtual ~mk_slice() { } + ~mk_slice() override { } - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; func_decl* get_predicate(func_decl* p) { func_decl* q = p; m_predicates.find(p, q); return q; } diff --git a/src/muz/transforms/dl_mk_subsumption_checker.cpp b/src/muz/transforms/dl_mk_subsumption_checker.cpp index c6bb2864b..da41b4ba4 100644 --- a/src/muz/transforms/dl_mk_subsumption_checker.cpp +++ b/src/muz/transforms/dl_mk_subsumption_checker.cpp @@ -24,8 +24,8 @@ Revision History: #include "ast/rewriter/rewriter.h" #include "ast/rewriter/rewriter_def.h" #include "muz/transforms/dl_mk_subsumption_checker.h" -#include "muz/base/fixedpoint_params.hpp" -#include "tactic/extension_model_converter.h" +#include "muz/base/fp_params.hpp" +#include "tactic/generic_model_converter.h" namespace datalog { @@ -39,8 +39,8 @@ namespace datalog { bool mk_subsumption_checker::is_total_rule(const rule * r) { - if (r->get_tail_size() != 0) { - return false; + if (r->get_tail_size() != 0) { + return false; } unsigned pt_len = r->get_positive_tail_size(); @@ -113,7 +113,7 @@ namespace datalog { } - bool mk_subsumption_checker::transform_rule(rule * r, + bool mk_subsumption_checker::transform_rule(rule * r, rule_subsumption_index& subs_index, rule_ref & res) { unsigned u_len = r->get_uninterpreted_tail_size(); @@ -133,7 +133,7 @@ namespace datalog { if(m_total_relations.contains(tail_atom->get_decl()) || subs_index.is_subsumed(tail_atom)) { if(neg) { - //rule contains negated total relation, this means that it is unsatisfiable + //rule contains negated total relation, this means that it is unsatisfiable //and can be removed return false; } @@ -143,8 +143,8 @@ namespace datalog { } } if(!neg && head.get()==tail_atom) { - //rule contains its head positively in the tail, therefore - //it will never add any new facts to the relation, so it + //rule contains its head positively in the tail, therefore + //it will never add any new facts to the relation, so it //can be removed return false; } @@ -197,9 +197,9 @@ namespace datalog { if (m_total_relations.contains(head_pred)) { if (!orig.is_output_predicate(head_pred) || total_relations_with_included_rules.contains(head_pred)) { - //We just skip definitions of total non-output relations as + //We just skip definitions of total non-output relations as //we'll eliminate them from the problem. - //We also skip rules of total output relations for which we have + //We also skip rules of total output relations for which we have //already output the rule which implies their totality. modified = true; continue; @@ -241,9 +241,9 @@ namespace datalog { } tgt.inherit_predicates(orig); if (!m_total_relations.empty() && m_context.get_model_converter()) { - extension_model_converter* mc0 = alloc(extension_model_converter, m); + generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl-subsumption"); for (func_decl* p : m_total_relations) { - mc0->insert(p, m.mk_true()); + mc0->add(p, m.mk_true()); } m_context.add_model_converter(mc0); } @@ -286,7 +286,7 @@ namespace datalog { obj_hashtable * head_store; if(m_ground_unconditional_rule_heads.find(pred, head_store)) { //Some relations may receive facts by ground unconditioned rules. - //We scanned for those earlier, so now we check whether we cannot get a + //We scanned for those earlier, so now we check whether we cannot get a //better estimate of relation size from these. unsigned gnd_rule_cnt = head_store->size(); @@ -297,7 +297,7 @@ namespace datalog { SASSERT(total_size>=rel_sz); if(total_size==rel_sz) { - on_discovered_total_relation(pred, 0); + on_discovered_total_relation(pred, nullptr); } } next_pred:; @@ -334,8 +334,8 @@ namespace datalog { rule_set * mk_subsumption_checker::operator()(rule_set const & source) { // TODO mc - if (!m_context.get_params ().xform_subsumption_checker()) - return 0; + if (!m_context.get_params ().xform_subsumption_checker()) + return nullptr; m_have_new_total_rule = false; collect_ground_unconditional_rule_heads(source); @@ -348,7 +348,7 @@ namespace datalog { if (!m_have_new_total_rule && !modified) { dealloc(res); - return 0; + return nullptr; } @@ -366,6 +366,5 @@ namespace datalog { return res; } - -}; +}; diff --git a/src/muz/transforms/dl_mk_subsumption_checker.h b/src/muz/transforms/dl_mk_subsumption_checker.h index 01d828d6e..8c71096c3 100644 --- a/src/muz/transforms/dl_mk_subsumption_checker.h +++ b/src/muz/transforms/dl_mk_subsumption_checker.h @@ -80,11 +80,11 @@ namespace datalog { m_context(ctx), m_ref_holder(ctx.get_rule_manager()), m_new_total_relation_discovery_during_transformation(true) {} - ~mk_subsumption_checker() { + ~mk_subsumption_checker() override { reset_dealloc_values(m_ground_unconditional_rule_heads); } - virtual rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_unbound_compressor.cpp b/src/muz/transforms/dl_mk_unbound_compressor.cpp index 47ce20a76..7844c3144 100644 --- a/src/muz/transforms/dl_mk_unbound_compressor.cpp +++ b/src/muz/transforms/dl_mk_unbound_compressor.cpp @@ -346,7 +346,7 @@ namespace datalog { // TODO mc if (!m_context.compress_unbound()) { - return 0; + return nullptr; } m_modified = false; @@ -387,7 +387,7 @@ namespace datalog { } } - rule_set * result = static_cast(0); + rule_set * result = static_cast(nullptr); if (m_modified) { result = alloc(rule_set, m_context); unsigned fin_rule_cnt = m_rules.size(); diff --git a/src/muz/transforms/dl_mk_unbound_compressor.h b/src/muz/transforms/dl_mk_unbound_compressor.h index 6f53e0707..b37a2f1a7 100644 --- a/src/muz/transforms/dl_mk_unbound_compressor.h +++ b/src/muz/transforms/dl_mk_unbound_compressor.h @@ -86,7 +86,7 @@ namespace datalog { public: mk_unbound_compressor(context & ctx); - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_mk_unfold.h b/src/muz/transforms/dl_mk_unfold.h index 8ebe2d328..02469e6fb 100644 --- a/src/muz/transforms/dl_mk_unfold.h +++ b/src/muz/transforms/dl_mk_unfold.h @@ -44,7 +44,7 @@ namespace datalog { */ mk_unfold(context & ctx); - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/muz/transforms/dl_transforms.cpp b/src/muz/transforms/dl_transforms.cpp index 95b0f6cd6..d4b9506e8 100644 --- a/src/muz/transforms/dl_transforms.cpp +++ b/src/muz/transforms/dl_transforms.cpp @@ -35,7 +35,7 @@ Revision History: #include "muz/transforms/dl_mk_scale.h" #include "muz/transforms/dl_mk_array_eq_rewrite.h" #include "muz/transforms/dl_mk_array_instantiation.h" -#include "muz/base/fixedpoint_params.hpp" +#include "muz/base/fp_params.hpp" namespace datalog { @@ -59,7 +59,7 @@ namespace datalog { transf.register_plugin(alloc(datalog::mk_quantifier_instantiation, ctx, 37000)); if (ctx.get_params().datalog_subsumption()) { - transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 35005)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 35005)); } transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 35000)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34990)); @@ -67,20 +67,20 @@ namespace datalog { //and another round of inlining if (ctx.get_params().datalog_subsumption()) { - transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34975)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34975)); } transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34970)); transf.register_plugin(alloc(datalog::mk_coi_filter, ctx, 34960)); transf.register_plugin(alloc(datalog::mk_interp_tail_simplifier, ctx, 34950)); if (ctx.get_params().datalog_subsumption()) { - transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34940)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34930)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34920)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34910)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34900)); - transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34890)); - transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34880)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34940)); + transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34930)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34920)); + transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34910)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34900)); + transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34890)); + transf.register_plugin(alloc(datalog::mk_subsumption_checker, ctx, 34880)); } else { transf.register_plugin(alloc(datalog::mk_rule_inliner, ctx, 34930)); diff --git a/src/nlsat/nlsat_assignment.h b/src/nlsat/nlsat_assignment.h index 097ad76b6..cae15f4e1 100644 --- a/src/nlsat/nlsat_assignment.h +++ b/src/nlsat/nlsat_assignment.h @@ -66,9 +66,9 @@ namespace nlsat { void reset() { m_assigned.reset(); } bool is_assigned(var x) const { return m_assigned.get(x, false); } anum const & value(var x) const { return m_values[x]; } - virtual anum_manager & m() const { return am(); } - virtual bool contains(var x) const { return is_assigned(x); } - virtual anum const & operator()(var x) const { SASSERT(is_assigned(x)); return value(x); } + anum_manager & m() const override { return am(); } + bool contains(var x) const override { return is_assigned(x); } + anum const & operator()(var x) const override { SASSERT(is_assigned(x)); return value(x); } void swap(var x, var y) { SASSERT(x < m_values.size() && y < m_values.size()); std::swap(m_assigned[x], m_assigned[y]); @@ -95,9 +95,9 @@ namespace nlsat { var m_y; public: undef_var_assignment(assignment const & a, var y):m_assignment(a), m_y(y) {} - virtual anum_manager & m() const { return m_assignment.am(); } - virtual bool contains(var x) const { return x != m_y && m_assignment.is_assigned(x); } - virtual anum const & operator()(var x) const { return m_assignment.value(x); } + anum_manager & m() const override { return m_assignment.am(); } + bool contains(var x) const override { return x != m_y && m_assignment.is_assigned(x); } + anum const & operator()(var x) const override { return m_assignment.value(x); } }; }; diff --git a/src/nlsat/nlsat_clause.h b/src/nlsat/nlsat_clause.h index 898c32449..93dfb0e80 100644 --- a/src/nlsat/nlsat_clause.h +++ b/src/nlsat/nlsat_clause.h @@ -44,6 +44,8 @@ namespace nlsat { bool is_learned() const { return m_learned; } literal * begin() { return m_lits; } literal * end() { return m_lits + m_size; } + literal const * begin() const { return m_lits; } + literal const * end() const { return m_lits + m_size; } literal const * c_ptr() const { return m_lits; } void inc_activity() { m_activity++; } void set_activity(unsigned v) { m_activity = v; } diff --git a/src/nlsat/nlsat_explain.cpp b/src/nlsat/nlsat_explain.cpp index 3a6c3f067..a93935fb6 100644 --- a/src/nlsat/nlsat_explain.cpp +++ b/src/nlsat/nlsat_explain.cpp @@ -146,7 +146,7 @@ namespace nlsat { m_todo(u), m_core1(s), m_core2(s), - m_result(0), + m_result(nullptr), m_evaluator(ev) { m_simplify_cores = false; m_full_dimensional = false; @@ -216,9 +216,10 @@ namespace nlsat { max_var(p) must be assigned in the current interpretation. */ int sign(polynomial_ref const & p) { - TRACE("nlsat_explain", tout << "p: " << p << " var: " << max_var(p) << "\n";); SASSERT(max_var(p) == null_var || m_assignment.is_assigned(max_var(p))); - return m_am.eval_sign_at(p, m_assignment); + int s = m_am.eval_sign_at(p, m_assignment); + TRACE("nlsat_explain", tout << "p: " << p << " var: " << max_var(p) << " sign: " << s << "\n";); + return s; } /** @@ -242,7 +243,7 @@ namespace nlsat { } /** - \breif Store in ps the polynomials occurring in the given literals. + \brief Store in ps the polynomials occurring in the given literals. */ void collect_polys(unsigned num, literal const * ls, polynomial_ref_vector & ps) { ps.reset(); @@ -332,7 +333,7 @@ namespace nlsat { if (!is_zero(lc)) { if (sign(lc) != 0) return; - // lc is not the zero polynomial, but it vanished in the current interpretaion. + // lc is not the zero polynomial, but it vanished in the current interpretation. // so we keep searching... add_zero_assumption(lc); } @@ -521,7 +522,7 @@ namespace nlsat { polynomial::var max_var(literal l) { atom * a = m_atoms[l.var()]; - if (a != 0) + if (a != nullptr) return a->max_var(); else return null_var; @@ -535,7 +536,7 @@ namespace nlsat { for (unsigned i = 0; i < sz; i++) { literal l = ls[i]; atom * a = m_atoms[l.var()]; - if (a != 0) { + if (a != nullptr) { var x = a->max_var(); SASSERT(x != null_var); if (max == null_var || x > max) @@ -574,7 +575,7 @@ namespace nlsat { if (is_const(p)) return; if (m_factor) { - TRACE("nlsat_explain", tout << "adding factors of\n"; display(tout, p); tout << "\n";); + TRACE("nlsat_explain", display(tout << "adding factors of\n", p); tout << "\n";); factor(p, m_factors); polynomial_ref f(m_pm); for (unsigned i = 0; i < m_factors.size(); i++) { @@ -705,7 +706,7 @@ namespace nlsat { m_result = &result; add_root_literal(k, y, i, p); reset_already_added(); - m_result = 0; + m_result = nullptr; } void add_root_literal(atom::kind k, var y, unsigned i, poly * p) { @@ -1232,7 +1233,7 @@ namespace nlsat { This method selects the equation of minimal degree in max. */ poly * select_eq(scoped_literal_vector & C, var max) { - poly * r = 0; + poly * r = nullptr; unsigned min_d = UINT_MAX; unsigned sz = C.size(); for (unsigned i = 0; i < sz; i++) { @@ -1289,7 +1290,7 @@ namespace nlsat { if (y >= max) continue; atom * eq = m_x2eq[y]; - if (eq == 0) + if (eq == nullptr) continue; SASSERT(eq->is_ineq_atom()); SASSERT(to_ineq_atom(eq)->size() == 1); @@ -1305,7 +1306,7 @@ namespace nlsat { } } } - return 0; + return nullptr; } /** @@ -1315,7 +1316,7 @@ namespace nlsat { // Simplify using equations in the core while (!C.empty()) { poly * eq = select_eq(C, max); - if (eq == 0) + if (eq == nullptr) break; TRACE("nlsat_simplify_core", tout << "using equality for simplifying core\n"; m_pm.display(tout, eq, m_solver.display_proc()); tout << "\n";); @@ -1325,7 +1326,7 @@ namespace nlsat { // Simplify using equations using variables from lower stages. while (!C.empty()) { ineq_atom * eq = select_lower_stage_eq(C, max); - if (eq == 0) + if (eq == nullptr) break; SASSERT(eq->size() == 1); SASSERT(!eq->is_even(0)); @@ -1452,20 +1453,25 @@ namespace nlsat { SASSERT(check_already_added()); SASSERT(num > 0); TRACE("nlsat_explain", tout << "[explain] set of literals is infeasible in the current interpretation\n"; display(tout, num, ls);); - // exit(0); m_result = &result; process(num, ls); reset_already_added(); - m_result = 0; - TRACE("nlsat_explain", tout << "[explain] result\n"; display(tout, result);); + m_result = nullptr; + TRACE("nlsat_explain", display(tout << "[explain] result\n", result);); CASSERT("nlsat", check_already_added()); } void project(var x, unsigned num, literal const * ls, scoped_literal_vector & result) { + m_result = &result; svector lits; - TRACE("nlsat", tout << "project x" << x << "\n"; m_solver.display(tout);); + TRACE("nlsat", tout << "project x" << x << "\n"; + for (unsigned i = 0; i < num; ++i) { + m_solver.display(tout, ls[i]) << " "; + } + tout << "\n"; + m_solver.display(tout);); DEBUG_CODE( for (unsigned i = 0; i < num; ++i) { @@ -1495,21 +1501,28 @@ namespace nlsat { project(m_ps, mx_var); } reset_already_added(); - m_result = 0; + m_result = nullptr; if (x != mx_var) { m_solver.restore_order(); } } else { reset_already_added(); - m_result = 0; + m_result = nullptr; } for (unsigned i = 0; i < result.size(); ++i) { result.set(i, ~result[i]); } DEBUG_CODE( - for (unsigned i = 0; i < result.size(); ++i) { - SASSERT(l_true == m_solver.value(result[i])); + TRACE("nlsat", + for (literal l : result) { + m_solver.display(tout << " ", l); + } + tout << "\n"; + ); + for (literal l : result) { + CTRACE("nlsat", l_true != m_solver.value(l), m_solver.display(tout, l) << " " << m_solver.value(l) << "\n";); + SASSERT(l_true == m_solver.value(l)); }); } @@ -1620,21 +1633,21 @@ namespace nlsat { roots.reset(); m_am.isolate_roots(p, undef_var_assignment(m_assignment, x), roots); bool glb_valid = false, lub_valid = false; - for (unsigned j = 0; j < roots.size(); ++j) { - int s = m_am.compare(x_val, roots[j]); + for (auto const& r : roots) { + int s = m_am.compare(x_val, r); SASSERT(s != 0); + + if (s < 0 && (!lub_valid || m_am.lt(r, lub))) { + lub_index = i; + m_am.set(lub, r); + } + + if (s > 0 && (!glb_valid || m_am.lt(glb, r))) { + glb_index = i; + m_am.set(glb, r); + } lub_valid |= s < 0; glb_valid |= s > 0; - - if (s < 0 && m_am.lt(roots[j], lub)) { - lub_index = i; - m_am.set(lub, roots[j]); - } - - if (s > 0 && m_am.lt(glb, roots[j])) { - glb_index = i; - m_am.set(glb, roots[j]); - } } if (glb_valid) { ++num_glb; @@ -1643,6 +1656,7 @@ namespace nlsat { ++num_lub; } } + TRACE("nlsat_explain", tout << ps << "\n";); if (num_lub == 0) { project_plus_infinity(x, ps); @@ -1668,7 +1682,7 @@ namespace nlsat { unsigned d = degree(p, x); lc = m_pm.coeff(p, x, d); if (!is_const(lc)) { - unsigned s = sign(p); + int s = sign(p); SASSERT(s != 0); atom::kind k = (s > 0)?(atom::GT):(atom::LT); add_simple_assumption(k, lc); @@ -1683,7 +1697,8 @@ namespace nlsat { unsigned d = degree(p, x); lc = m_pm.coeff(p, x, d); if (!is_const(lc)) { - unsigned s = sign(p); + int s = sign(p); + TRACE("nlsat_explain", tout << "degree: " << d << " " << lc << " sign: " << s << "\n";); SASSERT(s != 0); atom::kind k; if (s > 0) { @@ -1698,6 +1713,7 @@ namespace nlsat { } void project_pairs(var x, unsigned idx, polynomial_ref_vector const& ps) { + TRACE("nlsat_explain", tout << "project pairs\n";); polynomial_ref p(m_pm); p = ps.get(idx); for (unsigned i = 0; i < ps.size(); ++i) { @@ -1722,11 +1738,13 @@ namespace nlsat { void solve_eq(var x, unsigned idx, polynomial_ref_vector const& ps) { polynomial_ref p(m_pm), A(m_pm), B(m_pm), C(m_pm), D(m_pm), E(m_pm), q(m_pm), r(m_pm); - polynomial_ref_vector qs(m_pm); + polynomial_ref_vector As(m_pm), Bs(m_pm); p = ps.get(idx); SASSERT(degree(p, x) == 1); A = m_pm.coeff(p, x, 1); B = m_pm.coeff(p, x, 0); + As.push_back(m_pm.mk_const(rational(1))); + Bs.push_back(m_pm.mk_const(rational(1))); B = neg(B); TRACE("nlsat_explain", tout << "p: " << p << " A: " << A << " B: " << B << "\n";); // x = B/A @@ -1737,20 +1755,21 @@ namespace nlsat { D = m_pm.mk_const(rational(1)); E = D; r = m_pm.mk_zero(); - for (unsigned j = 0; j <= d; ++j) { - qs.push_back(D); - D = D*A; + for (unsigned j = As.size(); j <= d; ++j) { + D = As.back(); As.push_back(A * D); + D = Bs.back(); Bs.push_back(B * D); } for (unsigned j = 0; j <= d; ++j) { // A^d*p0 + A^{d-1}*B*p1 + ... + B^j*A^{d-j}*pj + ... + B^d*p_d C = m_pm.coeff(q, x, j); + TRACE("nlsat_explain", tout << "coeff: q" << j << ": " << C << "\n";); if (!is_zero(C)) { - D = qs.get(d-j); + D = As.get(d - j); + E = Bs.get(j); r = r + D*E*C; } - E = E*B; } - TRACE("nlsat_explain", tout << "q: " << q << " r: " << r << "\n";); + TRACE("nlsat_explain", tout << "p: " << p << " q: " << q << " r: " << r << "\n";); ensure_sign(r); } else { diff --git a/src/nlsat/nlsat_interval_set.cpp b/src/nlsat/nlsat_interval_set.cpp index 090f94eeb..922440fea 100644 --- a/src/nlsat/nlsat_interval_set.cpp +++ b/src/nlsat/nlsat_interval_set.cpp @@ -122,7 +122,7 @@ namespace nlsat { } void interval_set_manager::del(interval_set * s) { - if (s == 0) + if (s == nullptr) return; unsigned num = s->m_num_intervals; unsigned obj_sz = interval_set::get_obj_size(num); @@ -270,9 +270,9 @@ namespace nlsat { interval_set * interval_set_manager::mk_union(interval_set const * s1, interval_set const * s2) { TRACE("nlsat_interval", tout << "mk_union\ns1: "; display(tout, s1); tout << "\ns2: "; display(tout, s2); tout << "\n";); - if (s1 == 0 || s1 == s2) + if (s1 == nullptr || s1 == s2) return const_cast(s2); - if (s2 == 0) + if (s2 == nullptr) return const_cast(s1); if (s1->m_full) return const_cast(s1); @@ -514,22 +514,22 @@ namespace nlsat { } bool interval_set_manager::is_full(interval_set const * s) { - if (s == 0) + if (s == nullptr) return false; return s->m_full == 1; } unsigned interval_set_manager::num_intervals(interval_set const * s) const { - if (s == 0) return 0; + if (s == nullptr) return 0; return s->m_num_intervals; } bool interval_set_manager::subset(interval_set const * s1, interval_set const * s2) { if (s1 == s2) return true; - if (s1 == 0) + if (s1 == nullptr) return true; - if (s2 == 0) + if (s2 == nullptr) return false; if (s2->m_full) return true; @@ -616,7 +616,7 @@ namespace nlsat { } bool interval_set_manager::set_eq(interval_set const * s1, interval_set const * s2) { - if (s1 == 0 || s2 == 0) + if (s1 == nullptr || s2 == nullptr) return s1 == s2; if (s1->m_full || s2->m_full) return s1->m_full == s2->m_full; @@ -625,7 +625,7 @@ namespace nlsat { } bool interval_set_manager::eq(interval_set const * s1, interval_set const * s2) { - if (s1 == 0 || s2 == 0) + if (s1 == nullptr || s2 == nullptr) return s1 == s2; if (s1->m_num_intervals != s2->m_num_intervals) return false; @@ -674,7 +674,7 @@ namespace nlsat { void interval_set_manager::peek_in_complement(interval_set const * s, bool is_int, anum & w, bool randomize) { SASSERT(!is_full(s)); - if (s == 0) { + if (s == nullptr) { if (randomize) { int num = m_rand() % 2 == 0 ? 1 : -1; #define MAX_RANDOM_DEN_K 4 @@ -744,7 +744,7 @@ namespace nlsat { } void interval_set_manager::display(std::ostream & out, interval_set const * s) const { - if (s == 0) { + if (s == nullptr) { out << "{}"; return; } diff --git a/src/nlsat/nlsat_interval_set.h b/src/nlsat/nlsat_interval_set.h index 363d48de9..22d04b86a 100644 --- a/src/nlsat/nlsat_interval_set.h +++ b/src/nlsat/nlsat_interval_set.h @@ -40,7 +40,7 @@ namespace nlsat { /** \brief Return the empty set. */ - interval_set * mk_empty() { return 0; } + interval_set * mk_empty() { return nullptr; } /** \brief Return a set of composed of a single interval. @@ -64,7 +64,7 @@ namespace nlsat { \brief Return true if s is the empty set. */ bool is_empty(interval_set const * s) { - return s == 0; + return s == nullptr; } /** diff --git a/src/nlsat/nlsat_justification.h b/src/nlsat/nlsat_justification.h index 13759e035..61c6f40e2 100644 --- a/src/nlsat/nlsat_justification.h +++ b/src/nlsat/nlsat_justification.h @@ -54,8 +54,8 @@ namespace nlsat { void * m_data; public: enum kind { NULL_JST = 0, DECISION, CLAUSE, LAZY }; - justification():m_data(TAG(void *, static_cast(0), NULL_JST)) { SASSERT(is_null()); } - justification(bool):m_data(TAG(void *, static_cast(0), DECISION)) { SASSERT(is_decision()); } + justification():m_data(TAG(void *, nullptr, NULL_JST)) { SASSERT(is_null()); } + justification(bool):m_data(TAG(void *, nullptr, DECISION)) { SASSERT(is_decision()); } justification(clause * c):m_data(TAG(void *, c, CLAUSE)) { SASSERT(is_clause()); } justification(lazy_justification * j):m_data(TAG(void *, j, LAZY)) { SASSERT(is_lazy()); } kind get_kind() const { return static_cast(GET_TAG(m_data)); } diff --git a/src/nlsat/nlsat_params.pyg b/src/nlsat/nlsat_params.pyg index 6b1577113..0f2e03069 100644 --- a/src/nlsat/nlsat_params.pyg +++ b/src/nlsat/nlsat_params.pyg @@ -10,6 +10,7 @@ def_module_params('nlsat', ('randomize', BOOL, True, "randomize selection of a witness in nlsat."), ('max_conflicts', UINT, UINT_MAX, "maximum number of conflicts."), ('shuffle_vars', BOOL, False, "use a random variable order."), + ('inline_vars', BOOL, False, "inline variables that can be isolated from equations"), ('seed', UINT, 0, "random seed."), ('factor', BOOL, True, "factor polynomials produced during conflict resolution.") )) diff --git a/src/nlsat/nlsat_scoped_literal_vector.h b/src/nlsat/nlsat_scoped_literal_vector.h index 61760f06d..dffaa169f 100644 --- a/src/nlsat/nlsat_scoped_literal_vector.h +++ b/src/nlsat/nlsat_scoped_literal_vector.h @@ -34,9 +34,8 @@ namespace nlsat { bool empty() const { return m_lits.empty(); } literal operator[](unsigned i) const { return m_lits[i]; } void reset() { - unsigned sz = m_lits.size(); - for (unsigned i = 0; i < sz; i++) { - m_solver.dec_ref(m_lits[i]); + for (literal l : m_lits) { + m_solver.dec_ref(l); } m_lits.reset(); } @@ -50,6 +49,8 @@ namespace nlsat { m_lits[i] = l; } literal const * c_ptr() const { return m_lits.c_ptr(); } + literal const * begin() const { return m_lits.begin(); } + literal const * end() const { return m_lits.end(); } void shrink(unsigned new_sz) { SASSERT(new_sz <= m_lits.size()); unsigned sz = m_lits.size(); diff --git a/src/nlsat/nlsat_solver.cpp b/src/nlsat/nlsat_solver.cpp index e1dadf12b..f430f3a0c 100644 --- a/src/nlsat/nlsat_solver.cpp +++ b/src/nlsat/nlsat_solver.cpp @@ -18,19 +18,20 @@ Author: Revision History: --*/ +#include "util/z3_exception.h" +#include "util/chashtable.h" +#include "util/id_gen.h" +#include "util/map.h" +#include "util/dependency.h" +#include "util/permutation.h" +#include "math/polynomial/algebraic_numbers.h" +#include "math/polynomial/polynomial_cache.h" #include "nlsat/nlsat_solver.h" #include "nlsat/nlsat_clause.h" #include "nlsat/nlsat_assignment.h" #include "nlsat/nlsat_justification.h" #include "nlsat/nlsat_evaluator.h" #include "nlsat/nlsat_explain.h" -#include "math/polynomial/algebraic_numbers.h" -#include "util/z3_exception.h" -#include "util/chashtable.h" -#include "util/id_gen.h" -#include "util/dependency.h" -#include "math/polynomial/polynomial_cache.h" -#include "util/permutation.h" #include "nlsat/nlsat_params.hpp" #define NLSAT_EXTRA_VERBOSE @@ -68,6 +69,7 @@ namespace nlsat { reslimit& m_rlimit; small_object_allocator m_allocator; + bool m_incremental; unsynch_mpq_manager m_qm; pmanager m_pm; cache m_cache; @@ -78,6 +80,8 @@ namespace nlsat { interval_set_manager & m_ism; ineq_atom_table m_ineq_atoms; root_atom_table m_root_atoms; + svector m_patch_var; + polynomial_ref_vector m_patch_num, m_patch_denom; id_gen m_cid_gen; clause_vector m_clauses; // set of clauses @@ -85,7 +89,7 @@ namespace nlsat { unsigned m_num_bool_vars; atom_vector m_atoms; // bool_var -> atom - svector m_bvalues; // boolean assigment + svector m_bvalues; // boolean assignment unsigned_vector m_levels; // bool_var -> level svector m_justifications; vector m_bwatches; // bool_var (that are not attached to atoms) -> clauses where it is maximal @@ -106,13 +110,14 @@ namespace nlsat { display_var_proc const * m_proc; // display external var ids perm_display_var_proc(var_vector & perm): m_perm(perm), - m_proc(0) { + m_proc(nullptr) { } - virtual void operator()(std::ostream & out, var x) const { - if (m_proc == 0) + std::ostream& operator()(std::ostream & out, var x) const override { + if (m_proc == nullptr) m_default_display_var(out, x); else (*m_proc)(out, m_perm[x]); + return out; } }; perm_display_var_proc m_display_var; @@ -149,6 +154,7 @@ namespace nlsat { bool m_randomize; bool m_random_order; unsigned m_random_seed; + bool m_inline_vars; unsigned m_max_conflicts; // statistics @@ -158,9 +164,10 @@ namespace nlsat { unsigned m_stages; unsigned m_irrational_assignments; // number of irrational witnesses - imp(solver& s, reslimit& rlim, params_ref const & p): + imp(solver& s, reslimit& rlim, params_ref const & p, bool incremental): m_rlimit(rlim), m_allocator("nlsat"), + m_incremental(incremental), m_pm(rlim, m_qm, &m_allocator), m_cache(m_pm), m_am(rlim, m_qm, p, &m_allocator), @@ -168,6 +175,8 @@ namespace nlsat { m_assignment(m_am), m_evaluator(s, m_assignment, m_pm, m_allocator), m_ism(m_evaluator.ism()), + m_patch_num(m_pm), + m_patch_denom(m_pm), m_num_bool_vars(0), m_display_var(m_perm), m_explain(s, m_assignment, m_cache, m_atoms, m_var2eq, m_evaluator), @@ -188,7 +197,7 @@ namespace nlsat { bool_var b = mk_bool_var(); SASSERT(b == true_bool_var); literal true_lit(b, false); - mk_clause(1, &true_lit, false, 0); + mk_clause(1, &true_lit, false, nullptr); } void updt_params(params_ref const & _p) { @@ -202,6 +211,7 @@ namespace nlsat { m_max_conflicts = p.max_conflicts(); m_random_order = p.shuffle_vars(); m_random_seed = p.seed(); + m_inline_vars = p.inline_vars(); m_ism.set_seed(m_random_seed); m_explain.set_simplify_cores(m_simplify_cores); m_explain.set_minimize_cores(min_cores); @@ -249,18 +259,18 @@ namespace nlsat { void dec_ref(assumption) {} void inc_ref(_assumption_set a) { - if (a != 0) m_asm.inc_ref(a); + if (a != nullptr) m_asm.inc_ref(a); } void dec_ref(_assumption_set a) { - if (a != 0) m_asm.dec_ref(a); + if (a != nullptr) m_asm.dec_ref(a); } void inc_ref(bool_var b) { TRACE("ref", tout << "inc: " << b << "\n";); if (b == null_bool_var) return; - if (m_atoms[b] == 0) + if (m_atoms[b] == nullptr) return; m_atoms[b]->inc_ref(); } @@ -274,7 +284,7 @@ namespace nlsat { if (b == null_bool_var) return; atom * a = m_atoms[b]; - if (a == 0) + if (a == nullptr) return; SASSERT(a->ref_count() > 0); a->dec_ref(); @@ -330,9 +340,7 @@ namespace nlsat { */ bool_var max_bvar(clause const & cls) const { bool_var b = null_bool_var; - unsigned sz = cls.size(); - for (unsigned i = 0; i < sz; i++) { - literal l = cls[i]; + for (literal l : cls) { if (b == null_bool_var || l.var() > b) b = l.var(); } @@ -367,11 +375,9 @@ namespace nlsat { if (x == null_var) return 0; unsigned max = 0; - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - literal l = c[i]; + for (literal l : c) { atom const * a = m_atoms[l.var()]; - if (a == 0) + if (a == nullptr) continue; unsigned d = degree(a); if (d > max) @@ -389,7 +395,7 @@ namespace nlsat { bool_var mk_bool_var_core() { bool_var b = m_bid_gen.mk(); m_num_bool_vars++; - m_atoms .setx(b, 0, 0); + m_atoms .setx(b, nullptr, nullptr); m_bvalues .setx(b, l_undef, l_undef); m_levels .setx(b, UINT_MAX, UINT_MAX); m_justifications.setx(b, null_justification, null_justification); @@ -423,7 +429,7 @@ namespace nlsat { void vars(literal l, var_vector& vs) { vs.reset(); atom * a = m_atoms[l.var()]; - if (a == 0) { + if (a == nullptr) { } else if (a->is_ineq_atom()) { @@ -463,7 +469,7 @@ namespace nlsat { //SASSERT(m_bvalues[b] == l_undef); m_num_bool_vars--; m_dead[b] = true; - m_atoms[b] = 0; + m_atoms[b] = nullptr; m_bid_gen.recycle(b); } @@ -486,8 +492,9 @@ namespace nlsat { } void del(atom * a) { - if (a == 0) + if (a == nullptr) return ; + TRACE("nlsat", display(tout << "del: b" << a->m_bool_var << " ", *a) << "\n";); if (a->is_ineq_atom()) del(to_ineq_atom(a)); else @@ -496,13 +503,12 @@ namespace nlsat { // Delete atoms with ref_count == 0 void del_unref_atoms() { - unsigned sz = m_atoms.size(); - for (unsigned i = 0; i < sz; i++) { - del(m_atoms[i]); + for (auto* a : m_atoms) { + del(a); } } - bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { + ineq_atom* mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even, bool& is_new) { SASSERT(sz >= 1); SASSERT(k == atom::LT || k == atom::GT || k == atom::EQ); int sign = 1; @@ -522,23 +528,37 @@ namespace nlsat { void * mem = m_allocator.allocate(ineq_atom::get_obj_size(sz)); if (sign < 0) k = atom::flip(k); - ineq_atom * new_atom = new (mem) ineq_atom(k, sz, uniq_ps.c_ptr(), is_even, max); + ineq_atom * tmp_atom = new (mem) ineq_atom(k, sz, uniq_ps.c_ptr(), is_even, max); TRACE("nlsat_table_bug", ineq_atom::hash_proc h; - tout << "mk_ineq_atom hash: " << h(new_atom) << "\n"; display(tout, *new_atom, m_display_var); tout << "\n";); - ineq_atom * old_atom = m_ineq_atoms.insert_if_not_there(new_atom); - CTRACE("nlsat_table_bug", old_atom->max_var() != max, display(tout, *old_atom, m_display_var); tout << "\n";); - SASSERT(old_atom->max_var() == max); - if (old_atom != new_atom) { - deallocate(new_atom); - return old_atom->bvar(); + tout << "mk_ineq_atom hash: " << h(tmp_atom) << "\n"; display(tout, *tmp_atom, m_display_var); tout << "\n";); + ineq_atom * atom = m_ineq_atoms.insert_if_not_there(tmp_atom); + CTRACE("nlsat_table_bug", atom->max_var() != max, display(tout, *atom, m_display_var); tout << "\n";); + SASSERT(atom->max_var() == max); + is_new = (atom == tmp_atom); + if (is_new) { + for (unsigned i = 0; i < sz; i++) { + m_pm.inc_ref(atom->p(i)); + } } - bool_var b = mk_bool_var_core(); - m_atoms[b] = new_atom; - new_atom->m_bool_var = b; - for (unsigned i = 0; i < sz; i++) { - m_pm.inc_ref(new_atom->p(i)); + else { + deallocate(tmp_atom); + } + return atom; + } + + bool_var mk_ineq_atom(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { + bool is_new = false; + ineq_atom* atom = mk_ineq_atom(k, sz, ps, is_even, is_new); + if (!is_new) { + return atom->bvar(); + } + else { + bool_var b = mk_bool_var_core(); + m_atoms[b] = atom; + atom->m_bool_var = b; + TRACE("nlsat", display(tout << "create: b" << atom->m_bool_var << " ", *atom) << "\n";); + return b; } - return b; } literal mk_ineq_literal(atom::kind k, unsigned sz, poly * const * ps, bool const * is_even) { @@ -619,10 +639,14 @@ namespace nlsat { deallocate(cls); } + void del_clause(clause * cls, clause_vector& clauses) { + clauses.erase(cls); + del_clause(cls); + } + void del_clauses(ptr_vector & cs) { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) - del_clause(cs[i]); + for (clause* cp : cs) + del_clause(cp); } void del_clauses() { @@ -642,11 +666,11 @@ namespace nlsat { bool operator()(literal l1, literal l2) const { atom * a1 = m.m_atoms[l1.var()]; atom * a2 = m.m_atoms[l2.var()]; - if (a1 == 0 && a2 == 0) + if (a1 == nullptr && a2 == nullptr) return l1.index() < l2.index(); - if (a1 == 0) + if (a1 == nullptr) return true; - if (a2 == 0) + if (a2 == nullptr) return false; var x1 = a1->max_var(); var x2 = a2->max_var(); @@ -690,8 +714,8 @@ namespace nlsat { void mk_clause(unsigned num_lits, literal const * lits, assumption a) { SASSERT(num_lits > 0); - _assumption_set as = 0; - if (a != 0) + _assumption_set as = nullptr; + if (a != nullptr) as = m_asm.mk_leaf(a); mk_clause(num_lits, lits, false, as); } @@ -727,7 +751,7 @@ namespace nlsat { m_levels[b] = UINT_MAX; del_jst(m_allocator, m_justifications[b]); m_justifications[b] = null_justification; - if (m_atoms[b] == 0 && b < m_bk) + if (m_atoms[b] == nullptr && b < m_bk) m_bk = b; } @@ -873,7 +897,7 @@ namespace nlsat { \brief Assign literal using the given justification */ void assign(literal l, justification j) { - TRACE("nlsat", tout << "assigning literal:\n"; display(tout, l); + TRACE("nlsat", tout << "assigning literal: "; display(tout, l); tout << "\njustification kind: " << j.get_kind() << "\n";); SASSERT(assigned_value(l) == l_undef); SASSERT(j != null_justification); @@ -904,12 +928,13 @@ namespace nlsat { */ lbool value(literal l) { lbool val = assigned_value(l); + TRACE("nlsat_verbose", display(tout << " assigned value " << val << " for ", l) << "\n";); if (val != l_undef) { return val; } bool_var b = l.var(); atom * a = m_atoms[b]; - if (a == 0) { + if (a == nullptr) { return l_undef; } var max = a->max_var(); @@ -919,7 +944,7 @@ namespace nlsat { val = to_lbool(m_evaluator.eval(a, l.sign())); TRACE("value_bug", tout << "value of: "; display(tout, l); tout << " := " << val << "\n"; tout << "xk: " << m_xk << ", a->max_var(): " << a->max_var() << "\n"; - display_assignment(tout);); + display_assignment(tout);); return val; } @@ -927,10 +952,9 @@ namespace nlsat { \brief Return true if the given clause is already satisfied in the current partial interpretation. */ bool is_satisfied(clause const & cls) const { - unsigned sz = cls.size(); - for (unsigned i = 0; i < sz; i++) { - if (const_cast(this)->value(cls[i]) == l_true) { - TRACE("value_bug:", tout << cls[i] << " := true\n";); + for (literal l : cls) { + if (const_cast(this)->value(l) == l_true) { + TRACE("value_bug:", tout << l << " := true\n";); return true; } } @@ -1016,21 +1040,21 @@ namespace nlsat { if (m_bvalues[b] != l_true) return; atom * a = m_atoms[b]; - if (a == 0 || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0)) + if (a == nullptr || a->get_kind() != atom::EQ || to_ineq_atom(a)->size() > 1 || to_ineq_atom(a)->is_even(0)) return; var x = m_xk; SASSERT(a->max_var() == x); SASSERT(x != null_var); if (m_var2eq[x] != 0 && degree(m_var2eq[x]) <= degree(a)) return; // we only update m_var2eq if the new equality has smaller degree - TRACE("simplify_core", tout << "Saving equality for "; m_display_var(tout, x); tout << " (x" << x << ") "; + TRACE("simplify_core", tout << "Saving equality for "; m_display_var(tout, x) << " (x" << x << ") "; tout << "scope-lvl: " << scope_lvl() << "\n"; display(tout, literal(b, false)); tout << "\n";); save_updt_eq_trail(m_var2eq[x]); m_var2eq[x] = a; } /** - \brief Process a clause that contains nonlinar arithmetic literals + \brief Process a clause that contains nonlinear arithmetic literals If satisfy_learned is true, then learned clauses are satisfied even if m_lazy > 0 */ @@ -1045,10 +1069,9 @@ namespace nlsat { interval_set_ref first_undef_set(m_ism); // infeasible region of the first undefined literal interval_set * xk_set = m_infeasible[m_xk]; // current set of infeasible interval for current variable SASSERT(!m_ism.is_full(xk_set)); - unsigned sz = cls.size(); - for (unsigned i = 0; i < sz; i++) { + for (unsigned idx = 0; idx < cls.size(); ++idx) { + literal l = cls[idx]; checkpoint(); - literal l = cls[i]; if (value(l) == l_false) continue; SASSERT(value(l) == l_undef); @@ -1062,13 +1085,13 @@ namespace nlsat { TRACE("nlsat_inf_set", tout << "infeasible set for literal: "; display(tout, l); tout << "\n"; m_ism.display(tout, curr_set); tout << "\n";); if (m_ism.is_empty(curr_set)) { TRACE("nlsat_inf_set", tout << "infeasible set is empty, found literal\n";); - R_propagate(l, 0); + R_propagate(l, nullptr); SASSERT(is_satisfied(cls)); return true; } if (m_ism.is_full(curr_set)) { TRACE("nlsat_inf_set", tout << "infeasible set is R, skip literal\n";); - R_propagate(~l, 0); + R_propagate(~l, nullptr); continue; } if (m_ism.subset(curr_set, xk_set)) { @@ -1085,7 +1108,7 @@ namespace nlsat { } num_undef++; if (first_undef == UINT_MAX) { - first_undef = i; + first_undef = idx; first_undef_set = curr_set; } } @@ -1131,13 +1154,11 @@ namespace nlsat { Return 0, if the set was satisfied, or the violating clause otherwise */ clause * process_clauses(clause_vector const & cs) { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - clause * c = cs[i]; + for (clause* c : cs) { if (!process_clause(*c, false)) return c; } - return 0; // succeeded + return nullptr; // succeeded } /** @@ -1175,7 +1196,7 @@ namespace nlsat { m_ism.peek_in_complement(m_infeasible[m_xk], m_is_int[m_xk], w, m_randomize); TRACE("nlsat", tout << "infeasible intervals: "; m_ism.display(tout, m_infeasible[m_xk]); tout << "\n"; - tout << "assigning "; m_display_var(tout, m_xk); tout << "(x" << m_xk << ") -> " << w << "\n";); + tout << "assigning "; m_display_var(tout, m_xk) << "(x" << m_xk << ") -> " << w << "\n";); TRACE("nlsat_root", tout << "value as root object: "; m_am.display_root(tout, w); tout << "\n";); if (!m_am.is_rational(w)) m_irrational_assignments++; @@ -1205,6 +1226,7 @@ namespace nlsat { TRACE("nlsat_bug", tout << "xk: x" << m_xk << " bk: b" << m_bk << "\n";); if (m_bk == null_bool_var && m_xk >= num_vars()) { TRACE("nlsat", tout << "found model\n"; display_assignment(tout);); + fix_patch(); return l_true; // all variables were assigned, and all clauses were satisfied. } TRACE("nlsat", tout << "processing variable "; @@ -1216,7 +1238,7 @@ namespace nlsat { conflict_clause = process_clauses(m_bwatches[m_bk]); else conflict_clause = process_clauses(m_watches[m_xk]); - if (conflict_clause == 0) + if (conflict_clause == nullptr) break; if (!resolve(*conflict_clause)) return l_false; @@ -1280,7 +1302,7 @@ namespace nlsat { m_lemma.push_back(~mk_ineq_literal(atom::LT, 1, &p2, &is_even)); // perform branch and bound - clause * cls = mk_clause(m_lemma.size(), m_lemma.c_ptr(), false, 0); + clause * cls = mk_clause(m_lemma.size(), m_lemma.c_ptr(), false, nullptr); if (cls) { TRACE("nlsat", display(tout << "conflict " << lo << " " << hi, *cls); tout << "\n";); } @@ -1295,6 +1317,10 @@ namespace nlsat { init_search(); m_explain.set_full_dimensional(is_full_dimensional()); bool reordered = false; + + if (!m_incremental && m_inline_vars) + simplify(); + if (!can_reorder()) { } @@ -1380,7 +1406,7 @@ namespace nlsat { unsigned sz = assumptions.size(); literal const* ptr = assumptions.c_ptr(); _assumption_set asms = static_cast<_assumption_set>(c.assumptions()); - if (asms == 0) { + if (asms == nullptr) { return false; } vector deps; @@ -1497,9 +1523,9 @@ namespace nlsat { TRACE("nlsat_mathematica", display_mathematica_lemma(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr());); TRACE("nlsat_proof_sk", tout << "theory lemma\n"; display_abst(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr()); tout << "\n";); TRACE("nlsat_resolve", - tout << "m_xk: " << m_xk << ", "; m_display_var(tout, m_xk); tout << "\n"; + tout << "m_xk: " << m_xk << ", "; m_display_var(tout, m_xk) << "\n"; tout << "new valid clause:\n"; - display(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr()); tout << "\n";); + display(tout, m_lazy_clause.size(), m_lazy_clause.c_ptr()) << "\n";); DEBUG_CODE({ unsigned sz = m_lazy_clause.size(); @@ -1546,7 +1572,7 @@ namespace nlsat { max = lvl; } else { - // l must be a literal from a previous stage that is false in the current intepretation + // l must be a literal from a previous stage that is false in the current interpretation SASSERT(assigned_value(l) == l_undef); SASSERT(max_var(b) != null_var); SASSERT(m_xk != null_var); @@ -1633,19 +1659,14 @@ namespace nlsat { TRACE("nlsat_proof", tout << "STARTING RESOLUTION\n";); TRACE("nlsat_proof_sk", tout << "STARTING RESOLUTION\n";); m_conflicts++; - TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause); tout << "\n"; + TRACE("nlsat", tout << "resolve, conflicting clause:\n"; display(tout, *conflict_clause) << "\n"; tout << "xk: "; if (m_xk != null_var) m_display_var(tout, m_xk); else tout << ""; tout << "\n"; tout << "scope_lvl: " << scope_lvl() << "\n"; tout << "current assignment\n"; display_assignment(tout);); - - // static unsigned counter = 0; - // counter++; - // if (counter > 6) - // exit(0); m_num_marks = 0; m_lemma.reset(); - m_lemma_assumptions = 0; + m_lemma_assumptions = nullptr; resolve_clause(null_bool_var, *conflict_clause); @@ -1739,7 +1760,7 @@ namespace nlsat { // in a previous scope level. We may backjump many decisions. // unsigned sz = m_lemma.size(); - clause * new_cls = 0; + clause * new_cls = nullptr; if (!found_decision) { // Case 1) // We just have to find the maximal variable in m_lemma, and return to that stage @@ -1750,7 +1771,7 @@ namespace nlsat { SASSERT(m_xk == new_max_var); new_cls = mk_clause(sz, m_lemma.c_ptr(), true, m_lemma_assumptions.get()); TRACE("nlsat", tout << "new_level: " << scope_lvl() << "\nnew_stage: " << new_max_var << " "; - if (new_max_var != null_var) m_display_var(tout, new_max_var); tout << "\n";); + if (new_max_var != null_var) m_display_var(tout, new_max_var) << "\n";); } else { SASSERT(scope_lvl() >= 1); @@ -1872,7 +1893,7 @@ namespace nlsat { // ----------------------- // - // Variable reodering + // Variable reordering // // ----------------------- @@ -1906,7 +1927,7 @@ namespace nlsat { void collect(literal l) { bool_var b = l.var(); atom * a = m_atoms[b]; - if (a == 0) + if (a == nullptr) return; if (a->is_ineq_atom()) { unsigned sz = to_ineq_atom(a)->size(); @@ -1931,11 +1952,12 @@ namespace nlsat { collect(*(cs[i])); } - void display(std::ostream & out, display_var_proc const & proc) { + std::ostream& display(std::ostream & out, display_var_proc const & proc) { unsigned sz = m_num_occs.size(); for (unsigned i = 0; i < sz; i++) { proc(out, i); out << " -> " << m_max_degree[i] << " : " << m_num_occs[i] << "\n"; } + return out; } }; @@ -1992,9 +2014,9 @@ namespace nlsat { } bool can_reorder() const { - for (unsigned i = 0; i < m_atoms.size(); ++i) { - if (m_atoms[i]) { - if (m_atoms[i]->is_root_atom()) return false; + for (atom * a : m_atoms) { + if (a) { + if (a->is_root_atom()) return false; } } return true; @@ -2081,16 +2103,13 @@ namespace nlsat { \brief After variable reordering some lemmas containing root atoms may be ill-formed. */ void del_ill_formed_lemmas() { - unsigned sz = m_learned.size(); unsigned j = 0; - for (unsigned i = 0; i < sz; i++) { - clause * c = m_learned[i]; + for (clause* c : m_learned) { if (ill_formed(*c)) { del_clause(c); } else { - m_learned[j] = c; - j++; + m_learned[j++] = c; } } m_learned.shrink(j); @@ -2100,11 +2119,10 @@ namespace nlsat { \brief Return true if the clause contains an ill formed root atom */ bool ill_formed(clause const & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - bool_var b = c[i].var(); + for (literal lit : c) { + bool_var b = lit.var(); atom * a = m_atoms[b]; - if (a == 0) + if (a == nullptr) continue; if (a->is_ineq_atom()) continue; @@ -2120,26 +2138,23 @@ namespace nlsat { void reinit_cache() { reinit_cache(m_clauses); reinit_cache(m_learned); - for (unsigned i = 0; i < m_atoms.size(); ++i) { - reinit_cache(m_atoms[i]); - } + for (atom* a : m_atoms) + reinit_cache(a); } void reinit_cache(clause_vector const & cs) { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) - reinit_cache(*(cs[i])); + for (clause* c : cs) + reinit_cache(*c); } void reinit_cache(clause const & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) - reinit_cache(c[i]); + for (literal l : c) + reinit_cache(l); } void reinit_cache(literal l) { bool_var b = l.var(); reinit_cache(m_atoms[b]); } void reinit_cache(atom* a) { - if (a == 0) { + if (a == nullptr) { } else if (a->is_ineq_atom()) { @@ -2169,12 +2184,10 @@ namespace nlsat { } void reattach_arith_clauses(clause_vector const & cs) { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - clause & c = *cs[i]; - var x = max_var(c); + for (clause* cp : cs) { + var x = max_var(*cp); if (x != null_var) - m_watches[x].push_back(&c); + m_watches[x].push_back(cp); } } @@ -2233,7 +2246,7 @@ namespace nlsat { bool is_full_dimensional(literal l) const { atom * a = m_atoms[l.var()]; - if (a == 0) + if (a == nullptr) return true; switch (a->get_kind()) { case atom::EQ: return l.sign(); @@ -2251,18 +2264,16 @@ namespace nlsat { } bool is_full_dimensional(clause const & c) const { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - if (!is_full_dimensional(c[i])) + for (literal l : c) { + if (!is_full_dimensional(l)) return false; } return true; } bool is_full_dimensional(clause_vector const & cs) const { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - if (!is_full_dimensional(*(cs[i]))) + for (clause* c : cs) { + if (!is_full_dimensional(*c)) return false; } return true; @@ -2272,13 +2283,271 @@ namespace nlsat { return is_full_dimensional(m_clauses); } + + // ----------------------- + // + // Simplification + // + // ----------------------- + + // solve simple equalities + // TBD WU-Reit decomposition? + + /** + \brief isolate variables in unit equalities. + Assume a clause is c == v*p + q + and the context implies p > 0 + + replace v by -q/p + remove clause c, + The for other occurrences of v, + replace v*r + v*v*r' > 0 by + by p*p*v*r + p*p*v*v*r' > 0 + by p*q*r + q*q*r' > 0 + + The method ignores lemmas and assumes constraints don't use roots. + */ + + void simplify() { + polynomial_ref p(m_pm), q(m_pm); + var v; + init_var_signs(); + SASSERT(m_learned.empty()); + bool change = true; + while (change) { + change = false; + for (clause* c : m_clauses) { + if (solve_var(*c, v, p, q)) { + q = -q; + m_patch_var.push_back(v); + m_patch_num.push_back(q); + m_patch_denom.push_back(p); + del_clause(c, m_clauses); + substitute_var(v, p, q); + change = true; + break; + } + } + } + } + + void fix_patch() { + for (unsigned i = m_patch_var.size(); i-- > 0; ) { + var v = m_patch_var[i]; + poly* q = m_patch_num.get(i); + poly* p = m_patch_denom.get(i); + scoped_anum pv(m_am), qv(m_am), val(m_am); + m_pm.eval(p, m_assignment, pv); + m_pm.eval(q, m_assignment, qv); + val = qv / pv; + TRACE("nlsat", + m_display_var(tout << "patch ", v) << "\n"; + if (m_assignment.is_assigned(v)) m_am.display(tout << "previous value: ", m_assignment.value(v)); tout << "\n"; + m_am.display(tout << "updated value: ", val); tout << "\n"; + ); + m_assignment.set_core(v, val); + } + } + + void substitute_var(var x, poly* p, poly* q) { + polynomial_ref pr(m_pm); + polynomial_ref_vector ps(m_pm); + u_map b2l; + svector even; + unsigned num_atoms = m_atoms.size(); + for (unsigned j = 0; j < num_atoms; ++j) { + atom* a = m_atoms[j]; + if (a && a->is_ineq_atom()) { + ineq_atom const& a1 = *to_ineq_atom(a); + unsigned sz = a1.size(); + ps.reset(); + even.reset(); + bool change = false; + for (unsigned i = 0; i < sz; ++i) { + poly * po = a1.p(i); + m_pm.substitute(po, x, q, p, pr); + ps.push_back(pr); + even.push_back(a1.is_even(i)); + change |= pr != po; + if (m_pm.is_zero(pr)) { + ps.reset(); + even.reset(); + change = true; + break; + } + } + if (!change) continue; + literal l = mk_ineq_literal(a1.get_kind(), ps.size(), ps.c_ptr(), even.c_ptr()); + if (a1.m_bool_var != l.var()) { + b2l.insert(a1.m_bool_var, l); + inc_ref(l); + } + } + } + update_clauses(b2l); + for (auto const& kv : b2l) { + dec_ref(kv.m_value); + } + } + + + void update_clauses(u_map const& b2l) { + literal_vector lits; + clause_vector to_delete; + unsigned n = m_clauses.size(); + for (unsigned i = 0; i < n; ++i) { + clause* c = m_clauses[i]; + lits.reset(); + bool changed = false; + bool is_tautology = false; + for (literal l : *c) { + literal lit = null_literal; + if (b2l.find(l.var(), lit)) { + lit = l.sign() ? ~lit : lit; + if (lit == true_literal) { + is_tautology = true; + } + else if (lit != false_literal) { + lits.push_back(lit); + } + changed = true; + } + else { + lits.push_back(l); + } + } + if (changed) { + to_delete.push_back(c); + if (!is_tautology) { + mk_clause(lits.size(), lits.c_ptr(), c->is_learned(), static_cast<_assumption_set>(c->assumptions())); + } + } + } + for (clause* c : to_delete) { + del_clause(c, m_clauses); + } + } + + bool is_unit_ineq(clause const& c) const { + return + c.size() == 1 && + m_atoms[c[0].var()] && + m_atoms[c[0].var()]->is_ineq_atom(); + } + + bool is_unit_eq(clause const& c) const { + return + is_unit_ineq(c) && + !c[0].sign() && + m_atoms[c[0].var()]->is_eq(); + } + + /** + \brief determine whether the clause is a comparison v > k or v < k', where k >= 0 or k' <= 0. + */ + lbool is_cmp0(clause const& c, var& v) { + if (!is_unit_ineq(c)) return l_undef; + literal lit = c[0]; + ineq_atom const& a = *to_ineq_atom(m_atoms[lit.var()]); + bool sign = lit.sign(); + poly * p0; + if (!is_single_poly(a, p0)) return l_undef; + if (m_pm.is_var(p0, v)) { + if (!sign && a.get_kind() == atom::GT) { + return l_true; + } + if (!sign && a.get_kind() == atom::LT) { + return l_false; + } + return l_undef; + } + polynomial::scoped_numeral n(m_pm.m()); + if (m_pm.is_var_num(p0, v, n)) { + // x - k > 0 + if (!sign && a.get_kind() == atom::GT && m_pm.m().is_nonneg(n)) { + return l_true; + } + // x + k < 0 + if (!sign && a.get_kind() == atom::LT && m_pm.m().is_nonpos(n)) { + return l_false; + } + // !(x + k > 0) + if (sign && a.get_kind() == atom::GT && m_pm.m().is_pos(n)) { + return l_false; + } + // !(x - k < 0) + if (sign && a.get_kind() == atom::LT && m_pm.m().is_neg(n)) { + return l_true; + } + } + return l_undef; + } + + bool is_single_poly(ineq_atom const& a, poly*& p) { + unsigned sz = a.size(); + return sz == 1 && a.is_odd(0) && (p = a.p(0), true); + } + + svector m_var_signs; + + void init_var_signs() { + m_var_signs.reset(); + for (clause* cp : m_clauses) { + clause& c = *cp; + var x = 0; + lbool cmp = is_cmp0(c, x); + switch (cmp) { + case l_true: + m_var_signs.setx(x, l_true, l_undef); + break; + case l_false: + m_var_signs.setx(x, l_false, l_undef); + break; + default: + break; + } + } + } + + /** + \brief returns true then c is an equality that is equivalent to v*p + q, + and p > 0, v does not occur in p, q. + */ + bool solve_var(clause& c, var& v, polynomial_ref& p, polynomial_ref& q) { + poly* p0; + if (!is_unit_eq(c)) return false; + ineq_atom & a = *to_ineq_atom(m_atoms[c[0].var()]); + if (!is_single_poly(a, p0)) return false; + var mx = max_var(p0); + if (mx >= m_is_int.size()) return false; + for (var x = 0; x <= mx; ++x) { + if (m_is_int[x]) continue; + if (1 == m_pm.degree(p0, x)) { + p = m_pm.coeff(p0, x, 1, q); + switch (m_pm.sign(p, m_var_signs)) { + case l_true: + v = x; + return true; + case l_false: + v = x; + p = -p; + q = -q; + return true; + default: + break; + } + } + } + return false; + } + // ----------------------- // // Pretty printing // // ----------------------- - void display_num_assignment(std::ostream & out, display_var_proc const & proc) const { + std::ostream& display_num_assignment(std::ostream & out, display_var_proc const & proc) const { for (var x = 0; x < num_vars(); x++) { if (m_assignment.is_assigned(x)) { proc(out, x); @@ -2287,19 +2556,24 @@ namespace nlsat { out << "\n"; } } + return out; } - void display_bool_assignment(std::ostream & out) const { + std::ostream& display_bool_assignment(std::ostream & out) const { unsigned sz = m_atoms.size(); for (bool_var b = 0; b < sz; b++) { - if (m_atoms[b] == 0 && m_bvalues[b] != l_undef) { + if (m_atoms[b] == nullptr && m_bvalues[b] != l_undef) { out << "b" << b << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << "\n"; } + else if (m_atoms[b] != nullptr && m_bvalues[b] != l_undef) { + display(out << "b" << b << " ", *m_atoms[b]) << " -> " << (m_bvalues[b] == l_true ? "true" : "false") << "\n"; + } } TRACE("nlsat_bool_assignment", for (bool_var b = 0; b < sz; b++) { out << "b" << b << " -> " << m_bvalues[b] << " " << m_atoms[b] << "\n"; }); + return out; } bool display_mathematica_assignment(std::ostream & out) const { @@ -2317,16 +2591,17 @@ namespace nlsat { return !first; } - void display_num_assignment(std::ostream & out) const { - display_num_assignment(out, m_display_var); + std::ostream& display_num_assignment(std::ostream & out) const { + return display_num_assignment(out, m_display_var); } - void display_assignment(std::ostream& out) const { + std::ostream& display_assignment(std::ostream& out) const { display_bool_assignment(out); display_num_assignment(out); + return out; } - void display(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { + std::ostream& display(std::ostream & out, ineq_atom const & a, display_var_proc const & proc, bool use_star = false) const { unsigned sz = a.size(); for (unsigned i = 0; i < sz; i++) { if (use_star && i > 0) @@ -2346,9 +2621,10 @@ namespace nlsat { case atom::EQ: out << " = 0"; break; default: UNREACHABLE(); break; } + return out; } - - void display_mathematica(std::ostream & out, ineq_atom const & a) const { + + std::ostream& display_mathematica(std::ostream & out, ineq_atom const & a) const { unsigned sz = a.size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) @@ -2370,9 +2646,10 @@ namespace nlsat { case atom::EQ: out << " == 0"; break; default: UNREACHABLE(); break; } + return out; } - void display_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { + std::ostream& display_smt2(std::ostream & out, ineq_atom const & a, display_var_proc const & proc) const { switch (a.get_kind()) { case atom::LT: out << "(< "; break; case atom::GT: out << "(> "; break; @@ -2398,9 +2675,10 @@ namespace nlsat { if (sz > 1) out << ")"; out << " 0)"; + return out; } - void display(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { + std::ostream& display(std::ostream & out, root_atom const & a, display_var_proc const & proc) const { proc(out, a.x()); switch (a.get_kind()) { case atom::ROOT_LT: out << " < "; break; @@ -2413,21 +2691,22 @@ namespace nlsat { out << "root[" << a.i() << "]("; m_pm.display(out, a.p(), proc); out << ")"; + return out; } struct mathematica_var_proc : public display_var_proc { var m_x; public: mathematica_var_proc(var x):m_x(x) {} - virtual void operator()(std::ostream & out, var x) const { + std::ostream& operator()(std::ostream & out, var x) const override { if (m_x == x) - out << "#1"; + return out << "#1"; else - out << "x" << x; + return out << "x" << x; } }; - void display_mathematica(std::ostream & out, root_atom const & a) const { + std::ostream& display_mathematica(std::ostream & out, root_atom const & a) const { out << "x" << a.x(); switch (a.get_kind()) { case atom::ROOT_LT: out << " < "; break; @@ -2440,65 +2719,74 @@ namespace nlsat { out << "Root["; m_pm.display(out, a.p(), mathematica_var_proc(a.x()), true); out << " &, " << a.i() << "]"; + return out; } - void display_smt2(std::ostream & out, root_atom const & a) const { + std::ostream& display_smt2(std::ostream & out, root_atom const & a) const { NOT_IMPLEMENTED_YET(); + return out; } - void display(std::ostream & out, atom const & a, display_var_proc const & proc) const { + std::ostream& display(std::ostream & out, atom const & a, display_var_proc const & proc) const { if (a.is_ineq_atom()) - display(out, static_cast(a), proc); + return display(out, static_cast(a), proc); else - display(out, static_cast(a), proc); + return display(out, static_cast(a), proc); } - void display_mathematica(std::ostream & out, atom const & a) const { - if (a.is_ineq_atom()) - display_mathematica(out, static_cast(a)); - else - display_mathematica(out, static_cast(a)); + std::ostream& display(std::ostream & out, atom const & a) const { + return display(out, a, m_display_var); } - void display_smt2(std::ostream & out, atom const & a, display_var_proc const & proc) const { + std::ostream& display_mathematica(std::ostream & out, atom const & a) const { if (a.is_ineq_atom()) - display_smt2(out, static_cast(a), proc); + return display_mathematica(out, static_cast(a)); else - display_smt2(out, static_cast(a), proc); + return display_mathematica(out, static_cast(a)); } - void display_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { + std::ostream& display_smt2(std::ostream & out, atom const & a, display_var_proc const & proc) const { + if (a.is_ineq_atom()) + return display_smt2(out, static_cast(a), proc); + else + return display_smt2(out, static_cast(a), proc); + } + + std::ostream& display_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { if (b == 0) out << "true"; else if (m_atoms[b] == 0) out << "b" << b; else display(out, *(m_atoms[b]), proc); + return out; } - void display_atom(std::ostream & out, bool_var b) const { - display_atom(out, b, m_display_var); + std::ostream& display_atom(std::ostream & out, bool_var b) const { + return display_atom(out, b, m_display_var); } - void display_mathematica_atom(std::ostream & out, bool_var b) const { + std::ostream& display_mathematica_atom(std::ostream & out, bool_var b) const { if (b == 0) out << "(0 < 1)"; else if (m_atoms[b] == 0) out << "b" << b; else display_mathematica(out, *(m_atoms[b])); + return out; } - void display_smt2_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { + std::ostream& display_smt2_atom(std::ostream & out, bool_var b, display_var_proc const & proc) const { if (b == 0) out << "true"; else if (m_atoms[b] == 0) out << "b" << b; else display_smt2(out, *(m_atoms[b]), proc); + return out; } - void display(std::ostream & out, literal l, display_var_proc const & proc) const { + std::ostream& display(std::ostream & out, literal l, display_var_proc const & proc) const { if (l.sign()) { bool_var b = l.var(); out << "!"; @@ -2511,13 +2799,14 @@ namespace nlsat { else { display_atom(out, l.var(), proc); } + return out; } - void display(std::ostream & out, literal l) const { - display(out, l, m_display_var); + std::ostream& display(std::ostream & out, literal l) const { + return display(out, l, m_display_var); } - void display_mathematica(std::ostream & out, literal l) const { + std::ostream& display_mathematica(std::ostream & out, literal l) const { if (l.sign()) { bool_var b = l.var(); out << "!"; @@ -2530,9 +2819,10 @@ namespace nlsat { else { display_mathematica_atom(out, l.var()); } + return out; } - void display_smt2(std::ostream & out, literal l, display_var_proc const & proc) const { + std::ostream& display_smt2(std::ostream & out, literal l, display_var_proc const & proc) const { if (l.sign()) { bool_var b = l.var(); out << "(not "; @@ -2542,41 +2832,43 @@ namespace nlsat { else { display_smt2_atom(out, l.var(), proc); } + return out; } - void display_assumptions(std::ostream & out, _assumption_set s) const { - + std::ostream& display_assumptions(std::ostream & out, _assumption_set s) const { + return out; } - void display(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { + std::ostream& display(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { for (unsigned i = 0; i < num; i++) { if (i > 0) out << " or "; display(out, ls[i], proc); } + return out; } - void display(std::ostream & out, unsigned num, literal const * ls) { - display(out, num, ls, m_display_var); + std::ostream& display(std::ostream & out, unsigned num, literal const * ls) { + return display(out, num, ls, m_display_var); + } + + std::ostream& display(std::ostream & out, scoped_literal_vector const & cs) { + return display(out, cs.size(), cs.c_ptr(), m_display_var); } - void display(std::ostream & out, scoped_literal_vector const & cs) { - display(out, cs.size(), cs.c_ptr(), m_display_var); - } - - void display(std::ostream & out, clause const & c, display_var_proc const & proc) const { - if (c.assumptions() != 0) { + std::ostream& display(std::ostream & out, clause const & c, display_var_proc const & proc) const { + if (c.assumptions() != nullptr) { display_assumptions(out, static_cast<_assumption_set>(c.assumptions())); out << " |- "; } - display(out, c.size(), c.c_ptr(), proc); + return display(out, c.size(), c.c_ptr(), proc); } - void display(std::ostream & out, clause const & c) const { - display(out, c, m_display_var); + std::ostream& display(std::ostream & out, clause const & c) const { + return display(out, c, m_display_var); } - void display_smt2(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { + std::ostream& display_smt2(std::ostream & out, unsigned num, literal const * ls, display_var_proc const & proc) const { if (num == 0) { out << "false"; } @@ -2591,13 +2883,14 @@ namespace nlsat { } out << ")"; } + return out; } - void display_smt2(std::ostream & out, clause const & c, display_var_proc const & proc = display_var_proc()) const { - display_smt2(out, c.size(), c.c_ptr(), proc); + std::ostream& display_smt2(std::ostream & out, clause const & c, display_var_proc const & proc = display_var_proc()) const { + return display_smt2(out, c.size(), c.c_ptr(), proc); } - void display_abst(std::ostream & out, literal l) const { + std::ostream& display_abst(std::ostream & out, literal l) const { if (l.sign()) { bool_var b = l.var(); out << "!"; @@ -2609,25 +2902,27 @@ namespace nlsat { else { out << "b" << l.var(); } + return out; } - void display_abst(std::ostream & out, unsigned num, literal const * ls) const { + std::ostream& display_abst(std::ostream & out, unsigned num, literal const * ls) const { for (unsigned i = 0; i < num; i++) { if (i > 0) out << " or "; display_abst(out, ls[i]); } + return out; } - void display_abst(std::ostream & out, scoped_literal_vector const & cs) const { - display_abst(out, cs.size(), cs.c_ptr()); + std::ostream& display_abst(std::ostream & out, scoped_literal_vector const & cs) const { + return display_abst(out, cs.size(), cs.c_ptr()); } - void display_abst(std::ostream & out, clause const & c) const { - display_abst(out, c.size(), c.c_ptr()); + std::ostream& display_abst(std::ostream & out, clause const & c) const { + return display_abst(out, c.size(), c.c_ptr()); } - void display_mathematica(std::ostream & out, clause const & c) const { + std::ostream& display_mathematica(std::ostream & out, clause const & c) const { out << "("; unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { @@ -2636,12 +2931,13 @@ namespace nlsat { display_mathematica(out, c[i]); } out << ")"; + return out; } // Debugging support: // Display generated lemma in Mathematica format. // Mathematica must reduce lemma to True (modulo resource constraints). - void display_mathematica_lemma(std::ostream & out, unsigned num, literal const * ls, bool include_assignment = false) const { + std::ostream& display_mathematica_lemma(std::ostream & out, unsigned num, literal const * ls, bool include_assignment = false) const { out << "Resolve[ForAll[{"; // var definition for (unsigned i = 0; i < num_vars(); i++) { @@ -2662,71 +2958,69 @@ namespace nlsat { display_mathematica(out, ls[i]); } out << "], Reals]\n"; // end of exists + return out; } - void display(std::ostream & out, clause_vector const & cs, display_var_proc const & proc) const { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - display(out, *(cs[i]), proc); - out << "\n"; + std::ostream& display(std::ostream & out, clause_vector const & cs, display_var_proc const & proc) const { + for (clause* c : cs) { + display(out, *c, proc) << "\n"; } + return out; } - void display(std::ostream & out, clause_vector const & cs) const { - display(out, cs, m_display_var); + std::ostream& display(std::ostream & out, clause_vector const & cs) const { + return display(out, cs, m_display_var); } - void display_mathematica(std::ostream & out, clause_vector const & cs) const { + std::ostream& display_mathematica(std::ostream & out, clause_vector const & cs) const { unsigned sz = cs.size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) out << ",\n"; - out << " "; - display_mathematica(out, *(cs[i])); + display_mathematica(out << " ", *(cs[i])); } + return out; } - void display_abst(std::ostream & out, clause_vector const & cs) const { - unsigned sz = cs.size(); - for (unsigned i = 0; i < sz; i++) { - display_abst(out, *(cs[i])); - out << "\n"; + std::ostream& display_abst(std::ostream & out, clause_vector const & cs) const { + for (clause* c : cs) { + display_abst(out, *c) << "\n"; } + return out; } - void display(std::ostream & out, display_var_proc const & proc) const { + std::ostream& display(std::ostream & out, display_var_proc const & proc) const { display(out, m_clauses, proc); if (!m_learned.empty()) { - out << "Lemmas:\n"; - display(out, m_learned, proc); + display(out << "Lemmas:\n", m_learned, proc); } + return out; } - void display_mathematica(std::ostream & out) const { - out << "{\n"; - display_mathematica(out, m_clauses); - out << "}\n"; + std::ostream& display_mathematica(std::ostream & out) const { + return display_mathematica(out << "{\n", m_clauses) << "}\n"; } - void display_abst(std::ostream & out) const { + std::ostream& display_abst(std::ostream & out) const { display_abst(out, m_clauses); if (!m_learned.empty()) { - out << "Lemmas:\n"; - display_abst(out, m_learned); + display_abst(out << "Lemmas:\n", m_learned); } + return out; } - void display(std::ostream & out) const { + std::ostream& display(std::ostream & out) const { display(out, m_display_var); - display_assignment(out); + return display_assignment(out); } - void display_vars(std::ostream & out) const { + std::ostream& display_vars(std::ostream & out) const { for (unsigned i = 0; i < num_vars(); i++) { out << i << " -> "; m_display_var(out, i); out << "\n"; } + return out; } - void display_smt2_arith_decls(std::ostream & out) const { + std::ostream& display_smt2_arith_decls(std::ostream & out) const { unsigned sz = m_is_int.size(); for (unsigned i = 0; i < sz; i++) { if (m_is_int[i]) @@ -2734,31 +3028,32 @@ namespace nlsat { else out << "(declare-fun x" << i << " () Real)\n"; } + return out; } - void display_smt2_bool_decls(std::ostream & out) const { + std::ostream& display_smt2_bool_decls(std::ostream & out) const { unsigned sz = m_atoms.size(); for (unsigned i = 0; i < sz; i++) { - if (m_atoms[i] == 0) + if (m_atoms[i] == nullptr) out << "(declare-fun b" << i << " () Bool)\n"; } + return out; } - void display_smt2(std::ostream & out) const { + std::ostream& display_smt2(std::ostream & out) const { display_smt2_bool_decls(out); display_smt2_arith_decls(out); out << "(assert (and true\n"; - unsigned sz = m_clauses.size(); - for (unsigned i = 0; i < sz; i++) { - display_smt2(out, *(m_clauses[i])); - out << "\n"; + for (clause* c : m_clauses) { + display_smt2(out, *c) << "\n"; } out << "))\n(check-sat)" << std::endl; + return out; } }; - solver::solver(reslimit& rlim, params_ref const & p) { - m_imp = alloc(imp, *this, rlim, p); + solver::solver(reslimit& rlim, params_ref const & p, bool incremental) { + m_imp = alloc(imp, *this, rlim, p, incremental); } solver::~solver() { @@ -2866,13 +3161,24 @@ namespace nlsat { void solver::get_bvalues(svector& vs) { vs.reset(); - vs.append(m_imp->m_bvalues); + unsigned sz = m_imp->m_bvalues.size(); + for (bool_var b = 0; b < sz; ++b) { + if (m_imp->m_atoms[b] == nullptr) { + vs.push_back(m_imp->m_bvalues[b]); + } + else { + vs.push_back(l_undef); // don't save values from atoms. + } + } + TRACE("nlsat", display(tout);); } void solver::set_bvalues(svector const& vs) { + TRACE("nlsat", display(tout);); m_imp->m_bvalues.reset(); m_imp->m_bvalues.append(vs); m_imp->m_bvalues.resize(m_imp->m_atoms.size(), l_undef); + TRACE("nlsat", display(tout);); } var solver::mk_var(bool is_int) { @@ -2903,27 +3209,28 @@ namespace nlsat { return m_imp->mk_clause(num_lits, lits, a); } - void solver::display(std::ostream & out) const { - m_imp->display(out); + std::ostream& solver::display(std::ostream & out) const { + return m_imp->display(out); } - void solver::display(std::ostream & out, literal l) const { - m_imp->display(out, l); + std::ostream& solver::display(std::ostream & out, literal l) const { + return m_imp->display(out, l); } - void solver::display(std::ostream & out, unsigned n, literal const* ls) const { + std::ostream& solver::display(std::ostream & out, unsigned n, literal const* ls) const { for (unsigned i = 0; i < n; ++i) { display(out, ls[i]); out << "; "; } + return out; } - void solver::display(std::ostream & out, var x) const { - m_imp->m_display_var(out, x); + std::ostream& solver::display(std::ostream & out, var x) const { + return m_imp->m_display_var(out, x); } - void solver::display(std::ostream & out, atom const& a) const { - m_imp->display(out, a, m_imp->m_display_var); + std::ostream& solver::display(std::ostream & out, atom const& a) const { + return m_imp->display(out, a, m_imp->m_display_var); } display_var_proc const & solver::display_proc() const { diff --git a/src/nlsat/nlsat_solver.h b/src/nlsat/nlsat_solver.h index ac503c603..4ba1225bd 100644 --- a/src/nlsat/nlsat_solver.h +++ b/src/nlsat/nlsat_solver.h @@ -35,7 +35,7 @@ namespace nlsat { struct imp; imp * m_imp; public: - solver(reslimit& rlim, params_ref const & p); + solver(reslimit& rlim, params_ref const & p, bool incremental); ~solver(); /** @@ -108,7 +108,7 @@ namespace nlsat { /** \brief Create a new clause. */ - void mk_clause(unsigned num_lits, literal * lits, assumption a = 0); + void mk_clause(unsigned num_lits, literal * lits, assumption a = nullptr); // ----------------------- // @@ -225,21 +225,21 @@ namespace nlsat { /** \brief Display solver's state. */ - void display(std::ostream & out) const; + std::ostream& display(std::ostream & out) const; /** \brief Display literal */ - void display(std::ostream & out, literal l) const; + std::ostream& display(std::ostream & out, literal l) const; - void display(std::ostream & out, unsigned n, literal const* ls) const; + std::ostream& display(std::ostream & out, unsigned n, literal const* ls) const; - void display(std::ostream & out, atom const& a) const; + std::ostream& display(std::ostream & out, atom const& a) const; /** \brief Display variable */ - void display(std::ostream & out, var x) const; + std::ostream& display(std::ostream & out, var x) const; display_var_proc const & display_proc() const; }; diff --git a/src/nlsat/nlsat_types.h b/src/nlsat/nlsat_types.h index 8704f4444..647a5e3ee 100644 --- a/src/nlsat/nlsat_types.h +++ b/src/nlsat/nlsat_types.h @@ -47,6 +47,8 @@ namespace nlsat { typedef polynomial::var_vector var_vector; typedef polynomial::manager pmanager; typedef polynomial::polynomial poly; + typedef polynomial::monomial monomial; + typedef polynomial::numeral numeral; const var null_var = polynomial::null_var; const var true_bool_var = 0; @@ -148,10 +150,7 @@ namespace nlsat { typedef algebraic_numbers::anum anum; typedef algebraic_numbers::manager anum_manager; - class solver_exception : public default_exception { - public: - solver_exception(char const * msg):default_exception(msg) {} - }; + typedef default_exception solver_exception; class assignment; diff --git a/src/nlsat/tactic/goal2nlsat.cpp b/src/nlsat/tactic/goal2nlsat.cpp index 6d7e1c767..42ae36564 100644 --- a/src/nlsat/tactic/goal2nlsat.cpp +++ b/src/nlsat/tactic/goal2nlsat.cpp @@ -41,11 +41,11 @@ struct goal2nlsat::imp { m_solver(s) { } - virtual bool is_int(polynomial::var x) const { + bool is_int(polynomial::var x) const override { return m_solver.is_int(x); } - virtual polynomial::var mk_var(bool is_int) { + polynomial::var mk_var(bool is_int) override { return m_solver.mk_var(is_int); } }; @@ -198,7 +198,6 @@ struct goal2nlsat::imp { throw tactic_exception("apply simplify before applying nlsat"); case OP_AND: case OP_OR: - case OP_IFF: case OP_XOR: case OP_NOT: case OP_IMPLIES: @@ -269,12 +268,12 @@ struct goal2nlsat::scoped_set_imp { } ~scoped_set_imp() { - m_owner.m_imp = 0; + m_owner.m_imp = nullptr; } }; goal2nlsat::goal2nlsat() { - m_imp = 0; + m_imp = nullptr; } goal2nlsat::~goal2nlsat() { diff --git a/src/nlsat/tactic/nlsat_tactic.cpp b/src/nlsat/tactic/nlsat_tactic.cpp index 510f503e7..5e536bbe6 100644 --- a/src/nlsat/tactic/nlsat_tactic.cpp +++ b/src/nlsat/tactic/nlsat_tactic.cpp @@ -32,11 +32,11 @@ class nlsat_tactic : public tactic { ast_manager & m; expr_ref_vector m_var2expr; expr_display_var_proc(ast_manager & _m):m(_m), m_var2expr(_m) {} - virtual void operator()(std::ostream & out, nlsat::var x) const { + std::ostream& operator()(std::ostream & out, nlsat::var x) const override { if (x < m_var2expr.size()) - out << mk_ismt2_pp(m_var2expr.get(x), m); + return out << mk_ismt2_pp(m_var2expr.get(x), m); else - out << "x!" << x; + return out << "x!" << x; } }; @@ -51,7 +51,7 @@ class nlsat_tactic : public tactic { m(_m), m_params(p), m_display_var(_m), - m_solver(m.limit(), p) { + m_solver(m.limit(), p, false) { } void updt_params(params_ref const & p) { @@ -68,7 +68,7 @@ class nlsat_tactic : public tactic { } for (unsigned b = 0; b < b2a.size(); b++) { expr * a = b2a.get(b); - if (a == 0) + if (a == nullptr) continue; if (is_uninterp_const(a)) continue; @@ -83,9 +83,8 @@ class nlsat_tactic : public tactic { bool eval_model(model& model, goal& g) { unsigned sz = g.size(); for (unsigned i = 0; i < sz; i++) { - expr_ref val(m); - if (model.eval(g.form(i), val) && !m.is_true(val)) { - TRACE("nlsat", tout << mk_pp(g.form(i), m) << " -> " << val << "\n";); + if (!model.is_true(g.form(i))) { + TRACE("nlsat", tout << mk_pp(g.form(i), m) << " -> " << model(g.form(i)) << "\n";); return false; } } @@ -116,7 +115,7 @@ class nlsat_tactic : public tactic { } for (unsigned b = 0; b < b2a.size(); b++) { expr * a = b2a.get(b); - if (a == 0 || !is_uninterp_const(a)) + if (a == nullptr || !is_uninterp_const(a)) continue; lbool val = m_solver.bvalue(b); if (val == l_undef) @@ -129,12 +128,8 @@ class nlsat_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("nlsat", *g); if (g->is_decided()) { @@ -166,23 +161,25 @@ class nlsat_tactic : public tactic { if (!contains_unsupported(b2a, x2t)) { // If mk_model is false it means that the model produced by nlsat // assigns noninteger values to integer variables + model_converter_ref mc; if (mk_model(*g.get(), b2a, x2t, mc)) { // result goal is trivially SAT g->reset(); + g->add(mc.get()); } } } else { - expr_dependency* lcore = 0; + expr_dependency* lcore = nullptr; if (g->unsat_core_enabled()) { vector assumptions; m_solver.get_core(assumptions); - for (unsigned i = 0; i < assumptions.size(); ++i) { - expr_dependency* d = static_cast(assumptions[i]); + for (nlsat::assumption a : assumptions) { + expr_dependency* d = static_cast(a); lcore = m.mk_join(lcore, d); } } - g->assert_expr(m.mk_false(), 0, lcore); + g->assert_expr(m.mk_false(), nullptr, lcore); } g->inc_depth(); @@ -204,43 +201,40 @@ class nlsat_tactic : public tactic { ~scoped_set_imp() { m_owner.m_imp->m_solver.collect_statistics(m_owner.m_stats); - m_owner.m_imp = 0; + m_owner.m_imp = nullptr; } }; public: nlsat_tactic(params_ref const & p): m_params(p) { - m_imp = 0; + m_imp = nullptr; } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(nlsat_tactic, m_params); } - virtual ~nlsat_tactic() { + ~nlsat_tactic() override { SASSERT(m_imp == 0); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { goal2nlsat::collect_param_descrs(r); nlsat::solver::collect_param_descrs(r); algebraic_numbers::manager::collect_param_descrs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { imp local_imp(in->m(), m_params); scoped_set_imp setter(*this, local_imp); - local_imp(in, result, mc, pc, core); + local_imp(in, result); } catch (z3_error & ex) { throw ex; @@ -251,13 +245,13 @@ public: } } - virtual void cleanup() {} + void cleanup() override {} - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.copy(m_stats); } - virtual void reset_statistics() { + void reset_statistics() override { m_stats.reset(); } }; diff --git a/src/nlsat/tactic/qfnra_nlsat_tactic.cpp b/src/nlsat/tactic/qfnra_nlsat_tactic.cpp index 22f64af47..47b9e0505 100644 --- a/src/nlsat/tactic/qfnra_nlsat_tactic.cpp +++ b/src/nlsat/tactic/qfnra_nlsat_tactic.cpp @@ -48,11 +48,15 @@ tactic * mk_qfnra_nlsat_tactic(ast_manager & m, params_ref const & p) { purify_p), mk_propagate_values_tactic(m, p), mk_solve_eqs_tactic(m, p), + using_params(mk_purify_arith_tactic(m, p), + purify_p), mk_elim_uncnstr_tactic(m, p), mk_elim_term_ite_tactic(m, p)), and_then(/* mk_degree_shift_tactic(m, p), */ // may affect full dimensionality detection factor, mk_solve_eqs_tactic(m, p), + using_params(mk_purify_arith_tactic(m, p), + purify_p), using_params(mk_simplify_tactic(m, p), main_p), mk_tseitin_cnf_core_tactic(m, p), diff --git a/src/opt/CMakeLists.txt b/src/opt/CMakeLists.txt index 28a14be2e..dcb13c062 100644 --- a/src/opt/CMakeLists.txt +++ b/src/opt/CMakeLists.txt @@ -2,7 +2,6 @@ z3_add_component(opt SOURCES maxres.cpp maxsmt.cpp - mss.cpp opt_cmds.cpp opt_context.cpp opt_pareto.cpp diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index b06773223..f52c56a60 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -52,18 +52,18 @@ Notes: --*/ +#include "ast/ast_pp.h" +#include "ast/pb_decl_plugin.h" +#include "ast/ast_util.h" +#include "model/model_smt2_pp.h" #include "solver/solver.h" +#include "solver/mus.h" +#include "sat/sat_solver/inc_sat_solver.h" +#include "smt/smt_solver.h" +#include "opt/opt_context.h" +#include "opt/opt_params.hpp" #include "opt/maxsmt.h" #include "opt/maxres.h" -#include "ast/ast_pp.h" -#include "solver/mus.h" -#include "opt/mss.h" -#include "sat/sat_solver/inc_sat_solver.h" -#include "opt/opt_context.h" -#include "ast/pb_decl_plugin.h" -#include "opt/opt_params.hpp" -#include "ast/ast_util.h" -#include "smt/smt_solver.h" using namespace opt; @@ -88,9 +88,8 @@ private: expr_ref_vector m_asms; expr_ref_vector m_defs; obj_map m_asm2weight; - ptr_vector m_new_core; + expr_ref_vector m_new_core; mus m_mus; - mss m_mss; expr_ref_vector m_trail; strategy_t m_st; rational m_max_upper; @@ -120,8 +119,8 @@ public: maxsmt_solver_base(c, ws, soft), m_index(index), m_B(m), m_asms(m), m_defs(m), + m_new_core(m), m_mus(c.get_solver()), - m_mss(c.get_solver(), m), m_trail(m), m_st(st), m_correction_set_size(0), @@ -176,10 +175,11 @@ public: void new_assumption(expr* e, rational const& w) { IF_VERBOSE(13, verbose_stream() << "new assumption " << mk_pp(e, m) << " " << w << "\n";); - TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n";); m_asm2weight.insert(e, w); m_asms.push_back(e); m_trail.push_back(e); + TRACE("opt", tout << "insert: " << mk_pp(e, m) << " : " << w << "\n"; + tout << m_asms << " " << "\n"; ); } void trace() { @@ -193,7 +193,7 @@ public: trace(); if (is_sat != l_true) return is_sat; while (m_lower < m_upper) { - TRACE("opt", + TRACE("opt_verbose", display_vec(tout, m_asms); s().display(tout); tout << "\n"; @@ -205,7 +205,12 @@ public: } switch (is_sat) { case l_true: - SASSERT(is_true(m_asms)); + CTRACE("opt", !m_model->is_true(m_asms), + tout << *m_model; + tout << "assumptions: "; + for (expr* a : m_asms) tout << mk_pp(a, m) << " -> " << (*m_model)(a) << " "; + tout << "\n";); + SASSERT(m_model->is_true(m_asms)); found_optimum(); return l_true; case l_false: @@ -243,7 +248,7 @@ public: case l_true: get_current_correction_set(cs); if (cs.empty()) { - m_found_feasible_optimum = m_model.get() != 0; + m_found_feasible_optimum = m_model.get() != nullptr; m_lower = m_upper; } else { @@ -277,20 +282,19 @@ public: /** Give preference to cores that have large minmal values. */ - sort_assumptions(asms); - + sort_assumptions(asms); m_last_index = std::min(m_last_index, asms.size()-1); m_last_index = 0; unsigned index = m_last_index>0?m_last_index-1:0; m_last_index = 0; bool first = index > 0; SASSERT(index < asms.size() || asms.empty()); + IF_VERBOSE(10, verbose_stream() << "start hill climb " << index << " asms: " << asms.size() << "\n";); while (index < asms.size() && is_sat == l_true) { while (!first && asms.size() > 20*(index - m_last_index) && index < asms.size()) { index = next_index(asms, index); } first = false; - // IF_VERBOSE(3, verbose_stream() << "weight: " << get_weight(asms[0].get()) << " " << get_weight(asms[index-1].get()) << " num soft: " << index << "\n";); m_last_index = index; is_sat = check_sat(index, asms.c_ptr()); } @@ -306,8 +310,9 @@ public: if (r == l_true) { model_ref mdl; s().get_model(mdl); + TRACE("opt", tout << *mdl;); if (mdl.get()) { - update_assignment(mdl.get()); + update_assignment(mdl); } } return r; @@ -316,16 +321,17 @@ public: void found_optimum() { IF_VERBOSE(1, verbose_stream() << "found optimum\n";); m_lower.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - m_assignment[i] = is_true(m_soft[i]); - if (!m_assignment[i]) { - m_lower += m_weights[i]; + for (soft& s : m_soft) { + s.is_true = m_model->is_true(s.s); + if (!s.is_true) { + m_lower += s.weight; } } m_upper = m_lower; m_found_feasible_optimum = true; } + virtual lbool operator()() { m_defs.reset(); switch(m_st) { @@ -345,16 +351,17 @@ public: lbool get_cores(vector& cores) { // assume m_s is unsat. lbool is_sat = l_false; - expr_ref_vector asms(m_asms); cores.reset(); exprs core; while (is_sat == l_false) { core.reset(); - s().get_unsat_core(core); - // verify_core(core); + expr_ref_vector _core(m); + s().get_unsat_core(_core); model_ref mdl; get_mus_model(mdl); - is_sat = minimize_core(core); + is_sat = minimize_core(_core); + core.append(_core.size(), _core.c_ptr()); + // verify_core(core); ++m_stats.m_num_cores; if (is_sat != l_true) { IF_VERBOSE(100, verbose_stream() << "(opt.maxres minimization failed)\n";); @@ -366,22 +373,24 @@ public: m_lower = m_upper; return l_true; } + // 1. remove all core literals from m_asms + // 2. re-add literals of higher weight than min-weight. + // 3. 'core' stores the core literals that are + // re-encoded as assumptions, afterwards + remove_soft(core, m_asms); + split_core(core); cores.push_back(core); - if (core.size() >= m_max_core_size) { - break; - } - if (cores.size() >= m_max_num_cores) { - break; - } - remove_soft(core, asms); - is_sat = check_sat_hill_climb(asms); + + if (core.size() >= m_max_core_size) break; + if (cores.size() >= m_max_num_cores) break; + + is_sat = check_sat_hill_climb(m_asms); } + TRACE("opt", - tout << "num cores: " << cores.size() << "\n"; - for (unsigned i = 0; i < cores.size(); ++i) { - display_vec(tout, cores[i]); - } - tout << "num satisfying: " << asms.size() << "\n";); + tout << "sat: " << is_sat << " num cores: " << cores.size() << "\n"; + for (auto const& c : cores) display_vec(tout, c); + tout << "num assumptions: " << m_asms.size() << "\n";); return is_sat; } @@ -389,7 +398,7 @@ public: void get_current_correction_set(exprs& cs) { model_ref mdl; s().get_model(mdl); - update_assignment(mdl.get()); + update_assignment(mdl); get_current_correction_set(mdl.get(), cs); } @@ -397,10 +406,9 @@ public: cs.reset(); if (!mdl) return; for (expr* a : m_asms) { - if (is_false(mdl, a)) { + if (mdl->is_false(a)) { cs.push_back(a); } - TRACE("opt", expr_ref tmp(m); mdl->eval(a, tmp, true); tout << mk_pp(a, m) << ": " << tmp << "\n";); } TRACE("opt", display_vec(tout << "new correction set: ", cs);); } @@ -439,11 +447,11 @@ public: ++m_stats.m_num_cs; expr_ref fml(m), tmp(m); TRACE("opt", display_vec(tout << "corr_set: ", corr_set);); - remove_core(corr_set); + remove_soft(corr_set, m_asms); rational w = split_core(corr_set); cs_max_resolve(corr_set, w); IF_VERBOSE(2, verbose_stream() << "(opt.maxres.correction-set " << corr_set.size() << ")\n";); - m_csmodel = 0; + m_csmodel = nullptr; m_correction_set_size = 0; } @@ -464,35 +472,30 @@ public: unsigned max_core_size(vector const& cores) { unsigned result = 0; - for (unsigned i = 0; i < cores.size(); ++i) { - result = std::max(cores[i].size(), result); + for (auto const& c : cores) { + result = std::max(c.size(), result); } return result; } void process_unsat(vector const& cores) { - for (unsigned i = 0; i < cores.size(); ++i) { - process_unsat(cores[i]); + for (auto const & c : cores) { + process_unsat(c); } } void update_model(expr* def, expr* value) { SASSERT(is_uninterp_const(def)); if (m_csmodel) { - expr_ref val(m); - SASSERT(m_csmodel.get()); - if (m_csmodel->eval(value, val, true)) { - m_csmodel->register_decl(to_app(def)->get_decl(), val); - } + m_csmodel->register_decl(to_app(def)->get_decl(), (*m_csmodel)(value)); } } void process_unsat(exprs const& core) { - IF_VERBOSE(3, verbose_stream() << "(maxres cs model valid: " << (m_csmodel.get() != 0) << " cs size:" << m_correction_set_size << " core: " << core.size() << ")\n";); + IF_VERBOSE(3, verbose_stream() << "(maxres cs model valid: " << (m_csmodel.get() != nullptr) << " cs size:" << m_correction_set_size << " core: " << core.size() << ")\n";); expr_ref fml(m); - remove_core(core); SASSERT(!core.empty()); - rational w = split_core(core); + rational w = core_weight(core); TRACE("opt", display_vec(tout << "minimized core: ", core);); IF_VERBOSE(10, display_vec(verbose_stream() << "core: ", core);); max_resolve(core, w); @@ -524,19 +527,19 @@ public: if (m_c.sat_enabled()) { // SAT solver core extracts some model // during unsat core computation. - mdl = 0; + mdl = nullptr; s().get_model(mdl); } else { w = m_mus.get_best_model(mdl); } if (mdl.get() && w < m_upper) { - update_assignment(mdl.get()); + update_assignment(mdl); } - return 0 != mdl.get(); + return nullptr != mdl.get(); } - lbool minimize_core(exprs& core) { + lbool minimize_core(expr_ref_vector& core) { if (core.empty()) { return l_true; } @@ -558,19 +561,24 @@ public: return m_asm2weight.find(e); } - rational split_core(exprs const& core) { + rational core_weight(exprs const& core) { if (core.empty()) return rational(0); // find the minimal weight: rational w = get_weight(core[0]); for (unsigned i = 1; i < core.size(); ++i) { w = std::min(w, get_weight(core[i])); } + return w; + } + + rational split_core(exprs const& core) { + rational w = core_weight(core); // add fresh soft clauses for weights that are above w. - for (unsigned i = 0; i < core.size(); ++i) { - rational w2 = get_weight(core[i]); + for (expr* e : core) { + rational w2 = get_weight(e); if (w2 > w) { rational w3 = w2 - w; - new_assumption(core[i], w3); + new_assumption(e, w3); } } return w; @@ -592,8 +600,7 @@ public: } void display(std::ostream& out) { - for (unsigned i = 0; i < m_asms.size(); ++i) { - expr* a = m_asms[i].get(); + for (expr * a : m_asms) { out << mk_pp(a, m) << " : " << get_weight(a) << "\n"; } } @@ -698,10 +705,11 @@ public: s().assert_expr(fml); } - void update_assignment(model* mdl) { + void update_assignment(model_ref & mdl) { + mdl->set_model_completion(true); unsigned correction_set_size = 0; - for (unsigned i = 0; i < m_asms.size(); ++i) { - if (is_false(mdl, m_asms[i].get())) { + for (expr* a : m_asms) { + if (mdl->is_false(a)) { ++correction_set_size; } } @@ -710,96 +718,81 @@ public: m_correction_set_size = correction_set_size; } + TRACE("opt", tout << *mdl;); + rational upper(0); - expr_ref tmp(m); - for (unsigned i = 0; i < m_soft.size(); ++i) { - if (!is_true(mdl, m_soft[i])) { - upper += m_weights[i]; + + for (soft& s : m_soft) { + TRACE("opt", tout << s.s << ": " << (*mdl)(s.s) << " " << s.weight << "\n";); + if (!mdl->is_true(s.s)) { + upper += s.weight; } } - if (upper >= m_upper) { + if (upper > m_upper) { + TRACE("opt", tout << "new upper: " << upper << " vs existing upper: " << m_upper << "\n";); return; } - if (!m_c.verify_model(m_index, mdl, upper)) { + if (!m_c.verify_model(m_index, mdl.get(), upper)) { return; } m_model = mdl; + m_c.model_updated(mdl.get()); - for (unsigned i = 0; i < m_soft.size(); ++i) { - m_assignment[i] = is_true(m_soft[i]); + TRACE("opt", tout << "updated upper: " << upper << "\nmodel\n" << *m_model;); + + for (soft& s : m_soft) { + s.is_true = m_model->is_true(s.s); } - DEBUG_CODE(verify_assignment();); + // DEBUG_CODE(verify_assignment();); m_upper = upper; + trace(); add_upper_bound_block(); - } void add_upper_bound_block() { if (!m_add_upper_bound_block) return; pb_util u(m); expr_ref_vector nsoft(m); + vector weights; expr_ref fml(m); - for (unsigned i = 0; i < m_soft.size(); ++i) { - nsoft.push_back(mk_not(m, m_soft[i])); + for (soft& s : m_soft) { + nsoft.push_back(mk_not(m, s.s)); + weights.push_back(s.weight); } - fml = u.mk_lt(nsoft.size(), m_weights.c_ptr(), nsoft.c_ptr(), m_upper); + fml = u.mk_lt(nsoft.size(), weights.c_ptr(), nsoft.c_ptr(), m_upper); TRACE("opt", tout << "block upper bound " << fml << "\n";);; s().assert_expr(fml); } - bool is_true(model* mdl, expr* e) { - expr_ref tmp(m); - return mdl->eval(e, tmp, true) && m.is_true(tmp); - } - - bool is_false(model* mdl, expr* e) { - expr_ref tmp(m); - return mdl->eval(e, tmp, true) && m.is_false(tmp); - } - - bool is_true(expr* e) { - return is_true(m_model.get(), e); - } - - bool is_true(expr_ref_vector const& es) { - unsigned i = 0; - for (; i < es.size() && is_true(es[i]); ++i) { } - return i == es.size(); - } - void remove_soft(exprs const& core, expr_ref_vector& asms) { - for (unsigned i = 0; i < asms.size(); ++i) { - if (core.contains(asms[i].get())) { - asms[i] = asms.back(); - asms.pop_back(); - --i; - } - } + TRACE("opt", tout << "before remove: " << asms << "\n";); + unsigned j = 0; + for (expr* a : asms) + if (!core.contains(a)) + asms[j++] = a; + asms.shrink(j); + TRACE("opt", tout << "after remove: " << asms << "\n";); } - void remove_core(exprs const& core) { - remove_soft(core, m_asms); - } - - virtual void updt_params(params_ref& p) { - maxsmt_solver_base::updt_params(p); - opt_params _p(p); - m_hill_climb = _p.maxres_hill_climb(); - m_add_upper_bound_block = _p.maxres_add_upper_bound_block(); - m_max_num_cores = _p.maxres_max_num_cores(); - m_max_core_size = _p.maxres_max_core_size(); - m_maximize_assignment = _p.maxres_maximize_assignment(); - m_max_correction_set_size = _p.maxres_max_correction_set_size(); - m_pivot_on_cs = _p.maxres_pivot_on_correction_set(); - m_wmax = _p.maxres_wmax(); - m_dump_benchmarks = _p.dump_benchmarks(); + virtual void updt_params(params_ref& _p) { + maxsmt_solver_base::updt_params(_p); + opt_params p(_p); + m_hill_climb = p.maxres_hill_climb(); + m_add_upper_bound_block = p.maxres_add_upper_bound_block(); + m_max_num_cores = p.maxres_max_num_cores(); + m_max_core_size = p.maxres_max_core_size(); + m_maximize_assignment = p.maxres_maximize_assignment(); + m_max_correction_set_size = p.maxres_max_correction_set_size(); + m_pivot_on_cs = p.maxres_pivot_on_correction_set(); + m_wmax = p.maxres_wmax(); + m_dump_benchmarks = p.dump_benchmarks(); } lbool init_local() { @@ -811,25 +804,21 @@ public: if (is_sat != l_true) { return is_sat; } - obj_map::iterator it = new_soft.begin(), end = new_soft.end(); - for (; it != end; ++it) { - add_soft(it->m_key, it->m_value); + for (auto const& kv : new_soft) { + add_soft(kv.m_key, kv.m_value); } m_max_upper = m_upper; m_found_feasible_optimum = false; m_last_index = 0; add_upper_bound_block(); - m_csmodel = 0; + m_csmodel = nullptr; m_correction_set_size = 0; return l_true; } virtual void commit_assignment() { if (m_found_feasible_optimum) { - TRACE("opt", tout << "Committing feasible solution\n"; - tout << m_defs; - tout << m_asms; - ); + TRACE("opt", tout << "Committing feasible solution\n" << m_defs << " " << m_asms;); s().assert_expr(m_defs); s().assert_expr(m_asms); } @@ -839,11 +828,9 @@ public: void verify_core(exprs const& core) { IF_VERBOSE(3, verbose_stream() << "verify core\n";); ref smt_solver = mk_smt_solver(m, m_params, symbol()); - for (unsigned i = 0; i < s().get_num_assertions(); ++i) { - smt_solver->assert_expr(s().get_assertion(i)); - } + smt_solver->assert_expr(s().get_assertions()); smt_solver->assert_expr(core); - lbool is_sat = smt_solver->check_sat(0, 0); + lbool is_sat = smt_solver->check_sat(0, nullptr); if (is_sat == l_true) { IF_VERBOSE(0, verbose_stream() << "not a core\n";); } @@ -852,21 +839,20 @@ public: void verify_assignment() { IF_VERBOSE(1, verbose_stream() << "verify assignment\n";); ref smt_solver = mk_smt_solver(m, m_params, symbol()); - for (unsigned i = 0; i < s().get_num_assertions(); ++i) { - smt_solver->assert_expr(s().get_assertion(i)); - } + smt_solver->assert_expr(s().get_assertions()); expr_ref n(m); - for (unsigned i = 0; i < m_soft.size(); ++i) { - n = m_soft[i]; - if (!m_assignment[i]) { + for (soft& s : m_soft) { + n = s.s; + if (!s.is_true) { n = mk_not(m, n); } smt_solver->assert_expr(n); } - lbool is_sat = smt_solver->check_sat(0, 0); + lbool is_sat = smt_solver->check_sat(0, nullptr); if (is_sat == l_false) { IF_VERBOSE(0, verbose_stream() << "assignment is infeasible\n";); } + IF_VERBOSE(1, verbose_stream() << "verified\n";); } }; diff --git a/src/opt/maxsmt.cpp b/src/opt/maxsmt.cpp index 8df6c04a6..1b44b578b 100644 --- a/src/opt/maxsmt.cpp +++ b/src/opt/maxsmt.cpp @@ -21,6 +21,7 @@ Notes: #include "opt/maxsmt.h" #include "opt/maxres.h" #include "opt/wmax.h" +#include "opt/opt_params.hpp" #include "ast/ast_pp.h" #include "util/uint_set.h" #include "opt/opt_context.h" @@ -33,16 +34,17 @@ Notes: namespace opt { maxsmt_solver_base::maxsmt_solver_base( - maxsat_context& c, vector const& ws, expr_ref_vector const& soft): + maxsat_context& c, vector const& ws, expr_ref_vector const& softs): m(c.get_manager()), m_c(c), - m_soft(soft), - m_weights(ws), m_assertions(m), m_trail(m) { c.get_base_model(m_model); SASSERT(m_model); updt_params(c.params()); + for (unsigned i = 0; i < ws.size(); ++i) { + m_soft.push_back(soft(expr_ref(softs.get(i), m), ws[i], false)); + } } void maxsmt_solver_base::updt_params(params_ref& p) { @@ -55,17 +57,21 @@ namespace opt { void maxsmt_solver_base::commit_assignment() { expr_ref tmp(m); + expr_ref_vector fmls(m); rational k(0), cost(0); - for (unsigned i = 0; i < m_soft.size(); ++i) { - if (get_assignment(i)) { - k += m_weights[i]; + vector weights; + for (soft const& s : m_soft) { + if (s.is_true) { + k += s.weight; } else { - cost += m_weights[i]; + cost += s.weight; } + weights.push_back(s.weight); + fmls.push_back(s.s); } pb_util pb(m); - tmp = pb.mk_ge(m_weights.size(), m_weights.c_ptr(), m_soft.c_ptr(), k); + tmp = pb.mk_ge(weights.size(), weights.c_ptr(), fmls.c_ptr(), k); TRACE("opt", tout << "cost: " << cost << "\n" << tmp << "\n";); s().assert_expr(tmp); } @@ -73,21 +79,14 @@ namespace opt { bool maxsmt_solver_base::init() { m_lower.reset(); m_upper.reset(); - m_assignment.reset(); - for (unsigned i = 0; i < m_weights.size(); ++i) { - expr_ref val(m); - if (!m_model->eval(m_soft[i], val)) return false; - m_assignment.push_back(m.is_true(val)); - if (!m_assignment.back()) { - m_upper += m_weights[i]; - } + for (soft& s : m_soft) { + s.is_true = m.is_true(s.s); + if (!s.is_true) m_upper += s.weight; } TRACE("opt", tout << "upper: " << m_upper << " assignments: "; - for (unsigned i = 0; i < m_weights.size(); ++i) { - tout << (m_assignment[i]?"T":"F"); - } + for (soft& s : m_soft) tout << (s.is_true?"T":"F"); tout << "\n";); return true; } @@ -105,7 +104,7 @@ namespace opt { app* maxsmt_solver_base::mk_fresh_bool(char const* name) { app* result = m.mk_fresh_const(name, m.mk_bool_sort()); - m_c.fm().insert(result->get_decl()); + m_c.fm().hide(result); return result; } @@ -116,7 +115,7 @@ namespace opt { return dynamic_cast(th); } else { - return 0; + return nullptr; } } @@ -142,6 +141,7 @@ namespace opt { maxsmt_solver_base::scoped_ensure_theory::scoped_ensure_theory(maxsmt_solver_base& s) { m_wth = s.ensure_wmax_theory(); } + maxsmt_solver_base::scoped_ensure_theory::~scoped_ensure_theory() { if (m_wth) { m_wth->reset_local(); @@ -155,16 +155,18 @@ namespace opt { rational l = m_adjust_value(m_lower); rational u = m_adjust_value(m_upper); if (l > u) std::swap(l, u); - verbose_stream() << "(opt." << solver << " [" << l << ":" << u << "])\n";); + verbose_stream() << "(opt." << solver << " [" << l << ":" << u << "])\n";); } lbool maxsmt_solver_base::find_mutexes(obj_map& new_soft) { m_lower.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - new_soft.insert(m_soft[i], m_weights[i]); + expr_ref_vector fmls(m); + for (soft& s : m_soft) { + new_soft.insert(s.s, s.weight); + fmls.push_back(s.s); } vector mutexes; - lbool is_sat = s().find_mutexes(m_soft, mutexes); + lbool is_sat = s().find_mutexes(fmls, mutexes); if (is_sat != l_true) { return is_sat; } @@ -228,12 +230,10 @@ namespace opt { lbool maxsmt::operator()() { lbool is_sat = l_undef; - m_msolver = 0; + m_msolver = nullptr; symbol const& maxsat_engine = m_c.maxsat_engine(); IF_VERBOSE(1, verbose_stream() << "(maxsmt)\n";); - TRACE("opt", tout << "maxsmt\n"; - s().display(tout); tout << "\n"; - ); + TRACE("opt_verbose", s().display(tout << "maxsmt\n") << "\n";); if (m_soft_constraints.empty() || maxsat_engine == symbol("maxres") || maxsat_engine == symbol::null) { m_msolver = mk_maxres(m_c, m_index, m_weights, m_soft_constraints); } @@ -266,7 +266,7 @@ namespace opt { } } - IF_VERBOSE(1, verbose_stream() << "is-sat: " << is_sat << "\n"; + IF_VERBOSE(5, verbose_stream() << "is-sat: " << is_sat << "\n"; if (is_sat == l_true) { verbose_stream() << "Satisfying soft constraints\n"; display_answer(verbose_stream()); @@ -353,12 +353,26 @@ namespace opt { m_upper += w; } + struct cmp_first { + bool operator()(std::pair const& x, std::pair const& y) const { + return x.first < y.first; + } + }; + void maxsmt::display_answer(std::ostream& out) const { - for (unsigned i = 0; i < m_soft_constraints.size(); ++i) { - expr* e = m_soft_constraints[i]; + vector> sorted_weights; + unsigned n = m_weights.size(); + for (unsigned i = 0; i < n; ++i) { + sorted_weights.push_back(std::make_pair(i, m_weights[i])); + } + std::sort(sorted_weights.begin(), sorted_weights.end(), cmp_first()); + sorted_weights.reverse(); + for (unsigned i = 0; i < n; ++i) { + unsigned idx = sorted_weights[i].first; + expr* e = m_soft_constraints[idx]; bool is_not = m.is_not(e, e); - out << m_weights[i] << ": " << mk_pp(e, m) - << ((is_not != get_assignment(i))?" |-> true ":" |-> false ") + out << m_weights[idx] << ": " << mk_pp(e, m) + << ((is_not != get_assignment(idx))?" |-> true ":" |-> false ") << "\n"; } @@ -391,6 +405,62 @@ namespace opt { return m_c.get_solver(); } + void maxsmt::model_updated(model* mdl) { + m_c.model_updated(mdl); + } + class solver_maxsat_context : public maxsat_context { + params_ref m_params; + solver_ref m_solver; + model_ref m_model; + ref m_fm; + symbol m_maxsat_engine; + public: + solver_maxsat_context(params_ref& p, solver* s, model * m): + m_params(p), + m_solver(s), + m_model(m), + m_fm(alloc(generic_model_converter, s->get_manager(), "maxsmt")) { + opt_params _p(p); + m_maxsat_engine = _p.maxsat_engine(); + } + generic_model_converter& fm() override { return *m_fm.get(); } + bool sat_enabled() const override { return false; } + solver& get_solver() override { return *m_solver.get(); } + ast_manager& get_manager() const override { return m_solver->get_manager(); } + params_ref& params() override { return m_params; } + void enable_sls(bool force) override { } // no op + symbol const& maxsat_engine() const override { return m_maxsat_engine; } + void get_base_model(model_ref& _m) override { _m = m_model; }; + smt::context& smt_context() override { + throw default_exception("stand-alone maxsat context does not support wmax"); + } + unsigned num_objectives() override { return 1; } + bool verify_model(unsigned id, model* mdl, rational const& v) override { return true; }; + void set_model(model_ref& _m) override { m_model = _m; } + void model_updated(model* mdl) override { } // no-op + }; + lbool maxsmt_wrapper::operator()(vector>& soft) { + solver_maxsat_context ctx(m_params, m_solver.get(), m_model.get()); + maxsmt maxsmt(ctx, 0); + for (auto const& p : soft) { + maxsmt.add(p.first, p.second); + } + lbool r = maxsmt(); + if (r == l_true) { + ast_manager& m = m_solver->get_manager(); + svector labels; + maxsmt.get_model(m_model, labels); + // TBD: is m_fm applied or not? + unsigned j = 0; + for (auto const& p : soft) { + if (m_model->is_true(p.first)) { + soft[j++] = p; + } + } + soft.shrink(j); + } + return r; + } }; diff --git a/src/opt/maxsmt.h b/src/opt/maxsmt.h index 0541a9a88..b61d876b3 100644 --- a/src/opt/maxsmt.h +++ b/src/opt/maxsmt.h @@ -22,7 +22,6 @@ Notes: #include "ast/ast.h" #include "util/params.h" #include "solver/solver.h" -#include "tactic/filter_model_converter.h" #include "util/statistics.h" #include "smt/smt_context.h" #include "smt/smt_theory.h" @@ -57,31 +56,40 @@ namespace opt { // class maxsmt_solver_base : public maxsmt_solver { protected: + struct soft { + expr_ref s; + rational weight; + bool is_true; + soft(expr_ref const& s, rational const& w, bool t): s(s), weight(w), is_true(t) {} + soft(soft const& other):s(other.s), weight(other.weight), is_true(other.is_true) {} + soft& operator=(soft const& other) { s = other.s; weight = other.weight; is_true = other.is_true; return *this; } + }; ast_manager& m; - maxsat_context& m_c; - const expr_ref_vector m_soft; - vector m_weights; + maxsat_context& m_c; + vector m_soft; expr_ref_vector m_assertions; expr_ref_vector m_trail; rational m_lower; rational m_upper; model_ref m_model; svector m_labels; - svector m_assignment; // truth assignment to soft constraints + //const expr_ref_vector m_soft; + //vector m_weights; + //svector m_assignment; // truth assignment to soft constraints params_ref m_params; // config public: maxsmt_solver_base(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft); - virtual ~maxsmt_solver_base() {} - virtual rational get_lower() const { return m_lower; } - virtual rational get_upper() const { return m_upper; } - virtual bool get_assignment(unsigned index) const { return m_assignment[index]; } - virtual void collect_statistics(statistics& st) const { } - virtual void get_model(model_ref& mdl, svector& labels) { mdl = m_model.get(); labels = m_labels;} + ~maxsmt_solver_base() override {} + rational get_lower() const override { return m_lower; } + rational get_upper() const override { return m_upper; } + bool get_assignment(unsigned index) const override { return m_soft[index].is_true; } + void collect_statistics(statistics& st) const override { } + void get_model(model_ref& mdl, svector& labels) override { mdl = m_model.get(); labels = m_labels;} virtual void commit_assignment(); void set_model() { s().get_model(m_model); s().get_labels(m_labels); } - virtual void updt_params(params_ref& p); + void updt_params(params_ref& p) override; solver& s(); bool init(); void set_mus(bool f); @@ -147,12 +155,55 @@ namespace opt { bool get_assignment(unsigned index) const; void display_answer(std::ostream& out) const; void collect_statistics(statistics& st) const; + void model_updated(model* mdl); private: bool is_maxsat_problem(weights_t& ws) const; void verify_assignment(); solver& s(); }; + /** + \brief Standalone MaxSMT solver. + + It takes as input a solver object and provides a MaxSAT solver routine. + + It assumes the solver state is satisfiable and therefore there is a model + associated with the constraints asserted to the solver. A model of the + solver state must be supplied as a last argument. + + It assumes that the caller manages scope on the solver such that + the solver can be left in a stronger or inconsistent state upon return. + Callers should therefore use this feature under a push/pop. + */ + class maxsmt_wrapper { + params_ref m_params; + ref m_solver; + model_ref m_model; + public: + maxsmt_wrapper(params_ref & p, solver* s, model* m): + m_params(p), + m_solver(s), + m_model(m) {} + + lbool operator()(expr_ref_vector& soft) { + vector> _soft; + for (expr* e : soft) _soft.push_back(std::make_pair(e, rational::one())); + lbool r = (*this)(_soft); + soft.reset(); + for (auto const& p : _soft) soft.push_back(p.first); + return r; + } + + /** + \brief takes a vector of weighted soft constraints. + Returns a modified list of soft constraints that are + satisfied in the maximal satisfying assignment. + */ + lbool operator()(vector> & soft); + + model_ref get_model() { return m_model; } + }; + }; #endif diff --git a/src/opt/mss.cpp b/src/opt/mss.cpp deleted file mode 100644 index cc0fa8d7d..000000000 --- a/src/opt/mss.cpp +++ /dev/null @@ -1,285 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - mss.cpp - -Abstract: - - MSS/MCS extraction. - -Author: - - Nikolaj Bjorner (nbjorner) 2014-2-8 - -Notes: - - ---*/ - -#include "solver/solver.h" -#include "opt/mss.h" -#include "ast/ast_pp.h" -#include "model/model_smt2_pp.h" - -namespace opt { - - - mss::mss(solver& s, ast_manager& m): m_s(s), m(m) { - } - - mss::~mss() { - } - - bool mss::check_result() { - lbool is_sat = m_s.check_sat(m_mss.size(), m_mss.c_ptr()); - if (is_sat == l_undef) return true; - SASSERT(m_mss.empty() || is_sat == l_true); - if (is_sat == l_false) return false; - expr_set::iterator it = m_mcs.begin(), end = m_mcs.end(); - for (; it != end; ++it) { - m_mss.push_back(*it); - is_sat = m_s.check_sat(m_mss.size(), m_mss.c_ptr()); - m_mss.pop_back(); - if (is_sat == l_undef) return true; - SASSERT(is_sat == l_false); - if (is_sat == l_true) return false; - } - return true; - } - - void mss::initialize(exprs& literals) { - expr* n; - expr_set lits, core_lits; - for (unsigned i = 0; i < literals.size(); ++i) { - n = literals[i]; - lits.insert(n); - m.is_not(n, n); - if (!is_uninterp_const(n)) { - throw default_exception("arguments have to be uninterpreted literals"); - } - } - exprs rest_core; - expr_ref tmp(m); - // - // the last core is a dummy core. It contains literals that - // did not occur in previous cores and did not evaluate to true - // in the current model. - // - for (unsigned i = 0; i < m_cores.size(); ++i) { - exprs const& core = m_cores[i]; - for (unsigned j = 0; j < core.size(); ++j) { - expr* n = core[j]; - if (!core_lits.contains(n)) { - core_lits.insert(n); - if (m_model->eval(n, tmp) && m.is_true(tmp)) { - add_mss(n); - } - else { - m_todo.push_back(n); - } - } - } - } - for (unsigned i = 0; i < literals.size(); ++i) { - expr* n = literals[i]; - if (!core_lits.contains(n)) { - if (m_model->eval(n, tmp) && m.is_true(tmp)) { - m_mss.push_back(n); - } - else { - rest_core.push_back(n); - core_lits.insert(n); - m_todo.push_back(n); - } - } - } - m_cores.push_back(rest_core); - } - - void mss::add_mss(expr* n) { - if (!m_mss_set.contains(n)) { - m_mss_set.insert(n); - m_mss.push_back(n); - } - } - - void mss::update_core(exprs& core) { - unsigned j = 0; - for (unsigned i = 0; i < core.size(); ++i) { - expr* n = core[i]; - if (!m_mss_set.contains(n)) { - if (i != j) { - core[j] = core[i]; - } - ++j; - } - } - core.resize(j); - } - - void mss::update_mss() { - expr_ref tmp(m); - unsigned j = 0; - for (unsigned i = 0; i < m_todo.size(); ++i) { - expr* n = m_todo[i]; - SASSERT(!m_mss_set.contains(n)); - if (m_mcs.contains(n)) { - continue; // remove from cores. - } - if (m_model->eval(n, tmp) && m.is_true(tmp)) { - add_mss(n); - } - else { - if (j != i) { - m_todo[j] = m_todo[i]; - } - ++j; - } - } - m_todo.resize(j); - } - - lbool mss::operator()(model* initial_model, vector const& _cores, exprs& literals, exprs& mcs) { - m_mss.reset(); - m_todo.reset(); - m_model = initial_model; - m_cores.reset(); - SASSERT(m_model); - m_cores.append(_cores); - initialize(literals); - TRACE("opt", - display_vec(tout << "lits: ", literals.size(), literals.c_ptr()); - display(tout);); - lbool is_sat = l_true; - for (unsigned i = 0; is_sat == l_true && i < m_cores.size(); ++i) { - bool has_mcs = false; - bool is_last = i + 1 < m_cores.size(); - SASSERT(check_invariant()); - update_core(m_cores[i]); // remove members of mss - is_sat = process_core(1, m_cores[i], has_mcs, is_last); - } - if (is_sat == l_true) { - SASSERT(check_invariant()); - TRACE("opt", display(tout);); - literals.reset(); - literals.append(m_mss); - mcs.reset(); - expr_set::iterator it = m_mcs.begin(), end = m_mcs.end(); - for (; it != end; ++it) { - mcs.push_back(*it); - } - SASSERT(check_result()); - } - m_mcs.reset(); - m_mss_set.reset(); - IF_VERBOSE(2, display_vec(verbose_stream() << "mcs: ", mcs.size(), mcs.c_ptr());); - return is_sat; - } - - - // - // at least one literal in core is false in current model. - // pick literals in core that are not yet in mss. - // - lbool mss::process_core(unsigned sz, exprs& core, bool& has_mcs, bool is_last) { - SASSERT(sz > 0); - if (core.empty()) { - return l_true; - } - if (m.canceled()) { - return l_undef; - } - if (sz == 1 && core.size() == 1 && is_last && !has_mcs) { - // there has to be at least one false - // literal in the core. - TRACE("opt", tout << "mcs: " << mk_pp(core[0], m) << "\n";); - m_mcs.insert(core[0]); - return l_true; - } - sz = std::min(sz, core.size()); - TRACE("opt", display_vec(tout << "process (total " << core.size() << ") :", sz, core.c_ptr());); - unsigned sz_save = m_mss.size(); - m_mss.append(sz, core.c_ptr()); - lbool is_sat = m_s.check_sat(m_mss.size(), m_mss.c_ptr()); - IF_VERBOSE(3, display_vec(verbose_stream() << "mss: ", m_mss.size(), m_mss.c_ptr());); - m_mss.resize(sz_save); - switch (is_sat) { - case l_true: - m_s.get_model(m_model); - update_mss(); - DEBUG_CODE( - for (unsigned i = 0; i < sz; ++i) { - SASSERT(m_mss_set.contains(core[i])); - }); - update_core(core); - return process_core(2*sz, core, has_mcs, is_last); - case l_false: - if (sz == 1) { - has_mcs = true; - m_mcs.insert(core[0]); - core[0] = core.back(); - core.pop_back(); - } - else { - exprs core2; - core2.append(core.size()-sz, core.c_ptr()+sz); - core.resize(sz); - is_sat = process_core(sz, core2, has_mcs, false); - if (is_sat != l_true) { - return is_sat; - } - update_core(core); - } - return process_core(1, core, has_mcs, is_last); - case l_undef: - return l_undef; - } - - return l_true; - } - - void mss::display_vec(std::ostream& out, unsigned sz, expr* const* args) const { - for (unsigned i = 0; i < sz; ++i) { - out << mk_pp(args[i], m) << " "; - } - out << "\n"; - } - - void mss::display(std::ostream& out) const { - for (unsigned i = 0; i < m_cores.size(); ++i) { - display_vec(out << "core: ", m_cores[i].size(), m_cores[i].c_ptr()); - } - expr_set::iterator it = m_mcs.begin(), end = m_mcs.end(); - out << "mcs:\n"; - for (; it != end; ++it) { - out << mk_pp(*it, m) << "\n"; - } - out << "\n"; - out << "mss:\n"; - for (unsigned i = 0; i < m_mss.size(); ++i) { - out << mk_pp(m_mss[i], m) << "\n"; - } - out << "\n"; - if (m_model) { - model_smt2_pp(out, m, *(m_model.get()), 0); - } - } - - bool mss::check_invariant() const { - if (!m_model) return true; - expr_ref tmp(m); - for (unsigned i = 0; i < m_mss.size(); ++i) { - expr* n = m_mss[i]; - if (!m_model->eval(n, tmp)) return true; - CTRACE("opt", !m.is_true(tmp), tout << mk_pp(n, m) << " |-> " << mk_pp(tmp, m) << "\n";); - SASSERT(!m.is_false(tmp)); - } - return true; - } -} - - - - diff --git a/src/opt/mss.h b/src/opt/mss.h deleted file mode 100644 index af383634a..000000000 --- a/src/opt/mss.h +++ /dev/null @@ -1,57 +0,0 @@ -/*++ -Copyright (c) 2014 Microsoft Corporation - -Module Name: - - mss.h - -Abstract: - - Maximal satisfying subset/minimal correction sets: MSS/MCS - -Author: - - Nikolaj Bjorner (nbjorner) 2014-2-8 - -Notes: - ---*/ -#ifndef MSS_H_ -#define MSS_H_ - -namespace opt { - class mss { - solver& m_s; - ast_manager& m; - typedef ptr_vector exprs; - typedef obj_hashtable expr_set; - exprs m_mss; - expr_set m_mcs; - expr_set m_mss_set; - vector m_cores; - exprs m_todo; - model_ref m_model; - public: - mss(solver& s, ast_manager& m); - ~mss(); - - lbool operator()(model* initial_model, vector const& cores, exprs& literals, exprs& mcs); - - - void get_model(model_ref& mdl) { mdl = m_model; } - - private: - void initialize(exprs& literals); - bool check_result(); - void add_mss(expr* n); - void update_mss(); - void update_core(exprs& core); - lbool process_core(unsigned sz, exprs& core, bool& has_mcs, bool is_last); - void display(std::ostream& out) const; - void display_vec(std::ostream& out, unsigned sz, expr* const* args) const; - bool check_invariant() const; - }; - -}; - -#endif diff --git a/src/opt/opt_cmds.cpp b/src/opt/opt_cmds.cpp index 89264a9c8..d8e301e08 100644 --- a/src/opt/opt_cmds.cpp +++ b/src/opt/opt_cmds.cpp @@ -52,7 +52,7 @@ public: assert_soft_cmd(opt::context* opt): parametric_cmd("assert-soft"), m_idx(0), - m_formula(0), + m_formula(nullptr), m_opt(opt) {} @@ -61,7 +61,7 @@ public: virtual void reset(cmd_context & ctx) { m_idx = 0; - m_formula = 0; + m_formula = nullptr; } virtual char const * get_usage() const { return " [:weight ] [:id ]"; } @@ -166,7 +166,9 @@ public: } virtual void execute(cmd_context & ctx) { - get_opt(ctx, m_opt).display_assignment(ctx.regular_stream()); + if (!ctx.ignore_check()) { + get_opt(ctx, m_opt).display_assignment(ctx.regular_stream()); + } } }; diff --git a/src/opt/opt_cmds.h b/src/opt/opt_cmds.h index 60f83d201..3ca8228df 100644 --- a/src/opt/opt_cmds.h +++ b/src/opt/opt_cmds.h @@ -23,7 +23,7 @@ Notes: class cmd_context; -void install_opt_cmds(cmd_context & ctx, opt::context* opt = 0); +void install_opt_cmds(cmd_context & ctx, opt::context* opt = nullptr); #endif diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 3d296d92b..566aaa1f6 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -17,32 +17,33 @@ Notes: --*/ -#include "opt/opt_context.h" -#include "ast/ast_pp.h" -#include "opt/opt_solver.h" -#include "opt/opt_params.hpp" +#include "util/gparams.h" #include "ast/for_each_expr.h" +#include "ast/ast_pp.h" +#include "ast/bv_decl_plugin.h" +#include "ast/pb_decl_plugin.h" +#include "ast/ast_smt_pp.h" +#include "ast/ast_pp_util.h" +#include "model/model_smt2_pp.h" #include "tactic/goal.h" #include "tactic/tactic.h" #include "tactic/arith/lia2card_tactic.h" -#include "tactic/arith/elim01_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "tactic/tactical.h" -#include "model/model_smt2_pp.h" #include "tactic/arith/card2bv_tactic.h" #include "tactic/arith/eq2bv_tactic.h" #include "tactic/bv/dt2bv_tactic.h" +#include "tactic/generic_model_converter.h" #include "sat/sat_solver/inc_sat_solver.h" -#include "ast/bv_decl_plugin.h" -#include "ast/pb_decl_plugin.h" -#include "ast/ast_smt_pp.h" -#include "tactic/filter_model_converter.h" -#include "ast/ast_pp_util.h" #include "qe/qsat.h" +#include "opt/opt_context.h" +#include "opt/opt_solver.h" +#include "opt/opt_params.hpp" + namespace opt { @@ -121,11 +122,13 @@ namespace opt { m_arith(m), m_bv(m), m_hard_constraints(m), - m_solver(0), + m_solver(nullptr), + m_pareto1(false), m_box_index(UINT_MAX), m_optsmt(m), m_scoped_state(m), - m_fm(m), + m_fm(alloc(generic_model_converter, m, "opt")), + m_model_fixed(), m_objective_refs(m), m_enable_sat(false), m_is_clausal(false), @@ -137,6 +140,7 @@ namespace opt { p.set_bool("unsat_core", true); p.set_bool("elim_to_real", true); updt_params(p); + m_model_counter = 0; } context::~context() { @@ -144,9 +148,8 @@ namespace opt { } void context::reset_maxsmts() { - map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); - for (; it != end; ++it) { - dealloc(it->m_value); + for (auto& kv : m_maxsmts) { + dealloc(kv.m_value); } m_maxsmts.reset(); } @@ -169,7 +172,7 @@ namespace opt { r.append(m_labels); } - void context::get_unsat_core(ptr_vector & r) { + void context::get_unsat_core(expr_ref_vector & r) { throw default_exception("Unsat cores are not supported with optimization"); } @@ -263,6 +266,9 @@ namespace opt { normalize(); internalize(); update_solver(); + if (contains_quantifiers()) { + warning_msg("optimization with quantified constraints is not supported"); + } #if 0 if (is_qsat_opt()) { return run_qsat_opt(); @@ -270,20 +276,23 @@ namespace opt { #endif solver& s = get_solver(); s.assert_expr(m_hard_constraints); - display_benchmark(); - IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n";); + + opt_params optp(m_params); + symbol pri = optp.priority(); + + IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n"); lbool is_sat = s.check_sat(0,0); - TRACE("opt", tout << "initial search result: " << is_sat << "\n"; - s.display(tout);); + TRACE("opt", s.display(tout << "initial search result: " << is_sat << "\n");); if (is_sat != l_false) { s.get_model(m_model); s.get_labels(m_labels); + model_updated(m_model.get()); } if (is_sat != l_true) { - TRACE("opt", tout << m_hard_constraints << "\n";); + TRACE("opt", tout << m_hard_constraints << "\n";); return is_sat; } - IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n";); + IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n"); TRACE("opt", model_smt2_pp(tout, m, *m_model, 0);); m_optsmt.setup(*m_opt_solver.get()); update_lower(); @@ -292,7 +301,14 @@ namespace opt { case 0: break; case 1: - is_sat = execute(m_objectives[0], true, false); + if (m_pareto1) { + is_sat = l_false; + m_pareto1 = false; + } + else { + m_pareto1 = (pri == symbol("pareto")); + is_sat = execute(m_objectives[0], true, false); + } break; default: { opt_params optp(m_params); @@ -306,9 +322,9 @@ namespace opt { else { is_sat = execute_lex(); } - break; } } + if (is_sat == l_true) validate_model(); return adjust_unknown(is_sat); } @@ -324,17 +340,19 @@ namespace opt { } void context::fix_model(model_ref& mdl) { - if (mdl) { - if (m_model_converter) { - (*m_model_converter)(mdl, 0); - } - m_fm(mdl, 0); + if (mdl && !m_model_fixed.contains(mdl.get())) { + TRACE("opt", tout << "fix-model\n";); + (*m_fm)(mdl); + apply(m_model_converter, mdl); + m_model_fixed.push_back(mdl.get()); } } - void context::get_model(model_ref& mdl) { + void context::get_model_core(model_ref& mdl) { mdl = m_model; fix_model(mdl); + mdl->set_model_completion(true); + TRACE("opt", tout << *mdl;); } void context::get_box_model(model_ref& mdl, unsigned index) { @@ -361,7 +379,6 @@ namespace opt { if (result == l_true && committed) m_optsmt.commit_assignment(index); if (result == l_true && m_optsmt.is_unbounded(index, is_max) && contains_quantifiers()) { throw default_exception("unbounded objectives on quantified constraints is not supported"); - result = l_undef; } return result; } @@ -395,8 +412,8 @@ namespace opt { */ bool context::scoped_lex() { if (m_maxsat_engine == symbol("maxres")) { - for (unsigned i = 0; i < m_objectives.size(); ++i) { - if (m_objectives[i].m_type != O_MAXSMT) return true; + for (auto const& o : m_objectives) { + if (o.m_type != O_MAXSMT) return true; } return false; } @@ -406,14 +423,19 @@ namespace opt { lbool context::execute_lex() { lbool r = l_true; bool sc = scoped_lex(); - IF_VERBOSE(1, verbose_stream() << "(optsmt:lex)\n";); - for (unsigned i = 0; r == l_true && i < m_objectives.size(); ++i) { - bool is_last = i + 1 == m_objectives.size(); - r = execute(m_objectives[i], i + 1 < m_objectives.size(), sc && !is_last); - if (r == l_true && !get_lower_as_num(i).is_finite()) { + IF_VERBOSE(1, verbose_stream() << "(opt :lex)\n";); + unsigned sz = m_objectives.size(); + for (unsigned i = 0; r == l_true && i < sz; ++i) { + objective const& o = m_objectives[i]; + bool is_last = i + 1 == sz; + r = execute(o, i + 1 < sz, sc && !is_last && o.m_type != O_MAXSMT); + if (r == l_true && o.m_type == O_MINIMIZE && !get_lower_as_num(i).is_finite()) { return r; } - if (r == l_true && i + 1 < m_objectives.size()) { + if (r == l_true && o.m_type == O_MAXIMIZE && !get_upper_as_num(i).is_finite()) { + return r; + } + if (r == l_true && i + 1 < sz) { update_lower(); } } @@ -428,7 +450,7 @@ namespace opt { return l_true; } if (m_box_index < m_objectives.size()) { - m_model = 0; + m_model = nullptr; ++m_box_index; return l_undef; } @@ -482,7 +504,8 @@ namespace opt { case O_MINIMIZE: is_ge = !is_ge; case O_MAXIMIZE: - if (mdl->eval(obj.m_term, val) && is_numeral(val, k)) { + val = (*mdl)(obj.m_term); + if (is_numeral(val, k)) { if (is_ge) { result = mk_ge(obj.m_term, val); } @@ -502,9 +525,12 @@ namespace opt { for (unsigned i = 0; i < sz; ++i) { terms.push_back(obj.m_terms[i]); coeffs.push_back(obj.m_weights[i]); - if (mdl->eval(obj.m_terms[i], val) && m.is_true(val)) { + if (mdl->is_true(obj.m_terms[i])) { k += obj.m_weights[i]; } + else { + TRACE("opt", tout << (*mdl)(obj.m_terms[i]) << "\n";); + } } if (is_ge) { result = pb.mk_ge(sz, coeffs.c_ptr(), terms.c_ptr(), k); @@ -535,9 +561,11 @@ namespace opt { } void context::yield() { + SASSERT (m_pareto); m_pareto->get_model(m_model, m_labels); update_bound(true); update_bound(false); + TRACE("opt", model_smt2_pp(tout, m, *m_model.get(), 0);); } lbool context::execute_pareto() { @@ -546,7 +574,7 @@ namespace opt { } lbool is_sat = (*(m_pareto.get()))(); if (is_sat != l_true) { - set_pareto(0); + set_pareto(nullptr); } if (is_sat == l_true) { yield(); @@ -554,6 +582,7 @@ namespace opt { return is_sat; } + std::string context::reason_unknown() const { if (m.canceled()) { return Z3_CANCELED_MSG; @@ -583,7 +612,7 @@ namespace opt { void context::init_solver() { setup_arith_solver(); - m_opt_solver = alloc(opt_solver, m, m_params, m_fm); + m_opt_solver = alloc(opt_solver, m, m_params, *m_fm); m_opt_solver->set_logic(m_logic); m_solver = m_opt_solver.get(); m_opt_solver->ensure_pb(); @@ -735,8 +764,8 @@ namespace opt { } goal_ref g(alloc(goal, m, true, false)); - for (unsigned i = 0; i < fmls.size(); ++i) { - g->assert_expr(fmls[i].get()); + for (expr* fml : fmls) { + g->assert_expr(fml); } tactic_ref tac0 = and_then(mk_simplify_tactic(m, m_params), @@ -748,23 +777,21 @@ namespace opt { tactic_ref tac1, tac2, tac3, tac4; if (optp.elim_01()) { tac1 = mk_dt2bv_tactic(m); - tac2 = mk_elim01_tactic(m); - tac3 = mk_lia2card_tactic(m); - tac4 = mk_eq2bv_tactic(m); + tac2 = mk_lia2card_tactic(m); + tac3 = mk_eq2bv_tactic(m); params_ref lia_p; lia_p.set_bool("compile_equality", optp.pb_compile_equality()); - tac3->updt_params(lia_p); - set_simplify(and_then(tac0.get(), tac1.get(), tac2.get(), tac3.get(), tac4.get(), mk_simplify_tactic(m))); + tac2->updt_params(lia_p); + set_simplify(and_then(tac0.get(), tac1.get(), tac2.get(), tac3.get(), mk_simplify_tactic(m))); } else { set_simplify(tac0.get()); } - proof_converter_ref pc; - expr_dependency_ref core(m); goal_ref_buffer result; - (*m_simplify)(g, result, m_model_converter, pc, core); + (*m_simplify)(g, result); SASSERT(result.size() == 1); goal* r = result[0]; + m_model_converter = r->mc(); fmls.reset(); expr_ref tmp(m); for (unsigned i = 0; i < r->size(); ++i) { @@ -808,13 +835,13 @@ namespace opt { offset -= weight; } if (m.is_true(arg)) { - IF_VERBOSE(1, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> true\n";); + IF_VERBOSE(5, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> true\n";); } else if (weight.is_zero()) { // skip } else if (m.is_false(arg)) { - IF_VERBOSE(1, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> false\n";); + IF_VERBOSE(5, verbose_stream() << weight << ": " << mk_pp(m_objectives[index].m_terms[i].get(), m) << " |-> false\n";); offset += weight; } else { @@ -918,7 +945,7 @@ namespace opt { func_decl* f = m.mk_fresh_func_decl(name,"", domain.size(), domain.c_ptr(), m.mk_bool_sort()); m_objective_fns.insert(f, index); m_objective_refs.push_back(f); - m_objective_orig.insert(f, sz > 0 ? args[0] : 0); + m_objective_orig.insert(f, sz > 0 ? args[0] : nullptr); return m.mk_app(f, sz, args); } @@ -940,8 +967,7 @@ namespace opt { TRACE("opt", tout << fmls << "\n";); m_hard_constraints.reset(); expr_ref orig_term(m); - for (unsigned i = 0; i < fmls.size(); ++i) { - expr* fml = fmls[i]; + for (expr * fml : fmls) { app_ref tr(m); expr_ref_vector terms(m); vector weights; @@ -961,7 +987,7 @@ namespace opt { } mk_atomic(terms); SASSERT(obj.m_id == id); - obj.m_term = orig_term?to_app(orig_term):0; + obj.m_term = orig_term?to_app(orig_term):nullptr; obj.m_terms.reset(); obj.m_terms.append(terms); obj.m_weights.reset(); @@ -1002,6 +1028,23 @@ namespace opt { } } + + void context::model_updated(model* md) { + opt_params optp(m_params); + symbol prefix = optp.solution_prefix(); + if (prefix == symbol::null || prefix == symbol("")) return; + model_ref mdl = md->copy(); + fix_model(mdl); + std::ostringstream buffer; + buffer << prefix << (m_model_counter++) << ".smt2"; + std::ofstream out(buffer.str()); + if (out) { + out << *mdl; + out.close(); + } + } + + bool context::verify_model(unsigned index, model* md, rational const& _v) { rational r; app_ref term = m_objectives[index].m_term; @@ -1010,13 +1053,9 @@ namespace opt { } rational v = m_objectives[index].m_adjust_value(_v); expr_ref val(m); - model_ref mdl = md; + model_ref mdl = md->copy(); fix_model(mdl); - - if (!mdl->eval(term, val)) { - TRACE("opt", tout << "Term does not evaluate " << term << "\n";); - return false; - } + val = (*mdl)(term); unsigned bvsz; if (!m_arith.is_numeral(val, r) && !m_bv.is_numeral(val, r, bvsz)) { TRACE("opt", tout << "model does not evaluate objective to a value\n";); @@ -1033,12 +1072,10 @@ namespace opt { } void context::purify(app_ref& term) { - filter_model_converter_ref fm; + generic_model_converter_ref fm; if (m_arith.is_add(term)) { expr_ref_vector args(m); - unsigned sz = term->get_num_args(); - for (unsigned i = 0; i < sz; ++i) { - expr* arg = term->get_arg(i); + for (expr* arg : *term) { if (is_mul_const(arg)) { args.push_back(arg); } @@ -1070,13 +1107,13 @@ namespace opt { (m_arith.is_mul(e, e2, e1) && m_arith.is_numeral(e1) && is_uninterp_const(e2)); } - app* context::purify(filter_model_converter_ref& fm, expr* term) { + app* context::purify(generic_model_converter_ref& fm, expr* term) { std::ostringstream out; out << mk_pp(term, m); app* q = m.mk_fresh_const(out.str().c_str(), m.get_sort(term)); - if (!fm) fm = alloc(filter_model_converter, m); + if (!fm) fm = alloc(generic_model_converter, m, "opt"); m_hard_constraints.push_back(m.mk_eq(q, term)); - fm->insert(q->get_decl()); + fm->hide(q); return q; } @@ -1087,7 +1124,7 @@ namespace opt { - filter "obj" from generated model. */ void context::mk_atomic(expr_ref_vector& terms) { - filter_model_converter_ref fm; + generic_model_converter_ref fm; for (unsigned i = 0; i < terms.size(); ++i) { expr_ref p(terms[i].get(), m); app_ref q(m); @@ -1124,8 +1161,7 @@ namespace opt { } void context::internalize() { - for (unsigned i = 0; i < m_objectives.size(); ++i) { - objective & obj = m_objectives[i]; + for (objective & obj : m_objectives) { switch(obj.m_type) { case O_MINIMIZE: { app_ref tmp(m); @@ -1158,9 +1194,9 @@ namespace opt { rational r; switch(obj.m_type) { case O_MINIMIZE: { - bool evaluated = m_model->eval(obj.m_term, val); - TRACE("opt", tout << obj.m_term << " " << val << " " << evaluated << " " << is_numeral(val, r) << "\n";); - if (evaluated && is_numeral(val, r)) { + val = (*m_model)(obj.m_term); + TRACE("opt", tout << obj.m_term << " " << val << " " << is_numeral(val, r) << "\n";); + if (is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); TRACE("opt", tout << "adjusted value: " << val << "\n";); if (is_lower) { @@ -1173,9 +1209,9 @@ namespace opt { break; } case O_MAXIMIZE: { - bool evaluated = m_model->eval(obj.m_term, val); + val = (*m_model)(obj.m_term); TRACE("opt", tout << obj.m_term << " " << val << "\n";); - if (evaluated && is_numeral(val, r)) { + if (is_numeral(val, r)) { inf_eps val = inf_eps(obj.m_adjust_value(r)); TRACE("opt", tout << "adjusted value: " << val << "\n";); if (is_lower) { @@ -1190,26 +1226,21 @@ namespace opt { case O_MAXSMT: { bool ok = true; for (unsigned j = 0; ok && j < obj.m_terms.size(); ++j) { - bool evaluated = m_model->eval(obj.m_terms[j], val); + val = (*m_model)(obj.m_terms[j]); TRACE("opt", tout << mk_pp(obj.m_terms[j], m) << " " << val << "\n";); - if (evaluated) { - if (!m.is_true(val)) { - r += obj.m_weights[j]; - } - } - else { - ok = false; + if (!m.is_true(val)) { + r += obj.m_weights[j]; } } if (ok) { maxsmt& ms = *m_maxsmts.find(obj.m_id); if (is_lower) { ms.update_upper(r); - TRACE("opt", tout << r << " " << ms.get_upper() << "\n";); + TRACE("opt", tout << "update upper from " << r << " to " << ms.get_upper() << "\n";); } else { ms.update_lower(r); - TRACE("opt", tout << r << " " << ms.get_lower() << "\n";); + TRACE("opt", tout << "update lower from " << r << " to " << ms.get_lower() << "\n";); } } break; @@ -1219,6 +1250,9 @@ namespace opt { } void context::display_benchmark() { + display(verbose_stream()); + return; + if (opt_params(m_params).dump_benchmarks() && sat_enabled() && m_objectives.size() == 1 && @@ -1228,6 +1262,8 @@ namespace opt { unsigned sz = o.m_terms.size(); inc_sat_display(verbose_stream(), get_solver(), sz, o.m_terms.c_ptr(), o.m_weights.c_ptr()); } + + } void context::display(std::ostream& out) { @@ -1235,6 +1271,9 @@ namespace opt { } void context::display_assignment(std::ostream& out) { + if (m_scoped_state.m_objectives.size() != m_objectives.size()) { + throw default_exception("check-sat has not been called with latest objectives"); + } out << "(objectives\n"; for (unsigned i = 0; i < m_scoped_state.m_objectives.size(); ++i) { objective const& obj = m_scoped_state.m_objectives[i]; @@ -1358,13 +1397,15 @@ namespace opt { } void context::clear_state() { - set_pareto(0); + m_pareto = nullptr; m_box_index = UINT_MAX; m_model.reset(); + m_model_fixed.reset(); } void context::set_pareto(pareto_base* p) { m_pareto = p; + m_pareto1 = p != nullptr; } void context::collect_statistics(statistics& stats) const { @@ -1374,9 +1415,8 @@ namespace opt { if (m_simplify) { m_simplify->collect_statistics(stats); } - map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); - for (; it != end; ++it) { - it->m_value->collect_statistics(stats); + for (auto const& kv : m_maxsmts) { + kv.m_value->collect_statistics(stats); } get_memory_statistics(stats); get_rlimit_statistics(m.limit(), stats); @@ -1394,10 +1434,12 @@ namespace opt { if (m_solver) { m_solver->updt_params(m_params); } + if (m_sat_solver) { + m_sat_solver->updt_params(m_params); + } m_optsmt.updt_params(m_params); - map_t::iterator it = m_maxsmts.begin(), end = m_maxsmts.end(); - for (; it != end; ++it) { - it->m_value->updt_params(m_params); + for (auto & kv : m_maxsmts) { + kv.m_value->updt_params(m_params); } opt_params _p(p); m_enable_sat = _p.enable_sat(); @@ -1407,21 +1449,21 @@ namespace opt { } std::string context::to_string() const { - return to_string(m_scoped_state.m_hard, m_scoped_state.m_objectives); + return to_string(false, m_scoped_state.m_hard, m_scoped_state.m_objectives); } std::string context::to_string_internal() const { - return to_string(m_hard_constraints, m_objectives); + return to_string(true, m_hard_constraints, m_objectives); } - std::string context::to_string(expr_ref_vector const& hard, vector const& objectives) const { + std::string context::to_string(bool is_internal, expr_ref_vector const& hard, vector const& objectives) const { smt2_pp_environment_dbg env(m); ast_pp_util visitor(m); std::ostringstream out; visitor.collect(hard); + model_converter_ref mc = concat(m_model_converter.get(), m_fm.get()); - for (unsigned i = 0; i < objectives.size(); ++i) { - objective const& obj = objectives[i]; + for (objective const& obj : objectives) { switch(obj.m_type) { case O_MAXIMIZE: case O_MINIMIZE: @@ -1436,10 +1478,16 @@ namespace opt { } } + if (is_internal && mc) { + mc->set_env(&visitor); + } + + param_descrs descrs; + collect_param_descrs(descrs); + m_params.display_smt2(out, "opt", descrs); visitor.display_decls(out); visitor.display_asserts(out, hard, m_pp_neat); - for (unsigned i = 0; i < objectives.size(); ++i) { - objective const& obj = objectives[i]; + for (objective const& obj : objectives) { switch(obj.m_type) { case O_MAXIMIZE: out << "(maximize "; @@ -1474,30 +1522,48 @@ namespace opt { break; } } - - param_descrs descrs; - collect_param_descrs(descrs); - m_params.display_smt2(out, "opt", descrs); - + if (is_internal && mc) { + mc->display(out); + } + if (is_internal && mc) { + mc->set_env(nullptr); + } out << "(check-sat)\n"; return out.str(); } + void context::validate_model() { + return; + if (!gparams::get_ref().get_bool("model_validate", false)) return; + expr_ref_vector fmls(m); + get_hard_constraints(fmls); + expr_ref tmp(m); + model_ref mdl; + get_model(mdl); + mdl->set_model_completion(true); + for (expr * f : fmls) { + if (!mdl->is_true(f)) { + //IF_VERBOSE(0, m_fm->display(verbose_stream() << "fm\n")); + IF_VERBOSE(0, m_model_converter->display(verbose_stream() << "mc\n")); + IF_VERBOSE(0, verbose_stream() << "Failed to validate " << mk_pp(f, m) << "\n" << tmp << "\n"); + IF_VERBOSE(0, model_smt2_pp(verbose_stream(), m, *mdl, 0)); + IF_VERBOSE(11, verbose_stream() << to_string_internal() << "\n"); + exit(0); + } + } + } + void context::validate_maxsat(symbol const& id) { maxsmt& ms = *m_maxsmts.find(id); TRACE("opt", tout << "Validate: " << id << "\n";); - for (unsigned i = 0; i < m_objectives.size(); ++i) { - objective const& obj = m_objectives[i]; + for (objective const& obj : m_objectives) { if (obj.m_id == id && obj.m_type == O_MAXSMT) { SASSERT(obj.m_type == O_MAXSMT); rational value(0); expr_ref val(m); for (unsigned i = 0; i < obj.m_terms.size(); ++i) { - bool evaluated = m_model->eval(obj.m_terms[i], val); - SASSERT(evaluated); - CTRACE("opt", evaluated && !m.is_true(val) && !m.is_false(val), tout << mk_pp(obj.m_terms[i], m) << " " << val << "\n";); - CTRACE("opt", !evaluated, tout << mk_pp(obj.m_terms[i], m) << "\n";); - if (evaluated && !m.is_true(val)) { + auto const& t = obj.m_terms[i]; + if (!m_model->is_true(t)) { value += obj.m_weights[i]; } // TBD: check that optimal was not changed. @@ -1522,14 +1588,13 @@ namespace opt { if (m_optsmt.objective_is_model_valid(obj.m_index) && n.get_infinity().is_zero() && n.get_infinitesimal().is_zero() && - m_model->eval(obj.m_term, val) && - is_numeral(val, r1)) { + is_numeral((*m_model)(obj.m_term), r1)) { rational r2 = n.get_rational(); if (obj.m_type == O_MINIMIZE) { r1.neg(); } CTRACE("opt", r1 != r2, tout << obj.m_term << " evaluates to " << r1 << " but has objective " << r2 << "\n";); - CTRACE("opt", r1 != r2, model_smt2_pp(tout, m, *m_model, 0);); + CTRACE("opt", r1 != r2, tout << *m_model;); SASSERT(r1 == r2); } break; @@ -1537,8 +1602,7 @@ namespace opt { case O_MAXSMT: { rational value(0); for (unsigned i = 0; i < obj.m_terms.size(); ++i) { - bool evaluated = m_model->eval(obj.m_terms[i], val); - if (evaluated && !m.is_true(val)) { + if (!m_model->is_true(obj.m_terms[i])) { value += obj.m_weights[i]; } // TBD: check that optimal was not changed. @@ -1561,8 +1625,8 @@ namespace opt { if (!m_arith.is_real(m_objectives[0].m_term)) { return false; } - for (unsigned i = 0; i < m_hard_constraints.size(); ++i) { - if (has_quantifiers(m_hard_constraints[i].get())) { + for (expr* fml : m_hard_constraints) { + if (has_quantifiers(fml)) { return true; } } diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 1ae60ef87..1172e68b6 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -19,16 +19,17 @@ Notes: #define OPT_CONTEXT_H_ #include "ast/ast.h" +#include "ast/arith_decl_plugin.h" +#include "ast/bv_decl_plugin.h" +#include "tactic/model_converter.h" +#include "tactic/tactic.h" +#include "qe/qsat.h" #include "opt/opt_solver.h" #include "opt/opt_pareto.h" #include "opt/optsmt.h" #include "opt/maxsmt.h" -#include "tactic/model_converter.h" -#include "tactic/tactic.h" -#include "ast/arith_decl_plugin.h" -#include "ast/bv_decl_plugin.h" #include "cmd_context/cmd_context.h" -#include "qe/qsat.h" + namespace opt { @@ -45,7 +46,7 @@ namespace opt { class maxsat_context { public: - virtual filter_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification. + virtual generic_model_converter& fm() = 0; // converter that removes fresh names introduced by simplification. virtual bool sat_enabled() const = 0; // is using th SAT solver core enabled? virtual solver& get_solver() = 0; // retrieve solver object (SAT or SMT solver) virtual ast_manager& get_manager() const = 0; @@ -57,6 +58,7 @@ namespace opt { virtual unsigned num_objectives() = 0; virtual bool verify_model(unsigned id, model* mdl, rational const& v) = 0; virtual void set_model(model_ref& _m) = 0; + virtual void model_updated(model* mdl) = 0; }; /** @@ -144,7 +146,8 @@ namespace opt { ref m_opt_solver; ref m_solver; ref m_sat_solver; - scoped_ptr m_pareto; + scoped_ptr m_pareto; + bool m_pareto1; scoped_ptr m_qmax; sref_vector m_box_models; unsigned m_box_index; @@ -153,9 +156,11 @@ namespace opt { map_t m_maxsmts; scoped_state m_scoped_state; vector m_objectives; - model_ref m_model; + model_ref m_model; model_converter_ref m_model_converter; - filter_model_converter m_fm; + generic_model_converter_ref m_fm; + sref_vector m_model_fixed; + unsigned m_model_counter; obj_map m_objective_fns; obj_map m_objective_orig; func_decl_ref_vector m_objective_refs; @@ -170,7 +175,7 @@ namespace opt { std::string m_unknown; public: context(ast_manager& m); - virtual ~context(); + ~context() override; unsigned add_soft_constraint(expr* f, rational const& w, symbol const& id); unsigned add_objective(app* t, bool is_max); void add_hard_constraint(expr* f); @@ -178,30 +183,31 @@ namespace opt { void get_hard_constraints(expr_ref_vector& hard); expr_ref get_objective(unsigned i); - virtual void push(); - virtual void pop(unsigned n); - virtual bool empty() { return m_scoped_state.m_objectives.empty(); } - virtual void set_hard_constraints(ptr_vector & hard); - virtual lbool optimize(); - virtual void set_model(model_ref& _m) { m_model = _m; } - virtual void get_model(model_ref& _m); - virtual void get_box_model(model_ref& _m, unsigned index); - virtual void fix_model(model_ref& _m); - virtual void collect_statistics(statistics& stats) const; - virtual proof* get_proof() { return 0; } - virtual void get_labels(svector & r); - virtual void get_unsat_core(ptr_vector & r); - virtual std::string reason_unknown() const; - virtual void set_reason_unknown(char const* msg) { m_unknown = msg; } + 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; + lbool optimize() 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; + void fix_model(model_ref& _m) override; + void collect_statistics(statistics& stats) const override; + proof* get_proof() override { return nullptr; } + void get_labels(svector & r) override; + void get_unsat_core(expr_ref_vector & r) override; + std::string reason_unknown() const override; + void set_reason_unknown(char const* msg) override { m_unknown = msg; } + + void display_assignment(std::ostream& out) override; + bool is_pareto() override { return m_pareto.get() != nullptr; } + void set_logic(symbol const& s) override { m_logic = s; } - virtual void display_assignment(std::ostream& out); - virtual bool is_pareto() { return m_pareto.get() != 0; } - virtual void set_logic(symbol const& s) { m_logic = s; } void set_clausal(bool f) { m_is_clausal = f; } void display(std::ostream& out); static void collect_param_descrs(param_descrs & r); - virtual void updt_params(params_ref const& p); + void updt_params(params_ref const& p) override; params_ref& get_params() { return m_params; } expr_ref get_lower(unsigned idx); @@ -213,23 +219,25 @@ namespace opt { std::string to_string() const; - virtual unsigned num_objectives() { return m_scoped_state.m_objectives.size(); } - virtual expr_ref mk_gt(unsigned i, model_ref& model); - virtual expr_ref mk_ge(unsigned i, model_ref& model); - virtual expr_ref mk_le(unsigned i, model_ref& model); + unsigned num_objectives() override { return m_scoped_state.m_objectives.size(); } + expr_ref mk_gt(unsigned i, model_ref& model) override; + expr_ref mk_ge(unsigned i, model_ref& model) override; + expr_ref mk_le(unsigned i, model_ref& model) override; - virtual smt::context& smt_context() { return m_opt_solver->get_context(); } - virtual filter_model_converter& fm() { return m_fm; } - virtual bool sat_enabled() const { return 0 != m_sat_solver.get(); } - virtual solver& get_solver(); - virtual ast_manager& get_manager() const { return this->m; } - virtual params_ref& params() { return m_params; } - virtual void enable_sls(bool force); - virtual symbol const& maxsat_engine() const { return m_maxsat_engine; } - virtual void get_base_model(model_ref& _m); + generic_model_converter& fm() override { return *m_fm; } + smt::context& smt_context() override { return m_opt_solver->get_context(); } + bool sat_enabled() const override { return nullptr != m_sat_solver.get(); } + solver& get_solver() override; + ast_manager& get_manager() const override { return this->m; } + params_ref& params() override { return m_params; } + void enable_sls(bool force) override; + symbol const& maxsat_engine() const override { return m_maxsat_engine; } + void get_base_model(model_ref& _m) override; - virtual bool verify_model(unsigned id, model* mdl, rational const& v); + bool verify_model(unsigned id, model* mdl, rational const& v) override; + + void model_updated(model* mdl) override; private: lbool execute(objective const& obj, bool committed, bool scoped); @@ -254,7 +262,7 @@ namespace opt { vector& weights, rational& offset, bool& neg, symbol& id, expr_ref& orig_term, unsigned& index); void purify(app_ref& term); - app* purify(filter_model_converter_ref& fm, expr* e); + app* purify(generic_model_converter_ref& fm, expr* e); bool is_mul_const(expr* e); expr* mk_maximize(unsigned index, app* t); expr* mk_minimize(unsigned index, app* t); @@ -278,25 +286,26 @@ namespace opt { struct is_propositional_fn; bool is_propositional(expr* e); - void init_solver(); - void update_solver(); - void setup_arith_solver(); - void add_maxsmt(symbol const& id, unsigned index); - void set_simplify(tactic *simplify); - void set_pareto(pareto_base* p); - void clear_state(); + void init_solver(); + void update_solver(); + void setup_arith_solver(); + void add_maxsmt(symbol const& id, unsigned index); + void set_simplify(tactic *simplify); + void set_pareto(pareto_base* p); + void clear_state(); bool is_numeral(expr* e, rational& n) const; void display_objective(std::ostream& out, objective const& obj) const; void display_bounds(std::ostream& out, bounds_t const& b) const; - std::string to_string(expr_ref_vector const& hard, vector const& objectives) const; + std::string to_string(bool is_internal, expr_ref_vector const& hard, vector const& objectives) const; std::string to_string_internal() const; void validate_lex(); void validate_maxsat(symbol const& id); + void validate_model(); void display_benchmark(); diff --git a/src/opt/opt_params.pyg b/src/opt/opt_params.pyg index cfcc5e47e..f72bafbd8 100644 --- a/src/opt/opt_params.pyg +++ b/src/opt/opt_params.pyg @@ -3,8 +3,9 @@ def_module_params('opt', export=True, params=(('optsmt_engine', SYMBOL, 'basic', "select optimization engine: 'basic', 'farkas', 'symba'"), ('maxsat_engine', SYMBOL, 'maxres', "select engine for maxsat: 'core_maxsat', 'wmax', 'maxres', 'pd-maxres'"), - ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', or 'box'"), + ('priority', SYMBOL, 'lex', "select how to priortize objectives: 'lex' (lexicographic), 'pareto', 'box'"), ('dump_benchmarks', BOOL, False, 'dump benchmarks for profiling'), + ('solution_prefix', SYMBOL, '', "path prefix to dump intermediary, but non-optimal, solutions"), ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (UINT_MAX and 0 mean no timeout)'), ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), ('enable_sls', BOOL, False, 'enable SLS tuning during weighted maxsast'), diff --git a/src/opt/opt_pareto.cpp b/src/opt/opt_pareto.cpp index 026d10abc..fe92c3ba6 100644 --- a/src/opt/opt_pareto.cpp +++ b/src/opt/opt_pareto.cpp @@ -20,6 +20,7 @@ Notes: #include "opt/opt_pareto.h" #include "ast/ast_pp.h" +#include "ast/ast_util.h" #include "model/model_smt2_pp.h" namespace opt { @@ -29,7 +30,7 @@ namespace opt { lbool gia_pareto::operator()() { expr_ref fml(m); - lbool is_sat = m_solver->check_sat(0, 0); + lbool is_sat = m_solver->check_sat(0, nullptr); if (is_sat == l_true) { { solver::scoped_push _s(*m_solver.get()); @@ -39,13 +40,14 @@ namespace opt { } m_solver->get_model(m_model); m_solver->get_labels(m_labels); + m_model->set_model_completion(true); IF_VERBOSE(1, model_ref mdl(m_model); cb.fix_model(mdl); model_smt2_pp(verbose_stream() << "new model:\n", m, *mdl, 0);); // TBD: we can also use local search to tune solution coordinate-wise. mk_dominates(); - is_sat = m_solver->check_sat(0, 0); + is_sat = m_solver->check_sat(0, nullptr); } } if (is_sat == l_undef) { @@ -66,8 +68,8 @@ namespace opt { fmls.push_back(cb.mk_ge(i, m_model)); gt.push_back(cb.mk_gt(i, m_model)); } - fmls.push_back(m.mk_or(gt.size(), gt.c_ptr())); - fml = m.mk_and(fmls.size(), fmls.c_ptr()); + 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);); m_solver->assert_expr(fml); @@ -80,7 +82,7 @@ namespace opt { for (unsigned i = 0; i < sz; ++i) { le.push_back(cb.mk_le(i, m_model)); } - fml = m.mk_not(m.mk_and(le.size(), le.c_ptr())); + fml = m.mk_not(mk_and(le)); IF_VERBOSE(10, verbose_stream() << "not dominated by: " << fml << "\n";); TRACE("opt", tout << fml << "\n";); m_solver->assert_expr(fml); @@ -91,13 +93,14 @@ namespace opt { lbool oia_pareto::operator()() { solver::scoped_push _s(*m_solver.get()); - lbool is_sat = m_solver->check_sat(0, 0); + lbool is_sat = m_solver->check_sat(0, nullptr); if (m.canceled()) { is_sat = l_undef; } if (is_sat == l_true) { m_solver->get_model(m_model); m_solver->get_labels(m_labels); + m_model->set_model_completion(true); mk_not_dominated_by(); } return is_sat; diff --git a/src/opt/opt_pareto.h b/src/opt/opt_pareto.h index c0ccc93da..5a2aab6f3 100644 --- a/src/opt/opt_pareto.h +++ b/src/opt/opt_pareto.h @@ -87,9 +87,9 @@ namespace opt { params_ref & p): pareto_base(m, cb, s, p) { } - virtual ~gia_pareto() {} + ~gia_pareto() override {} - virtual lbool operator()(); + lbool operator()() override; }; // opportunistic improvement algorithm. @@ -101,9 +101,9 @@ namespace opt { params_ref & p): pareto_base(m, cb, s, p) { } - virtual ~oia_pareto() {} + ~oia_pareto() override {} - virtual lbool operator()(); + lbool operator()() override; }; } diff --git a/src/opt/opt_parse.cpp b/src/opt/opt_parse.cpp index 2cba7561f..fec97d675 100644 --- a/src/opt/opt_parse.cpp +++ b/src/opt/opt_parse.cpp @@ -36,40 +36,35 @@ public: void next() { m_val = m_stream.get(); } bool eof() const { return ch() == EOF; } unsigned line() const { return m_line; } - void skip_whitespace(); - void skip_space(); - void skip_line(); + void skip_whitespace() { + while ((ch() >= 9 && ch() <= 13) || ch() == 32) { + if (ch() == 10) ++m_line; + next(); + } + } + void skip_space() { + while (ch() != 10 && ((ch() >= 9 && ch() <= 13) || ch() == 32)) + next(); + } + void skip_line() { + while(true) { + if (eof()) { + return; + } + if (ch() == '\n') { + ++m_line; + next(); + return; + } + next(); + } + } bool parse_token(char const* token); int parse_int(); unsigned parse_unsigned(); }; -void opt_stream_buffer::skip_whitespace() { - while ((ch() >= 9 && ch() <= 13) || ch() == 32) { - if (ch() == 10) ++m_line; - next(); - } -} - -void opt_stream_buffer::skip_space() { - while (ch() != 10 && ((ch() >= 9 && ch() <= 13) || ch() == 32)) { - next(); - } -} -void opt_stream_buffer::skip_line() { - while(true) { - if (eof()) { - return; - } - if (ch() == '\n') { - ++m_line; - next(); - return; - } - next(); - } -} bool opt_stream_buffer::parse_token(char const* token) { skip_whitespace(); @@ -314,4 +309,542 @@ void parse_opb(opt::context& opt, std::istream& is, unsigned_vector& h) { opb.parse(); } +/** + * \brief Parser for a modest subset of the CPLEX LP format. + * Reference: http://eaton.math.rpi.edu/cplex90html/reffileformatscplex/reffileformatscplex5.html + */ + +struct asymbol { + bool m_is_num; + symbol m_sym; + rational m_num; + unsigned m_line; + asymbol(symbol const& s, unsigned l): m_is_num(false), m_sym(s), m_line(l) {} + asymbol(rational const& r, unsigned l): m_is_num(true), m_num(r), m_line(l) {} +}; + +class lp_tokenizer { + vector m_tokens; + unsigned m_pos; + svector m_buffer; +public: + lp_tokenizer(opt_stream_buffer& in): + m_pos(0) + { + parse_all(in); + } + + symbol const& peek(unsigned i) { + if (i + m_pos >= m_tokens.size()) { + return symbol::null; + } + return m_tokens[i + m_pos].m_sym; + } + + bool peek_num(unsigned i) { + if (i + m_pos >= m_tokens.size()) { + return false; + } + return m_tokens[i + m_pos].m_is_num; + } + + rational const& get_num(unsigned i) { + return m_tokens[i + m_pos].m_num; + } + + void next(unsigned delta = 1) { + m_pos += delta; + } + + bool eof() const { + return m_pos == m_tokens.size(); + } + + unsigned line() const { + if (m_pos < m_tokens.size()) return m_tokens[m_pos].m_line; + return 0; + } + +private: + + bool is_separator(char c) { + return c == '\n' || c == '\\' || c == '*' || c == '+'; + } + + char lower(char c) { + if ('A' <= c && c <= 'Z') + return c - ('A' - 'a'); + return c; + } + + void parse_all(opt_stream_buffer& in) { + while (!in.eof()) { + in.skip_whitespace(); + char c = in.ch(); + if (c == '\\') { + in.skip_line(); + continue; + } + bool neg = false; + if (c == '-') { + in.next(); + c = in.ch(); + m_buffer.reset(); + m_buffer.push_back('-'); + if (is_num(c)) { + neg = true; + } + else { + while (!is_ws(c) && !in.eof()) { + m_buffer.push_back(c); + in.next(); + c = in.ch(); + } + m_buffer.push_back(0); + m_tokens.push_back(asymbol(symbol(m_buffer.c_ptr()), in.line())); + continue; + } + } + + if (is_num(c)) { + rational n(0); + unsigned div = 1; + while (is_num(c) && !in.eof()) { + n = n*rational(10) + rational(c - '0'); + in.next(); + c = in.ch(); + } + if (c == '.') { + in.next(); + while (is_num(c) && !in.eof()) { + n = n*rational(10) + rational(c - '0'); + in.next(); + div *= 10; + c = in.ch(); + } + } + if (div > 1) n = n / rational(div); + if (neg) n.neg(); + m_tokens.push_back(asymbol(n, in.line())); + continue; + } + m_buffer.reset(); + if (is_alpha(c)) { + while (is_sym(c) && !in.eof()) { + m_buffer.push_back(lower(c)); + in.next(); + c = in.ch(); + } + } + else { + while (!is_ws(c) && !in.eof()) { + m_buffer.push_back(c); + in.next(); + c = in.ch(); + } + } + m_buffer.push_back(0); + m_tokens.push_back(asymbol(symbol(m_buffer.c_ptr()), in.line())); + } + } + + bool is_alpha(char c) const { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); + } + + bool is_num(char c) const { + return '0' <= c && c <= '9'; + } + + bool is_ws(char c) const { + return c == ' ' || c == '\n' || c == '\t'; + } + + bool is_sym(char c) const { + return + is_alpha(c) || + is_num(c) || + c == '!' || + c == '"' || + c == '#' || + c == '$' || + c == '%' || + c == '&' || + c == '{' || + c == '}' || + c == ',' || + c == '_' || + c == '.' || + c == ';' || + c == '?' || + c == '@' || + c == '`' || + c == '\'' || + c == '(' || + c == ')' || + c == '~'; + } + +}; + +class lp_parse { + typedef vector > lin_term; + + struct objective { + bool m_is_max; + symbol m_name; + lin_term m_expr; + }; + + enum rel_op { + le, ge, eq + }; + + struct constraint { + symbol m_name; + symbol m_bvar; + rational m_bval; + lin_term m_expr; + rel_op m_rel; + rational m_bound; + constraint(symbol const& name, symbol const& v, rational const& val, lin_term& terms, rel_op r, rational const& bound): + m_name(name), m_bvar(v), m_bval(val), m_expr(terms), m_rel(r), m_bound(bound) {} + }; + + struct bound { + optional m_lo, m_hi; + bool m_int; + bound() : m_int(false) {} + }; + + opt::context& opt; + unsigned_vector& m_h; + lp_tokenizer tok; + objective m_objective; + vector m_constraints; + map m_bounds; + +public: + + lp_parse(opt::context& opt, opt_stream_buffer& in, unsigned_vector& h) : + opt(opt), m_h(h), tok(in) {} + + void parse() { + parse_objective(); + if (!try_subject_to()) { + error("subject to section expected"); + return; + } + + while (!is_section()) { + parse_constraint(); + } + + while (true) { + if (is_bounds()) { + tok.next(); + while (!is_section()) { + parse_bound(); + } + } + else if (is_binary()) { + tok.next(); + while (!is_section()) { + parse_binary(); + } + } + else if (is_general()) { + tok.next(); + while (!is_section()) { + parse_general(); + } + } + else { + break; + } + } + post_process(); + } + +private: + + void error(char const* msg) { + std::ostringstream ous; + ous << tok.line() << ": " << msg << " got: " << peek(0) << "\n"; + throw default_exception(ous.str()); + } + + symbol const& peek(unsigned i) { return tok.peek(i); } + + bool try_accept(char const * token) { + if (peek(0) == token) { + tok.next(); + return true; + } + return false; + } + + void parse_objective() { + m_objective.m_is_max = minmax(); + if (peek(1) == ":") { + m_objective.m_name = peek(0); + tok.next(2); + } + parse_expr(m_objective.m_expr); + } + + bool minmax() { + if (try_accept("minimize")) + return false; + if (try_accept("min")) + return false; + if (try_accept("maximize")) + return true; + if (try_accept("max")) + return true; + error("expected min or max objective"); + return false; + } + + void parse_constraint() { + symbol name; + if (peek(1) == ":") { + name = peek(0); + tok.next(2); + } + rational val(0); + symbol var; + parse_indicator(var, val); + lin_term terms; + parse_expr(terms); + rel_op op = parse_relation(); + rational rhs = tok.get_num(0); + tok.next(); + m_constraints.push_back(constraint(name, var, val, terms, op, rhs)); + } + + void parse_expr(lin_term& terms) { + bool pos = true; + if (peek(0) == "-") { + pos = false; + tok.next(); + } + while (peek(0) == "+") { + tok.next(); + } + terms.push_back(parse_term()); + if (!pos) terms.back().first = -terms.back().first; + while (peek(0) == "+" || peek(0) == "-") { + bool pos = peek(0) == "+"; + tok.next(); + terms.push_back(parse_term()); + if (!pos) terms.back().first = -terms.back().first; + } + } + + std::pair parse_term() { + std::pair r(rational::one(), peek(0)); + if (tok.peek_num(0)) { + r.first = tok.get_num(0); + r.second = peek(1); + tok.next(2); + } + else { + tok.next(1); + } + return r; + } + + rel_op parse_relation() { + if (try_accept("<=")) return le; + if (try_accept("=<")) return le; + if (try_accept(">=")) return ge; + if (try_accept("=>")) return ge; + if (try_accept("=")) return eq; + error("expected relation"); + return eq; + } + + bool peek_le(unsigned pos) { + return peek(pos) == "<=" || peek(pos) == "=<"; + } + + bool peek_minus_infty(unsigned pos) { + return peek(pos) == "-" && (peek(pos+1) == "inf" || peek(pos+1) == "infinity"); + } + + bool peek_plus_infty(unsigned pos) { + return peek(pos) == "+" && (peek(pos+1) == "inf" || peek(pos+1) == "infinity"); + } + + void parse_indicator(symbol& var, rational& val) { + if (peek(1) == "=" && tok.peek_num(2) && peek(3) == "->") { + var = peek(0); + val = tok.get_num(2); + tok.next(4); + } + } + + bool try_subject_to() { + if (try_accept("subject") && try_accept("to")) return true; + if (try_accept("such") && try_accept("that")) return true; + if (try_accept("st")) return true; + if (try_accept("s.t.")) return true; + return false; + } + + bool is_section() { return is_general() || is_binary() || is_bounds() || is_end();} + bool is_bounds() { return peek(0) == "bounds"; } + bool is_general() { return peek(0) == "general" || peek(0) == "gen" || peek(0) == "generals"; } + bool is_binary() { return peek(0) == "binary" || peek(0) == "binaries" || peek(0) == "bin"; } + bool is_end() { return peek(0) == "end" || tok.eof(); } + + // lo <= x + // x <= hi + // lo <= x <= hi + // + void parse_bound() { + symbol v; + if (peek_le(1) && tok.peek_num(0)) { + rational lhs = tok.get_num(0); + v = peek(2); + update_lower(lhs, v); + tok.next(3); + parse_upper(v); + } + else if (peek_minus_infty(0) && peek_le(2)) { + v = peek(3); + tok.next(4); + parse_upper(v); + } + else if (peek_plus_infty(2) && peek_le(1)) { + tok.next(4); + } + else if (peek_le(1) && tok.peek_num(2)) { + v = peek(0); + tok.next(2); + rational rhs = tok.get_num(0); + update_upper(v, rhs); + tok.next(1); + } + else { + error("bound expected"); + } + } + + void parse_upper(symbol const& v) { + if (peek_le(0) && tok.peek_num(1)) { + rational rhs = tok.get_num(1); + update_upper(v, rhs); + tok.next(2); + } + else if (peek_le(0) && peek_plus_infty(1)) { + tok.next(3); + } + + } + + void update_lower(rational const& r, symbol const& v) { + bound b; + m_bounds.find(v, b); + b.m_lo = r; + m_bounds.insert(v, b); + } + + void update_upper(symbol const& v, rational const& r) { + bound b; + if (!m_bounds.find(v, b)) { + // set the lower bound to default 0 + b.m_lo = rational::zero(); + } + b.m_hi = r; + m_bounds.insert(v, b); + } + + void parse_binary() { + symbol const& v = peek(0); + update_lower(rational::zero(), v); + update_upper(v, rational::one()); + m_bounds[v].m_int = true; + tok.next(); + } + + void parse_general() { + symbol const& v = peek(0); + bound b; + m_bounds.find(v, b); + b.m_int = true; + m_bounds.insert(v, b); + tok.next(); + } + + void post_process() { + ast_manager& m = opt.get_manager(); + arith_util a(m); + for (constraint const& c : m_constraints) { + expr_ref fml(m); + expr_ref term = process_terms(c.m_expr); + bool is_int = a.is_int(term) && c.m_bound.is_int(); + switch (c.m_rel) { + case le: fml = a.mk_le(term, a.mk_numeral(c.m_bound, is_int)); break; + case ge: fml = a.mk_ge(term, a.mk_numeral(c.m_bound, is_int)); break; + case eq: fml = m.mk_eq(term, a.mk_numeral(c.m_bound, is_int)); break; + } + if (c.m_bvar != symbol::null) { + term = mk_var(c.m_bvar); + bool is_int = c.m_bval.is_int() && a.is_int(term); + term = m.mk_eq(mk_var(c.m_bvar), a.mk_numeral(c.m_bval, is_int)); + fml = m.mk_implies(term, fml); + } + opt.add_hard_constraint(fml); + } + for (auto const& kv : m_bounds) { + bound const& b = kv.m_value; + expr_ref term = mk_var(kv.m_key); + if (b.m_lo) { + bool is_int = b.m_lo->is_int() && a.is_int(term); + opt.add_hard_constraint(a.mk_le(a.mk_numeral(*b.m_lo, is_int), term)); + } + if (b.m_hi) { + bool is_int = b.m_hi->is_int() && a.is_int(term); + opt.add_hard_constraint(a.mk_le(term, a.mk_numeral(*b.m_hi, is_int))); + } + } + expr_ref term = process_terms(m_objective.m_expr); + m_h.push_back(opt.add_objective(to_app(term), m_objective.m_is_max)); + } + + expr_ref process_terms(lin_term const& terms) { + ast_manager& m = opt.get_manager(); + arith_util a(m); + expr_ref_vector result(m); + for (auto const& kv : terms) { + expr_ref term = mk_var(kv.second); + if (!kv.first.is_one()) { + bool is_int = kv.first.is_int() && a.is_int(term); + term = a.mk_mul(a.mk_numeral(kv.first, is_int), term); + } + result.push_back(term); + } + return expr_ref(a.mk_add(result.size(), result.c_ptr()), m); + } + + expr_ref mk_var(symbol const& v) { + ast_manager& m = opt.get_manager(); + arith_util a(m); + bound b; + if (!m_bounds.find(v, b)) { + b.m_lo = rational::zero(); + m_bounds.insert(v, b); + } + return expr_ref(m.mk_const(v, b.m_int ? a.mk_int() : a.mk_real()), m); + } + +}; + +void parse_lp(opt::context& opt, std::istream& is, unsigned_vector& h) { + opt_stream_buffer _is(is); + lp_parse lp(opt, _is, h); + lp.parse(); +} diff --git a/src/opt/opt_parse.h b/src/opt/opt_parse.h index b058efcac..6cf92bd9c 100644 --- a/src/opt/opt_parse.h +++ b/src/opt/opt_parse.h @@ -23,6 +23,8 @@ void parse_wcnf(opt::context& opt, std::istream& is, unsigned_vector& h); void parse_opb(opt::context& opt, std::istream& is, unsigned_vector& h); +void parse_lp(opt::context& opt, std::istream& is, unsigned_vector& h); + #endif /* OPT_PARSE_H_ */ diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index 49b48e68f..ca0474314 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -37,7 +37,7 @@ Notes: namespace opt { opt_solver::opt_solver(ast_manager & mgr, params_ref const & p, - filter_model_converter& fm): + generic_model_converter& fm): solver_na2as(mgr), m_params(p), m_context(mgr, m_params), @@ -69,7 +69,7 @@ namespace opt { solver* opt_solver::translate(ast_manager& m, params_ref const& p) { UNREACHABLE(); - return 0; + return nullptr; } void opt_solver::collect_param_descrs(param_descrs & r) { @@ -80,7 +80,7 @@ namespace opt { m_context.collect_statistics(st); } - void opt_solver::assert_expr(expr * t) { + void opt_solver::assert_expr_core(expr * t) { if (has_quantifiers(t)) { m_params.m_relevancy_lvl = 2; } @@ -227,9 +227,13 @@ namespace opt { smt::theory_var v = m_objective_vars[i]; bool has_shared = false; inf_eps val = get_optimizer().maximize(v, blocker, has_shared); + get_model(m_model); inf_eps val2; m_valid_objectives[i] = true; TRACE("opt", tout << (has_shared?"has shared":"non-shared") << "\n";); + if (!m_models[i]) { + set_model(i); + } if (m_context.get_context().update_model(has_shared)) { if (has_shared && val != current_objective_value(i)) { decrement_value(i, val); @@ -247,7 +251,7 @@ namespace opt { tout << "objective: " << mk_pp(m_objective_terms[i].get(), m) << "\n"; tout << "maximal value: " << val << "\n"; tout << "new condition: " << blocker << "\n"; - model_smt2_pp(tout << "update model:\n", m, *m_models[i], 0); }); + if (m_models[i]) model_smt2_pp(tout << "update model:\n", m, *m_models[i], 0); }); } void opt_solver::set_model(unsigned i) { @@ -261,7 +265,7 @@ namespace opt { expr_ref ge = mk_ge(i, val); TRACE("opt", tout << ge << "\n";); assert_expr(ge); - lbool is_sat = m_context.check(0, 0); + lbool is_sat = m_context.check(0, nullptr); is_sat = adjust_result(is_sat); if (is_sat == l_true) { set_model(i); @@ -290,15 +294,17 @@ namespace opt { return r; } - void opt_solver::get_unsat_core(ptr_vector & r) { + void opt_solver::get_unsat_core(expr_ref_vector & r) { + r.reset(); unsigned sz = m_context.get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { r.push_back(m_context.get_unsat_core_expr(i)); } } - void opt_solver::get_model(model_ref & m) { + void opt_solver::get_model_core(model_ref & m) { m_context.get_model(m); + if (!m) m = m_model; else m_model = m; } proof * opt_solver::get_proof() { @@ -316,7 +322,7 @@ namespace opt { void opt_solver::get_labels(svector & r) { r.reset(); buffer tmp; - m_context.get_relevant_labels(0, tmp); + m_context.get_relevant_labels(nullptr, tmp); r.append(tmp.size(), tmp.c_ptr()); } @@ -340,7 +346,7 @@ namespace opt { m_objective_values.push_back(inf_eps(rational(-1), inf_rational())); m_objective_terms.push_back(term); m_valid_objectives.push_back(true); - m_models.push_back(0); + m_models.push_back(nullptr); return v; } diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index 4bd23c120..39562ec54 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -30,7 +30,7 @@ Notes: #include "smt/params/smt_params.h" #include "smt/smt_types.h" #include "smt/theory_opt.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace opt { @@ -70,9 +70,10 @@ namespace opt { smt_params m_params; smt::kernel m_context; ast_manager& m; - filter_model_converter& m_fm; + generic_model_converter& m_fm; progress_callback * m_callback; symbol m_logic; + model_ref m_model; svector m_objective_vars; vector m_objective_values; sref_vector m_models; @@ -84,29 +85,31 @@ namespace opt { bool m_first; bool m_was_unknown; public: - opt_solver(ast_manager & m, params_ref const & p, filter_model_converter& fm); - virtual ~opt_solver(); + opt_solver(ast_manager & m, params_ref const & p, generic_model_converter& fm); + ~opt_solver() override; + + solver* translate(ast_manager& m, params_ref const& p) override; + void updt_params(params_ref const& p) override; + void collect_param_descrs(param_descrs & r) override; + void collect_statistics(statistics & st) const override; + void assert_expr_core(expr * t) override; + void push_core() override; + void pop_core(unsigned n) override; + lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override; + void get_unsat_core(expr_ref_vector & r) override; + void get_model_core(model_ref & _m) override; + proof * get_proof() override; + std::string reason_unknown() const override; + void set_reason_unknown(char const* msg) override; + void get_labels(svector & r) override; + void set_progress_callback(progress_callback * callback) override; + unsigned get_num_assertions() const override; + expr * get_assertion(unsigned idx) const override; + ast_manager& get_manager() const override { return m; } + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override; + lbool preferred_sat(expr_ref_vector const& asms, vector& cores) override; + expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); } - virtual solver* translate(ast_manager& m, params_ref const& p); - virtual void updt_params(params_ref const& p); - virtual void collect_param_descrs(param_descrs & r); - virtual void collect_statistics(statistics & st) const; - virtual void assert_expr(expr * t); - virtual void push_core(); - virtual void pop_core(unsigned n); - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions); - virtual void get_unsat_core(ptr_vector & r); - virtual void get_model(model_ref & _m); - virtual proof * get_proof(); - virtual std::string reason_unknown() const; - virtual void set_reason_unknown(char const* msg); - virtual void get_labels(svector & r); - virtual void set_progress_callback(progress_callback * callback); - virtual unsigned get_num_assertions() const; - virtual expr * get_assertion(unsigned idx) const; - virtual ast_manager& get_manager() const { return m; } - virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes); - virtual lbool preferred_sat(expr_ref_vector const& asms, vector& cores); void set_logic(symbol const& logic); smt::theory_var add_objective(app* term); @@ -115,7 +118,7 @@ namespace opt { void maximize_objectives(expr_ref_vector& blockers); inf_eps const & saved_objective_value(unsigned obj_index); inf_eps current_objective_value(unsigned obj_index); - model* get_model(unsigned obj_index) { return m_models[obj_index]; } + model* get_model_idx(unsigned obj_index) { return m_models[obj_index]; } bool objective_is_model_valid(unsigned obj_index) const { return m_valid_objectives[obj_index]; } diff --git a/src/opt/optsmt.cpp b/src/opt/optsmt.cpp index 3258cb33d..a461d4a22 100644 --- a/src/opt/optsmt.cpp +++ b/src/opt/optsmt.cpp @@ -46,7 +46,7 @@ namespace opt { for (unsigned i = 0; i < src.size(); ++i) { if (src[i] >= dst[i]) { dst[i] = src[i]; - m_models.set(i, m_s->get_model(i)); + m_models.set(i, m_s->get_model_idx(i)); m_s->get_labels(m_labels); m_lower_fmls[i] = fmls[i].get(); if (dst[i].is_pos() && !dst[i].is_finite()) { // review: likely done already. @@ -112,7 +112,7 @@ namespace opt { while (!m.canceled()) { SASSERT(delta_per_step.is_int()); SASSERT(delta_per_step.is_pos()); - is_sat = m_s->check_sat(0, 0); + is_sat = m_s->check_sat(0, nullptr); if (is_sat == l_true) { bound = update_lower(); if (!get_max_delta(lower, delta_index)) { @@ -188,7 +188,7 @@ namespace opt { while (!m.canceled()) { SASSERT(delta_per_step.is_int()); SASSERT(delta_per_step.is_pos()); - is_sat = m_s->check_sat(0, 0); + is_sat = m_s->check_sat(0, nullptr); if (is_sat == l_true) { m_s->maximize_objective(obj_index, bound); m_s->get_model(m_model); @@ -318,7 +318,7 @@ namespace opt { m_s->get_labels(m_labels); for (unsigned i = 0; i < ors.size(); ++i) { expr_ref tmp(m); - if (m_model->eval(ors[i].get(), tmp) && m.is_true(tmp)) { + if (m_model->is_true(ors[i].get())) { m_lower[i] = m_upper[i]; ors[i] = m.mk_false(); disj[i] = m.mk_false(); @@ -416,7 +416,7 @@ namespace opt { bounds.push_back(bound); } else { - bounds.push_back(0); + bounds.push_back(nullptr); mid.push_back(inf_eps()); } } @@ -429,7 +429,7 @@ namespace opt { case l_true: IF_VERBOSE(2, verbose_stream() << "(optsmt lower bound for v" << m_vars[i] << " := " << m_upper[i] << ")\n";); m_lower[i] = mid[i]; - th.enable_record_conflict(0); + th.enable_record_conflict(nullptr); m_s->assert_expr(update_lower()); break; case l_false: @@ -444,10 +444,10 @@ namespace opt { } break; default: - th.enable_record_conflict(0); + th.enable_record_conflict(nullptr); return l_undef; } - th.enable_record_conflict(0); + th.enable_record_conflict(nullptr); progress = true; } } @@ -505,7 +505,7 @@ namespace opt { commit_assignment(i); } while (is_sat == l_true && !m.canceled()) { - is_sat = m_s->check_sat(0, 0); + is_sat = m_s->check_sat(0, nullptr); if (is_sat != l_true) break; m_s->maximize_objective(obj_index, bound); @@ -599,7 +599,7 @@ namespace opt { m_lower.push_back(inf_eps(rational(-1),inf_rational(0))); m_upper.push_back(inf_eps(rational(1), inf_rational(0))); m_lower_fmls.push_back(m.mk_true()); - m_models.push_back(0); + m_models.push_back(nullptr); return m_objs.size()-1; } @@ -615,7 +615,7 @@ namespace opt { m_vars.reset(); m_model.reset(); m_lower_fmls.reset(); - m_s = 0; + m_s = nullptr; } } diff --git a/src/opt/optsmt.h b/src/opt/optsmt.h index 93fa3f624..6db7eaadf 100644 --- a/src/opt/optsmt.h +++ b/src/opt/optsmt.h @@ -41,7 +41,7 @@ namespace opt { sref_vector m_models; public: optsmt(ast_manager& m): - m(m), m_s(0), m_objs(m), m_lower_fmls(m) {} + m(m), m_s(nullptr), m_objs(m), m_lower_fmls(m) {} void setup(opt_solver& solver); diff --git a/src/opt/pb_sls.cpp b/src/opt/pb_sls.cpp index e28c3cd3d..9054e2b00 100644 --- a/src/opt/pb_sls.cpp +++ b/src/opt/pb_sls.cpp @@ -179,7 +179,7 @@ namespace smt { m_orig_model = mdl; for (unsigned i = 0; i < m_var2decl.size(); ++i) { expr_ref tmp(m); - m_assignment[i] = mdl->eval(m_var2decl[i], tmp) && m.is_true(tmp); + m_assignment[i] = mdl->is_true(m_var2decl[i]); } } @@ -343,10 +343,7 @@ namespace smt { for (unsigned i = 0; i < m_clauses.size(); ++i) { if (!eval(m_clauses[i])) { m_hard_false.insert(i); - expr_ref tmp(m); - if (!m_orig_model->eval(m_orig_clauses[i].get(), tmp)) { - return; - } + expr_ref tmp = (*m_orig_model)(m_orig_clauses[i].get()); IF_VERBOSE(0, verbose_stream() << "original evaluation: " << tmp << "\n"; verbose_stream() << mk_pp(m_orig_clauses[i].get(), m) << "\n"; @@ -521,14 +518,13 @@ namespace smt { literal mk_aux_literal(expr* f) { unsigned var; - expr_ref tmp(m); if (!m_decl2var.find(f, var)) { var = m_hard_occ.size(); SASSERT(m_var2decl.size() == var); SASSERT(m_soft_occ.size() == var); m_hard_occ.push_back(unsigned_vector()); m_soft_occ.push_back(unsigned_vector()); - m_assignment.push_back(m_orig_model->eval(f, tmp) && m.is_true(tmp)); + m_assignment.push_back(m_orig_model->is_true(f)); m_decl2var.insert(f, var); m_var2decl.push_back(f); } diff --git a/src/opt/sortmax.cpp b/src/opt/sortmax.cpp index 00cab488c..4313cfbec 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -24,18 +24,18 @@ Notes: #include "smt/smt_context.h" #include "opt/opt_context.h" #include "util/sorting_network.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace opt { class sortmax : public maxsmt_solver_base { public: - typedef expr* literal; - typedef ptr_vector literal_vector; + typedef expr* pliteral; + typedef ptr_vector pliteral_vector; psort_nw m_sort; expr_ref_vector m_trail; func_decl_ref_vector m_fresh; - ref m_filter; + ref m_filter; 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) {} @@ -50,7 +50,7 @@ namespace opt { if (is_sat != l_true) { return is_sat; } - m_filter = alloc(filter_model_converter, m); + m_filter = alloc(generic_model_converter, m, "sortmax"); rational offset = m_lower; m_upper = offset; expr_ref_vector in(m); @@ -73,8 +73,7 @@ namespace opt { unsigned first = 0; it = soft.begin(); for (; it != end; ++it) { - expr_ref tmp(m); - if (m_model->eval(it->m_key, tmp) && m.is_true(tmp)) { + if (m_model->is_true(it->m_key)) { unsigned n = it->m_value.get_unsigned(); while (n > 0) { s().assert_expr(out[first]); @@ -89,7 +88,7 @@ namespace opt { while (l_true == is_sat && first < out.size() && m_lower < m_upper) { trace_bounds("sortmax"); s().assert_expr(out[first]); - is_sat = s().check_sat(0, 0); + is_sat = s().check_sat(0, nullptr); TRACE("opt", tout << is_sat << "\n"; s().display(tout); tout << "\n";); if (m.canceled()) { is_sat = l_undef; @@ -115,38 +114,35 @@ namespace opt { } void update_assignment() { - for (unsigned i = 0; i < m_soft.size(); ++i) { - m_assignment[i] = is_true(m_soft[i]); - } + for (soft& s : m_soft) s.is_true = is_true(s.s); } bool is_true(expr* e) { - expr_ref tmp(m); - return m_model->eval(e, tmp) && m.is_true(tmp); + return m_model->is_true(e); } // definitions used for sorting network - literal mk_false() { return m.mk_false(); } - literal mk_true() { return m.mk_true(); } - literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } - literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } - literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } + pliteral mk_false() { return m.mk_false(); } + pliteral mk_true() { return m.mk_true(); } + pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } + pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } + pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } - std::ostream& pp(std::ostream& out, literal lit) { return out << mk_pp(lit, m); } + std::ostream& pp(std::ostream& out, pliteral lit) { return out << mk_pp(lit, m); } - literal trail(literal l) { + pliteral trail(pliteral l) { m_trail.push_back(l); return l; } - literal fresh() { - expr_ref fr(m.mk_fresh_const("sn", m.mk_bool_sort()), m); + pliteral fresh(char const* n) { + expr_ref fr(m.mk_fresh_const(n, m.mk_bool_sort()), m); func_decl* f = to_app(fr)->get_decl(); m_fresh.push_back(f); - m_filter->insert(f); + m_filter->hide(f); return trail(fr); } - void mk_clause(unsigned n, literal const* lits) { + void mk_clause(unsigned n, pliteral const* lits) { s().assert_expr(mk_or(m, n, lits)); } diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index afc0334eb..e4eb7e06b 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -80,10 +80,13 @@ namespace opt { while (!m.canceled() && m_lower < m_upper) { //mk_assumptions(asms); //is_sat = s().preferred_sat(asms, cores); - is_sat = s().check_sat(0, 0); + is_sat = s().check_sat(0, nullptr); if (m.canceled()) { is_sat = l_undef; } + if (is_sat == l_undef) { + break; + } if (is_sat == l_false) { TRACE("opt", tout << "Unsat\n";); break; @@ -97,9 +100,6 @@ namespace opt { //DEBUG_CODE(verify_cores(cores);); s().assert_expr(fml); } - else { - //DEBUG_CODE(verify_cores(cores);); - } update_cores(wth(), cores); wth().init_min_cost(m_upper - m_lower); trace_bounds("wmax"); @@ -120,15 +120,11 @@ namespace opt { } bool is_true(expr* e) { - expr_ref tmp(m); - return m_model->eval(e, tmp) && m.is_true(tmp); + return m_model->is_true(e); } void update_assignment() { - m_assignment.reset(); - for (unsigned i = 0; i < m_soft.size(); ++i) { - m_assignment.push_back(is_true(m_soft[i])); - } + for (soft& s : m_soft) s.is_true = is_true(s.s); } struct compare_asm { @@ -162,7 +158,7 @@ namespace opt { void verify_core(expr_ref_vector const& core) { s().push(); s().assert_expr(core); - VERIFY(l_false == s().check_sat(0, 0)); + VERIFY(l_false == s().check_sat(0, nullptr)); s().pop(1); } @@ -216,7 +212,7 @@ namespace opt { rational remove_negations(smt::theory_wmaxsat& th, expr_ref_vector const& core, ptr_vector& keys, vector& weights) { rational min_weight(-1); for (unsigned i = 0; i < core.size(); ++i) { - expr* e = 0; + expr* e = nullptr; VERIFY(m.is_not(core[i], e)); keys.push_back(m_keys[e]); rational weight = m_weights[e]; @@ -307,9 +303,8 @@ namespace opt { } void update_model(expr* def, expr* value) { - expr_ref val(m); - if (m_model && m_model->eval(value, val, true)) { - m_model->register_decl(to_app(def)->get_decl(), val); + if (m_model) { + m_model->register_decl(to_app(def)->get_decl(), (*m_model)(value)); } } diff --git a/src/parsers/smt2/marshal.cpp b/src/parsers/smt2/marshal.cpp index ae144e491..11244760a 100644 --- a/src/parsers/smt2/marshal.cpp +++ b/src/parsers/smt2/marshal.cpp @@ -36,7 +36,7 @@ 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(0, 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(); diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index a06438c73..016c6f4e5 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -46,7 +46,7 @@ namespace smt2 { struct local { expr * m_term; unsigned m_level; - local():m_term(0), m_level(0) {} + local():m_term(nullptr), m_level(0) {} local(expr * t, unsigned l):m_term(t), m_level(l) {} }; symbol_table m_env; @@ -96,6 +96,8 @@ namespace smt2 { symbol m_check_sat; symbol m_define_fun; symbol m_define_const; + symbol m_model_add; + symbol m_model_del; symbol m_declare_fun; symbol m_declare_const; symbol m_define_sort; @@ -222,31 +224,31 @@ namespace smt2 { } psort_ref_vector & psort_stack() { - if (m_psort_stack.get() == 0) + if (m_psort_stack.get() == nullptr) m_psort_stack = alloc(psort_ref_vector, pm()); return *(m_psort_stack.get()); } sort_ref_vector & sort_stack() { - if (m_sort_stack.get() == 0) + if (m_sort_stack.get() == nullptr) m_sort_stack = alloc(sort_ref_vector, m()); return *(m_sort_stack.get()); } expr_ref_vector & expr_stack() { - if (m_expr_stack.get() == 0) + if (m_expr_stack.get() == nullptr) m_expr_stack = alloc(expr_ref_vector, m()); return *(m_expr_stack.get()); } template static unsigned size(scoped_ptr & v) { - return v.get() == 0 ? 0 : v->size(); + return v.get() == nullptr ? 0 : v->size(); } template static void shrink(scoped_ptr & v, unsigned old_sz) { - if (v.get() == 0) { + if (v.get() == nullptr) { SASSERT(old_sz == 0); } else { @@ -255,13 +257,13 @@ namespace smt2 { } expr_ref_vector & pattern_stack() { - if (m_pattern_stack.get() == 0) + if (m_pattern_stack.get() == nullptr) m_pattern_stack = alloc(expr_ref_vector, m()); return *(m_pattern_stack.get()); } expr_ref_vector & nopattern_stack() { - if (m_nopattern_stack.get() == 0) + if (m_nopattern_stack.get() == nullptr) m_nopattern_stack = alloc(expr_ref_vector, m()); return *(m_nopattern_stack.get()); } @@ -271,44 +273,44 @@ namespace smt2 { } sexpr_ref_vector & sexpr_stack() { - if (m_sexpr_stack.get() == 0) + if (m_sexpr_stack.get() == nullptr) m_sexpr_stack = alloc(sexpr_ref_vector, sm()); return *(m_sexpr_stack.get()); } arith_util & autil() { - if (m_arith_util.get() == 0) + if (m_arith_util.get() == nullptr) m_arith_util = alloc(arith_util, m()); return *(m_arith_util.get()); } datatype_util & dtutil() { - if (m_datatype_util.get() == 0) + if (m_datatype_util.get() == nullptr) m_datatype_util = alloc(datatype_util, m()); return *(m_datatype_util.get()); } seq_util & sutil() { - if (m_seq_util.get() == 0) + if (m_seq_util.get() == nullptr) m_seq_util = alloc(seq_util, m()); return *(m_seq_util.get()); } bv_util & butil() { - if (m_bv_util.get() == 0) + if (m_bv_util.get() == nullptr) m_bv_util = alloc(bv_util, m()); return *(m_bv_util.get()); } pattern_validator & pat_validator() { - if (m_pattern_validator.get() == 0) { + if (m_pattern_validator.get() == nullptr) { m_pattern_validator = alloc(pattern_validator, m()); } return *(m_pattern_validator.get()); } var_shifter & shifter() { - if (m_var_shifter.get() == 0) + if (m_var_shifter.get() == nullptr) m_var_shifter = alloc(var_shifter, m()); return *(m_var_shifter.get()); } @@ -569,12 +571,12 @@ namespace smt2 { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); - if (d == 0) + if (d == nullptr) unknown_sort(id, context); if (!d->has_var_params() && d->get_num_params() != 0) throw parser_exception("sort constructor expects parameters"); sort * r = d->instantiate(pm()); - if (r == 0) + if (r == nullptr) throw parser_exception("invalid sort application"); next(); return r; @@ -584,7 +586,7 @@ namespace smt2 { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); - if (d != 0) { + if (d != nullptr) { if (!d->has_var_params() && d->get_num_params() != 0) throw parser_exception("sort constructor expects parameters"); next(); @@ -601,7 +603,7 @@ namespace smt2 { unknown_sort(id); UNREACHABLE(); } - return 0; + return nullptr; } } } @@ -612,7 +614,7 @@ namespace smt2 { next(); symbol id = check_identifier_next("invalid indexed sort, symbol expected"); psort_decl * d = m_ctx.find_psort_decl(id); - if (d == 0) + if (d == nullptr) unknown_sort(id); sbuffer args; while (!curr_is_rparen()) { @@ -624,7 +626,7 @@ namespace smt2 { if (args.empty()) throw parser_exception("invalid indexed sort, index expected"); sort * r = d->instantiate(pm(), args.size(), args.c_ptr()); - if (r == 0) + if (r == nullptr) throw parser_exception("invalid sort application"); next(); return r; @@ -634,7 +636,7 @@ namespace smt2 { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); - if (d == 0) { + if (d == nullptr) { unknown_sort(id); } next(); @@ -697,7 +699,7 @@ namespace smt2 { SASSERT(curr_is_identifier()); symbol id = curr_id(); psort_decl * d = m_ctx.find_psort_decl(id); - if (d == 0) + if (d == nullptr) unknown_sort(id); next(); void * mem = m_stack.allocate(sizeof(sort_frame)); @@ -717,7 +719,7 @@ namespace smt2 { throw parser_exception("invalid number of parameters to sort constructor"); } sort * r = d->instantiate(pm(), num, sort_stack().c_ptr() + spos); - if (r == 0) + if (r == nullptr) throw parser_exception("invalid sort application"); sort_stack().shrink(spos); sort_stack().push_back(r); @@ -783,7 +785,7 @@ namespace smt2 { SASSERT(curr_is_identifier()); psort * p = parse_psort_name(true); ptype result; - if (p != 0) { + if (p != nullptr) { result = ptype(p); } else { @@ -828,7 +830,7 @@ namespace smt2 { symbol r_name(r_str.c_str()); next(); TRACE("datatype_parser_bug", tout << ct_name << " " << r_name << "\n";); - ct_decls.push_back(pm().mk_pconstructor_decl(m_sort_id2param_idx.size(), ct_name, r_name, 0, 0)); + ct_decls.push_back(pm().mk_pconstructor_decl(m_sort_id2param_idx.size(), ct_name, r_name, 0, nullptr)); } else { check_lparen_next("invalid datatype declaration, '(' or ')' expected"); @@ -949,8 +951,9 @@ namespace smt2 { check_duplicate(d, line, pos); d->commit(pm()); - check_rparen_next("invalid end of datatype declaration, ')' expected"); + check_rparen("invalid end of datatype declaration, ')' expected"); m_ctx.print_success(); + next(); } @@ -970,6 +973,9 @@ namespace smt2 { check_rparen_next("invalid datatype declaration, ')' expected"); } else { + if (dt_name) { + m_ctx.insert(pm().mk_psort_dt_decl(0, *dt_name)); + } parse_constructor_decls(ct_decls); } check_rparen_next("invalid datatype declaration, ')' expected"); @@ -999,13 +1005,13 @@ namespace smt2 { TRACE("name_expr", tout << "naming: " << s << " ->\n" << mk_pp(n, m()) << "\n";); if (!is_ground(n) && has_free_vars(n)) throw parser_exception("invalid named expression, expression contains free variables"); - m_ctx.insert(s, 0, 0, n); + m_ctx.insert(s, 0, nullptr, n); m_last_named_expr.first = s; m_last_named_expr.second = n; } bool in_quant_ctx(attr_expr_frame * fr) { - return fr != 0 && fr->m_prev != 0 && fr->m_prev->m_kind == EF_QUANT; + return fr != nullptr && fr->m_prev != nullptr && fr->m_prev->m_kind == EF_QUANT; } void check_in_quant_ctx(attr_expr_frame * fr) { @@ -1018,7 +1024,7 @@ namespace smt2 { return; if (fr->m_last_symbol == m_pattern) { expr * pat = expr_stack().back(); - if (pat == 0) { + if (pat == nullptr) { if (!ignore_bad_patterns()) throw parser_exception("invalid empty pattern"); } @@ -1191,7 +1197,7 @@ namespace smt2 { if (!ignore_bad_patterns()) throw parser_exception("invalid pattern, '(' expected"); consume_sexpr(); - expr_stack().push_back(0); // empty pattern + expr_stack().push_back(nullptr); // empty pattern return; } @@ -1203,7 +1209,7 @@ namespace smt2 { } else if (curr_is_rparen()) { next(); - expr_stack().push_back(0); // empty pattern + expr_stack().push_back(nullptr); // empty pattern } else { // unary pattern @@ -1211,7 +1217,7 @@ namespace smt2 { // when Simplify benchmarks were converted into SMT2 ones. if (curr_is_identifier()) { symbol id = curr_id(); - func_decl * f = 0; + func_decl * f = nullptr; try { f = m_ctx.find_func_decl(id); } @@ -1223,7 +1229,7 @@ namespace smt2 { while (!curr_is_rparen()) consume_sexpr(); next(); - expr_stack().push_back(0); // empty pattern + expr_stack().push_back(nullptr); // empty pattern return; // no frame is created } } @@ -1411,7 +1417,7 @@ namespace smt2 { else { SASSERT(is_app(pattern)); func_decl * f = to_app(pattern)->get_decl(); - func_decl * r = dtutil().get_constructor_recognizer(f); + func_decl * r = dtutil().get_constructor_is(f); ptr_vector const * acc = dtutil().get_constructor_accessors(f); shifter()(t, acc->size(), tsh); for (func_decl* a : *acc) { @@ -1761,7 +1767,7 @@ namespace smt2 { return; } expr_ref t_ref(m()); - m_ctx.mk_app(r, 0, 0, num_indices, m_param_stack.c_ptr() + param_spos, has_as ? sort_stack().back() : 0, t_ref); + m_ctx.mk_app(r, 0, nullptr, num_indices, m_param_stack.c_ptr() + param_spos, has_as ? sort_stack().back() : nullptr, t_ref); m_param_stack.shrink(param_spos); expr_stack().push_back(t_ref.get()); if (has_as) { @@ -1851,7 +1857,7 @@ namespace smt2 { expr_stack().c_ptr() + fr->m_expr_spos, num_indices, m_param_stack.c_ptr() + fr->m_param_spos, - fr->m_as_sort ? sort_stack().back() : 0, + fr->m_as_sort ? sort_stack().back() : nullptr, t_ref); expr_stack().shrink(fr->m_expr_spos); m_param_stack.shrink(fr->m_param_spos); @@ -2050,7 +2056,7 @@ namespace smt2 { parse_bv_numeral(); break; case scanner::LEFT_PAREN: - push_expr_frame(m_num_expr_frames == 0 ? 0 : static_cast(m_stack.top())); + push_expr_frame(m_num_expr_frames == 0 ? nullptr : static_cast(m_stack.top())); break; case scanner::KEYWORD_TOKEN: throw parser_exception("invalid expression, unexpected keyword"); @@ -2149,17 +2155,17 @@ namespace smt2 { check_identifier("invalid sort declaration, symbol expected"); symbol id = curr_id(); - if (m_ctx.find_psort_decl(id) != 0) + if (m_ctx.find_psort_decl(id) != nullptr) throw parser_exception("invalid sort declaration, sort already declared/defined"); next(); if (curr_is_rparen()) { - psort_decl * decl = pm().mk_psort_user_decl(0, id, 0); + psort_decl * decl = pm().mk_psort_user_decl(0, id, nullptr); m_ctx.insert(decl); } else { check_int("invalid sort declaration, arity () or ')' expected"); unsigned u = curr_unsigned(); - psort_decl * decl = pm().mk_psort_user_decl(u, id, 0); + psort_decl * decl = pm().mk_psort_user_decl(u, id, nullptr); m_ctx.insert(decl); next(); check_rparen("invalid sort declaration, ')' expected"); @@ -2174,7 +2180,7 @@ namespace smt2 { next(); check_identifier("invalid sort definition, symbol expected"); symbol id = curr_id(); - if (m_ctx.find_psort_decl(id) != 0) + if (m_ctx.find_psort_decl(id) != nullptr) throw parser_exception("invalid sort definition, sort already declared/defined"); next(); parse_sort_decl_params(); @@ -2188,9 +2194,9 @@ namespace smt2 { next(); } - void parse_define_fun() { + void parse_define(bool is_fun) { SASSERT(curr_is_identifier()); - SASSERT(curr_id() == m_define_fun); + SASSERT(curr_id() == (is_fun ? m_define_fun : m_model_add)); SASSERT(m_num_bindings == 0); next(); check_identifier("invalid function/constant definition, symbol expected"); @@ -2204,7 +2210,10 @@ namespace smt2 { parse_expr(); if (m().get_sort(expr_stack().back()) != sort_stack().back()) throw parser_exception("invalid function/constant definition, sort mismatch"); - m_ctx.insert(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); + if (is_fun) + m_ctx.insert(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); + else + m_ctx.model_add(id, num_vars, sort_stack().c_ptr() + sort_spos, expr_stack().back()); check_rparen("invalid function/constant definition, ')' expected"); // restore stacks & env symbol_stack().shrink(sym_spos); @@ -2217,6 +2226,24 @@ namespace smt2 { next(); } + void parse_define_fun() { + parse_define(true); + } + + void parse_model_add() { + parse_define(false); + } + + void parse_model_del() { + next(); + symbol id = curr_id(); + func_decl * f = m_ctx.find_func_decl(id); + m_ctx.model_del(f); + next(); + check_rparen_next("invalid model-del, ')' expected"); + m_ctx.print_success(); + } + void parse_define_fun_rec() { // ( define-fun-rec hfun_defi ) SASSERT(curr_is_identifier()); @@ -2355,7 +2382,7 @@ namespace smt2 { parse_expr(); if (m().get_sort(expr_stack().back()) != sort_stack().back()) throw parser_exception("invalid constant definition, sort mismatch"); - m_ctx.insert(id, 0, 0, expr_stack().back()); + m_ctx.insert(id, 0, nullptr, expr_stack().back()); check_rparen("invalid constant definition, ')' expected"); expr_stack().pop_back(); sort_stack().pop_back(); @@ -2574,22 +2601,18 @@ namespace smt2 { } check_rparen("invalid get-value command, ')' expected"); - if (!m_ctx.is_model_available() || m_ctx.get_check_sat_result() == 0) - throw cmd_exception("model is not available"); model_ref md; - if (index == 0) { - m_ctx.get_check_sat_result()->get_model(md); - } - else { + if (!m_ctx.is_model_available(md) || m_ctx.get_check_sat_result() == 0) + throw cmd_exception("model is not available"); + if (index != 0) { m_ctx.get_opt()->get_box_model(md, index); - } m_ctx.regular_stream() << "("; expr ** expr_it = expr_stack().c_ptr() + spos; expr ** expr_end = expr_it + m_cached_strings.size(); for (unsigned i = 0; expr_it < expr_end; expr_it++, i++) { - expr_ref v(m()); - md->eval(*expr_it, v, true); + model::scoped_model_completion _scm(md, true); + expr_ref v = (*md)(*expr_it); if (i > 0) m_ctx.regular_stream() << "\n "; m_ctx.regular_stream() << "(" << m_cached_strings[i] << " "; @@ -2804,7 +2827,7 @@ namespace smt2 { void parse_ext_cmd(int line, int pos) { symbol s = curr_id(); m_curr_cmd = m_ctx.find_cmd(s); - if (m_curr_cmd == 0) { + if (m_curr_cmd == nullptr) { parse_unknown_cmd(); return; } @@ -2823,7 +2846,7 @@ namespace smt2 { throw parser_exception("invalid command, argument(s) missing"); m_curr_cmd->execute(m_ctx); next(); - m_curr_cmd = 0; + m_curr_cmd = nullptr; shrink(m_sort_stack, sort_spos); shrink(m_expr_stack, expr_spos); shrink(m_sexpr_stack, sexpr_spos); @@ -2919,16 +2942,24 @@ namespace smt2 { parse_define_funs_rec(); return; } + if (s == m_model_add) { + parse_model_add(); + return; + } + if (s == m_model_del) { + parse_model_del(); + return; + } parse_ext_cmd(line, pos); } public: - parser(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & p, char const * filename=0): + parser(cmd_context & ctx, std::istream & is, bool interactive, params_ref const & p, char const * filename=nullptr): m_ctx(ctx), m_params(p), m_scanner(ctx, is, interactive), m_curr(scanner::NULL_TOKEN), - m_curr_cmd(0), + m_curr_cmd(nullptr), m_num_bindings(0), m_let("let"), m_bang("!"), @@ -2950,6 +2981,8 @@ namespace smt2 { m_check_sat("check-sat"), m_define_fun("define-fun"), m_define_const("define-const"), + m_model_add("model-add"), + m_model_del("model-del"), m_declare_fun("declare-fun"), m_declare_const("declare-const"), m_define_sort("define-sort"), @@ -2988,23 +3021,23 @@ namespace smt2 { void reset() { reset_stack(); m_num_bindings = 0; - m_psort_stack = 0; - m_sort_stack = 0; - m_expr_stack = 0; - m_pattern_stack = 0; - m_nopattern_stack = 0; - m_sexpr_stack = 0; + m_psort_stack = nullptr; + m_sort_stack = nullptr; + m_expr_stack = nullptr; + m_pattern_stack = nullptr; + m_nopattern_stack = nullptr; + m_sexpr_stack = nullptr; m_symbol_stack .reset(); m_param_stack .reset(); m_env .reset(); m_sort_id2param_idx .reset(); m_dt_name2idx .reset(); - m_bv_util = 0; - m_arith_util = 0; - m_seq_util = 0; - m_pattern_validator = 0; - m_var_shifter = 0; + m_bv_util = nullptr; + m_arith_util = nullptr; + m_seq_util = nullptr; + m_pattern_validator = nullptr; + m_var_shifter = nullptr; } bool operator()() { diff --git a/src/parsers/smt2/smt2parser.h b/src/parsers/smt2/smt2parser.h index 0976fc5f4..70ea287e0 100644 --- a/src/parsers/smt2/smt2parser.h +++ b/src/parsers/smt2/smt2parser.h @@ -21,6 +21,6 @@ Revision History: #include "cmd_context/cmd_context.h" -bool parse_smt2_commands(cmd_context & ctx, std::istream & is, bool interactive = false, params_ref const & p = params_ref(), char const * filename = 0); +bool parse_smt2_commands(cmd_context & ctx, std::istream & is, bool interactive = false, params_ref const & p = params_ref(), char const * filename = nullptr); #endif diff --git a/src/parsers/util/cost_parser.cpp b/src/parsers/util/cost_parser.cpp index 765b8ade9..91362b37a 100644 --- a/src/parsers/util/cost_parser.cpp +++ b/src/parsers/util/cost_parser.cpp @@ -32,7 +32,7 @@ cost_parser::cost_parser(ast_manager & m): add_builtin_op("or", fid, OP_OR); add_builtin_op("ite", fid, OP_ITE); add_builtin_op("=", fid, OP_EQ); - add_builtin_op("iff", fid, OP_IFF); + add_builtin_op("iff", fid, OP_EQ); add_builtin_op("xor", fid, OP_XOR); fid = m_util.get_family_id(); diff --git a/src/parsers/util/cost_parser.h b/src/parsers/util/cost_parser.h index f0330d824..d2a96dc25 100644 --- a/src/parsers/util/cost_parser.h +++ b/src/parsers/util/cost_parser.h @@ -27,9 +27,9 @@ class cost_parser : public simple_parser { var_ref_vector m_vars; public: cost_parser(ast_manager & m); - virtual ~cost_parser() {} - virtual expr * parse_int(rational const & r); - virtual expr * parse_float(rational const & r); + ~cost_parser() override {} + expr * parse_int(rational const & r) override; + expr * parse_float(rational const & r) override; unsigned add_var(symbol name); unsigned add_var(char const * name) { return add_var(symbol(name)); } void reset_vars(); diff --git a/src/parsers/util/pattern_validation.cpp b/src/parsers/util/pattern_validation.cpp index 0d076aadd..df1f6cd00 100644 --- a/src/parsers/util/pattern_validation.cpp +++ b/src/parsers/util/pattern_validation.cpp @@ -48,7 +48,7 @@ struct pattern_validation_functor { bool is_forbidden(func_decl const * decl) { family_id fid = decl->get_family_id(); - if (fid == m_bfid && decl->get_decl_kind() != OP_TRUE && decl->get_decl_kind() != OP_FALSE) + if (fid == m_bfid && decl->get_decl_kind() != OP_EQ && decl->get_decl_kind() != OP_TRUE && decl->get_decl_kind() != OP_FALSE) return true; if (fid == m_lfid) return true; diff --git a/src/parsers/util/simple_parser.cpp b/src/parsers/util/simple_parser.cpp index 770da9dbb..c9d00ebcc 100644 --- a/src/parsers/util/simple_parser.cpp +++ b/src/parsers/util/simple_parser.cpp @@ -87,7 +87,7 @@ expr * simple_parser::parse_expr(scanner & s) { } throw parser_error(); case scanner::RIGHT_PAREN: - return 0; + return nullptr; case scanner::ID_TOKEN: if (m_builtin.find(s.get_id(), op)) { expr * r = m_manager.mk_const(op.m_family_id, op.m_kind); @@ -123,7 +123,7 @@ bool simple_parser::parse(std::istream & in, expr_ref & result) { return false; } m_exprs.reset(); - return result.get() != 0; + return result.get() != nullptr; } bool simple_parser::parse_string(char const * str, expr_ref & result) { @@ -133,7 +133,7 @@ bool simple_parser::parse_string(char const * str, expr_ref & result) { } bool simple_parser::parse_file(char const * file, expr_ref & result) { - if (file != 0) { + if (file != nullptr) { std::ifstream stream(file); if (!stream) { warning_msg("ERROR: could not open file '%s'.", file); diff --git a/src/qe/CMakeLists.txt b/src/qe/CMakeLists.txt index 2e6052382..e9f91ae3e 100644 --- a/src/qe/CMakeLists.txt +++ b/src/qe/CMakeLists.txt @@ -15,8 +15,11 @@ z3_add_component(qe qe_dl_plugin.cpp qe_lite.cpp qe_mbp.cpp + qe_mbi.cpp qe_sat_tactic.cpp + qe_solve_plugin.cpp qe_tactic.cpp + qe_term_graph.cpp qsat.cpp COMPONENT_DEPENDENCIES nlsat_tactic diff --git a/src/qe/nlarith_util.cpp b/src/qe/nlarith_util.cpp index 4f07b59dd..12977b449 100644 --- a/src/qe/nlarith_util.cpp +++ b/src/qe/nlarith_util.cpp @@ -32,7 +32,7 @@ namespace nlarith { svector m_comps; public: - literal_set(ast_manager& m) : m_inf(m), m_sup(m), m_x(0), m_lits(m) {} + literal_set(ast_manager& m) : m_inf(m), m_sup(m), m_x(nullptr), m_lits(m) {} unsigned size() const { return m_lits.size(); } app_ref_vector& lits() { return m_lits; } @@ -123,7 +123,7 @@ namespace nlarith { return false; } - if (!get_polys(contains_x, num_lits, lits, polys, comps, &branch_conds, 0)) { + if (!get_polys(contains_x, num_lits, lits, polys, comps, &branch_conds, nullptr)) { TRACE("nlarith", tout << "could not extract polynomials " << mk_pp(x, m()) << "\n"; for (unsigned i = 0; i < num_lits; ++i) { @@ -1018,13 +1018,13 @@ namespace nlarith { app* m_x; public: basic_subst(imp& i, app* x) : isubst(i), m_x(x) {} - virtual void mk_lt(poly const& p, app_ref& r) { + void mk_lt(poly const& p, app_ref& r) override { imp& I = m_imp; app_ref result(I.m()); I.mk_polynomial(m_x, p, result); r = I.mk_lt(result); } - virtual void mk_eq(poly const& p, app_ref& r) { + void mk_eq(poly const& p, app_ref& r) override { imp& I = m_imp; app_ref result(I.m()); I.mk_polynomial(m_x, p, result); @@ -1039,7 +1039,7 @@ namespace nlarith { // p[e/x] < 0: (a*parity(d) < 0 /\ 0 < a*a - b*b*c) \/ // (b*parity(d) <= 0 /\ (a*parity(d) < 0 \/ a*a - b*b*c < 0)) - virtual void mk_lt(poly const& p, app_ref& r) { + void mk_lt(poly const& p, app_ref& r) override { imp& I = m_imp; ast_manager& m = I.m(); app_ref a(m), b(m), c(m_s.m_c), d(m); @@ -1061,7 +1061,7 @@ namespace nlarith { // p[e/x] = 0: a*b <= 0 & a*a - b*b*c = 0 - virtual void mk_eq(poly const& p, app_ref& r) { + void mk_eq(poly const& p, app_ref& r) override { imp& I = m_imp; ast_manager& m = I.m(); app_ref a(m), b(m), c(m_s.m_c),d(m), aabbc(m); @@ -1076,7 +1076,7 @@ namespace nlarith { } // p[e/x] <= 0: a*parity(d) <= 0 /\ 0 <= a*a - b*b*c \/ b*parity(d) <= 0 /\ a*a - b*b*c <= 0 - virtual void mk_le(poly const& p, app_ref& r) { + void mk_le(poly const& p, app_ref& r) override { imp& I = m_imp; ast_manager& m = I.m(); app_ref a(m), b(m), c(m_s.m_c), d(m); @@ -1125,10 +1125,10 @@ namespace nlarith { public: plus_eps_subst(imp& i, isubst& s) : isubst(i), m_s(s) {} - virtual void mk_lt(poly const& p, app_ref& r) { mk_nu(p, r); } + void mk_lt(poly const& p, app_ref& r) override { mk_nu(p, r); } // /\ p[i] = 0 - virtual void mk_eq(poly const& p, app_ref& r) { r = m_imp.mk_zero(p); } + void mk_eq(poly const& p, app_ref& r) override { r = m_imp.mk_zero(p); } }; class minus_eps_subst : public isubst { @@ -1172,10 +1172,10 @@ namespace nlarith { public: minus_eps_subst(imp& i, isubst& s) : isubst(i), m_s(s) {} - virtual void mk_lt(poly const& p, app_ref& r) { mk_nu(p, true, r); } + void mk_lt(poly const& p, app_ref& r) override { mk_nu(p, true, r); } // /\ p[i] = 0 - virtual void mk_eq(poly const& p, app_ref& r) { r = m_imp.mk_zero(p); } + void mk_eq(poly const& p, app_ref& r) override { r = m_imp.mk_zero(p); } }; class minus_inf_subst : public isubst { @@ -1208,12 +1208,12 @@ namespace nlarith { public: minus_inf_subst(imp& i) : isubst(i) {} - virtual void mk_lt(poly const& p, app_ref& r) { + void mk_lt(poly const& p, app_ref& r) override { r = mk_lt(p, p.size()); } // /\ p[i] = 0 - virtual void mk_eq(poly const& p, app_ref& r) { r = m_imp.mk_zero(p); } + void mk_eq(poly const& p, app_ref& r) override { r = m_imp.mk_zero(p); } }; @@ -1238,10 +1238,10 @@ namespace nlarith { public: plus_inf_subst(imp& i) : isubst(i) {} - virtual void mk_lt(poly const& p, app_ref& r) { r = mk_lt(p, p.size()); } + void mk_lt(poly const& p, app_ref& r) override { r = mk_lt(p, p.size()); } // /\ p[i] = 0 - virtual void mk_eq(poly const& p, app_ref& r) { r = m_imp.mk_zero(p); } + void mk_eq(poly const& p, app_ref& r) override { r = m_imp.mk_zero(p); } }; /** @@ -1514,9 +1514,9 @@ namespace nlarith { new_atoms.reset(); app_ref tmp(m()); expr_ref_vector conjs(m()); - mk_exists_zero(literals, true, 0, conjs, new_atoms); + mk_exists_zero(literals, true, nullptr, conjs, new_atoms); mk_same_sign (literals, true, conjs, new_atoms); - mk_exists_zero(literals, false, 0, conjs, new_atoms); + mk_exists_zero(literals, false, nullptr, conjs, new_atoms); mk_same_sign (literals, false, conjs, new_atoms); mk_lt(literals.x(), literals.x_inf(), conjs, new_atoms); mk_lt(literals.x_sup(), literals.x(), conjs, new_atoms); @@ -1615,9 +1615,9 @@ namespace nlarith { public: simple_branch(ast_manager& m, app* cnstr): m_cnstr(cnstr, m), m_atoms(m) {} - virtual ~simple_branch() {} - virtual app* get_constraint() { return m_cnstr.get(); } - virtual void get_updates(ptr_vector& atoms, svector& updates) { + ~simple_branch() override {} + app* get_constraint() override { return m_cnstr.get(); } + void get_updates(ptr_vector& atoms, svector& updates) override { for (unsigned i = 0; i < m_atoms.size(); ++i) { atoms.push_back(m_atoms[i].get()); updates.push_back(m_updates[i]); @@ -1635,7 +1635,7 @@ namespace nlarith { public: ins_rem_branch(ast_manager& m, app* a, app* r, app* cnstr): simple_branch(m, cnstr) { insert(a); remove(r); } - virtual ~ins_rem_branch() {} + ~ins_rem_branch() override {} }; /** @@ -1922,7 +1922,7 @@ namespace nlarith { } extract_non_linear(atms.size(), atms.begin(), nlvars); if (nlvars.empty()) { - lits = 0; + lits = nullptr; return true; } app* x = nlvars.back(); @@ -1930,11 +1930,11 @@ namespace nlarith { expr* const* _atoms = (expr*const*)atms.begin(); lits = alloc(util::literal_set, m()); lits->set_x(x); - if (get_polys(contains_x, atms.size(), _atoms, lits->polys(), lits->comps(), 0, &lits->lits())) { + if (get_polys(contains_x, atms.size(), _atoms, lits->polys(), lits->comps(), nullptr, &lits->lits())) { return true; } dealloc(lits); - lits = 0; + lits = nullptr; return false; } diff --git a/src/qe/nlqsat.cpp b/src/qe/nlqsat.cpp index ab5095328..1ae9723c9 100644 --- a/src/qe/nlqsat.cpp +++ b/src/qe/nlqsat.cpp @@ -205,8 +205,7 @@ namespace qe { nlsat::scoped_literal_vector new_result(m_solver); result.reset(); // project quantified Boolean variables. - for (unsigned i = 0; i < m_asms.size(); ++i) { - nlsat::literal lit = m_asms[i]; + for (nlsat::literal lit : m_asms) { if (!m_b2a.contains(lit.var()) || fvars.contains(lit.var())) { result.push_back(lit); } @@ -215,12 +214,13 @@ namespace qe { // project quantified real variables. // They are sorted by size, so we project the largest variables first to avoid // renaming variables. - for (unsigned i = vars.size(); i > 0;) { - --i; + for (unsigned i = vars.size(); i-- > 0;) { new_result.reset(); + TRACE("qe", m_solver.display(tout << "project: ", vars[i]) << "\n";); ex.project(vars[i], result.size(), result.c_ptr(), new_result); result.swap(new_result); - TRACE("qe", m_solver.display(tout, result.size(), result.c_ptr()); tout << "\n";); + TRACE("qe", m_solver.display(tout, vars[i]) << ": "; + m_solver.display(tout, result.size(), result.c_ptr()); tout << "\n";); } negate_clause(result); } @@ -555,7 +555,7 @@ namespace qe { } - void reset() { + void reset() override { //m_solver.reset(); m_asms.reset(); m_cached_asms.reset(); @@ -596,6 +596,7 @@ namespace qe { } void display(std::ostream& out) { + out << "level " << level() << "\n"; display_preds(out); display_assumptions(out); m_solver.display(out << "solver:\n"); @@ -645,12 +646,8 @@ namespace qe { while (!vars.empty()); SASSERT(qvars.size() >= 2); SASSERT(qvars.back().empty()); - ackermanize_div(is_forall, qvars, fml); - init_expr2var(qvars); - - goal2nlsat g2s; expr_ref is_true(m), fml1(m), fml2(m); @@ -658,11 +655,9 @@ namespace qe { fml = m.mk_iff(is_true, fml); goal_ref g = alloc(goal, m); g->assert_expr(fml); - proof_converter_ref pc; expr_dependency_ref core(m); - model_converter_ref mc; goal_ref_buffer result; - (*m_nftactic)(g, result, mc, pc, core); + (*m_nftactic)(g, result); SASSERT(result.size() == 1); TRACE("qe", result[0]->display(tout);); g2s(*result[0], m_params, m_solver, m_a2b, m_t2x); @@ -673,9 +668,7 @@ namespace qe { m_bound_rvars.push_back(nlsat::var_vector()); max_level lvl; if (is_exists(i)) lvl.m_ex = i; else lvl.m_fa = i; - for (unsigned j = 0; j < qvars[i].size(); ++j) { - app* v = qvars[i][j].get(); - + for (app* v : qvars[i]) { if (m_a2b.is_var(v)) { SASSERT(m.is_bool(v)); nlsat::bool_var b = m_a2b.to_var(v); @@ -684,7 +677,7 @@ namespace qe { } else if (m_t2x.is_var(v)) { nlsat::var w = m_t2x.to_var(v); - TRACE("qe", tout << mk_pp(v, m) << " |-> " << w << "\n";); + TRACE("qe", tout << mk_pp(v, m) << " |-> x" << w << "\n";); m_bound_rvars.back().push_back(w); m_rvar2level.setx(w, lvl, max_level()); } @@ -697,7 +690,7 @@ namespace qe { m_is_true = nlsat::literal(m_a2b.to_var(is_true), false); // insert literals from arithmetical sub-formulas nlsat::atom_vector const& atoms = m_solver.get_atoms(); - TRACE("qe", m_solver.display(tout); ); + TRACE("qe", m_solver.display(tout);); for (unsigned i = 0; i < atoms.size(); ++i) { if (atoms[i]) { get_level(nlsat::literal(i, false)); @@ -726,13 +719,11 @@ namespace qe { } void init_var2expr() { - expr2var::iterator it = m_t2x.begin(), end = m_t2x.end(); - for (; it != end; ++it) { - m_x2t.insert(it->m_value, it->m_key); + for (auto const& kv : m_t2x) { + m_x2t.insert(kv.m_value, kv.m_key); } - it = m_a2b.begin(), end = m_a2b.end(); - for (; it != end; ++it) { - m_b2a.insert(it->m_value, it->m_key); + for (auto const& kv : m_a2b) { + m_b2a.insert(kv.m_value, kv.m_key); } } @@ -743,10 +734,9 @@ namespace qe { bool ok = true; model_ref md = alloc(model, m); arith_util util(m); - expr2var::iterator it = m_t2x.begin(), end = m_t2x.end(); - for (; it != end; ++it) { - nlsat::var x = it->m_value; - expr * t = it->m_key; + for (auto const& kv : m_t2x) { + nlsat::var x = kv.m_value; + expr * t = kv.m_key; if (!is_uninterp_const(t) || !m_free_vars.contains(t) || m_aux_vars.contains(t)) continue; expr * v; @@ -762,11 +752,10 @@ namespace qe { } md->register_decl(to_app(t)->get_decl(), v); } - it = m_a2b.begin(), end = m_a2b.end(); - for (; it != end; ++it) { - expr * a = it->m_key; - nlsat::bool_var b = it->m_value; - if (a == 0 || !is_uninterp_const(a) || b == m_is_true.var() || !m_free_vars.contains(a) || m_aux_vars.contains(a)) + for (auto const& kv : m_a2b) { + expr * a = kv.m_key; + nlsat::bool_var b = kv.m_value; + if (a == nullptr || !is_uninterp_const(a) || b == m_is_true.var() || !m_free_vars.contains(a) || m_aux_vars.contains(a)) continue; lbool val = m_bmodel0.get(b, l_undef); if (val == l_undef) @@ -782,8 +771,8 @@ namespace qe { m(m), m_mode(mode), m_params(p), - m_solver(m.limit(), p), - m_nftactic(0), + m_solver(m.limit(), p, true), + m_nftactic(nullptr), m_rmodel(m_solver.am()), m_rmodel0(m_solver.am()), m_valid_model(false), @@ -798,30 +787,26 @@ namespace qe { m_nftactic = mk_tseitin_cnf_tactic(m); } - virtual ~nlqsat() { + ~nlqsat() override { } - void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { params_ref p2(p); p2.set_bool("factor", false); m_solver.updt_params(p2); } - void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { } void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) { + /* out */ goal_ref_buffer & result) override { tactic_report report("nlqsat-tactic", *in); ptr_vector fmls; expr_ref fml(m); - mc = 0; pc = 0; core = 0; in->get_formulas(fmls); fml = mk_and(m, fmls.size(), fmls.c_ptr()); if (m_mode == elim_t) { @@ -852,7 +837,9 @@ namespace qe { in->inc_depth(); result.push_back(in.get()); if (in->models_enabled()) { + model_converter_ref mc; VERIFY(mk_model(mc)); + in->add(mc.get()); } break; case l_undef: @@ -863,27 +850,27 @@ namespace qe { } - void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.copy(m_st); st.update("qsat num rounds", m_stats.m_num_rounds); } - void reset_statistics() { + void reset_statistics() override { m_stats.reset(); m_solver.reset_statistics(); } - void cleanup() { + void cleanup() override { reset(); } - void set_logic(symbol const & l) { + void set_logic(symbol const & l) override { } - void set_progress_callback(progress_callback * callback) { + void set_progress_callback(progress_callback * callback) override { } - tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(nlqsat, m, m_mode, m_params); } }; diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index 061f403e3..3916e547c 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -130,8 +130,7 @@ namespace qe { visited.mark(e, true); } } - for (unsigned i = 0; i < conjs.size(); ++i) { - expr* e = conjs[i].get(); + for (expr* e : conjs) { bool cv = contains_var.is_marked(e); bool cu = contains_uf.is_marked(e); if (cv && cu) { @@ -362,7 +361,7 @@ namespace qe { } app* ite; if (find_ite(fml, ite)) { - expr* cond = 0, *th = 0, *el = 0; + expr* cond = nullptr, *th = nullptr, *el = nullptr; VERIFY(m.is_ite(ite, cond, th, el)); expr_ref tmp1(fml, m), tmp2(fml, m); m_replace->apply_substitution(ite, th, tmp1); @@ -449,7 +448,7 @@ namespace qe { } expr* lookup(expr* e, bool p) { - expr* r = 0; + expr* r = nullptr; if (p && m_pos.find(e, r)) { return r; } @@ -458,7 +457,7 @@ namespace qe { } m_todo.push_back(e); m_pols.push_back(p); - return 0; + return nullptr; } void insert(expr* e, bool p, expr* r) { @@ -615,7 +614,7 @@ namespace qe { else if (m.is_ite(a)) { nnf_ite(a, p); } - else if (m.is_iff(a) || (m.is_eq(a) && m.is_bool(a->get_arg(0)))) { + else if (m.is_iff(a)) { nnf_iff(a, p); } else if (m.is_xor(a)) { @@ -688,7 +687,7 @@ namespace qe { bool visit(app* e) { bool all_visit = true; - expr* f = 0; + expr* f = nullptr; expr_ref tmp(m); if (!m_is_relevant(e)) { m_cache.insert(e, e); @@ -971,7 +970,7 @@ namespace qe { app* var() const { SASSERT(has_var()); return m_var; } - bool has_var() const { return 0 != m_var.get(); } + bool has_var() const { return nullptr != m_var.get(); } search_tree* parent() const { return m_parent; } @@ -989,11 +988,11 @@ namespace qe { todo.pop_back(); if (st->m_children.empty() && st->fml() && st->m_vars.empty() && !st->has_var()) { + TRACE("qe", st->display(tout << "appending leaf\n");); result.push_back(st->fml()); } - for (unsigned i = 0; i < st->m_children.size(); ++i) { - todo.push_back(st->m_children[i]); - } + for (auto * ch : st->m_children) + todo.push_back(ch); } } @@ -1020,15 +1019,13 @@ namespace qe { void reset() { TRACE("qe",tout << "resetting\n";); - for (unsigned i = 0; i < m_children.size(); ++i) { - dealloc(m_children[i]); - } + for (auto* ch : m_children) dealloc(ch); m_pos.reset(); m_neg.reset(); m_children.reset(); m_vars.reset(); m_branch_index.reset(); - m_var = 0; + m_var = nullptr; m_def.reset(); m_num_branches = rational::zero(); m_pure = true; @@ -1046,9 +1043,10 @@ namespace qe { } unsigned num_free_vars() const { return m_vars.size(); } - app* const* free_vars() const { return m_vars.c_ptr(); } + // app* const* free_vars() const { return m_vars.c_ptr(); } + app_ref_vector const& free_vars() const { return m_vars; } app* free_var(unsigned i) const { return m_vars[i]; } - void reset_free_vars() { m_vars.reset(); } + void reset_free_vars() { TRACE("qe", tout << m_vars << "\n";); m_vars.reset(); } atom_set const& pos_atoms() const { return m_pos; } atom_set const& neg_atoms() const { return m_neg; } @@ -1119,7 +1117,7 @@ namespace qe { st->init(fml); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); - TRACE("qe", tout << mk_pp(m_fml, m) << " child: " << mk_pp(fml, m) << "\n";); + TRACE("qe", display_node(tout); st->display_node(tout);); return st; } @@ -1133,7 +1131,7 @@ namespace qe { m_branch_index.insert(branch_id, index); st->m_vars.append(m_vars.size(), m_vars.c_ptr()); SASSERT(invariant()); - //TRACE("qe", tout << mk_pp(m_fml, m) << " child: " << mk_pp(st->fml(), m) << "\n";); + TRACE("qe", display_node(tout); st->display_node(tout);); return st; } @@ -1141,27 +1139,29 @@ namespace qe { display(out, ""); } - void display(std::ostream& out, char const* indent) const { - - out << indent << "node\n"; + void display_node(std::ostream& out, char const* indent = "") const { + out << indent << "node " << std::hex << this << std::dec << "\n"; if (m_var) { - out << indent << " var: " << mk_ismt2_pp(m_var.get(), m) << "\n"; + out << indent << " var: " << m_var << "\n"; } - for (unsigned i = 0; i < m_vars.size(); ++i) { - out << indent << " free: " << mk_ismt2_pp(m_vars[i], m) << "\n"; + for (app* v : m_vars) { + out << indent << " free: " << mk_pp(v, m) << "\n"; } if (m_fml) { - out << indent << " fml: " << mk_ismt2_pp(m_fml.get(), m) << "\n"; + out << indent << " fml: " << m_fml << "\n"; } for (unsigned i = 0; i < m_def.size(); ++i) { out << indent << " def: " << m_def.var(i)->get_name() << " = " << mk_ismt2_pp(m_def.def(i), m) << "\n"; } out << indent << " branches: " << m_num_branches << "\n"; + } + void display(std::ostream& out, char const* indent) const { + display_node(out, indent); std::string new_indent(indent); new_indent += " "; - for (unsigned i = 0; i < m_children.size(); ++i) { - m_children[i]->display(out, new_indent.c_str()); + for (auto * ch : m_children) { + ch->display(out, new_indent.c_str()); } } @@ -1214,6 +1214,7 @@ namespace qe { out << "(push)\n"; pp.display_smt2(out, fml); out << "(pop)\n\n"; +#if 0 DEBUG_CODE( smt_params params; params.m_simplify_bit2int = true; @@ -1227,7 +1228,8 @@ namespace qe { std::cout << "; Validation failed:\n"; std::cout << mk_pp(fml, m) << "\n"; } -); + ); +#endif } for (unsigned i = 0; i < m_children.size(); ++i) { @@ -1369,11 +1371,11 @@ namespace qe { m_trail(m), m_fml(m), m_subfml(m), - m_root(0, m, m.mk_true()), - m_current(0), + m_root(nullptr, m, m.mk_true()), + m_current(nullptr), m_new_vars(m), m_get_first(false), - m_defs(0), + m_defs(nullptr), m_nnf(m, get_is_relevant(), get_mk_atom()) { params_ref params; @@ -1381,7 +1383,7 @@ namespace qe { m_rewriter.updt_params(params); } - virtual ~quant_elim_plugin() { + ~quant_elim_plugin() override { reset(); } @@ -1396,8 +1398,8 @@ namespace qe { m_var2branch.reset(); m_root.reset(); m_new_vars.reset(); - m_fml = 0; - m_defs = 0; + m_fml = nullptr; + m_defs = nullptr; m_nnf.reset(); } @@ -1486,9 +1488,7 @@ namespace qe { tout << "subformula: " << mk_ismt2_pp(m_subfml, m) << "\n"; m_root.display(tout); m_root.display_validate(tout); - for (unsigned i = 0; i < m_free_vars.size(); ++i) tout << mk_ismt2_pp(m_free_vars[i].get(), m) << " "; - tout << "\n"; - ); + tout << "free: " << m_free_vars << "\n";); free_vars.append(m_free_vars); if (!m_free_vars.empty() || m_solver.inconsistent()) { @@ -1501,7 +1501,7 @@ namespace qe { } reset(); m_solver.pop(1); - f = 0; + f = nullptr; } void collect_statistics(statistics& st) { @@ -1536,24 +1536,24 @@ namespace qe { pop(*model_eval); } - ast_manager& get_manager() { return m; } + ast_manager& get_manager() override { return m; } - atom_set const& pos_atoms() const { return m_current->pos_atoms(); } + atom_set const& pos_atoms() const override { return m_current->pos_atoms(); } - atom_set const& neg_atoms() const { return m_current->neg_atoms(); } + atom_set const& neg_atoms() const override { return m_current->neg_atoms(); } - unsigned get_num_vars() const { return m_current->num_free_vars(); } + unsigned get_num_vars() const override { return m_current->num_free_vars(); } - app* get_var(unsigned idx) const { return m_current->free_var(idx); } + app* get_var(unsigned idx) const override { return m_current->free_var(idx); } - app* const* get_vars() const { return m_current->free_vars(); } + app_ref_vector const& get_vars() const override { return m_current->free_vars(); } - contains_app& contains(unsigned idx) { return contains(get_var(idx)); } + contains_app& contains(unsigned idx) override { return contains(get_var(idx)); } // // The variable at idx is eliminated (without branching). // - void elim_var(unsigned idx, expr* _fml, expr* def) { + void elim_var(unsigned idx, expr* _fml, expr* def) override { app* x = get_var(idx); expr_ref fml(_fml, m); TRACE("qe", tout << mk_pp(x,m) << " " << mk_pp(def, m) << "\n";); @@ -1564,14 +1564,14 @@ namespace qe { normalize(*m_current); } - void add_var(app* x) { + void add_var(app* x) override { m_new_vars.push_back(x); if (m_var2branch.contains(x)) { return; } contains_app* ca = alloc(contains_app, m, x); m_var2contains.insert(x, ca); - app* bv = 0; + app* bv = nullptr; if (m.is_bool(x) || m_bv.is_bv(x)) { bv = x; } @@ -1583,7 +1583,7 @@ namespace qe { m_var2branch.insert(x, bv); } - virtual void add_constraint(bool use_current_val, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) { + void add_constraint(bool use_current_val, expr* l1 = nullptr, expr* l2 = nullptr, expr* l3 = nullptr) override { search_tree* node = m_current; if (!use_current_val) { node = m_current->parent(); @@ -1598,12 +1598,12 @@ namespace qe { add_literal(l3); expr_ref fml(m); fml = m.mk_or(m_literals.size(), m_literals.c_ptr()); - TRACE("qe", tout << mk_ismt2_pp(fml, m) << "\n";); + TRACE("qe", tout << fml << "\n";); m_solver.assert_expr(fml); } - void blast_or(app* var, expr_ref& fml) { - m_qe.eliminate_exists(1, &var, fml, m_free_vars, false, 0); + void blast_or(app* var, expr_ref& fml) override { + m_qe.eliminate_exists(1, &var, fml, m_free_vars, false, nullptr); } lbool eliminate_exists(unsigned num_vars, app* const* vars, expr_ref& fml, bool get_first, guarded_defs* defs) { @@ -1613,7 +1613,7 @@ namespace qe { private: void add_literal(expr* l) { - if (l != 0) { + if (l != nullptr) { m_literals.push_back(l); } } @@ -1654,9 +1654,7 @@ namespace qe { } app* get_branch_id(app* x) { - app* result = 0; - VERIFY (m_var2branch.find(x, result)); - return result; + return m_var2branch[x]; } bool extract_partition(ptr_vector& vars) { @@ -1707,11 +1705,11 @@ namespace qe { return NEED_PROPAGATION; } m_current = m_current->child(branch); - if (m_current->fml() == 0) { + if (m_current->fml() == nullptr) { SASSERT(!m_current->has_var()); if (apply) { expr_ref def(m); - plugin(x).subst(contains(x), branch, fml, m_defs?&def:0); + plugin(x).subst(contains(x), branch, fml, m_defs?&def:nullptr); SASSERT(!contains(x)(fml)); m_current->consume_vars(m_new_vars); m_current->init(fml); @@ -1912,6 +1910,7 @@ namespace qe { } m_current->set_var(x, k); + TRACE("qe", tout << mk_pp(x, m) << " := " << k << "\n";); if (m_bv.is_bv(x)) { return; } @@ -1952,7 +1951,7 @@ namespace qe { vars.reset(); closed = closed && (r != l_undef); } - TRACE("qe", tout << mk_pp(fml, m) << "\n";); + TRACE("qe", tout << fml << " free: " << m_current->free_vars() << "\n";); m_current->add_child(fml)->reset_free_vars(); block_assignment(); } @@ -1961,9 +1960,7 @@ namespace qe { // variable queueing. contains_app& contains(app* x) { - contains_app* result = 0; - VERIFY(m_var2contains.find(x, result)); - return *result; + return *m_var2contains[x]; } bool find_min_weight(app*& x, rational& num_branches) { @@ -2003,6 +2000,7 @@ namespace qe { bool solved = true; while (solved) { expr_ref fml(m_current->fml(), m); + TRACE("qe", tout << fml << "\n";); conj_enum conjs(m, fml); solved = false; for (unsigned i = 0; !solved && i < m_plugins.size(); ++i) { @@ -2037,7 +2035,7 @@ namespace qe { { } - virtual ~quant_elim_new() { + ~quant_elim_new() override { reset(); } @@ -2054,17 +2052,17 @@ namespace qe { } - void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { for (unsigned i = 0; i < m_plugins.size(); ++i) { m_plugins[i]->collect_statistics(st); } } - void updt_params(params_ref const& p) { + void updt_params(params_ref const& p) override { m_eliminate_variables_as_block = p.get_bool("eliminate_variables_as_block", m_eliminate_variables_as_block); } - void eliminate(bool is_forall, unsigned num_vars, app* const* vars, expr_ref& fml) { + void eliminate(bool is_forall, unsigned num_vars, app* const* vars, expr_ref& fml) override { if (is_forall) { eliminate_forall_bind(num_vars, vars, fml); } @@ -2094,14 +2092,14 @@ namespace qe { } } - virtual void set_assumption(expr* fml) { + void set_assumption(expr* fml) override { m_assumption = fml; } - virtual lbool eliminate_exists( + lbool eliminate_exists( unsigned num_vars, app* const* vars, expr_ref& fml, - app_ref_vector& free_vars, bool get_first, guarded_defs* defs) { + app_ref_vector& free_vars, bool get_first, guarded_defs* defs) override { if (get_first) { return eliminate_block(num_vars, vars, fml, free_vars, get_first, defs); } @@ -2199,7 +2197,7 @@ namespace qe { void eliminate_exists_bind(unsigned num_vars, app* const* vars, expr_ref& fml) { checkpoint(); app_ref_vector free_vars(m); - eliminate_exists(num_vars, vars, fml, free_vars, false, 0); + eliminate_exists(num_vars, vars, fml, free_vars, false, nullptr); bind_variables(free_vars.size(), free_vars.c_ptr(), fml); } @@ -2221,7 +2219,7 @@ namespace qe { m_fparams(fp), m_params(p), m_trail(m), - m_qe(0), + m_qe(nullptr), m_assumption(m.mk_true()) { } @@ -2304,7 +2302,7 @@ namespace qe { m_trail.push_back(result); todo.push_back(result); - expr* e = 0, *r = 0; + expr* e = nullptr, *r = nullptr; while (!todo.empty()) { e = todo.back(); @@ -2474,8 +2472,8 @@ namespace qe { public: simplify_solver_context(ast_manager& m): m(m), - m_vars(0), - m_fml(0) + m_vars(nullptr), + m_fml(nullptr) { add_plugin(mk_bool_plugin(*this)); add_plugin(mk_arith_plugin(*this, false, m_fparams)); @@ -2485,7 +2483,7 @@ namespace qe { m_fparams.updt_params(p); } - virtual ~simplify_solver_context() { reset(); } + ~simplify_solver_context() override { reset(); } void solve(expr_ref& fml, app_ref_vector& vars) { @@ -2504,16 +2502,16 @@ namespace qe { while (solved); } - virtual ast_manager& get_manager() { return m; } + ast_manager& get_manager() override { return m; } - virtual atom_set const& pos_atoms() const { return m_pos; } - virtual atom_set const& neg_atoms() const { return m_neg; } + atom_set const& pos_atoms() const override { return m_pos; } + atom_set const& neg_atoms() const override { return m_neg; } // Access current set of variables to solve - virtual unsigned get_num_vars() const { return m_vars->size(); } - virtual app* get_var(unsigned idx) const { return (*m_vars)[idx].get(); } - virtual app*const* get_vars() const { return m_vars->c_ptr(); } - virtual bool is_var(expr* e, unsigned& idx) const { + unsigned get_num_vars() const override { return m_vars->size(); } + app* get_var(unsigned idx) const override { return (*m_vars)[idx].get(); } + app_ref_vector const& get_vars() const override { return *m_vars; } + bool is_var(expr* e, unsigned& idx) const override { for (unsigned i = 0; i < m_vars->size(); ++i) { if ((*m_vars)[i].get() == e) { idx = i; @@ -2523,12 +2521,12 @@ namespace qe { return false; } - virtual contains_app& contains(unsigned idx) { + contains_app& contains(unsigned idx) override { return *m_contains[idx]; } // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' - virtual void elim_var(unsigned idx, expr* fml, expr* def) { + void elim_var(unsigned idx, expr* fml, expr* def) override { TRACE("qe", tout << mk_pp(m_vars->get(idx), m) << " " << mk_pp(fml, m) << "\n";); *m_fml = fml; m_vars->set(idx, m_vars->get(m_vars->size()-1)); @@ -2539,16 +2537,17 @@ namespace qe { } // callback to add new variable to branch. - virtual void add_var(app* x) { + void add_var(app* x) override { + TRACE("qe", tout << "add var: " << mk_pp(x, m) << "\n";); m_vars->push_back(x); } // callback to add constraints in branch. - virtual void add_constraint(bool use_var, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) { + void add_constraint(bool use_var, expr* l1 = nullptr, expr* l2 = nullptr, expr* l3 = nullptr) override { UNREACHABLE(); } // eliminate finite domain variable 'var' from fml. - virtual void blast_or(app* var, expr_ref& fml) { + void blast_or(app* var, expr_ref& fml) override { UNREACHABLE(); } @@ -2606,7 +2605,7 @@ namespace qe { } m_ctx.solve(result, vars); if (old_q->is_forall()) { - expr* e = 0; + expr* e = nullptr; result = m.is_not(result, e)?e:mk_not(m, result); } var_shifter shift(m); @@ -2622,7 +2621,7 @@ namespace qe { if (!vars.empty()) { result = m.mk_quantifier(old_q->is_forall(), vars.size(), sorts.c_ptr(), names.c_ptr(), result, 1); } - result_pr = 0; + result_pr = nullptr; return true; } diff --git a/src/qe/qe.h b/src/qe/qe.h index b6754b384..1027f0b61 100644 --- a/src/qe/qe.h +++ b/src/qe/qe.h @@ -49,13 +49,13 @@ namespace qe { i_solver_context& m_s; public: is_relevant(i_solver_context& s):m_s(s) {} - virtual bool operator()(expr* e); + bool operator()(expr* e) override; }; class mk_atom_fn : public i_nnf_atom { i_solver_context& m_s; public: mk_atom_fn(i_solver_context& s) : m_s(s) {} - void operator()(expr* e, bool p, expr_ref& result); + void operator()(expr* e, bool p, expr_ref& result) override; }; is_relevant m_is_relevant; @@ -86,7 +86,7 @@ namespace qe { // Access current set of variables to solve virtual unsigned get_num_vars() const = 0; virtual app* get_var(unsigned idx) const = 0; - virtual app*const* get_vars() const = 0; + virtual app_ref_vector const& get_vars() const = 0; virtual bool is_var(expr* e, unsigned& idx) const; virtual contains_app& contains(unsigned idx) = 0; @@ -97,7 +97,7 @@ namespace qe { virtual void add_var(app* x) = 0; // callback to add constraints in branch. - virtual void add_constraint(bool use_var, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) = 0; + virtual void add_constraint(bool use_var, expr* l1 = nullptr, expr* l2 = nullptr, expr* l3 = nullptr) = 0; // eliminate finite domain variable 'var' from fml. virtual void blast_or(app* var, expr_ref& fml) = 0; diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 6b3b3a11f..ff4c7dc52 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -30,6 +30,7 @@ Revision History: #include "ast/rewriter/expr_safe_replace.h" #include "math/simplex/model_based_opt.h" #include "model/model_evaluator.h" +#include "model/model_smt2_pp.h" namespace qe { @@ -37,6 +38,7 @@ namespace qe { ast_manager& m; arith_util a; + bool m_check_purified; // check that variables are properly pure void insert_mul(expr* x, rational const& v, obj_map& ts) { TRACE("qe", tout << "Adding variable " << mk_pp(x, m) << " " << v << "\n";); @@ -90,6 +92,8 @@ namespace qe { rational r1, r2; expr_ref val1 = eval(e1); expr_ref val2 = eval(e2); + TRACE("qe", tout << mk_pp(e1, m) << " " << val1 << "\n";); + TRACE("qe", tout << mk_pp(e2, m) << " " << val2 << "\n";); if (!a.is_numeral(val1, r1)) return false; if (!a.is_numeral(val2, r2)) return false; SASSERT(r1 != r2); @@ -107,6 +111,7 @@ namespace qe { vector > nums; for (expr* arg : *alit) { val = eval(arg); + TRACE("qe", tout << mk_pp(arg, m) << " " << val << "\n";); if (!a.is_numeral(val, r)) return false; nums.push_back(std::make_pair(arg, r)); } @@ -126,9 +131,10 @@ namespace qe { map values; bool found_eq = false; for (unsigned i = 0; !found_eq && i < to_app(lit)->get_num_args(); ++i) { - expr* arg1 = to_app(lit)->get_arg(i), *arg2 = 0; + expr* arg1 = to_app(lit)->get_arg(i), *arg2 = nullptr; rational r; expr_ref val = eval(arg1); + TRACE("qe", tout << mk_pp(arg1, m) << " " << val << "\n";); if (!a.is_numeral(val, r)) return false; if (values.find(r, arg2)) { ty = opt::t_eq; @@ -256,7 +262,7 @@ namespace qe { }; bool is_arith(expr* e) { - return a.is_int(e) || a.is_real(e); + return a.is_int_real(e); } rational n_sign(rational const& b) { @@ -264,7 +270,7 @@ namespace qe { } imp(ast_manager& m): - m(m), a(m) {} + m(m), a(m), m_check_purified(true) {} ~imp() {} @@ -275,7 +281,7 @@ namespace qe { bool operator()(model& model, app* v, app_ref_vector& vars, expr_ref_vector& lits) { app_ref_vector vs(m); vs.push_back(v); - (*this)(model, vs, lits); + project(model, vs, lits, false); return vs.empty(); } @@ -283,36 +289,43 @@ namespace qe { typedef opt::model_based_opt::row row; typedef vector vars; - void operator()(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { + expr_ref var2expr(ptr_vector const& index2expr, var const& v) { + expr_ref t(index2expr[v.m_id], m); + if (!v.m_coeff.is_one()) { + t = a.mk_mul(a.mk_numeral(v.m_coeff, a.is_int(t)), t); + } + return t; + } + + vector project(model& model, app_ref_vector& vars, expr_ref_vector& fmls, bool compute_def) { bool has_arith = false; - for (unsigned i = 0; !has_arith && i < vars.size(); ++i) { - expr* v = vars[i].get(); + for (expr* v : vars) { has_arith |= is_arith(v); } if (!has_arith) { - return; + return vector(); } model_evaluator eval(model); + TRACE("qe", model_smt2_pp(tout, m, model, 0);); // eval.set_model_completion(true); opt::model_based_opt mbo; obj_map tids; expr_ref_vector pinned(m); unsigned j = 0; + TRACE("qe", tout << "fmls: " << fmls << "\n";); for (unsigned i = 0; i < fmls.size(); ++i) { - expr* fml = fmls[i].get(); + expr * fml = fmls.get(i); if (!linearize(mbo, eval, fml, fmls, tids)) { - if (i != j) { - fmls[j] = fmls[i].get(); - } - ++j; + TRACE("qe", tout << "could not linearize: " << mk_pp(fml, m) << "\n";); + fmls[j++] = fml; } else { - TRACE("qe", tout << mk_pp(fml, m) << "\n";); pinned.push_back(fml); } } - fmls.resize(j); + fmls.shrink(j); + TRACE("qe", tout << "linearized: " << fmls << "\n";); // fmls holds residue, // mbo holds linear inequalities that are in scope @@ -328,51 +341,53 @@ namespace qe { if (is_arith(v) && !tids.contains(v)) { rational r; expr_ref val = eval(v); - a.is_numeral(val, r); + VERIFY(a.is_numeral(val, r)); TRACE("qe", tout << mk_pp(v, m) << " " << val << "\n";); tids.insert(v, mbo.add_var(r, a.is_int(v))); } } - for (expr* fml : fmls) { - fmls_mark.mark(fml); + if (m_check_purified) { + for (expr* fml : fmls) { + mark_rec(fmls_mark, fml); + } + for (auto& kv : tids) { + expr* e = kv.m_key; + if (!var_mark.is_marked(e)) { + mark_rec(fmls_mark, e); + } + } } + ptr_vector index2expr; for (auto& kv : tids) { - expr* e = kv.m_key; - if (!var_mark.is_marked(e)) { - mark_rec(fmls_mark, e); - } - index2expr.setx(kv.m_value, e, 0); + index2expr.setx(kv.m_value, kv.m_key, nullptr); } + j = 0; unsigned_vector real_vars; - for (unsigned i = 0; i < vars.size(); ++i) { - app* v = vars[i].get(); + for (app* v : vars) { if (is_arith(v) && !fmls_mark.is_marked(v)) { real_vars.push_back(tids.find(v)); } else { - if (i != j) { - vars[j] = v; - } - ++j; + vars[j++] = v; } } - vars.resize(j); + vars.shrink(j); + TRACE("qe", tout << "remaining vars: " << vars << "\n"; for (unsigned v : real_vars) { tout << "v" << v << " " << mk_pp(index2expr[v], m) << "\n"; } mbo.display(tout);); - mbo.project(real_vars.size(), real_vars.c_ptr()); + vector defs = mbo.project(real_vars.size(), real_vars.c_ptr(), compute_def); TRACE("qe", mbo.display(tout);); vector rows; mbo.get_live_rows(rows); - for (unsigned i = 0; i < rows.size(); ++i) { + for (row const& r : rows) { expr_ref_vector ts(m); expr_ref t(m), s(m), val(m); - row const& r = rows[i]; if (r.m_vars.size() == 0) { continue; } @@ -394,41 +409,87 @@ namespace qe { CTRACE("qe", !m.is_true(val), tout << "Evaluated unit " << t << " to " << val << "\n";); continue; } - for (j = 0; j < r.m_vars.size(); ++j) { - var const& v = r.m_vars[j]; + for (var const& v : r.m_vars) { t = index2expr[v.m_id]; if (!v.m_coeff.is_one()) { t = a.mk_mul(a.mk_numeral(v.m_coeff, a.is_int(t)), t); } ts.push_back(t); } - s = a.mk_numeral(-r.m_coeff, a.is_int(t)); - if (ts.size() == 1) { - t = ts[0].get(); - } - else { - t = a.mk_add(ts.size(), ts.c_ptr()); - } + t = mk_add(ts); + s = a.mk_numeral(-r.m_coeff, r.m_coeff.is_int() && a.is_int(t)); switch (r.m_type) { case opt::t_lt: t = a.mk_lt(t, s); break; case opt::t_le: t = a.mk_le(t, s); break; case opt::t_eq: t = a.mk_eq(t, s); break; - case opt::t_mod: { + case opt::t_mod: if (!r.m_coeff.is_zero()) { t = a.mk_sub(t, s); } t = a.mk_eq(a.mk_mod(t, a.mk_numeral(r.m_mod, true)), a.mk_int(0)); break; } - } fmls.push_back(t); - val = eval(t); CTRACE("qe", !m.is_true(val), tout << "Evaluated " << t << " to " << val << "\n";); - } + vector result; + if (compute_def) { + SASSERT(defs.size() == real_vars.size()); + for (unsigned i = 0; i < defs.size(); ++i) { + auto const& d = defs[i]; + expr* x = index2expr[real_vars[i]]; + bool is_int = a.is_int(x); + expr_ref_vector ts(m); + expr_ref t(m); + for (var const& v : d.m_vars) { + ts.push_back(var2expr(index2expr, v)); + } + if (!d.m_coeff.is_zero()) + ts.push_back(a.mk_numeral(d.m_coeff, is_int)); + t = mk_add(ts); + if (!d.m_div.is_one() && is_int) { + t = a.mk_idiv(t, a.mk_numeral(d.m_div, is_int)); + } + else if (!d.m_div.is_one() && !is_int) { + t = a.mk_div(t, a.mk_numeral(d.m_div, is_int)); + } + update_model(model, to_app(x), eval(t)); + + SASSERT(eval(t) == eval(x)); + result.push_back(def(expr_ref(x, m), t)); + } + } + return result; } + void update_model(model& mdl, app* x, expr_ref const& val) { + if (is_uninterp_const(x)) { + mdl.register_decl(x->get_decl(), val); + } + else { + func_interp* fi = mdl.get_func_interp(x->get_decl()); + if (!fi) return; + model_evaluator eval(mdl); + expr_ref_vector args(m); + for (expr* arg : *x) { + args.push_back(eval(arg)); + } + fi->insert_entry(args.c_ptr(), val); + } + } + + expr_ref mk_add(expr_ref_vector const& ts) { + switch (ts.size()) { + case 0: + return expr_ref(a.mk_int(0), m); + case 1: + return expr_ref(ts.get(0), m); + default: + return expr_ref(a.mk_add(ts.size(), ts.c_ptr()), m); + } + } + opt::inf_eps maximize(expr_ref_vector const& fmls0, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { SASSERT(a.is_real(t)); expr_ref_vector fmls(fmls0); @@ -539,7 +600,15 @@ namespace qe { } void arith_project_plugin::operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - (*m_imp)(model, vars, lits); + m_imp->project(model, vars, lits, false); + } + + vector arith_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return m_imp->project(model, vars, lits, true); + } + + void arith_project_plugin::set_check_purified(bool check_purified) { + m_imp->m_check_purified = check_purified; } bool arith_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index 88675d5a4..b55e63fcf 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -25,14 +25,20 @@ namespace qe { imp* m_imp; public: arith_project_plugin(ast_manager& m); - virtual ~arith_project_plugin(); - virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); - virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); - virtual family_id get_family_id(); - virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits); - + ~arith_project_plugin() override; + bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; + bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; + family_id get_family_id() override; + void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; + vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); + + /** + * \brief check if formulas are purified, or leave it to caller to ensure that + * arithmetic variables nested under foreign functions are handled properly. + */ + void set_check_purified(bool check_purified); }; bool arith_project(model& model, app* var, expr_ref_vector& lits); diff --git a/src/qe/qe_arith_plugin.cpp b/src/qe/qe_arith_plugin.cpp index 1085ec3c2..21e50182f 100644 --- a/src/qe/qe_arith_plugin.cpp +++ b/src/qe/qe_arith_plugin.cpp @@ -105,6 +105,7 @@ namespace qe { public: arith_util m_arith; // initialize before m_zero_i, etc. th_rewriter simplify; + app_ref_vector m_vars_added; private: arith_eq_solver m_arith_solver; bv_util m_bv; @@ -126,6 +127,7 @@ namespace qe { m_ctx(ctx), m_arith(m), simplify(m), + m_vars_added(m), m_arith_solver(m), m_bv(m), m_zero_i(m_arith.mk_numeral(numeral(0), true), m), @@ -783,6 +785,11 @@ namespace qe { } } + void add_var(app* v, bool track = true) { + m_ctx.add_var(v); + if (track) m_vars_added.push_back(v); + } + private: @@ -850,10 +857,11 @@ namespace qe { << mk_pp(result, m) << "\n";); } + void mk_big_or_symbolic(numeral up, app* x, expr* body, expr_ref& result) { app_ref z_bv(m); mk_big_or_symbolic(up, x, body, z_bv, result); - m_ctx.add_var(z_bv); + add_var(z_bv); } void mk_big_or_symbolic_blast(numeral up, app* x, expr* body, expr_ref& result) { @@ -883,7 +891,7 @@ namespace qe { while (m_arith.is_add(p)) { bool found_x = false; - expr* next_p = 0; + expr* next_p = nullptr; for (unsigned i = 0; i < to_app(p)->get_num_args(); ++i) { expr* arg = to_app(p)->get_arg(i); if (contains_x(arg)) { @@ -999,9 +1007,9 @@ namespace qe { bool solve_linear(expr* p, expr* fml) { vector values; unsigned num_vars = m_ctx.get_num_vars(); - app*const* vars_ptr = m_ctx.get_vars(); + app_ref_vector const& vars = m_ctx.get_vars(); - if (!is_linear(p, num_vars, vars_ptr, values)) { + if (!is_linear(p, num_vars, vars.c_ptr(), values)) { return false; } @@ -1025,7 +1033,7 @@ namespace qe { // it has coefficient 'm' = values[index]. SASSERT(values[index] >= rational(3)); z = m.mk_fresh_const("x", m_arith.mk_int()); - m_ctx.add_var(z); + add_var(z); p1 = m_arith.mk_mul(m_arith.mk_numeral(values[index], true), z); } else { @@ -1279,7 +1287,7 @@ namespace qe { m_div_coeffs.reset(); m_div_divisors.reset(); m_div_atoms.reset(); - m_div_z = 0; + m_div_z = nullptr; m_nested_div_terms.reset(); m_nested_div_coeffs.reset(); m_nested_div_divisors.reset(); @@ -1475,16 +1483,18 @@ public: expr* m_result; rational m_coeff; expr* m_term; + ptr_vector m_vars; - branch_formula(): m_fml(0), m_var(0), m_branch(0), m_result(0), m_term(0) {} + branch_formula(): m_fml(nullptr), m_var(nullptr), m_branch(0), m_result(nullptr), m_term(nullptr) {} - branch_formula(expr* fml, app* var, unsigned b, expr* r, rational coeff, expr* term): + branch_formula(expr* fml, app* var, unsigned b, expr* r, rational coeff, expr* term, app_ref_vector const& vars): m_fml(fml), m_var(var), m_branch(b), m_result(r), m_coeff(coeff), - m_term(term) + m_term(term), + m_vars(vars.size(), vars.c_ptr()) {} unsigned mk_hash() const { @@ -1526,14 +1536,14 @@ public: m_trail(m) {} - ~arith_plugin() { + ~arith_plugin() override { bounds_cache::iterator it = m_bounds_cache.begin(), end = m_bounds_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); } } - virtual void assign(contains_app& contains_x, expr* fml, rational const& vl) { + void assign(contains_app& contains_x, expr* fml, rational const& vl) override { SASSERT(vl.is_unsigned()); app* x = contains_x.x(); unsigned v = vl.get_unsigned(); @@ -1544,6 +1554,7 @@ public: if (get_cache(x, fml, v, result)) { return; } + m_util.m_vars_added.reset(); bounds_proc& bounds = get_bounds(x, fml); bool is_lower = get_bound_sizes(bounds, x, t_size, e_size); @@ -1601,8 +1612,7 @@ public: expr_ref t(bounds.exprs(is_strict, is_lower)[index], m); rational a = bounds.coeffs(is_strict, is_lower)[index]; - - + mk_bounds(bounds, x, true, is_eq, is_strict, is_lower, index, a, t, result); mk_bounds(bounds, x, false, is_eq, is_strict, is_lower, index, a, t, result); @@ -1617,13 +1627,13 @@ public: { tout << vl << " " << mk_pp(bounds.atoms(is_strict, is_lower)[index],m) << "\n"; tout << mk_pp(fml, m) << "\n"; - tout << mk_pp(result, m) << "\n"; + tout << result << "\n"; } ); } - virtual bool get_num_branches(contains_app& contains_x, expr* fml, rational& nb) { + bool get_num_branches(contains_app& contains_x, expr* fml, rational& nb) override { app* x = contains_x.x(); if (!update_bounds(contains_x, fml)) { return false; @@ -1635,16 +1645,16 @@ public: return true; } - virtual void subst(contains_app& contains_x, rational const& vl, expr_ref& fml, expr_ref* def) { + void subst(contains_app& contains_x, rational const& vl, expr_ref& fml, expr_ref* def) override { SASSERT(vl.is_unsigned()); - if (def) { + if (def) { get_def(contains_x, vl.get_unsigned(), fml, *def); } VERIFY(get_cache(contains_x.x(), fml, vl.get_unsigned(), fml)); TRACE("qe", tout << mk_pp(contains_x.x(), m) << " " << vl << "\n" << mk_pp(fml, m) << "\n";); } - virtual bool project(contains_app& x, model_ref& model, expr_ref& fml) { + bool project(contains_app& x, model_ref& model, expr_ref& fml) override { if (!update_bounds(x, fml)) { TRACE("qe", tout << mk_pp(x.x(), m) << " failed to update bounds\n";); return false; @@ -1658,19 +1668,19 @@ public: } - virtual unsigned get_weight(contains_app& contains_x, expr* fml) { + unsigned get_weight(contains_app& contains_x, expr* fml) override { return 2; } - virtual bool solve(conj_enum& conjs, expr* fml) { + bool solve(conj_enum& conjs, expr* fml) override { return m_util.solve(conjs, fml); } - virtual bool mk_atom(expr* e, bool p, expr_ref& result) { + bool mk_atom(expr* e, bool p, expr_ref& result) override { return m_util.mk_atom(e, p, result); } - virtual bool is_uninterpreted(app* f) { + bool is_uninterpreted(app* f) override { switch(f->get_decl_kind()) { case OP_NUM: case OP_LE: @@ -1740,7 +1750,7 @@ public: x_subst x_t(m_util); bounds_proc& bounds = get_bounds(x, fml); branch_formula bf; - VERIFY (m_subst.find(branch_formula(fml, x, v, 0, rational::zero(), 0), bf)); + VERIFY (m_subst.find(branch_formula(fml, x, v, nullptr, rational::zero(), nullptr, m_util.m_vars_added), bf)); x_t.set_term(bf.m_term); x_t.set_coeff(bf.m_coeff); @@ -1939,7 +1949,7 @@ public: } } assign(x, fml, vl); - subst(x, vl, fml, 0); + subst(x, vl, fml, nullptr); TRACE("qe", tout << mk_pp(fml, m) << "\n";); return true; } @@ -1962,7 +1972,7 @@ public: vl = numeral(0); } assign(x, fml, vl); - subst(x, vl, fml, 0); + subst(x, vl, fml, nullptr); TRACE("qe", tout << mk_pp(fml, m) << "\n";); return true; @@ -2022,16 +2032,19 @@ public: m_trail.push_back(fml); m_trail.push_back(result); if (term) m_trail.push_back(term); - m_subst.insert(branch_formula(fml, x, v, result, coeff, term)); + m_subst.insert(branch_formula(fml, x, v, result, coeff, term, m_util.m_vars_added)); } bool get_cache(app* x, expr* fml, unsigned v, expr_ref& result) { branch_formula bf; - if (!m_subst.find(branch_formula(fml, x, v, 0, rational::zero(), 0), bf)) { + if (!m_subst.find(branch_formula(fml, x, v, nullptr, rational::zero(), nullptr, m_util.m_vars_added), bf)) { return false; } SASSERT(bf.m_result); result = bf.m_result; + for (app* v : bf.m_vars) { + m_util.add_var(v, false); + } return true; } @@ -2043,7 +2056,7 @@ public: if (!bounds.div_z(d, z_bv, z)) { return; } - m_ctx.add_var(z_bv); + m_util.add_var(z_bv); // // assert @@ -2120,7 +2133,7 @@ public: app* z1_bv = bounds.nested_div_z_bv(i); app* z1 = bounds.nested_div_z(i); - m_ctx.add_var(z1_bv); + m_util.add_var(z1_bv); // // assert @@ -2158,7 +2171,7 @@ public: } bounds_proc& get_bounds(app* x, expr* fml) { - bounds_proc* result = 0; + bounds_proc* result = nullptr; VERIFY (m_bounds_cache.find(x, fml, result)); return *result; } @@ -2394,7 +2407,7 @@ public: } bool update_bounds(contains_app& contains_x, expr* fml) { - bounds_proc* bounds = 0; + bounds_proc* bounds = nullptr; if (m_bounds_cache.find(contains_x.x(), fml, bounds)) { return true; } @@ -2443,7 +2456,7 @@ public: m_util.set_enable_linear(true); // (produce_models); } - virtual ~nlarith_plugin() { + ~nlarith_plugin() override { bcs_t::iterator it = m_cache.begin(), end = m_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); @@ -2454,7 +2467,7 @@ public: } } - virtual bool simplify(expr_ref& fml) { + bool simplify(expr_ref& fml) override { expr_ref tmp(m), tmp2(m); m_factor_rw(fml, tmp); m_rewriter(tmp, tmp2); @@ -2465,8 +2478,8 @@ public: return false; } - virtual void assign(contains_app& x, expr* fml, rational const& vl) { - nlarith::branch_conditions *brs = 0; + void assign(contains_app& x, expr* fml, rational const& vl) override { + nlarith::branch_conditions *brs = nullptr; VERIFY (m_cache.find(x.x(), fml, brs)); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < brs->size()); @@ -2478,8 +2491,8 @@ public: m_ctx.add_constraint(true, result); } - virtual bool get_num_branches(contains_app& x, - expr* fml, rational& num_branches) { + bool get_num_branches(contains_app& x, + expr* fml, rational& num_branches) override { nlarith::branch_conditions *brs; if (m_cache.find(x.x(), fml, brs)) { num_branches = rational(brs->size()); @@ -2502,8 +2515,8 @@ public: return true; } - virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { - nlarith::branch_conditions *brs = 0; + void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { + nlarith::branch_conditions *brs = nullptr; VERIFY (m_cache.find(x.x(), fml, brs)); SASSERT(vl.is_unsigned()); SASSERT(vl.get_unsigned() < brs->size()); @@ -2521,8 +2534,8 @@ public: } - virtual unsigned get_weight(contains_app& x, expr* fml) { - obj_map* weights = 0; + unsigned get_weight(contains_app& x, expr* fml) override { + obj_map* weights = nullptr; unsigned weight = 0; if (!m_weights.find(fml, weights)) { weights = alloc(weight_m); @@ -2540,12 +2553,12 @@ public: return UINT_MAX; } - virtual bool solve(conj_enum& conjs, expr* fml) { return false; } + bool solve(conj_enum& conjs, expr* fml) override { return false; } // we don't need to modify the atom. - virtual bool mk_atom(expr* e, bool p, expr_ref& result) { return false; } + bool mk_atom(expr* e, bool p, expr_ref& result) override { return false; } - virtual bool is_uninterpreted(app* f) { + bool is_uninterpreted(app* f) override { if (m_produce_models) { return true; } diff --git a/src/qe/qe_array_plugin.cpp b/src/qe/qe_array_plugin.cpp index 9eeccd6a4..775dd0443 100644 --- a/src/qe/qe_array_plugin.cpp +++ b/src/qe/qe_array_plugin.cpp @@ -27,23 +27,23 @@ namespace qe { { } - virtual ~array_plugin() {} + ~array_plugin() override {} - virtual void assign(contains_app& x, expr* fml, rational const& vl) { + void assign(contains_app& x, expr* fml, rational const& vl) override { UNREACHABLE(); } - virtual bool get_num_branches( contains_app& x, expr* fml, rational& num_branches) { + bool get_num_branches( contains_app& x, expr* fml, rational& num_branches) override { return false; } - virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { + void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { UNREACHABLE(); } - virtual bool solve(conj_enum& conjs, expr* fml) { + bool solve(conj_enum& conjs, expr* fml) override { conj_enum::iterator it = conjs.begin(), end = conjs.end(); for (; it != end; ++it) { @@ -65,7 +65,7 @@ namespace qe { return false; } - virtual bool is_uninterpreted(app* f) { + bool is_uninterpreted(app* f) override { return true; } diff --git a/src/qe/qe_arrays.cpp b/src/qe/qe_arrays.cpp index 6d0bf82bf..4c81418b6 100644 --- a/src/qe/qe_arrays.cpp +++ b/src/qe/qe_arrays.cpp @@ -18,95 +18,1179 @@ Revision History: --*/ -#include "qe/qe_arrays.h" +#include "util/lbool.h" #include "ast/rewriter/rewriter_def.h" #include "ast/expr_functors.h" #include "ast/rewriter/expr_safe_replace.h" -#include "util/lbool.h" +#include "ast/rewriter/th_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" +#include "model/model_evaluator.h" +#include "qe/qe_arrays.h" + + +namespace { + bool is_partial_eq (app* a); + + /** + * \brief utility class for partial equalities + * + * A partial equality (a ==I b), for two arrays a,b and a finite set of indices I holds + * iff (Forall i. i \not\in I => a[i] == b[i]); in other words, it is a + * restricted form of the extensionality axiom + * + * using this class, we denote (a =I b) as f(a,b,i0,i1,...) + * where f is an uninterpreted predicate with name PARTIAL_EQ and + * I = {i0,i1,...} + */ + + // TBD: make work for arrays with multiple arguments. + class peq { + ast_manager& m; + expr_ref m_lhs; + expr_ref m_rhs; + vector m_diff_indices; + func_decl_ref m_decl; // the partial equality declaration + app_ref m_peq; // partial equality application + app_ref m_eq; // equivalent std equality using def. of partial eq + array_util m_arr_u; + + public: + static const char* PARTIAL_EQ; + + peq (app* p, ast_manager& m): + m (m), + m_lhs (p->get_arg (0), m), + m_rhs (p->get_arg (1), m), + m_decl (p->get_decl (), m), + m_peq (p, m), + m_eq (m), + m_arr_u (m) + { + VERIFY (is_partial_eq (p)); + SASSERT (m_arr_u.is_array (m_lhs) && + m_arr_u.is_array (m_rhs) && + m.get_sort(m_lhs) == m.get_sort(m_rhs)); + unsigned arity = get_array_arity(m.get_sort(m_lhs)); + for (unsigned i = 2; i < p->get_num_args (); i += arity) { + SASSERT(arity + i <= p->get_num_args()); + expr_ref_vector vec(m); + vec.append(arity, p->get_args() + i); + m_diff_indices.push_back (vec); + } + } + + peq (expr* lhs, expr* rhs, vector const& diff_indices, ast_manager& m): + m (m), + m_lhs (lhs, m), + m_rhs (rhs, m), + m_diff_indices (diff_indices), + m_decl (m), + m_peq (m), + m_eq (m), + m_arr_u (m) { + SASSERT (m_arr_u.is_array (lhs) && + m_arr_u.is_array (rhs) && + m.get_sort(lhs) == m.get_sort(rhs)); + ptr_vector sorts; + sorts.push_back (m.get_sort (m_lhs)); + sorts.push_back (m.get_sort (m_rhs)); + for (auto const& v : diff_indices) { + SASSERT(v.size() == get_array_arity(m.get_sort(m_lhs))); + for (expr* e : v) + sorts.push_back (m.get_sort(e)); + } + m_decl = m.mk_func_decl (symbol (PARTIAL_EQ), sorts.size (), sorts.c_ptr (), m.mk_bool_sort ()); + } + + expr_ref lhs () { return m_lhs; } + + expr_ref rhs () { return m_rhs; } + + void get_diff_indices (vector& result) { result.append(m_diff_indices); } + + app_ref mk_peq () { + if (!m_peq) { + ptr_vector args; + args.push_back (m_lhs); + args.push_back (m_rhs); + for (auto const& v : m_diff_indices) { + args.append (v.size(), v.c_ptr()); + } + m_peq = m.mk_app (m_decl, args.size (), args.c_ptr ()); + } + return m_peq; + } + + app_ref mk_eq (app_ref_vector& aux_consts, bool stores_on_rhs = true) { + if (!m_eq) { + expr_ref lhs (m_lhs, m), rhs (m_rhs, m); + if (!stores_on_rhs) { + std::swap (lhs, rhs); + } + // lhs = (...(store (store rhs i0 v0) i1 v1)...) + sort* val_sort = get_array_range (m.get_sort (lhs)); + for (expr_ref_vector const& diff : m_diff_indices) { + ptr_vector store_args; + store_args.push_back (rhs); + store_args.append (diff.size(), diff.c_ptr()); + app_ref val(m.mk_fresh_const ("diff", val_sort), m); + store_args.push_back (val); + aux_consts.push_back (val); + rhs = m_arr_u.mk_store (store_args.size (), store_args.c_ptr ()); + } + m_eq = m.mk_eq (lhs, rhs); + } + return m_eq; + } + }; + + const char* peq::PARTIAL_EQ = "!partial_eq"; + + bool is_partial_eq (app* a) { + return a->get_decl ()->get_name () == peq::PARTIAL_EQ; + } +} namespace qe { - struct array_project_plugin::imp { - // rewriter or direct procedure. - struct rw_cfg : public default_rewriter_cfg { - ast_manager& m; - array_util& a; - expr_ref_vector m_lits; - model* m_model; - imp* m_imp; - - rw_cfg(ast_manager& m, array_util& a): - m(m), a(a), m_lits(m), m_model(0) {} - - br_status reduce_app(func_decl* f, unsigned n, expr* const* args, expr_ref& result, proof_ref & pr) { - if (a.is_select(f) && a.is_store(args[0])) { - expr_ref val1(m), val2(m); - app* b = to_app(args[0]); - SASSERT(b->get_num_args() == n + 1); - for (unsigned i = 1; i < n; ++i) { - expr* arg1 = args[i]; - expr* arg2 = b->get_arg(i); - if (arg1 == arg2) { - val1 = val2 = arg1; + static bool is_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { + for (unsigned i = 0; i < xs.size(); ++i) if (xs[i] != ys[i]) return false; + return true; + } + + static expr_ref mk_eq(expr_ref_vector const& xs, expr_ref_vector const& ys) { + ast_manager& m = xs.get_manager(); + expr_ref_vector eqs(m); + for (unsigned i = 0; i < xs.size(); ++i) eqs.push_back(m.mk_eq(xs[i], ys[i])); + return mk_and(eqs); + } + + /** + * 1. Extract all equalities (store (store .. (store x i1 v1) i2 v2) .. ) = ... + * where x appears and the equalities do not evaluate to false in the current model. + * 2. Track them as partial equivalence relations. + * 3. Sort them according to nesting depth. + * 4. Solve for x by potentially introducing fresh variables. + * Thus, (store x i v) = y, then + * x = (store y i w), (select y i) = v, where w is fresh and evaluates to (select x i). + * Generally, equalities are tracked as x =_idxs y, where x, y are equal up to idxs. + */ + + class array_project_eqs_util { + ast_manager& m; + array_util m_arr_u; + model_ref M; + model_evaluator* m_mev; + app_ref m_v; // array var to eliminate + ast_mark m_has_stores_v; // has stores for m_v + expr_ref m_subst_term_v; // subst term for m_v + expr_safe_replace m_true_sub_v; // subst for true equalities + expr_safe_replace m_false_sub_v; // subst for false equalities + expr_ref_vector m_aux_lits_v; + expr_ref_vector m_idx_lits_v; + app_ref_vector m_aux_vars; + + void reset_v () { + m_v = nullptr; + m_has_stores_v.reset (); + m_subst_term_v = nullptr; + m_true_sub_v.reset (); + m_false_sub_v.reset (); + m_aux_lits_v.reset (); + m_idx_lits_v.reset (); + } + + void reset () { + M = nullptr; + m_mev = nullptr; + reset_v (); + m_aux_vars.reset (); + } + + /** + * find all array equalities on m_v or containing stores on/of m_v + * + * also mark terms containing stores on/of m_v + */ + void find_arr_eqs (expr_ref const& fml, app_ref_vector& eqs) { + if (!is_app (fml)) return; + ast_mark done; + ptr_vector todo; + todo.push_back (to_app (fml)); + while (!todo.empty ()) { + app* a = todo.back (); + if (done.is_marked (a)) { + todo.pop_back (); + continue; + } + bool all_done = true; + bool args_have_stores = false; + for (expr * arg : *a) { + if (!is_app (arg)) continue; + if (!done.is_marked (arg)) { + all_done = false; + todo.push_back (to_app (arg)); + } + else if (!args_have_stores && m_has_stores_v.is_marked (arg)) { + args_have_stores = true; + } + } + if (!all_done) continue; + todo.pop_back (); + + // mark if a has stores + if ((!m_arr_u.is_select (a) && args_have_stores) || + (m_arr_u.is_store (a) && (a->get_arg (0) == m_v))) { + m_has_stores_v.mark (a, true); + + TRACE ("qe", + tout << "has stores:\n"; + tout << mk_pp (a, m) << "\n"; + ); + } + + // check if a is a relevant array equality + expr * a0 = nullptr, *a1 = nullptr; + if (m.is_eq (a, a0, a1)) { + if (a0 == m_v || a1 == m_v || + (m_arr_u.is_array (a0) && m_has_stores_v.is_marked (a))) { + eqs.push_back (a); + } + } + // else, we can check for disequalities and handle them using extensionality, + // but it's not necessary + + done.mark (a, true); + } + } + + /** + * factor out select terms on m_v using fresh consts + */ + void factor_selects (app_ref& fml) { + expr_map sel_cache (m); + ast_mark done; + ptr_vector todo; + expr_ref_vector pinned (m); // to ensure a reference + + todo.push_back (fml); + while (!todo.empty ()) { + app* a = todo.back (); + if (done.is_marked (a)) { + todo.pop_back (); + continue; + } + expr_ref_vector args (m); + bool all_done = true; + for (expr * arg : *a) { + if (!is_app (arg)) { + args.push_back(arg); + } + else if (!done.is_marked (arg)) { + all_done = false; + todo.push_back (to_app (arg)); + } + else if (all_done) { // all done so far.. + expr* arg_new = nullptr; proof* pr; + sel_cache.get (arg, arg_new, pr); + if (!arg_new) { + arg_new = arg; } - else { - VERIFY(m_model->eval(arg1, val1)); - VERIFY(m_model->eval(arg2, val2)); - } - switch(compare(val1, val2)) { - case l_true: - if (arg1 != arg2) { - m_lits.push_back(m.mk_eq(arg1, arg2)); + args.push_back (arg_new); + } + } + if (!all_done) continue; + todo.pop_back (); + + expr_ref a_new (m.mk_app (a->get_decl (), args.size (), args.c_ptr ()), m); + + // if a_new is select on m_v, introduce new constant + if (m_arr_u.is_select (a) && + (args.get (0) == m_v || m_has_stores_v.is_marked (args.get (0)))) { + sort* val_sort = get_array_range (m.get_sort (m_v)); + app_ref val_const (m.mk_fresh_const ("sel", val_sort), m); + m_aux_vars.push_back (val_const); + // extend M to include val_const + expr_ref val = (*m_mev)(a_new); + M->register_decl (val_const->get_decl (), val); + // add equality + m_aux_lits_v.push_back (m.mk_eq (val_const, a_new)); + // replace select by const + a_new = val_const; + } + + if (a != a_new) { + sel_cache.insert (a, a_new, nullptr); + pinned.push_back (a_new); + } + done.mark (a, true); + } + expr* res = nullptr; proof* pr; + sel_cache.get (fml, res, pr); + if (res) { + fml = to_app (res); + } + } + + /** + * convert partial equality expression p_exp to an equality by + * recursively adding stores on diff indices + * + * add stores on lhs or rhs depending on whether stores_on_rhs is false/true + */ + void convert_peq_to_eq (expr* p_exp, app_ref& eq, bool stores_on_rhs = true) { + peq p (to_app (p_exp), m); + app_ref_vector diff_val_consts (m); + eq = p.mk_eq (diff_val_consts, stores_on_rhs); + m_aux_vars.append (diff_val_consts); + // extend M to include diff_val_consts + vector I; + expr_ref arr = p.lhs (); + p.get_diff_indices (I); + expr_ref val (m); + unsigned num_diff = diff_val_consts.size (); + SASSERT (num_diff == I.size ()); + for (unsigned i = 0; i < num_diff; i++) { + // mk val term + ptr_vector sel_args; + sel_args.push_back (arr); + sel_args.append(I[i].size(), I[i].c_ptr()); + expr_ref val_term (m_arr_u.mk_select (sel_args.size (), sel_args.c_ptr ()), m); + // evaluate and assign to ith diff_val_const + val = (*m_mev)(val_term); + M->register_decl (diff_val_consts.get (i)->get_decl (), val); + } + } + + /** + * mk (e0 ==indices e1) + * + * result has stores if either e0 or e1 or an index term has stores + */ + app_ref mk_peq (expr* e0, expr* e1, vector const& indices) { + peq p (e0, e1, indices, m); + return p.mk_peq (); + } + + void find_subst_term (app* eq) { + SASSERT(m.is_eq(eq)); + vector empty; + app_ref p_exp = mk_peq (eq->get_arg (0), eq->get_arg (1), empty); + bool subst_eq_found = false; + while (true) { + TRACE ("qe", tout << "processing peq:\n" << p_exp << "\n";); + + peq p (p_exp, m); + expr_ref lhs = p.lhs(), rhs = p.rhs(); + if (!m_has_stores_v.is_marked (lhs)) { + std::swap (lhs, rhs); + } + if (m_has_stores_v.is_marked (lhs)) { + /** project using the equivalence: + * + * (store(arr0,idx,x) ==I arr1) <-> + * + * (idx \in I => (arr0 ==I arr1)) /\ + * (idx \not\in I => (arr0 ==I+idx arr1) /\ (arr1[idx] == x))) + */ + vector I; + expr_ref_vector idxs (m); + p.get_diff_indices (I); + app* a_lhs = to_app (lhs); + expr* arr0 = a_lhs->get_arg (0); + idxs.append(a_lhs->get_num_args() - 2, a_lhs->get_args() + 1); + expr* x = a_lhs->get_arg (2); + expr* arr1 = rhs; + // check if (idx \in I) in M + bool idx_in_I = false; + expr_ref_vector idx_diseq (m); + if (!I.empty ()) { + expr_ref_vector vals = (*m_mev)(idxs); + for (unsigned i = 0; i < I.size () && !idx_in_I; i++) { + if (is_eq(idxs, I.get(i))) { + idx_in_I = true; } - break; - case l_false: { - ptr_vector new_args; - if (i > 0) { - m_lits.resize(m_lits.size() - i); + else { + expr_ref idx_eq = mk_eq(idxs, I[i]); + expr_ref_vector vals1 = (*m_mev)(I[i]); + if (is_eq(vals, vals1)) { + idx_in_I = true; + m_idx_lits_v.push_back (idx_eq); + } + else { + idx_diseq.push_back (m.mk_not (idx_eq)); + } } - m_lits.push_back(m.mk_not(m.mk_eq(arg1, arg2))); - new_args.push_back(b->get_arg(0)); - new_args.append(n-1, args+1); - result = m.mk_app(f, n, new_args.c_ptr()); - return BR_REWRITE1; - } - case l_undef: - return BR_FAILED; } } - result = b->get_arg(n); - return BR_DONE; + if (idx_in_I) { + TRACE ("qe", + tout << "store index in diff indices:\n"; + tout << mk_pp (m_idx_lits_v.back (), m) << "\n"; + ); + + // arr0 ==I arr1 + p_exp = mk_peq (arr0, arr1, I); + + TRACE ("qe", + tout << "new peq:\n"; + tout << mk_pp (p_exp, m) << "\n"; + ); + } + else { + m_idx_lits_v.append (idx_diseq); + // arr0 ==I+idx arr1 + I.push_back (idxs); + p_exp = mk_peq (arr0, arr1, I); + + TRACE ("qe", tout << "new peq:\n" << p_exp << "\n"; ); + + // arr1[idx] == x + ptr_vector sel_args; + sel_args.push_back (arr1); + sel_args.append(idxs.size(), idxs.c_ptr()); + expr_ref arr1_idx (m_arr_u.mk_select (sel_args.size (), sel_args.c_ptr ()), m); + expr_ref eq (m.mk_eq (arr1_idx, x), m); + m_aux_lits_v.push_back (eq); + + TRACE ("qe", + tout << "new eq:\n"; + tout << mk_pp (eq, m) << "\n"; + ); + } + } + else if (lhs == rhs) { // trivial peq (a ==I a) + break; + } + else if (lhs == m_v || rhs == m_v) { + subst_eq_found = true; + TRACE ("qe", + tout << "subst eq found!\n"; + ); + break; + } + else { + UNREACHABLE (); } - return BR_FAILED; } - lbool compare(expr* x, expr* y) { - NOT_IMPLEMENTED_YET(); - return l_undef; + // factor out select terms on m_v from p_exp using fresh constants + if (subst_eq_found) { + factor_selects (p_exp); + + TRACE ("qe", + tout << "after factoring selects:\n"; + tout << mk_pp (p_exp, m) << "\n"; + for (unsigned i = m_aux_lits_v.size () - m_aux_vars.size (); i < m_aux_lits_v.size (); i++) { + tout << mk_pp (m_aux_lits_v.get (i), m) << "\n"; + } + ); + + // find subst_term + bool stores_on_rhs = true; + app* a = to_app (p_exp); + if (a->get_arg (1) == m_v) { + stores_on_rhs = false; + } + app_ref eq (m); + convert_peq_to_eq (p_exp, eq, stores_on_rhs); + m_subst_term_v = eq->get_arg (1); + + TRACE ("qe", + tout << "subst term found:\n"; + tout << mk_pp (m_subst_term_v, m) << "\n"; + ); + } + } + + /** + * compute nesting depths of stores on m_v in true_eqs, as follows: + * 0 if m_v appears on both sides of equality + * 1 if equality is (m_v=t) + * 2 if equality is (store(m_v,i,v)=t) + * ... + */ + unsigned get_nesting_depth(app* eq) { + SASSERT(m.is_eq(eq)); + expr* lhs = eq->get_arg (0); + expr* rhs = eq->get_arg (1); + bool lhs_has_v = (lhs == m_v || m_has_stores_v.is_marked (lhs)); + bool rhs_has_v = (rhs == m_v || m_has_stores_v.is_marked (rhs)); + app* store = nullptr; + + SASSERT (lhs_has_v || rhs_has_v); + + if (!lhs_has_v && is_app(rhs)) { + store = to_app (rhs); + } + else if (!rhs_has_v && is_app(lhs)) { + store = to_app (lhs); + } + else { + // v appears on both sides -- trivial equality + // put it in the beginning to simplify it away + return 0; + } + + unsigned nd = 0; // nesting depth + for (nd = 1; m_arr_u.is_store (store); nd++, store = to_app (store->get_arg (0))) + /* empty */ ; + SASSERT (store == m_v); + return nd; + } + + struct compare_nd { + bool operator()(std::pair const& x, std::pair const& y) const { + return x < y; } }; + /** + * try to substitute for m_v, using array equalities + * + * compute substitution term and aux lits + */ + bool project (expr_ref const& fml) { + app_ref_vector eqs (m); + svector > true_eqs; + + find_arr_eqs (fml, eqs); + TRACE ("qe", tout << "array equalities:\n" << eqs << "\n";); + + // evaluate eqs in M + for (app * eq : eqs) { + TRACE ("qe", tout << "array equality:\n" << mk_pp (eq, m) << "\n"; ); + + if (m_mev->is_false(eq)) { + m_false_sub_v.insert (eq, m.mk_false()); + } + else { + true_eqs.push_back (std::make_pair(get_nesting_depth(eq), eq)); + } + } + std::sort(true_eqs.begin(), true_eqs.end(), compare_nd()); + DEBUG_CODE(for (unsigned i = 0; i + 1 < true_eqs.size(); ++i) SASSERT(true_eqs[i].first <= true_eqs[i+1].first);); + + // search for subst term + for (unsigned i = 0; !m_subst_term_v && i < true_eqs.size(); i++) { + app* eq = true_eqs[i].second; + m_true_sub_v.insert (eq, m.mk_true ()); + // try to find subst term + find_subst_term (eq); + } + + return true; + } + + void mk_result (expr_ref& fml) { + th_rewriter rw(m); + rw (fml); + // add in aux_lits and idx_lits + expr_ref_vector lits (m); + // TODO: eliminate possible duplicates, especially in idx_lits + // theory rewriting is a possibility, but not sure if it + // introduces unwanted terms such as ite's + lits.append (m_idx_lits_v); + lits.append (m_aux_lits_v); + lits.push_back (fml); + fml = mk_and(lits); + + if (m_subst_term_v) { + m_true_sub_v.insert (m_v, m_subst_term_v); + m_true_sub_v (fml); + } + else { + m_true_sub_v (fml); + m_false_sub_v (fml); + } + rw(fml); + SASSERT (!m.is_false (fml)); + } + + public: + + array_project_eqs_util (ast_manager& m): + m (m), + m_arr_u (m), + m_v (m), + m_subst_term_v (m), + m_true_sub_v (m), + m_false_sub_v (m), + m_aux_lits_v (m), + m_idx_lits_v (m), + m_aux_vars (m) + {} + + void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { + reset (); + model_evaluator mev(mdl); + mev.set_model_completion(true); + M = &mdl; + m_mev = &mev; + + unsigned j = 0; + for (unsigned i = 0; i < arr_vars.size (); i++) { + reset_v (); + m_v = arr_vars.get (i); + if (!m_arr_u.is_array (m_v)) { + TRACE ("qe", + tout << "not an array variable: " << m_v << "\n"; + ); + aux_vars.push_back (m_v); + continue; + } + TRACE ("qe", tout << "projecting equalities on variable: " << m_v << "\n"; ); + + if (project (fml)) { + mk_result (fml); + + contains_app contains_v (m, m_v); + if (!m_subst_term_v || contains_v (m_subst_term_v)) { + arr_vars[j++] = m_v; + } + TRACE ("qe", tout << "after projection: \n" << fml << "\n";); + } + else { + IF_VERBOSE(2, verbose_stream() << "can't project:" << m_v << "\n";); + TRACE ("qe", tout << "Failed to project: " << m_v << "\n";); + arr_vars[j++] = m_v; + } + } + arr_vars.shrink(j); + aux_vars.append (m_aux_vars); + } + }; + + /** + * Eliminate (select (store ..) ..) redexes by evaluating indices under model M. + * This does not eliminate variables, but introduces additional constraints on index equalities. + */ + + class array_select_reducer { + ast_manager& m; + array_util m_arr_u; + obj_map m_cache; + expr_ref_vector m_pinned; // to ensure a reference + expr_ref_vector m_idx_lits; + model_ref M; + model_evaluator* m_mev; + th_rewriter m_rw; + ast_mark m_arr_test; + ast_mark m_has_stores; + bool m_reduce_all_selects; + + void reset () { + m_cache.reset (); + m_pinned.reset (); + m_idx_lits.reset (); + M = nullptr; + m_mev = nullptr; + m_arr_test.reset (); + m_has_stores.reset (); + m_reduce_all_selects = false; + } + + bool is_equals (expr *e1, expr *e2) { + return e1 == e2 || (*m_mev)(e1) == (*m_mev)(e2); + } + + bool is_equals (unsigned arity, expr * const* xs, expr * const * ys) { + for (unsigned i = 0; i < arity; ++i) { + if (!is_equals(xs[i], ys[i])) return false; + } + return true; + } + + expr_ref mk_eq(unsigned arity, expr * const* xs, expr * const * ys) { + expr_ref_vector r(m); + for (unsigned i = 0; i < arity; ++i) { + r.push_back(m.mk_eq(xs[i], ys[i])); + } + return mk_and(r); + } + + void add_idx_cond (expr_ref& cond) { + m_rw (cond); + if (!m.is_true (cond)) m_idx_lits.push_back (cond); + } + + bool has_stores (expr* e) { + if (m_reduce_all_selects) return true; + return m_has_stores.is_marked (e); + } + + void mark_stores (app* a, bool args_have_stores) { + if (m_reduce_all_selects) return; + if (args_have_stores || + (m_arr_u.is_store (a) && m_arr_test.is_marked (a->get_arg (0)))) { + m_has_stores.mark (a, true); + } + } + + bool reduce (expr_ref& e) { + if (!is_app (e)) return true; + + expr *r = nullptr; + if (m_cache.find (e, r)) { + e = r; + return true; + } + + ptr_vector todo; + todo.push_back (to_app (e)); + expr_ref_vector args (m); + + while (!todo.empty ()) { + app *a = todo.back (); + unsigned sz = todo.size (); + bool dirty = false; + bool args_have_stores = false; + args.reset(); + for (expr * arg : *a) { + expr *narg = nullptr; + if (!is_app (arg)) { + args.push_back (arg); + } + else if (m_cache.find (arg, narg)) { + args.push_back (narg); + dirty |= (arg != narg); + if (!args_have_stores && has_stores (narg)) { + args_have_stores = true; + } + } + else { + todo.push_back (to_app (arg)); + } + } + + if (todo.size () > sz) continue; + todo.pop_back (); + + if (dirty) { + r = m.mk_app (a->get_decl (), args.size (), args.c_ptr ()); + m_pinned.push_back (r); + } + else { + r = a; + } + + if (m_arr_u.is_select (r) && has_stores (to_app (r)->get_arg (0))) { + r = reduce_core (to_app(r)); + } + else { + mark_stores (to_app (r), args_have_stores); + } + + m_cache.insert (a, r); + } + + SASSERT (r); + e = r; + return true; + } + + /** + * \brief reduce (select (store (store x i1 v1) i2 v2) idx) under model M + * such that the result is v2 if idx = i2 under M, it is v1 if idx = i1, idx != i2 under M, + * and it is (select x idx) if idx != i1, idx !+ i2 under M. + */ + expr* reduce_core (app *a) { + if (!m_arr_u.is_store (a->get_arg (0))) return a; + expr* array = a->get_arg(0); + unsigned arity = get_array_arity(m.get_sort(array)); + + expr* const* js = a->get_args() + 1; + + while (m_arr_u.is_store (array)) { + a = to_app (array); + expr* const* idxs = a->get_args() + 1; + expr_ref cond = mk_eq(arity, idxs, js); + + if (is_equals (arity, idxs, js)) { + add_idx_cond (cond); + return a->get_arg (2); + } + else { + cond = m.mk_not (cond); + add_idx_cond (cond); + array = a->get_arg (0); + } + } + ptr_vector args; + args.push_back(array); + args.append(arity, js); + expr* r = m_arr_u.mk_select (args.size(), args.c_ptr()); + m_pinned.push_back (r); + return r; + } + + void mk_result (expr_ref& fml) { + // conjoin idx lits + expr_ref_vector lits (m); + lits.append (m_idx_lits); + lits.push_back (fml); + fml = mk_and(lits); + // simplify all trivial expressions introduced + m_rw (fml); + TRACE ("qe", tout << "after reducing selects:\n" << fml << "\n";); + } + + public: + + array_select_reducer (ast_manager& m): + m (m), + m_arr_u (m), + m_pinned (m), + m_idx_lits (m), + m_rw (m), + m_reduce_all_selects (false) + {} + + void operator () (model& mdl, app_ref_vector const& arr_vars, expr_ref& fml, bool reduce_all_selects = false) { + if (!reduce_all_selects && arr_vars.empty ()) return; + + reset (); + model_evaluator mev(mdl); + mev.set_model_completion(true); + M = &mdl; + m_mev = &mev; + m_reduce_all_selects = reduce_all_selects; + + // mark vars to eliminate + for (app* v : arr_vars) { + m_arr_test.mark (v, true); + } + + // assume all arr_vars are of array sort + // and assume no store equalities on arr_vars + if (reduce (fml)) { + mk_result (fml); + } + else { + IF_VERBOSE(2, verbose_stream() << "can't project arrays:" << "\n";); + TRACE ("qe", tout << "Failed to project arrays\n";); + } + } + }; + + /** + * Perform Ackerman reduction on arrays. + * for occurrences (select a i1), (select a i2), ... assuming these are all occurrences. + * - collect i1, i2, i3, into equivalence classes according to M + * - for distinct index classes accumulate constraint i1 < i2 < i3 .. (for arithmetic) + * and generally distinct(i1, i2, i3) for arbitrary index sorts. + * - for equal indices accumulate constraint i1 = i2, i3 = i5, .. + * - introduce variables v1, v2, .., for each equivalence class. + * - replace occurrences of select by v1, v2, ... + * - update M to evaluate v1, v2, the same as (select a i1) (select a i2) + */ + + class array_project_selects_util { + typedef obj_map*> sel_map; + + struct idx_val { + expr_ref_vector idx; + expr_ref_vector val; + vector rval; + idx_val(expr_ref_vector & idx, expr_ref_vector & val, vector const& rval): idx(idx), val(val), rval(rval) {} + idx_val& operator=(idx_val const& o) { + idx.reset(); val.reset(); rval.reset(); + idx.append(o.idx); val.append(o.val); rval.append(o.rval); + return *this; + } + }; + ast_manager& m; + array_util m_arr_u; + arith_util m_ari_u; + bv_util m_bv_u; + sel_map m_sel_terms; + // representative indices for eliminating selects + vector m_idxs; + app_ref_vector m_sel_consts; + expr_ref_vector m_idx_lits; + model_ref M; + model_evaluator* m_mev; + expr_safe_replace m_sub; + ast_mark m_arr_test; + + void reset () { + m_sel_terms.reset (); + m_idxs.reset(); + m_sel_consts.reset (); + m_idx_lits.reset (); + M = nullptr; + m_mev = nullptr; + m_sub.reset (); + m_arr_test.reset (); + } + + /** + * collect sel terms on array vars as given by m_arr_test + */ + void collect_selects (expr* fml) { + if (!is_app (fml)) return; + ast_mark done; + ptr_vector todo; + todo.push_back (to_app (fml)); + for (unsigned i = 0; i < todo.size(); ++i) { + app* a = todo[i]; + if (done.is_marked (a)) continue; + done.mark (a, true); + for (expr* arg : *a) { + if (!done.is_marked (arg) && is_app (arg)) { + todo.push_back (to_app (arg)); + } + } + if (m_arr_u.is_select (a)) { + expr* arr = a->get_arg (0); + if (m_arr_test.is_marked (arr)) { + ptr_vector* lst = m_sel_terms.find (to_app (arr));; + lst->push_back (a); + } + } + } + } + + vector to_num(expr_ref_vector const& vals) { + vector rs; + rational r; + for (expr* v : vals) { + if (m_bv_u.is_bv(v)) { + VERIFY (m_bv_u.is_numeral(v, r)); + } + else if (m_ari_u.is_real(v) || m_ari_u.is_int(v)) { + VERIFY (m_ari_u.is_numeral(v, r)); + } + else { + r.reset(); + } + rs.push_back(r); + } + return rs; + } + + struct compare_idx { + array_project_selects_util& u; + compare_idx(array_project_selects_util& u):u(u) {} + bool operator()(idx_val const& x, idx_val const& y) { + SASSERT(x.rval.size() == y.rval.size()); + for (unsigned j = 0; j < x.rval.size(); ++j) { + rational const& xv = x.rval[j]; + rational const& yv = y.rval[j]; + if (xv < yv) return true; + if (xv > yv) return false; + } + return false; + } + }; + + expr* mk_lt(expr* x, expr* y) { + if (m_bv_u.is_bv(x)) { + return m.mk_not(m_bv_u.mk_ule(y, x)); + } + else { + return m_ari_u.mk_lt(x, y); + } + } + + expr_ref mk_lex_lt(expr_ref_vector const& xs, expr_ref_vector const& ys) { + SASSERT(xs.size() == ys.size() && !xs.empty()); + expr_ref result(mk_lt(xs.back(), ys.back()), m); + for (unsigned i = xs.size()-1; i-- > 0; ) { + result = m.mk_or(mk_lt(xs[i], ys[i]), + m.mk_and(m.mk_eq(xs[i], ys[i]), result)); + } + return result; + } + + /** + * model based ackermannization for sel terms of some array + * + * update sub with val consts for sel terms + */ + void ackermann (ptr_vector const& sel_terms) { + if (sel_terms.empty ()) return; + + expr* v = sel_terms.get (0)->get_arg (0); // array variable + sort* v_sort = m.get_sort (v); + sort* val_sort = get_array_range (v_sort); + unsigned arity = get_array_arity(v_sort); + bool is_numeric = true; + for (unsigned i = 0; i < arity && is_numeric; ++i) { + sort* srt = get_array_domain(v_sort, i); + if (!m_ari_u.is_real(srt) && !m_ari_u.is_int(srt) && !m_bv_u.is_bv_sort(srt)) { + TRACE("qe", tout << "non-numeric index sort for Ackerman" << mk_pp(srt, m) << "\n";); + is_numeric = false; + } + } + + unsigned start = m_idxs.size (); // append at the end + for (app * a : sel_terms) { + expr_ref_vector idxs(m, arity, a->get_args() + 1); + expr_ref_vector vals = (*m_mev)(idxs); + bool is_new = true; + for (unsigned j = start; j < m_idxs.size (); j++) { + if (!is_eq(m_idxs[j].val, vals)) continue; + // idx belongs to the jth equivalence class; + // substitute sel term with ith sel const + expr* c = m_sel_consts.get (j); + m_sub.insert (a, c); + // add equality (idx == repr) + m_idx_lits.push_back (mk_eq (idxs, m_idxs[j].idx)); + is_new = false; + break; + } + if (is_new) { + // new repr, val, and sel const + vector rvals = to_num(vals); + m_idxs.push_back(idx_val(idxs, vals, rvals)); + app_ref c (m.mk_fresh_const ("sel", val_sort), m); + m_sel_consts.push_back (c); + // substitute sel term with new const + m_sub.insert (a, c); + // extend M to include c + expr_ref val = (*m_mev)(a); + M->register_decl (c->get_decl (), val); + } + } + + if (start + 1 == m_idxs.size()) { + // nothing to differentiate. + } + else if (is_numeric) { + // sort reprs by their value and add a chain of strict inequalities + compare_idx cmp(*this); + std::sort(m_idxs.begin() + start, m_idxs.end(), cmp); + for (unsigned i = start; i + 1 < m_idxs.size(); ++i) { + m_idx_lits.push_back (mk_lex_lt(m_idxs[i].idx, m_idxs[i+1].idx)); + } + } + else if (arity == 1) { + // create distinct constraint. + expr_ref_vector xs(m); + for (unsigned i = start; i < m_idxs.size(); ++i) { + xs.append(m_idxs[i].idx); + } + m_idx_lits.push_back(m.mk_distinct(xs.size(), xs.c_ptr())); + } + else { + datatype::util dt(m); + sort_ref_vector srts(m); + ptr_vector acc; + unsigned i = 0; + for (expr * x : m_idxs[0].idx) { + std::stringstream name; + name << "get" << (i++); + acc.push_back(mk_accessor_decl(m, symbol(name.str().c_str()), type_ref(m.get_sort(x)))); + } + constructor_decl* constrs[1] = { mk_constructor_decl(symbol("tuple"), symbol("is-tuple"), acc.size(), acc.c_ptr()) }; + datatype::def* dts = mk_datatype_decl(dt, symbol("tuple"), 0, nullptr, 1, constrs); + VERIFY(dt.get_plugin()->mk_datatypes(1, &dts, 0, nullptr, srts)); + del_datatype_decl(dts); + sort* tuple = srts.get(0); + ptr_vector const & decls = *dt.get_datatype_constructors(tuple); + expr_ref_vector xs(m); + for (unsigned i = start; i < m_idxs.size(); ++i) { + xs.push_back(m.mk_app(decls[0], m_idxs[i].idx.size(), m_idxs[i].idx.c_ptr())); + } + m_idx_lits.push_back(m.mk_distinct(xs.size(), xs.c_ptr())); + } + } + + void mk_result (expr_ref& fml) { + // conjoin idx lits + m_idx_lits.push_back(fml); + fml = mk_and (m_idx_lits); + + // substitute for sel terms + m_sub (fml); + + TRACE ("qe", tout << "after projection of selects:\n" << fml << "\n";); + } + + /** + * project selects + * populates idx lits and obtains substitution for sel terms + */ + bool project (expr* fml) { + // collect sel terms -- populate the map m_sel_terms + collect_selects (fml); + // model based ackermannization + for (auto & kv : m_sel_terms) { + TRACE ("qe",tout << "ackermann for var: " << mk_pp (kv.m_key, m) << "\n";); + ackermann (*(kv.m_value)); + } + TRACE ("qe", tout << "idx lits:\n" << m_idx_lits; ); + return true; + } + + public: + + array_project_selects_util (ast_manager& m): + m (m), + m_arr_u (m), + m_ari_u (m), + m_bv_u (m), + m_sel_consts (m), + m_idx_lits (m), + m_sub (m) + {} + + void operator () (model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars) { + if (arr_vars.empty()) return; + reset (); + model_evaluator mev(mdl); + mev.set_model_completion(true); + M = &mdl; + m_mev = &mev; + + // mark vars to eliminate + // alloc empty map from array var to sel terms over it + for (app* v : arr_vars) { + m_arr_test.mark(v, true); + m_sel_terms.insert(v, alloc (ptr_vector)); + } + + // assume all arr_vars are of array sort + // and they only appear in select terms + if (project (fml)) { + mk_result (fml); + aux_vars.append (m_sel_consts); + arr_vars.reset (); + } + else { + IF_VERBOSE(2, verbose_stream() << "can't project arrays:" << "\n";); + TRACE ("qe", tout << "Failed to project arrays\n";); + } + + // dealloc + for (auto & kv : m_sel_terms) dealloc(kv.m_value); + m_sel_terms.reset (); + } + }; + + + struct array_project_plugin::imp { + struct indices { expr_ref_vector m_values; expr* const* m_vars; - indices(ast_manager& m, model& model, unsigned n, expr* const* vars): + indices(ast_manager& m, model& model, unsigned n, expr* const* vars): m_values(m), m_vars(vars) { expr_ref val(m); - for (unsigned i = 0; i < n; ++i) { - VERIFY(model.eval(vars[i], val)); - m_values.push_back(val); + for (unsigned i = 0; i < n; ++i) { + m_values.push_back(model(vars[i])); } } }; - + ast_manager& m; array_util a; scoped_ptr m_var; - + imp(ast_manager& m): m(m), a(m) {} ~imp() {} @@ -114,159 +1198,25 @@ namespace qe { return false; } - bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - - TRACE("qe", tout << mk_pp(var, m) << "\n" << lits;); - m_var = alloc(contains_app, m, var); - - // reduce select-store redeces based on model. - // rw_cfg rw(m); - // rw(lits); - - // try first to solve for var. - if (solve_eq(model, vars, lits)) { - return true; - } - - app_ref_vector selects(m); - - // check that only non-select occurrences are in disequalities. - if (!check_diseqs(lits, selects)) { - TRACE("qe", tout << "Could not project " << mk_pp(var, m) << " for:\n" << lits << "\n";); - return false; - } - - // remove disequalities. - elim_diseqs(lits); - - // Ackerman reduction on remaining select occurrences - // either replace occurrences by model value or other node - // that is congruent to model value. - - ackermanize_select(model, selects, vars, lits); - - TRACE("qe", tout << selects << "\n" << lits << "\n";); - return true; - } - - void ackermanize_select(model& model, app_ref_vector const& selects, app_ref_vector& vars, expr_ref_vector& lits) { - expr_ref_vector vals(m), reps(m); - expr_ref val(m); - expr_safe_replace sub(m); - - if (selects.empty()) { - return; - } - - app_ref sel(m); - for (unsigned i = 0; i < selects.size(); ++i) { - sel = m.mk_fresh_const("sel", m.get_sort(selects[i])); - VERIFY (model.eval(selects[i], val)); - model.register_decl(sel->get_decl(), val); - vals.push_back(to_app(val)); - reps.push_back(val); // TODO: direct pass could handle nested selects. - vars.push_back(sel); - sub.insert(selects[i], val); - } - - sub(lits); - remove_true(lits); - project_plugin::partition_args(model, selects, lits); - project_plugin::partition_values(model, reps, lits); - } - void remove_true(expr_ref_vector& lits) { for (unsigned i = 0; i < lits.size(); ++i) { if (m.is_true(lits[i].get())) { project_plugin::erase(lits, i); } - } + } } bool contains_x(expr* e) { return (*m_var)(e); } - void mk_eq(indices& x, indices y, expr_ref_vector& lits) { + void mk_eq(indices const& x, indices const& y, expr_ref_vector& lits) { + SASSERT(x.m_values.size() == y.m_values.size()); unsigned n = x.m_values.size(); for (unsigned j = 0; j < n; ++j) { lits.push_back(m.mk_eq(x.m_vars[j], y.m_vars[j])); } } - - // check that x occurs only under selects or in disequalities. - bool check_diseqs(expr_ref_vector const& lits, app_ref_vector& selects) { - expr_mark mark; - ptr_vector todo; - app* e; - for (unsigned i = 0; i < lits.size(); ++i) { - e = to_app(lits[i]); - if (is_diseq_x(e)) { - continue; - } - if (contains_x(e)) { - todo.push_back(e); - } - } - while (!todo.empty()) { - e = todo.back(); - todo.pop_back(); - if (mark.is_marked(e)) { - continue; - } - mark.mark(e); - if (m_var->x() == e) { - return false; - } - unsigned start = 0; - if (a.is_select(e)) { - if (e->get_arg(0) == m_var->x()) { - start = 1; - selects.push_back(e); - } - } - for (unsigned i = start; i < e->get_num_args(); ++i) { - todo.push_back(to_app(e->get_arg(i))); - } - } - return true; - } - - void elim_diseqs(expr_ref_vector& lits) { - for (unsigned i = 0; i < lits.size(); ++i) { - if (is_diseq_x(lits[i].get())) { - project_plugin::erase(lits, i); - } - } - } - - bool is_update_x(app* e) { - do { - if (m_var->x() == e) { - return true; - } - if (a.is_store(e) && contains_x(e->get_arg(0))) { - for (unsigned i = 1; i < e->get_num_args(); ++i) { - if (contains_x(e->get_arg(i))) { - return false; - } - } - e = to_app(e->get_arg(0)); - continue; - } - } - while (false); - return false; - } - - bool is_diseq_x(expr* e) { - expr *f, * s, *t; - if (m.is_not(e, f) && m.is_eq(f, s, t)) { - if (contains_x(s) && !contains_x(t) && is_update_x(to_app(s))) return true; - if (contains_x(t) && !contains_x(s) && is_update_x(to_app(t))) return true; - } - return false; - } bool solve_eq(model& model, app_ref_vector& vars, expr_ref_vector& lits) { // find an equality to solve for. @@ -314,13 +1264,13 @@ namespace qe { var = m.mk_fresh_const("value", range); vars.push_back(var); args.reset(); - + args.push_back (s); args.append(idxs[i].m_values.size(), idxs[i].m_vars); sel = a.mk_select (args.size (), args.c_ptr ()); - VERIFY (model.eval (sel, val)); + val = model(sel); model.register_decl (var->get_decl (), val); - + args[0] = result; args.push_back(var); result = a.mk_store(args.size(), args.c_ptr()); @@ -341,7 +1291,7 @@ namespace qe { if (contains_x(s->get_arg(i))) { return false; } - } + } unsigned i; expr_ref_vector args(m); switch (contains(idx, idxs, i)) { @@ -361,7 +1311,7 @@ namespace qe { return solve(model, to_app(s->get_arg(0)), t, idxs, vars, lits); case l_undef: return false; - } + } } return false; } @@ -369,13 +1319,13 @@ namespace qe { lbool contains(indices const& idx, vector const& idxs, unsigned& j) { for (unsigned i = 0; i < idxs.size(); ++i) { switch (compare(idx, idxs[i])) { - case l_true: + case l_true: j = i; return l_true; case l_false: break; case l_undef: - return l_undef; + return l_undef; } } return l_false; @@ -399,36 +1349,72 @@ namespace qe { lbool compare(expr* val1, expr* val2) { if (m.are_equal (val1, val2)) return l_true; if (m.are_distinct (val1, val2)) return l_false; - + if (is_uninterp(val1) || is_uninterp(val2)) { // TBD chase definition of nested array. return l_undef; } return l_undef; - } + } }; - - + + array_project_plugin::array_project_plugin(ast_manager& m) { m_imp = alloc(imp, m); } - + array_project_plugin::~array_project_plugin() { dealloc(m_imp); } - + bool array_project_plugin::operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - return (*m_imp)(model, var, vars, lits); + ast_manager& m = vars.get_manager(); + app_ref_vector vvars(m, 1, &var); + expr_ref fml = mk_and(lits); + (*this)(model, vvars, fml, vars, false); + lits.reset(); + flatten_and(fml, lits); + return true; } bool array_project_plugin::solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) { return m_imp->solve(model, vars, lits); } - + family_id array_project_plugin::get_family_id() { return m_imp->a.get_family_id(); } -}; + void array_project_plugin::operator()(model& mdl, app_ref_vector& arr_vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects) { + // 1. project array equalities + ast_manager& m = fml.get_manager(); + array_project_eqs_util pe (m); + pe (mdl, arr_vars, fml, aux_vars); + TRACE ("qe", + tout << "Projected array eqs: " << fml << "\n"; + tout << "Remaining array vars: " << arr_vars << "\n"; + tout << "Aux vars: " << aux_vars << "\n"; + ); + // 2. reduce selects + array_select_reducer rs (m); + rs (mdl, arr_vars, fml, reduce_all_selects); + + TRACE ("qe", tout << "Reduced selects:\n" << fml << "\n"; ); + + // 3. project selects using model based ackermannization + array_project_selects_util ps (m); + ps (mdl, arr_vars, fml, aux_vars); + + TRACE ("qe", + tout << "Projected array selects: " << fml << "\n"; + tout << "All aux vars: " << aux_vars << "\n"; + ); + } + + vector array_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return vector(); + } + +}; diff --git a/src/qe/qe_arrays.h b/src/qe/qe_arrays.h index e100ebce4..3bb90335d 100644 --- a/src/qe/qe_arrays.h +++ b/src/qe/qe_arrays.h @@ -31,10 +31,12 @@ namespace qe { imp* m_imp; public: array_project_plugin(ast_manager& m); - virtual ~array_project_plugin(); - virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); - virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); - virtual family_id get_family_id(); + ~array_project_plugin() override; + bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; + bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; + void operator()(model& model, app_ref_vector& vars, expr_ref& fml, app_ref_vector& aux_vars, bool reduce_all_selects); + family_id get_family_id() override; + vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; }; }; diff --git a/src/qe/qe_bool_plugin.cpp b/src/qe/qe_bool_plugin.cpp index 82e09bbed..f8bac21c8 100644 --- a/src/qe/qe_bool_plugin.cpp +++ b/src/qe/qe_bool_plugin.cpp @@ -39,16 +39,16 @@ namespace qe { m_replace(m) {} - virtual void assign(contains_app& x, expr* fml, rational const& vl) { + void assign(contains_app& x, expr* fml, rational const& vl) override { SASSERT(vl.is_zero() || vl.is_one()); } - virtual bool get_num_branches(contains_app& x, expr* fml, rational& nb) { + bool get_num_branches(contains_app& x, expr* fml, rational& nb) override { nb = rational(2); return true; } - virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { + void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { SASSERT(vl.is_one() || vl.is_zero()); expr* tf = (vl.is_one())?m.mk_true():m.mk_false(); m_replace.apply_substitution(x.x(), tf, fml); @@ -57,7 +57,7 @@ namespace qe { } } - virtual bool project(contains_app& x, model_ref& model, expr_ref& fml) { + bool project(contains_app& x, model_ref& model, expr_ref& fml) override { model_evaluator model_eval(*model); expr_ref val_x(m); rational val; @@ -65,11 +65,11 @@ namespace qe { CTRACE("qe", (!m.is_true(val_x) && !m.is_false(val_x)), tout << "Boolean is a don't care: " << mk_pp(x.x(), m) << "\n";); val = m.is_true(val_x)?rational::one():rational::zero(); - subst(x, val, fml, 0); + subst(x, val, fml, nullptr); return true; } - virtual unsigned get_weight(contains_app& contains_x, expr* fml) { + unsigned get_weight(contains_app& contains_x, expr* fml) override { app* x = contains_x.x(); bool p = m_ctx.pos_atoms().contains(x); bool n = m_ctx.neg_atoms().contains(x); @@ -79,13 +79,13 @@ namespace qe { return 0; } - virtual bool solve(conj_enum& conjs,expr* fml) { + bool solve(conj_enum& conjs,expr* fml) override { return solve_units(conjs, fml) || solve_polarized(fml); } - virtual bool is_uninterpreted(app* a) { + bool is_uninterpreted(app* a) override { return false; } @@ -93,10 +93,8 @@ namespace qe { bool solve_units(conj_enum& conjs, expr* _fml) { expr_ref fml(_fml, m); - conj_enum::iterator it = conjs.begin(), end = conjs.end(); unsigned idx; - for (; it != end; ++it) { - expr* e = *it; + for (expr * e : conjs) { if (!is_app(e)) { continue; } @@ -138,13 +136,11 @@ namespace qe { return false; } else if (p && !n) { - atom_set::iterator it = m_ctx.pos_atoms().begin(), end = m_ctx.pos_atoms().end(); - for (; it != end; ++it) { - if (x != *it && contains_x(*it)) return false; + for (expr* y : m_ctx.pos_atoms()) { + if (x != y && contains_x(y)) return false; } - it = m_ctx.neg_atoms().begin(), end = m_ctx.neg_atoms().end(); - for (; it != end; ++it) { - if (contains_x(*it)) return false; + for (expr* y : m_ctx.neg_atoms()) { + if (contains_x(y)) return false; } // only occurrences of 'x' must be in positive atoms def = m.mk_true(); @@ -152,13 +148,11 @@ namespace qe { return true; } else if (!p && n) { - atom_set::iterator it = m_ctx.pos_atoms().begin(), end = m_ctx.pos_atoms().end(); - for (; it != end; ++it) { - if (contains_x(*it)) return false; + for (expr* y : m_ctx.pos_atoms()) { + if (contains_x(y)) return false; } - it = m_ctx.neg_atoms().begin(), end = m_ctx.neg_atoms().end(); - for (; it != end; ++it) { - if (x != *it && contains_x(*it)) return false; + for (expr* y : m_ctx.neg_atoms()) { + if (x != y && contains_x(y)) return false; } def = m.mk_false(); m_replace.apply_substitution(x, def, fml); diff --git a/src/qe/qe_bv_plugin.cpp b/src/qe/qe_bv_plugin.cpp index 6678d6fcf..603004020 100644 --- a/src/qe/qe_bv_plugin.cpp +++ b/src/qe/qe_bv_plugin.cpp @@ -37,16 +37,16 @@ namespace qe { m_bv(m) {} - virtual void assign(contains_app& x, expr* fml, rational const& vl) { + void assign(contains_app& x, expr* fml, rational const& vl) override { } - virtual bool get_num_branches(contains_app& x, expr* fml, rational& nb) { + bool get_num_branches(contains_app& x, expr* fml, rational& nb) override { unsigned sz = m_bv.get_bv_size(x.x()); nb = power(rational(2), sz); return true; } - virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { + void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { app_ref c(m_bv.mk_numeral(vl, m_bv.get_bv_size(x.x())), m); m_replace.apply_substitution(x.x(), c, fml); if (def) { @@ -54,24 +54,24 @@ namespace qe { } } - virtual bool project(contains_app& x, model_ref& model, expr_ref& fml) { + bool project(contains_app& x, model_ref& model, expr_ref& fml) override { model_evaluator model_eval(*model); expr_ref val_x(m); rational val(0); unsigned bv_size; model_eval(x.x(), val_x); m_bv.is_numeral(val_x, val, bv_size); - subst(x, val, fml, 0); + subst(x, val, fml, nullptr); return true; } - virtual unsigned get_weight(contains_app& contains_x, expr* fml) { + unsigned get_weight(contains_app& contains_x, expr* fml) override { return 2; } - bool solve(conj_enum& conjs, expr* fml) { return false; } + bool solve(conj_enum& conjs, expr* fml) override { return false; } - virtual bool is_uninterpreted(app* f) { + bool is_uninterpreted(app* f) override { switch(f->get_decl_kind()) { case OP_BSDIV0: case OP_BUDIV0: diff --git a/src/qe/qe_cmd.cpp b/src/qe/qe_cmd.cpp index 3c55c0f49..f708d5223 100644 --- a/src/qe/qe_cmd.cpp +++ b/src/qe/qe_cmd.cpp @@ -14,36 +14,36 @@ class qe_cmd : public parametric_cmd { public: qe_cmd(char const* name = "elim-quantifiers"):parametric_cmd(name) {} - virtual char const * get_usage() const { return " ( )*"; } + char const * get_usage() const override { return " ( )*"; } - virtual char const * get_main_descr() const { + char const * get_main_descr() const override { return "apply quantifier elimination to the supplied expression"; } - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { insert_timeout(p); p.insert("print", CPK_BOOL, "(default: true) print the simplified term."); p.insert("print_statistics", CPK_BOOL, "(default: false) print statistics."); } - virtual ~qe_cmd() { + ~qe_cmd() override { } - virtual void prepare(cmd_context & ctx) { + void prepare(cmd_context & ctx) override { parametric_cmd::prepare(ctx); - m_target = 0; + m_target = nullptr; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { - if (m_target == 0) return CPK_EXPR; + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { + if (m_target == nullptr) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } - virtual void set_next_arg(cmd_context & ctx, expr * arg) { + void set_next_arg(cmd_context & ctx, expr * arg) override { m_target = arg; } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { proof_ref pr(ctx.m()); qe::simplify_rewriter_star qe(ctx.m()); expr_ref result(ctx.m()); diff --git a/src/qe/qe_datatype_plugin.cpp b/src/qe/qe_datatype_plugin.cpp index c3525aa33..81a402ba4 100644 --- a/src/qe/qe_datatype_plugin.cpp +++ b/src/qe/qe_datatype_plugin.cpp @@ -261,7 +261,7 @@ namespace qe { return false; } func_decl* c = a->get_decl(); - func_decl* r = m_util.get_constructor_recognizer(c); + func_decl_ref r(m_util.get_constructor_is(c), m); ptr_vector const & acc = *m_util.get_constructor_accessors(c); SASSERT(acc.size() == a->get_num_args()); // @@ -380,7 +380,7 @@ namespace qe { } func_decl* c = l->get_decl(); ptr_vector const& acc = *m_util.get_constructor_accessors(c); - func_decl* rec = m_util.get_constructor_recognizer(c); + func_decl* rec = m_util.get_constructor_is(c); expr_ref_vector conj(m); conj.push_back(m.mk_app(rec, r)); for (unsigned i = 0; i < acc.size(); ++i) { @@ -389,7 +389,7 @@ namespace qe { conj.push_back(m.mk_eq(l_i, r_i)); } expr* e = m.mk_and(conj.size(), conj.c_ptr()); - m_map.insert(a, e, 0); + m_map.insert(a, e, nullptr); TRACE("qe", tout << "replace: " << mk_pp(a, m) << " ==> \n" << mk_pp(e, m) << "\n";); return true; } @@ -435,7 +435,7 @@ namespace qe { { } - virtual ~datatype_plugin() { + ~datatype_plugin() override { { eqs_cache::iterator it = m_eqs_cache.begin(), end = m_eqs_cache.end(); for (; it != end; ++it) { @@ -451,7 +451,7 @@ namespace qe { } - virtual bool get_num_branches( contains_app& x, expr* fml, rational& num_branches) { + bool get_num_branches( contains_app& x, expr* fml, rational& num_branches) override { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); if (m_datatype_util.is_recursive(s)) { @@ -463,7 +463,7 @@ namespace qe { } - virtual void assign(contains_app& x, expr* fml, rational const& vl) { + void assign(contains_app& x, expr* fml, rational const& vl) override { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); TRACE("qe", tout << mk_pp(x.x(), m) << " " << vl << "\n";); @@ -475,7 +475,7 @@ namespace qe { } } - virtual void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) { + void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); TRACE("qe", tout << mk_pp(x.x(), m) << " " << vl << "\n";); @@ -487,20 +487,20 @@ namespace qe { } } - virtual unsigned get_weight( contains_app& x, expr* fml) { + unsigned get_weight( contains_app& x, expr* fml) override { return UINT_MAX; } - virtual bool solve( conj_enum& conj, expr* fml) { + bool solve( conj_enum& conj, expr* fml) override { return false; } - virtual bool simplify( expr_ref& fml) { + bool simplify( expr_ref& fml) override { lift_foreign_vars lift(m, m_datatype_util, m_ctx); return lift.lift(fml); } - virtual bool mk_atom(expr* e, bool p, expr_ref& result) { + bool mk_atom(expr* e, bool p, expr_ref& result) override { return false; } @@ -521,7 +521,7 @@ namespace qe { // replace x by C(y1,..,yn) where y1,..,yn are fresh variables. // void subst_constructor(contains_app& x, func_decl* c, expr_ref& fml, expr_ref* def) { - subst_clos* sub = 0; + subst_clos* sub = nullptr; if (m_subst_cache.find(x.x(), c, sub)) { m_replace.apply_substitution(x.x(), sub->first, fml); @@ -588,7 +588,7 @@ namespace qe { unsigned sz = m_datatype_util.get_datatype_num_constructors(s); num_branches = rational(sz); - func_decl* c = 0, *r = 0; + func_decl* c = nullptr, *r = nullptr; // // If 'x' does not yet have a recognizer, then branch according to recognizers. @@ -620,14 +620,14 @@ namespace qe { void assign_rec(contains_app& contains_x, expr* fml, rational const& vl) { app* x = contains_x.x(); sort* s = x->get_decl()->get_range(); - func_decl* c = 0, *r = 0; + func_decl* c = nullptr, *r = nullptr; // // If 'x' does not yet have a recognizer, then branch according to recognizers. // if (!has_recognizer(x, fml, r, c)) { c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned()); - r = m_datatype_util.get_constructor_recognizer(c); + r = m_datatype_util.get_constructor_is(c); app* is_c = m.mk_app(r, x); // assert v => r(x) m_ctx.add_constraint(true, is_c); @@ -665,7 +665,7 @@ namespace qe { app* x = contains_x.x(); sort* s = x->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); - func_decl* c = 0, *r = 0; + func_decl* c = nullptr, *r = nullptr; TRACE("qe", tout << mk_pp(x, m) << " " << vl << " " << mk_pp(fml, m) << " " << (def != 0) << "\n";); // @@ -674,7 +674,7 @@ namespace qe { // if (!has_recognizer(x, fml, r, c)) { c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned()); - r = m_datatype_util.get_constructor_recognizer(c); + r = m_datatype_util.get_constructor_is(c); app* is_c = m.mk_app(r, x); fml = m.mk_and(is_c, fml); app_ref fresh_x(m.mk_fresh_const("x", s), m); @@ -748,7 +748,7 @@ namespace qe { sort* s = x.x()->get_decl()->get_range(); unsigned sz = m_datatype_util.get_datatype_num_constructors(s); num_branches = rational(sz); - func_decl* c = 0, *r = 0; + func_decl* c = nullptr, *r = nullptr; if (sz != 1 && has_recognizer(x.x(), fml, r, c)) { TRACE("qe", tout << mk_pp(x.x(), m) << " has a recognizer\n";); @@ -768,14 +768,14 @@ namespace qe { if (sz == 1) { return; } - func_decl* c = 0, *r = 0; + func_decl* c = nullptr, *r = nullptr; if (has_recognizer(x, fml, r, c)) { TRACE("qe", tout << mk_pp(x, m) << " has a recognizer\n";); return; } c = m_datatype_util.get_datatype_constructors(s)->get(vl.get_unsigned()); - r = m_datatype_util.get_constructor_recognizer(c); + r = m_datatype_util.get_constructor_is(c); app* is_c = m.mk_app(r, x); // assert v => r(x) @@ -787,7 +787,7 @@ namespace qe { sort* s = x.x()->get_decl()->get_range(); SASSERT(m_datatype_util.is_datatype(s)); SASSERT(!m_datatype_util.is_recursive(s)); - func_decl* c = 0, *r = 0; + func_decl* c = nullptr, *r = nullptr; if (has_recognizer(x.x(), fml, r, c)) { TRACE("qe", tout << mk_pp(x.x(), m) << " has a recognizer\n";); } @@ -807,7 +807,7 @@ namespace qe { public: has_select(app* x, func_decl* c, datatype_util& u): m_x(x), m_c(c), m_util(u) {} - virtual bool operator()(expr* e) { + bool operator()(expr* e) override { if (!is_app(e)) return false; app* a = to_app(e); if (!m_util.is_accessor(a)) return false; @@ -824,13 +824,13 @@ namespace qe { } datatype_atoms& get_eqs(app* x, expr* fml) { - datatype_atoms* eqs = 0; + datatype_atoms* eqs = nullptr; VERIFY (m_eqs_cache.find(x, fml, eqs)); return *eqs; } bool update_eqs(contains_app& contains_x, expr* fml) { - datatype_atoms* eqs = 0; + datatype_atoms* eqs = nullptr; if (m_eqs_cache.find(contains_x.x(), fml, eqs)) { return true; } diff --git a/src/qe/qe_datatypes.cpp b/src/qe/qe_datatypes.cpp index db1e6ec85..5499d638d 100644 --- a/src/qe/qe_datatypes.cpp +++ b/src/qe/qe_datatypes.cpp @@ -42,8 +42,7 @@ namespace qe { } bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) { - expr_ref val(m); - VERIFY(model.eval(var, val)); + expr_ref val = model(var); SASSERT(is_app(val)); TRACE("qe", tout << mk_pp(var, m) << " := " << val << "\n";); m_val = to_app(val); @@ -151,7 +150,7 @@ namespace qe { return false; } func_decl* c = a->get_decl(); - func_decl* rec = dt.get_constructor_recognizer(c); + func_decl_ref rec(dt.get_constructor_is(c), m); ptr_vector const & acc = *dt.get_constructor_accessors(c); SASSERT(acc.size() == a->get_num_args()); // @@ -232,7 +231,7 @@ namespace qe { func_decl* c = to_app(l)->get_decl(); ptr_vector const& acc = *dt.get_constructor_accessors(c); if (!is_app_of(r, c)) { - lits.push_back(m.mk_app(dt.get_constructor_recognizer(c), r)); + lits.push_back(m.mk_app(dt.get_constructor_is(c), r)); } for (unsigned i = 0; i < acc.size(); ++i) { lits.push_back(m.mk_eq(to_app(l)->get_arg(i), access(c, i, acc, r))); @@ -300,6 +299,9 @@ namespace qe { return m_imp->solve(model, vars, lits); } + vector datatype_project_plugin::project(model& model, app_ref_vector& vars, expr_ref_vector& lits) { + return vector(); + } family_id datatype_project_plugin::get_family_id() { return m_imp->dt.get_family_id(); diff --git a/src/qe/qe_datatypes.h b/src/qe/qe_datatypes.h index 7352b4ca7..0483f4cce 100644 --- a/src/qe/qe_datatypes.h +++ b/src/qe/qe_datatypes.h @@ -31,10 +31,11 @@ namespace qe { imp* m_imp; public: datatype_project_plugin(ast_manager& m); - virtual ~datatype_project_plugin(); - virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); - virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); - virtual family_id get_family_id(); + ~datatype_project_plugin() override; + bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) override; + bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; + family_id get_family_id() override; + vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) override; }; }; diff --git a/src/qe/qe_dl_plugin.cpp b/src/qe/qe_dl_plugin.cpp index 7ff7bc11e..6e411511d 100644 --- a/src/qe/qe_dl_plugin.cpp +++ b/src/qe/qe_dl_plugin.cpp @@ -56,7 +56,7 @@ namespace qe { { } - virtual ~dl_plugin() { + ~dl_plugin() override { eqs_cache::iterator it = m_eqs_cache.begin(), end = m_eqs_cache.end(); for (; it != end; ++it) { dealloc(it->get_value()); @@ -65,12 +65,12 @@ namespace qe { - bool get_num_branches(contains_app & x,expr * fml,rational & num_branches) { + bool get_num_branches(contains_app & x,expr * fml,rational & num_branches) override { if (!update_eqs(x, fml)) { return false; } eq_atoms& eqs = get_eqs(x.x(), fml); - uint64 domain_size; + uint64_t domain_size; if (is_small_domain(x, eqs, domain_size)) { num_branches = rational(domain_size, rational::ui64()); } @@ -80,11 +80,11 @@ namespace qe { return true; } - void assign(contains_app & x,expr * fml,const rational & v) { + void assign(contains_app & x,expr * fml,const rational & v) override { SASSERT(v.is_unsigned()); eq_atoms& eqs = get_eqs(x.x(), fml); unsigned uv = v.get_unsigned(); - uint64 domain_size; + uint64_t domain_size; if (is_small_domain(x, eqs, domain_size)) { SASSERT(v < rational(domain_size, rational::ui64())); assign_small_domain(x, eqs, uv); @@ -94,11 +94,11 @@ namespace qe { } } - void subst(contains_app & x,const rational & v,expr_ref & fml, expr_ref* def) { + void subst(contains_app & x,const rational & v,expr_ref & fml, expr_ref* def) override { SASSERT(v.is_unsigned()); eq_atoms& eqs = get_eqs(x.x(), fml); unsigned uv = v.get_unsigned(); - uint64 domain_size; + uint64_t domain_size; if (is_small_domain(x, eqs, domain_size)) { SASSERT(uv < domain_size); subst_small_domain(x, eqs, uv, fml); @@ -107,15 +107,15 @@ namespace qe { subst_large_domain(x, eqs, uv, fml); } if (def) { - *def = 0; // TBD + *def = nullptr; // TBD } } - virtual bool solve(conj_enum& conjs, expr* fml) { return false; } + bool solve(conj_enum& conjs, expr* fml) override { return false; } private: - bool is_small_domain(contains_app& x, eq_atoms& eqs, uint64& domain_size) { + bool is_small_domain(contains_app& x, eq_atoms& eqs, uint64_t& domain_size) { VERIFY(m_util.try_get_size(m.get_sort(x.x()), domain_size)); return domain_size < eqs.num_eqs() + eqs.num_neqs(); } @@ -169,13 +169,13 @@ namespace qe { eq_atoms& get_eqs(app* x, expr* fml) { - eq_atoms* eqs = 0; + eq_atoms* eqs = nullptr; VERIFY(m_eqs_cache.find(x, fml, eqs)); return *eqs; } bool update_eqs(contains_app& contains_x, expr* fml) { - eq_atoms* eqs = 0; + eq_atoms* eqs = nullptr; if (m_eqs_cache.find(contains_x.x(), fml, eqs)) { return true; } diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index 01806bc24..3b8d41316 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -36,18 +36,21 @@ Revision History: #include "ast/datatype_decl_plugin.h" #include "qe/qe_vartest.h" +#include "qe/qe_solve_plugin.h" namespace eq { bool occurs_var(unsigned idx, expr* e) { + if (is_ground(e)) return false; ptr_buffer todo; - todo.push_back(e); + todo.push_back(e); ast_mark mark; while (!todo.empty()) { expr* e = todo.back(); todo.pop_back(); if (mark.is_marked(e)) continue; mark.mark(e, true); + if (is_ground(e)) continue; if (is_var(e)) { if (to_var(e)->get_idx() == idx) return true; } @@ -67,15 +70,16 @@ namespace eq { arith_util a; datatype_util dt; is_variable_proc* m_is_variable; - var_subst m_subst; + beta_reducer m_subst; + expr_ref_vector m_subst_map; expr_ref_vector m_new_exprs; + plugin_manager m_solvers; ptr_vector m_map; int_vector m_pos2var; int_vector m_var2pos; ptr_vector m_inx2var; unsigned_vector m_order; - expr_ref_vector m_subst_map; expr_ref_buffer m_new_args; th_rewriter m_rewriter; params_ref m_params; @@ -88,8 +92,8 @@ namespace eq { for (unsigned i = 0; i < definitions.size(); i++) { var * v = vars[i]; expr * t = definitions[i]; - if (t == 0 || has_quantifiers(t) || occurs_var(v->get_idx(), t)) - definitions[i] = 0; + if (t == nullptr || has_quantifiers(t) || occurs_var(v->get_idx(), t)) + definitions[i] = nullptr; else found = true; // found at least one candidate } @@ -106,7 +110,7 @@ namespace eq { unsigned vidx, num; for (unsigned i = 0; i < definitions.size(); i++) { - if (definitions[i] == 0) + if (definitions[i] == nullptr) continue; var * v = vars[i]; SASSERT(v->get_idx() == i); @@ -116,7 +120,7 @@ namespace eq { start: frame & fr = todo.back(); expr * t = fr.first; - if (t->get_ref_count() > 1 && done.is_marked(t)) { + if (done.is_marked(t)) { todo.pop_back(); continue; } @@ -125,12 +129,12 @@ namespace eq { vidx = to_var(t)->get_idx(); if (fr.second == 0) { CTRACE("der_bug", vidx >= definitions.size(), tout << "vidx: " << vidx << "\n";); - // Remark: The size of definitions may be smaller than the number of variables occuring in the quantified formula. - if (definitions.get(vidx, 0) != 0) { + // Remark: The size of definitions may be smaller than the number of variables occurring in the quantified formula. + if (definitions.get(vidx, nullptr) != nullptr) { if (visiting.is_marked(t)) { // cycle detected: remove t visiting.reset_mark(t); - definitions[vidx] = 0; + definitions[vidx] = nullptr; } else { visiting.mark(t); @@ -142,16 +146,13 @@ namespace eq { } else { SASSERT(fr.second == 1); - if (definitions.get(vidx, 0) != 0) { - visiting.reset_mark(t); - order.push_back(vidx); - } - else { - // var was removed from the list of candidate vars to elim cycle - // do nothing + visiting.reset_mark(t); + if (!done.is_marked(t)) { + if (definitions.get(vidx, nullptr) != nullptr) + order.push_back(vidx); + done.mark(t); } } - if (t->get_ref_count() > 1) done.mark(t); todo.pop_back(); break; @@ -164,13 +165,11 @@ namespace eq { while (fr.second < num) { expr * arg = to_app(t)->get_arg(fr.second); fr.second++; - if (arg->get_ref_count() > 1 && done.is_marked(arg)) - continue; + if (done.is_marked(arg)) continue; todo.push_back(frame(arg, 0)); goto start; } - if (t->get_ref_count() > 1) - done.mark(t); + done.mark(t); todo.pop_back(); break; default: @@ -224,97 +223,6 @@ namespace eq { } } - bool is_invertible_const(bool is_int, expr* x, rational& a_val) { - expr* y; - if (a.is_uminus(x, y) && is_invertible_const(is_int, y, a_val)) { - a_val.neg(); - return true; - } - else if (a.is_numeral(x, a_val) && !a_val.is_zero()) { - if (!is_int || a_val.is_one() || a_val.is_minus_one()) { - return true; - } - } - return false; - } - - bool is_invertible_mul(bool is_int, expr*& arg, rational& a_val) { - if (is_variable(arg)) { - a_val = rational(1); - return true; - } - expr* x, *y; - if (a.is_mul(arg, x, y)) { - if (is_variable(x) && is_invertible_const(is_int, y, a_val)) { - arg = x; - return true; - } - if (is_variable(y) && is_invertible_const(is_int, x, a_val)) { - arg = y; - return true; - } - } - return false; - } - - typedef std::pair signed_expr; - - expr_ref solve_arith(bool is_int, rational const& r, bool sign, svector const& exprs) { - expr_ref_vector result(m); - for (unsigned i = 0; i < exprs.size(); ++i) { - bool sign2 = exprs[i].first; - expr* e = exprs[i].second; - rational r2(r); - if (sign == sign2) { - r2.neg(); - } - if (!r2.is_one()) { - result.push_back(a.mk_mul(a.mk_numeral(r2, is_int), e)); - } - else { - result.push_back(e); - } - } - return expr_ref(a.mk_add(result.size(), result.c_ptr()), m); - } - - bool solve_arith(expr* lhs, expr* rhs, ptr_vector& vs, expr_ref_vector& ts) { - if (!a.is_int(lhs) && !a.is_real(rhs)) { - return false; - } - rational a_val; - bool is_int = a.is_int(lhs); - svector todo, done; - todo.push_back(std::make_pair(true, lhs)); - todo.push_back(std::make_pair(false, rhs)); - while (!todo.empty()) { - expr* e = todo.back().second; - bool sign = todo.back().first; - todo.pop_back(); - if (a.is_add(e)) { - for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - todo.push_back(std::make_pair(sign, to_app(e)->get_arg(i))); - } - } - else if (is_invertible_mul(is_int, e, a_val)) { - done.append(todo); - vs.push_back(to_var(e)); - a_val = rational(1)/a_val; - ts.push_back(solve_arith(is_int, a_val, sign, done)); - TRACE("qe_lite", tout << mk_pp(lhs, m) << " " << mk_pp(rhs, m) << " " << mk_pp(e, m) << " := " << mk_pp(ts.back(), m) << "\n";); - return true; - } - else { - done.push_back(std::make_pair(sign, e)); - } - } - return false; - } - - bool arith_solve(expr * lhs, expr * rhs, expr * eq, ptr_vector& vs, expr_ref_vector& ts) { - return solve_arith(lhs, rhs, vs, ts); - } - bool trivial_solve(expr* lhs, expr* rhs, expr* eq, ptr_vector& vs, expr_ref_vector& ts) { if (!is_variable(lhs)) { std::swap(lhs, rhs); @@ -346,65 +254,25 @@ namespace eq { */ bool is_var_eq(expr * e, ptr_vector& vs, expr_ref_vector & ts) { - expr* lhs, *rhs; - var* v; + expr* lhs = nullptr, *rhs = nullptr; // (= VAR t), (iff VAR t), (iff (not VAR) t), (iff t (not VAR)) cases - if (m.is_eq(e, lhs, rhs) || m.is_iff(e, lhs, rhs)) { - // (iff (not VAR) t) (iff t (not VAR)) cases - if (!is_variable(lhs) && !is_variable(rhs) && m.is_bool(lhs)) { - if (!is_neg_var(m, lhs, v)) { - std::swap(lhs, rhs); - } - if (!is_neg_var(m, lhs, v)) { - return false; - } - vs.push_back(v); - ts.push_back(m.mk_not(rhs)); - TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); - return true; - } - if (trivial_solve(lhs, rhs, e, vs, ts)) { - return true; - } - if (arith_solve(lhs, rhs, e, vs, ts)) { - return true; - } - return false; - } - - // (ite cond (= VAR t) (= VAR t2)) case - expr* cond, *e2, *e3; - if (m.is_ite(e, cond, e2, e3)) { - if (is_var_eq(e2, vs, ts)) { - expr_ref_vector ts2(m); - ptr_vector vs2; - if (is_var_eq(e3, vs2, ts2) && same_vars(vs, vs2)) { - for (unsigned i = 0; i < vs.size(); ++i) { - ts[i] = m.mk_ite(cond, ts[i].get(), ts2[i].get()); - } - return true; - } - } - return false; - } - - // VAR = true case - if (is_variable(e)) { - ts.push_back(m.mk_true()); - vs.push_back(to_var(e)); - TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); + if (m.is_eq(e, lhs, rhs) && trivial_solve(lhs, rhs, e, vs, ts)) { return true; } - - // VAR = false case - if (is_neg_var(m, e, v)) { - ts.push_back(m.mk_false()); - vs.push_back(v); - TRACE("qe_lite", tout << mk_pp(e, m) << "\n";); - return true; + family_id fid = get_sort(e)->get_family_id(); + if (m.is_eq(e, lhs, rhs)) { + fid = get_sort(lhs)->get_family_id(); + } + qe::solve_plugin* p = m_solvers.get_plugin(fid); + if (p) { + expr_ref res = (*p)(e); + if (res != e && m.is_eq(res, lhs, rhs) && is_variable(lhs)) { + vs.push_back(to_var(lhs)); + ts.push_back(rhs); + return true; + } } - return false; } @@ -431,26 +299,29 @@ namespace eq { TRACE("qe_lite", tout << "Elimination m_order:" << std::endl; - for(unsigned i=0; iget_num_patterns(); j++) { expr_ref new_pat(m); - m_subst(q->get_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_pat); + m_subst(q->get_pattern(j), new_pat); new_patterns.push_back(new_pat); } for (unsigned j = 0; j < q->get_num_no_patterns(); j++) { expr_ref new_nopat(m); - m_subst(q->get_no_pattern(j), m_subst_map.size(), m_subst_map.c_ptr(), new_nopat); + m_subst(q->get_no_pattern(j), new_nopat); new_no_patterns.push_back(new_nopat); } @@ -544,7 +415,7 @@ namespace eq { } if (m.proofs_enabled()) { - pr = r == q ? 0 : m.mk_der(q, r); + pr = r == q ? nullptr : m.mk_der(q, r); } } @@ -574,12 +445,15 @@ namespace eq { checkpoint(); ptr_vector vs; expr_ref_vector ts(m); + expr_ref t(m); if (is_var_def(is_exists, args[i], vs, ts)) { for (unsigned j = 0; j < vs.size(); ++j) { var* v = vs[j]; - expr* t = ts[j].get(); + t = ts.get(j); + m_rewriter(t); + if (t != ts.get(j)) m_new_exprs.push_back(t); unsigned idx = v->get_idx(); - if (m_map.get(idx, 0) == 0) { + if (m_map.get(idx, nullptr) == nullptr) { m_map.reserve(idx + 1, 0); m_inx2var.reserve(idx + 1, 0); m_map[idx] = t; @@ -692,7 +566,7 @@ namespace eq { } } else { - func_decl* rec = dt.get_constructor_recognizer(d); + func_decl* rec = dt.get_constructor_is(d); conjs.push_back(m.mk_app(rec, r)); ptr_vector const& acc = *dt.get_constructor_accessors(d); for (unsigned i = 0; i < acc.size(); ++i) { @@ -750,7 +624,7 @@ namespace eq { expr_ref r(m), new_r(m); r = m.mk_and(conjs.size(), conjs.c_ptr()); create_substitution(largest_vinx + 1); - m_subst(r, m_subst_map.size(), m_subst_map.c_ptr(), new_r); + m_subst(r, new_r); m_rewriter(new_r); conjs.reset(); flatten_and(new_r, conjs); @@ -776,19 +650,25 @@ namespace eq { m(m), a(m), dt(m), - m_is_variable(0), + m_is_variable(nullptr), m_subst(m), - m_new_exprs(m), m_subst_map(m), + m_new_exprs(m), m_new_args(m), m_rewriter(m), - m_params(p) {} + m_params(p) { + } - void set_is_variable_proc(is_variable_proc& proc) { m_is_variable = &proc;} + void set_is_variable_proc(is_variable_proc& proc) { + m_is_variable = &proc; + m_solvers.reset(); + m_solvers.register_plugin(qe::mk_arith_solve_plugin(m, proc)); + m_solvers.register_plugin(qe::mk_basic_solve_plugin(m, proc)); + } void operator()(quantifier * q, expr_ref & r, proof_ref & pr) { TRACE("qe_lite", tout << mk_pp(q, m) << "\n";); - pr = 0; + pr = nullptr; r = q; reduce_quantifier(q, r, pr); if (r != q) { @@ -955,7 +835,7 @@ namespace ar { public: - der(ast_manager& m): m(m), a(m), m_is_variable(0) {} + der(ast_manager& m): m(m), a(m), m_is_variable(nullptr) {} void operator()(expr_ref_vector& fmls) { for (unsigned i = 0; i < fmls.size(); ++i) { @@ -974,7 +854,7 @@ namespace ar { // ------------------------------------------------------------ -// fm_tactic adapted to eliminate designated de-Brujin indices. +// fm_tactic adapted to eliminate designated de-Bruijn indices. namespace fm { typedef ptr_vector clauses; @@ -1473,7 +1353,7 @@ namespace fm { fm(ast_manager & _m): m(_m), - m_is_variable(0), + m_is_variable(nullptr), m_allocator("fm-elim"), m_util(m), m_bvar2expr(m), @@ -1534,7 +1414,7 @@ namespace fm { reset_constraints(); m_bvar2expr.reset(); m_bvar2sign.reset(); - m_bvar2expr.push_back(0); // bvar 0 is not used + m_bvar2expr.push_back(nullptr); // bvar 0 is not used m_bvar2sign.push_back(0); m_expr2var.reset(); m_is_int.reset(); @@ -1547,7 +1427,7 @@ namespace fm { m_new_fmls.reset(); m_counter = 0; m_inconsistent = false; - m_inconsistent_core = 0; + m_inconsistent_core = nullptr; init_forbidden_set(g); } @@ -1583,7 +1463,7 @@ namespace fm { // 0 <= 0 -- > true if (c.m_c.is_pos() || (!c.m_strict && c.m_c.is_zero())) return m.mk_true(); - ineq = 0; + ineq = nullptr; } else { bool int_cnstr = all_int(c); @@ -1833,7 +1713,7 @@ namespace fm { for (unsigned i = 0; !m_inconsistent && i < sz; i++) { expr * f = g[i]; if (is_occ(f)) - add_constraint(f, 0); + add_constraint(f, nullptr); else m_new_fmls.push_back(f); } @@ -2065,7 +1945,7 @@ namespace fm { display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); - return 0; // no constraint needs to be created. + return nullptr; // no constraint needs to be created. } new_lits.reset(); @@ -2109,7 +1989,7 @@ namespace fm { display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); - return 0; + return nullptr; } expr_dependency * new_dep = m.mk_join(l.m_dep, u.m_dep); @@ -2121,7 +2001,7 @@ namespace fm { display(tout, u); tout << "\n";); m_inconsistent = true; m_inconsistent_core = new_dep; - return 0; + return nullptr; } constraint * new_cnstr = mk_constraint(new_lits.size(), @@ -2191,7 +2071,7 @@ namespace fm { constraint const & l_c = *(l[i]); constraint const & u_c = *(u[j]); constraint * new_c = resolve(l_c, u_c, x); - if (new_c != 0) { + if (new_c != nullptr) { num_new_cnstrs++; new_constraints.push_back(new_c); } @@ -2461,9 +2341,7 @@ public: fmls[index] = fml; return; } - TRACE("qe_lite", for (unsigned i = 0; i < fmls.size(); ++i) { - tout << mk_pp(fmls[i].get(), m) << "\n"; - }); + TRACE("qe_lite", tout << fmls << "\n";); is_variable_test is_var(index_set, index_of_bound); m_der.set_is_variable_proc(is_var); m_fm.set_is_variable_proc(is_var); @@ -2555,12 +2433,8 @@ class qe_lite_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("qe-lite", *g); proof_ref new_pr(m); expr_ref new_f(m); @@ -2607,43 +2481,40 @@ public: m_imp = alloc(imp, m, p); } - virtual ~qe_lite_tactic() { + ~qe_lite_tactic() override { dealloc(m_imp); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(qe_lite_tactic, m, m_params); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; // m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { // m_imp->collect_param_descrs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { // m_imp->collect_statistics(st); } - virtual void reset_statistics() { + void reset_statistics() override { // m_imp->reset_statistics(); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m; dealloc(m_imp); m_imp = alloc(imp, m, m_params); diff --git a/src/qe/qe_lite.h b/src/qe/qe_lite.h index 4fc5572a2..63ad8bedd 100644 --- a/src/qe/qe_lite.h +++ b/src/qe/qe_lite.h @@ -57,7 +57,6 @@ public: void operator()(uint_set const& index_set, bool index_of_bound, expr_ref& fml); void operator()(uint_set const& index_set, bool index_of_bound, expr_ref_vector& conjs); - /** \brief full rewriting based light-weight quantifier elimination round. */ diff --git a/src/qe/qe_mbi.cpp b/src/qe/qe_mbi.cpp new file mode 100644 index 000000000..86fc1ac3c --- /dev/null +++ b/src/qe/qe_mbi.cpp @@ -0,0 +1,545 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + qe_mbi.cpp + +Abstract: + + Model-based interpolation utilities + +Author: + + Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 + +Revision History: + + +Notes: + + Reduction into: + T_EUF + T_LIRA + + Other theories: DT, ARR reduced to EUF + BV is EUF/Boolean. + + Purify EUF1 & LIRA1 & EUF2 & LIRA2 + + Then EUF1 & EUF2 |- false + LIRA1 & LIRA2 |- false + + Sketch of approach by example: + + A: s <= 2a <= t & f(a) = q + + B: t <= 2b <= s + 1 & f(b) != q + + 1. Extract arithmetic consequences of A over shared vocabulary. + + A -> s <= t & (even(t) | s < t) + + 2a. Send to B, have B solve shared variables with EUF_B. + epsilon b . B & A_pure + epsilon b . t <= 2b <= s + 1 & s <= t & (even(t) | s < t) + = t <= s + 1 & (even(t) | t <= s) & s <= t & (even(t) | s < t) + = even(t) & t = s + b := t div 2 + + B & A_pure -> B[b/t div 2] = f(t div 2) != q & t <= s + 1 + + 3a. Send purified result to A + A & B_pure -> false + + Invoke the ping-pong principle to extract interpolant. + + 2b. Solve for shared variables with EUF. + + epsilon a . A + = a := (s + 1) div 2 & s < t & f((s + 1) div 2) = q + + 3b. Send to B. Produces core + s < t & f((s + 1) div 2) = q + + 4b Solve again in arithmetic for shared variables with EUF. + + epsion a . A & (s >= t | f((s + 1) div 2) != q) + + a := t div 2 | s = t & f(t div 2) = q & even(t) + + Send to B, produces core (s != t | f(t div 2) != q) + + 5b. There is no longer a solution for A. A is unsat. + +--*/ + +#include "ast/ast_util.h" +#include "ast/for_each_expr.h" +#include "ast/rewriter/bool_rewriter.h" +#include "ast/arith_decl_plugin.h" +#include "model/model_evaluator.h" +#include "solver/solver.h" +#include "qe/qe_mbi.h" +#include "qe/qe_term_graph.h" +#include "qe/qe_arith.h" + + +namespace qe { + + lbool mbi_plugin::check(expr_ref_vector& lits, model_ref& mdl) { + while (true) { + switch ((*this)(lits, mdl)) { + case mbi_sat: + return l_true; + case mbi_unsat: + return l_false; + case mbi_undef: + return l_undef; + case mbi_augment: + break; + } + } + } + + + // ------------------------------- + // prop_mbi + + prop_mbi_plugin::prop_mbi_plugin(solver* s): mbi_plugin(s->get_manager()), m_solver(s) {} + + // sketch of propositional + + mbi_result prop_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { + lbool r = m_solver->check_sat(lits); + switch (r) { + case l_false: + lits.reset(); + m_solver->get_unsat_core(lits); + return mbi_unsat; + case l_true: + m_solver->get_model(mdl); + lits.reset(); + for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { + func_decl* c = mdl->get_constant(i); + if (m_shared.contains(c)) { + if (m.is_true(mdl->get_const_interp(c))) { + lits.push_back(m.mk_const(c)); + } + else if (m.is_false(mdl->get_const_interp(c))) { + lits.push_back(m.mk_not(m.mk_const(c))); + } + } + } + return mbi_sat; + default: + return mbi_undef; + } + } + + void prop_mbi_plugin::block(expr_ref_vector const& lits) { + m_solver->assert_expr(mk_not(mk_and(lits))); + } + + // ------------------------------- + // euf_mbi + + struct euf_mbi_plugin::is_atom_proc { + ast_manager& m; + expr_ref_vector& m_atoms; + is_atom_proc(expr_ref_vector& atoms): m(atoms.m()), m_atoms(atoms) {} + void operator()(app* a) { + if (m.is_eq(a)) { + m_atoms.push_back(a); + } + else if (m.is_bool(a) && a->get_family_id() != m.get_basic_family_id()) { + m_atoms.push_back(a); + } + } + void operator()(expr*) {} + }; + + euf_mbi_plugin::euf_mbi_plugin(solver* s, solver* sNot): + mbi_plugin(s->get_manager()), + m_atoms(m), + m_solver(s), + m_dual_solver(sNot) { + params_ref p; + p.set_bool("core.minimize", true); + m_solver->updt_params(p); + m_dual_solver->updt_params(p); + expr_ref_vector fmls(m); + m_solver->get_assertions(fmls); + expr_fast_mark1 marks; + is_atom_proc proc(m_atoms); + for (expr* e : fmls) { + quick_for_each_expr(proc, marks, e); + } + } + + mbi_result euf_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { + lbool r = m_solver->check_sat(lits); + switch (r) { + case l_false: + lits.reset(); + m_solver->get_unsat_core(lits); + // optionally minimize core using superposition. + return mbi_unsat; + case l_true: { + m_solver->get_model(mdl); + model_evaluator mev(*mdl.get()); + lits.reset(); + for (expr* e : m_atoms) { + if (mev.is_true(e)) { + lits.push_back(e); + } + else if (mev.is_false(e)) { + lits.push_back(m.mk_not(e)); + } + } + TRACE("qe", tout << "atoms from model: " << lits << "\n";); + r = m_dual_solver->check_sat(lits); + expr_ref_vector core(m); + term_graph tg(m); + switch (r) { + case l_false: + // use the dual solver to find a 'small' implicant + m_dual_solver->get_unsat_core(core); + TRACE("qe", tout << "core: " << core << "\n";); + // project the implicant onto vars + tg.set_vars(m_shared, false); + tg.add_lits(core); + lits.reset(); + lits.append(tg.project(*mdl)); + TRACE("qe", tout << "project: " << lits << "\n";); + return mbi_sat; + case l_undef: + return mbi_undef; + case l_true: + UNREACHABLE(); + return mbi_undef; + } + return mbi_sat; + } + default: + // TBD: if not running solver to completion, then: + // 1. extract unit literals from m_solver. + // 2. run a cc over the units + // 3. extract equalities or other assignments over the congruence classes + // 4. ensure that at least some progress is made over original lits. + return mbi_undef; + } + } + + void euf_mbi_plugin::block(expr_ref_vector const& lits) { + m_solver->assert_expr(mk_not(mk_and(lits))); + } + + + // ------------------------------- + // euf_arith_mbi + + struct euf_arith_mbi_plugin::is_atom_proc { + ast_manager& m; + expr_ref_vector& m_atoms; + is_atom_proc(expr_ref_vector& atoms): m(atoms.m()), m_atoms(atoms) {} + void operator()(app* a) { + if (m.is_eq(a)) { + m_atoms.push_back(a); + } + else if (m.is_bool(a) && a->get_family_id() != m.get_basic_family_id()) { + m_atoms.push_back(a); + } + } + void operator()(expr*) {} + }; + + struct euf_arith_mbi_plugin::is_arith_var_proc { + ast_manager& m; + app_ref_vector& m_vars; + arith_util arith; + obj_hashtable m_exclude; + is_arith_var_proc(app_ref_vector& vars, func_decl_ref_vector const& shared): + m(vars.m()), m_vars(vars), arith(m) { + for (func_decl* f : shared) m_exclude.insert(f); + } + void operator()(app* a) { + if (arith.is_int_real(a) && a->get_family_id() != arith.get_family_id() && !m_exclude.contains(a->get_decl())) { + m_vars.push_back(a); + } + } + void operator()(expr*) {} + + }; + + euf_arith_mbi_plugin::euf_arith_mbi_plugin(solver* s, solver* sNot): + mbi_plugin(s->get_manager()), + m_atoms(m), + m_solver(s), + m_dual_solver(sNot) { + params_ref p; + p.set_bool("core.minimize", true); + m_solver->updt_params(p); + m_dual_solver->updt_params(p); + expr_ref_vector fmls(m); + m_solver->get_assertions(fmls); + expr_fast_mark1 marks; + is_atom_proc proc(m_atoms); + for (expr* e : fmls) { + quick_for_each_expr(proc, marks, e); + } + } + + bool euf_arith_mbi_plugin::get_literals(model_ref& mdl, expr_ref_vector& lits) { + model_evaluator mev(*mdl.get()); + lits.reset(); + for (expr* e : m_atoms) { + if (mev.is_true(e)) { + lits.push_back(e); + } + else if (mev.is_false(e)) { + lits.push_back(m.mk_not(e)); + } + } + TRACE("qe", tout << "atoms from model: " << lits << "\n";); + lbool r = m_dual_solver->check_sat(lits); + if (l_false == r) { + // use the dual solver to find a 'small' implicant + lits.reset(); + m_dual_solver->get_unsat_core(lits); + return true; + } + else { + return false; + } + } + + app_ref_vector euf_arith_mbi_plugin::get_arith_vars(expr_ref_vector const& lits) { + arith_util a(m); + app_ref_vector avars(m); + is_arith_var_proc _proc(avars, m_shared); + for_each_expr(_proc, lits); + return avars; + } + + mbi_result euf_arith_mbi_plugin::operator()(expr_ref_vector& lits, model_ref& mdl) { + lbool r = m_solver->check_sat(lits); + + switch (r) { + case l_false: + lits.reset(); + m_solver->get_unsat_core(lits); + TRACE("qe", tout << "unsat core: " << lits << "\n";); + // optionally minimize core using superposition. + return mbi_unsat; + case l_true: { + m_solver->get_model(mdl); + if (!get_literals(mdl, lits)) { + return mbi_undef; + } + app_ref_vector avars = get_arith_vars(lits); + + TRACE("qe", tout << "vars: " << avars << " lits: " << lits << "\n";); + + // 1. project arithmetic variables using mdl that satisfies core. + // ground any remaining arithmetic variables using model. + arith_project_plugin ap(m); + ap.set_check_purified(false); + + auto defs = ap.project(*mdl.get(), avars, lits); + // 2. Add the projected definitions to the remaining (euf) literals + for (auto const& def : defs) { + lits.push_back(m.mk_eq(def.var, def.term)); + } + TRACE("qe", tout << "# arith defs" << defs.size() << " avars: " << avars << " " << lits << "\n";); + + // 3. Project the remaining literals with respect to EUF. + term_graph tg(m); + tg.set_vars(m_shared, false); + tg.add_lits(lits); + lits.reset(); + lits.append(tg.project(*mdl)); + //lits.append(tg.project()); + TRACE("qe", tout << "project: " << lits << "\n";); + return mbi_sat; + } + default: + // TBD: if not running solver to completion, then: + // 1. extract unit literals from m_solver. + // 2. run a cc over the units + // 3. extract equalities or other assignments over the congruence classes + // 4. ensure that at least some progress is made over original lits. + return mbi_undef; + } + } + + void euf_arith_mbi_plugin::block(expr_ref_vector const& lits) { + m_solver->assert_expr(mk_not(mk_and(lits))); + } + + + /** -------------------------------------------------------------- + * ping-pong interpolation of Gurfinkel & Vizel + * compute a binary interpolant. + */ + lbool interpolator::pingpong(mbi_plugin& a, mbi_plugin& b, expr_ref& itp) { + model_ref mdl; + expr_ref_vector lits(m); + bool turn = true; + vector itps, blocks; + itps.push_back(expr_ref_vector(m)); + itps.push_back(expr_ref_vector(m)); + blocks.push_back(expr_ref_vector(m)); + blocks.push_back(expr_ref_vector(m)); + mbi_result last_res = mbi_undef; + bool_rewriter rw(m); + while (true) { + auto* t1 = turn ? &a : &b; + auto* t2 = turn ? &b : &a; + mbi_result next_res = (*t1)(lits, mdl); + switch (next_res) { + case mbi_sat: + if (last_res == mbi_sat) { + itp = nullptr; + return l_true; + } + TRACE("mbi", tout << "new lits " << lits << "\n";); + break; // continue + case mbi_unsat: { + if (lits.empty()) { + // TBD, either a => itp and itp => !b + // or b => itp and itp => !a + itp = mk_and(itps[!turn]); + return l_false; + } + t2->block(lits); + expr_ref lemma(mk_not(mk_and(lits))); + TRACE("mbi", tout << lemma << "\n";); + blocks[turn].push_back(lemma); + itp = m.mk_implies(mk_and(blocks[!turn]), lemma); + // TBD: compute closure over variables not in vars + itps[turn].push_back(itp); + lits.reset(); // or find a prefix of lits? + break; + } + case mbi_augment: + break; + case mbi_undef: + return l_undef; + } + turn = !turn; + last_res = next_res; + } + } + + /** + * One-sided pogo creates clausal interpolants. + * It creates a set of consequences of b that are inconsistent with a. + */ + lbool interpolator::pogo(mbi_plugin& a, mbi_plugin& b, expr_ref& itp) { + expr_ref_vector lits(m), itps(m); + while (true) { + model_ref mdl; + lits.reset(); + switch (a.check(lits, mdl)) { + case l_true: + switch (b.check(lits, mdl)) { + case l_true: + return l_true; + case l_false: + a.block(lits); + itps.push_back(mk_not(mk_and(lits))); + break; + case l_undef: + return l_undef; + } + break; + case l_false: + itp = mk_and(itps); + return l_false; + case l_undef: + return l_undef; + } + } + } + + lbool interpolator::vurtego(mbi_plugin& a, mbi_plugin& b, expr_ref& itp, model_ref &mdl) { + /** + Assumptions on mbi_plugin() + Let local be assertions local to the plugin + Let blocked be clauses added by blocked, kept separately from local + mbi_plugin::check(lits, mdl, bool force_model): + if lits.empty() and mdl == nullptr then + if is_sat(local & blocked) then + return l_true, mbp of local, mdl of local & blocked + else + return l_false + else if !lits.empty() then + if is_sat(local & mdl & blocked) + return l_true, lits, extension of mdl to local + else if is_sat(local & lits & blocked) + if (force_model) then + return l_false, core of model, nullptr + else + return l_true, mbp of local, mdl of local & blocked + else if !is_sat(local & lits) then + return l_false, mbp of local, nullptr + else if is_sat(local & lits) && !is_sat(local & lits & blocked) + MISSING CASE + MUST PRODUCE AN IMPLICANT OF LOCAL that is inconsistent with lits & blocked + in this case !is_sat(local & lits & mdl) and is_sat(mdl, blocked) + let mdl_blocked be lits of blocked that are true in mdl + return l_false, core of lits & mdl_blocked, nullptr + + mbi_plugin::block(phi): add phi to blocked + + probably should use the operator() instead of check. + mbi_augment -- means consistent with lits but not with the mdl + mbi_sat -- means consistent with lits and mdl + + */ + expr_ref_vector lits(m), itps(m); + while (true) { + // when lits.empty(), this picks an A-implicant consistent with B + // when !lits.empty(), checks whether mdl of shared vocab extends to A + bool force_model = !lits.empty(); + switch (a.check_ag(lits, mdl, force_model)) { + case l_true: + if (force_model) + // mdl is a model for a && b + return l_true; + switch (b.check_ag(lits, mdl, false)) { + case l_true: + /* can return true if know that b did not change + the model. For now, cycle back to A. */ + SASSERT(!lits.empty()); + SASSERT(mdl); + break; + case l_false: + // Force a different A-implicant + a.block(lits); + lits.reset(); + mdl.reset(); + break; + case l_undef: + return l_undef; + } + case l_false: + if (lits.empty()) { + // no more A-implicants, terminate + itp = mk_and(itps); + return l_false; + } + // force B to pick a different model or a different implicant + b.block(lits); + itps.push_back(mk_not(mk_and(lits))); + lits.reset(); + mdl.reset(); + break; + case l_undef: + return l_undef; + } + } + } + +}; diff --git a/src/qe/qe_mbi.h b/src/qe/qe_mbi.h new file mode 100644 index 000000000..22a0864ba --- /dev/null +++ b/src/qe/qe_mbi.h @@ -0,0 +1,135 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + qe_mbi.h + +Abstract: + + Model-based interpolation utilities + +Author: + + Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 + +Revision History: + + +--*/ + +#pragma once + +namespace qe { + enum mbi_result { + mbi_sat, + mbi_unsat, + mbi_augment, + mbi_undef, + }; + + class mbi_plugin { + protected: + ast_manager& m; + func_decl_ref_vector m_shared; + public: + mbi_plugin(ast_manager& m): m(m), m_shared(m) {} + virtual ~mbi_plugin() {} + + /** + * Set the shared symbols. + */ + virtual void set_shared(func_decl_ref_vector const& vars) { + m_shared.reset(); + m_shared.append(vars); + } + + /** + * \brief Utility that works modulo a background state. + * - vars + * variables to preferrably project onto (other variables would require quantification to fit interpolation signature) + * - lits + * set of literals to check satisfiability with respect to. + * - mdl + * optional model for caller. + * on return: + * - mbi_sat: + * populates mdl with a satisfying state, and lits with implicant for background state. + * - mbi_unsat: + * populates lits to be inconsistent with background state. + * For all practical purposes it is a weakening of lits or even a subset of lits. + * - mbi_augment: + * populates lits with strengthening of lits (superset) + * - mbi_undef: + * inconclusive, + */ + virtual mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) = 0; + + /** + * \brief Block conjunction of lits from future mbi_augment or mbi_sat. + */ + virtual void block(expr_ref_vector const& lits) = 0; + + /** + * \brief perform a full check, consume internal auguments if necessary. + */ + lbool check(expr_ref_vector& lits, model_ref& mdl); + + virtual lbool check_ag(expr_ref_vector& lits, model_ref& mdl, bool force_model) { + return l_undef; + } + + + }; + + class prop_mbi_plugin : public mbi_plugin { + solver_ref m_solver; + public: + prop_mbi_plugin(solver* s); + ~prop_mbi_plugin() override {} + mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) override; + void block(expr_ref_vector const& lits) override; + }; + + class euf_mbi_plugin : public mbi_plugin { + expr_ref_vector m_atoms; + solver_ref m_solver; + solver_ref m_dual_solver; + struct is_atom_proc; + public: + euf_mbi_plugin(solver* s, solver* sNot); + ~euf_mbi_plugin() override {} + mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) override; + void block(expr_ref_vector const& lits) override; + }; + + class euf_arith_mbi_plugin : public mbi_plugin { + expr_ref_vector m_atoms; + solver_ref m_solver; + solver_ref m_dual_solver; + struct is_atom_proc; + struct is_arith_var_proc; + + app_ref_vector get_arith_vars(expr_ref_vector const& lits); + bool get_literals(model_ref& mdl, expr_ref_vector& lits); + + public: + euf_arith_mbi_plugin(solver* s, solver* sNot); + ~euf_arith_mbi_plugin() override {} + mbi_result operator()(expr_ref_vector& lits, model_ref& mdl) override; + void block(expr_ref_vector const& lits) override; + }; + + /** + * use cases for interpolation. + */ + class interpolator { + ast_manager& m; + public: + interpolator(ast_manager& m):m(m) {} + lbool pingpong(mbi_plugin& a, mbi_plugin& b, expr_ref& itp); + lbool pogo(mbi_plugin& a, mbi_plugin& b, expr_ref& itp); + lbool vurtego(mbi_plugin &a, mbi_plugin &b, expr_ref &itp, model_ref &mdl); + }; + +}; diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 329687a36..2f44983ff 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -18,17 +18,19 @@ Revision History: --*/ +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/occurs.h" +#include "ast/rewriter/th_rewriter.h" +#include "ast/expr_functors.h" +#include "ast/for_each_expr.h" +#include "ast/scoped_proof.h" #include "qe/qe_mbp.h" #include "qe/qe_arith.h" #include "qe/qe_arrays.h" #include "qe/qe_datatypes.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/ast_pp.h" -#include "ast/ast_util.h" -#include "ast/rewriter/th_rewriter.h" -#include "model/model_v2_pp.h" -#include "ast/expr_functors.h" -#include "ast/for_each_expr.h" +#include "qe/qe_lite.h" #include "model/model_evaluator.h" @@ -61,9 +63,9 @@ expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { expr_ref_vector vals(m); obj_map val2expr; app* alit = to_app(t); - for (unsigned i = 0; i < alit->get_num_args(); ++i) { - expr* e1 = alit->get_arg(i), *e2; - VERIFY(model.eval(e1, val)); + for (expr * e1 : *alit) { + expr *e2; + val = model(e1); if (val2expr.find(val, e2)) { return expr_ref(m.mk_eq(e1, e2), m); } @@ -71,43 +73,9 @@ expr_ref project_plugin::pick_equality(ast_manager& m, model& model, expr* t) { vals.push_back(val); } UNREACHABLE(); - return expr_ref(0, m); + return expr_ref(nullptr, m); } -void project_plugin::partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits) { - ast_manager& m = vals.get_manager(); - expr_ref val(m); - expr_ref_vector trail(m), reps(m); - obj_map roots; - for (unsigned i = 0; i < vals.size(); ++i) { - expr* v = vals[i], *root; - VERIFY (model.eval(v, val)); - if (roots.find(val, root)) { - lits.push_back(m.mk_eq(v, root)); - } - else { - roots.insert(val, v); - trail.push_back(val); - reps.push_back(v); - } - } - if (reps.size() > 1) { - lits.push_back(mk_distinct(reps)); - } -} - -void project_plugin::partition_args(model& model, app_ref_vector const& selects, expr_ref_vector& lits) { - ast_manager& m = selects.get_manager(); - if (selects.empty()) return; - unsigned num_args = selects[0]->get_decl()->get_arity(); - for (unsigned j = 1; j < num_args; ++j) { - expr_ref_vector args(m); - for (unsigned i = 0; i < selects.size(); ++i) { - args.push_back(selects[i]->get_arg(j)); - } - project_plugin::partition_values(model, args, lits); - } -} void project_plugin::erase(expr_ref_vector& lits, unsigned& i) { lits[i] = lits.back(); @@ -123,11 +91,16 @@ void project_plugin::push_back(expr_ref_vector& lits, expr* e) { class mbp::impl { ast_manager& m; + params_ref m_params; th_rewriter m_rw; ptr_vector m_plugins; expr_mark m_visited; expr_mark m_bool_visited; + // parameters + bool m_reduce_all_selects; + bool m_dont_sub; + void add_plugin(project_plugin* p) { family_id fid = p->get_family_id(); SASSERT(!m_plugins.get(fid, 0)); @@ -166,12 +139,9 @@ class mbp::impl { } if (reduced) { unsigned j = 0; - for (unsigned i = 0; i < vars.size(); ++i) { - if (!is_rem.is_marked(vars[i].get())) { - if (i != j) { - vars[j] = vars[i].get(); - } - ++j; + for (app* v : vars) { + if (!is_rem.is_marked(v)) { + vars[j++] = v; } } vars.shrink(j); @@ -198,20 +168,13 @@ class mbp::impl { void filter_variables(model& model, app_ref_vector& vars, expr_ref_vector& lits, expr_ref_vector& unused_lits) { expr_mark lit_visited; project_plugin::mark_rec(lit_visited, lits); - unsigned j = 0; - for (unsigned i = 0; i < vars.size(); ++i) { - app* var = vars[i].get(); + for (app* var : vars) { if (lit_visited.is_marked(var)) { - if (i != j) { - vars[j] = vars[i].get(); + vars[j++] = var; } - ++j; } - } - if (vars.size() != j) { - vars.resize(j); - } + vars.shrink(j); } bool extract_bools(model_evaluator& eval, expr_ref_vector& fmls, expr* fml) { @@ -258,47 +221,124 @@ class mbp::impl { return found_bool; } - void project_bools(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { + void project_bools(model& mdl, app_ref_vector& vars, expr_ref_vector& fmls) { expr_safe_replace sub(m); expr_ref val(m); + model_evaluator eval(mdl, m_params); + eval.set_model_completion(true); unsigned j = 0; for (unsigned i = 0; i < vars.size(); ++i) { app* var = vars[i].get(); if (m.is_bool(var)) { - VERIFY(model.eval(var, val)); - sub.insert(var, val); + sub.insert(var, eval(var)); } else { - if (j != i) { - vars[j] = vars[i].get(); + vars[j++] = var; } - ++j; } + if (j == vars.size()) { + return; } - if (j != vars.size()) { - vars.resize(j); + vars.shrink(j); j = 0; for (unsigned i = 0; i < fmls.size(); ++i) { - sub(fmls[i].get(), val); + expr* fml = fmls[i].get(); + sub(fml, val); m_rw(val); if (!m.is_true(val)) { - TRACE("qe", tout << mk_pp(fmls[i].get(), m) << " -> " << val << "\n";); - fmls[i] = val; - if (j != i) { - fmls[j] = fmls[i].get(); + TRACE("qe", tout << mk_pp(fml, m) << " -> " << val << "\n";); + fmls[j++] = val; } - ++j; } + fmls.shrink(j); } - if (j != fmls.size()) { - fmls.resize(j); + + + void subst_vars(model_evaluator& eval, app_ref_vector const& vars, expr_ref& fml) { + expr_safe_replace sub (m); + for (app * v : vars) { + sub.insert(v, eval(v)); + } + sub(fml); + } + + struct index_term_finder { + ast_manager& m; + array_util m_array; + app_ref m_var; + expr_ref_vector& m_res; + + index_term_finder (ast_manager &mgr, app* v, expr_ref_vector &res): + m(mgr), m_array (m), m_var (v, m), m_res (res) {} + + void operator() (var *n) {} + void operator() (quantifier *n) {} + void operator() (app *n) { + expr *e1, *e2; + if (m_array.is_select (n)) { + for (expr * arg : *n) { + if (m.get_sort(arg) == m.get_sort(m_var) && arg != m_var) + m_res.push_back (arg); + } + } + else if (m.is_eq(n, e1, e2)) { + if (e1 == m_var) + m_res.push_back(e2); + else if (e2 == m_var) + m_res.push_back(e1); } } + }; + + bool project_var (model_evaluator& eval, app* var, expr_ref& fml) { + expr_ref val = eval(var); + + TRACE ("mbqi_project_verbose", tout << "MBQI: var: " << mk_pp (var, m) << "\n" << "fml: " << fml << "\n";); + expr_ref_vector terms (m); + index_term_finder finder (m, var, terms); + for_each_expr (finder, fml); + + TRACE ("mbqi_project_verbose", tout << "terms:\n" << terms;); + + for (expr * term : terms) { + expr_ref tval = eval(term); + + TRACE ("mbqi_project_verbose", tout << "term: " << mk_pp (term, m) << " tval: " << tval << " val: " << val << "\n";); + + // -- if the term does not contain an occurrence of var + // -- and is in the same equivalence class in the model + if (tval == val && !occurs (var, term)) { + TRACE ("mbqi_project", + tout << "MBQI: replacing " << mk_pp (var, m) << " with " << mk_pp (term, m) << "\n";); + expr_safe_replace sub(m); + sub.insert (var, term); + sub (fml); + return true; + } + } + + TRACE ("mbqi_project", + tout << "MBQI: failed to eliminate " << mk_pp (var, m) << " from " << fml << "\n";); + + return false; + } + + void project_vars (model &M, app_ref_vector &vars, expr_ref &fml) { + model_evaluator eval(M); + eval.set_model_completion(false); + // -- evaluate to initialize eval cache + (void) eval (fml); + unsigned j = 0; + for (app * v : vars) { + if (!project_var (eval, v, fml)) { + vars[j++] = v; + } + } + vars.shrink(j); } public: - opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { arith_project_plugin arith(m); return arith.maximize(fmls, mdl, t, ge, gt); @@ -426,23 +466,30 @@ public: m_bool_visited.reset(); } - impl(ast_manager& m):m(m), m_rw(m) { + impl(ast_manager& m, params_ref const& p):m(m), m_params(p), m_rw(m) { add_plugin(alloc(arith_project_plugin, m)); add_plugin(alloc(datatype_project_plugin, m)); add_plugin(alloc(array_project_plugin, m)); + updt_params(p); } ~impl() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); } + void updt_params(params_ref const& p) { + m_params.append(p); + m_reduce_all_selects = m_params.get_bool("reduce_all_selects", false); + m_dont_sub = m_params.get_bool("dont_sub", false); + } + void preprocess_solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { extract_literals(model, fmls); bool change = true; while (change && !vars.empty()) { change = solve(model, vars, fmls); - for (unsigned i = 0; i < m_plugins.size(); ++i) { - if (m_plugins[i] && m_plugins[i]->solve(model, vars, fmls)) { + for (auto* p : m_plugins) { + if (p && p->solve(model, vars, fmls)) { change = true; } } @@ -451,8 +498,9 @@ public: bool validate_model(model& model, expr_ref_vector const& fmls) { expr_ref val(m); - for (unsigned i = 0; i < fmls.size(); ++i) { - VERIFY(model.eval(fmls[i], val) && m.is_true(val)); + model_evaluator eval(model); + for (expr * f : fmls) { + VERIFY(model.is_true(f)); } return true; } @@ -469,8 +517,7 @@ public: while (progress && !vars.empty() && !fmls.empty()) { app_ref_vector new_vars(m); progress = false; - for (unsigned i = 0; i < m_plugins.size(); ++i) { - project_plugin* p = m_plugins[i]; + for (project_plugin * p : m_plugins) { if (p) { (*p)(model, vars, fmls); } @@ -490,7 +537,7 @@ public: var = new_vars.back(); new_vars.pop_back(); expr_safe_replace sub(m); - VERIFY(model.eval(var, val)); + val = model(var); sub.insert(var, val); for (unsigned i = 0; i < fmls.size(); ++i) { sub(fmls[i].get(), tmp); @@ -517,28 +564,146 @@ public: TRACE("qe", tout << vars << " " << fmls << "\n";); } + void do_qe_lite(app_ref_vector& vars, expr_ref& fml) { + qe_lite qe(m, m_params, false); + qe (vars, fml); + m_rw (fml); + TRACE ("qe", tout << "After qe_lite:\n" << fml << "\n" << "Vars: " << vars << "\n";); + SASSERT (!m.is_false (fml)); + } + + void do_qe_bool(model& mdl, app_ref_vector& vars, expr_ref& fml) { + expr_ref_vector fmls(m); + fmls.push_back(fml); + project_bools(mdl, vars, fmls); + fml = mk_and(fmls); + } + + void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) { + TRACE ("qe", tout << "Before projection:\n" << fml << "\n" << "Vars: " << vars << "\n";); + + model_evaluator eval(mdl, m_params); + eval.set_model_completion(true); + app_ref_vector other_vars (m); + app_ref_vector array_vars (m); + array_util arr_u (m); + arith_util ari_u (m); + + flatten_and(fml); + + + while (!vars.empty()) { + + do_qe_lite(vars, fml); + + do_qe_bool(mdl, vars, fml); + + // sort out vars into bools, arith (int/real), and arrays + for (app* v : vars) { + if (arr_u.is_array(v)) { + array_vars.push_back (v); + } + else { + other_vars.push_back (v); + } + } + + TRACE ("qe", tout << "Array vars: " << array_vars << "\n";); + + vars.reset (); + + // project arrays + qe::array_project_plugin ap(m); + ap(mdl, array_vars, fml, vars, m_reduce_all_selects); + SASSERT (array_vars.empty ()); + m_rw(fml); + SASSERT (!m.is_false (fml)); + + TRACE ("qe", + tout << "extended model:\n" << mdl; + tout << "Vars: " << vars << "\n"; + ); + } + + // project reals, ints and other variables. + if (!other_vars.empty ()) { + TRACE ("qe", tout << "Other vars: " << other_vars << "\n" << mdl;); + + expr_ref_vector fmls(m); + flatten_and (fml, fmls); + + (*this)(false, other_vars, mdl, fmls); + fml = mk_and (fmls); + m_rw(fml); + + TRACE ("qe", + tout << "Projected other vars:\n" << fml << "\n"; + tout << "Remaining other vars:\n" << other_vars << "\n";); + SASSERT (!m.is_false (fml)); + } + + if (!other_vars.empty ()) { + project_vars (mdl, other_vars, fml); + m_rw(fml); + } + + // substitute any remaining other vars + if (!m_dont_sub && !other_vars.empty ()) { + subst_vars (eval, other_vars, fml); + // an extra round of simplification because subst_vars is not simplifying + m_rw(fml); + TRACE ("qe", tout << "After substituting remaining other vars:\n" << fml << "\n";); + other_vars.reset(); + } + + SASSERT(eval.is_true(fml)); + + vars.reset (); + vars.append(other_vars); + } + }; -mbp::mbp(ast_manager& m) { - m_impl = alloc(impl, m); +mbp::mbp(ast_manager& m, params_ref const& p) { + scoped_no_proof _sp (m); + m_impl = alloc(impl, m, p); } mbp::~mbp() { dealloc(m_impl); } +void mbp::updt_params(params_ref const& p) { + m_impl->updt_params(p); +} + +void mbp::get_param_descrs(param_descrs & r) { + r.insert("reduce_all_selects", CPK_BOOL, "(default: false) reduce selects"); + r.insert("dont_sub", CPK_BOOL, "(default: false) disable substitution of values for free variables"); +} + void mbp::operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls) { + scoped_no_proof _sp (fmls.get_manager()); (*m_impl)(force_elim, vars, mdl, fmls); } +void mbp::spacer(app_ref_vector& vars, model& mdl, expr_ref& fml) { + scoped_no_proof _sp (fml.get_manager()); + m_impl->spacer(vars, mdl, fml); +} + void mbp::solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { + scoped_no_proof _sp (fmls.get_manager()); m_impl->preprocess_solve(model, vars, fmls); } void mbp::extract_literals(model& model, expr_ref_vector& lits) { + scoped_no_proof _sp (lits.get_manager()); m_impl->extract_literals(model, lits); } + opt::inf_eps mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt) { + scoped_no_proof _sp (fmls.get_manager()); return m_impl->maximize(fmls, mdl, t, ge, gt); } diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index d1695843c..0bb8ba00f 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -31,17 +31,33 @@ namespace qe { struct cant_project {}; + struct def { + expr_ref var, term; + def(const expr_ref& v, expr_ref& t): var(v), term(t) {} + }; + class project_plugin { public: virtual ~project_plugin() {} virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits) = 0; + /** + \brief partial solver. + */ virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0; virtual family_id get_family_id() = 0; + virtual void operator()(model& model, app_ref_vector& vars, expr_ref_vector& lits) { }; + /** + \brief project vars modulo model, return set of definitions for eliminated variables. + - vars in/out: returns variables that were not eliminated + - lits in/out: returns projected literals + - returns set of definitions + (TBD: in triangular form, the last definition can be substituted into definitions that come before) + */ + virtual vector project(model& model, app_ref_vector& vars, expr_ref_vector& lits) = 0; + static expr_ref pick_equality(ast_manager& m, model& model, expr* t); - static void partition_values(model& model, expr_ref_vector const& vals, expr_ref_vector& lits); - static void partition_args(model& model, app_ref_vector const& sels, expr_ref_vector& lits); static void erase(expr_ref_vector& lits, unsigned& i); static void push_back(expr_ref_vector& lits, expr* lit); static void mark_rec(expr_mark& visited, expr* e); @@ -52,14 +68,18 @@ namespace qe { class impl; impl * m_impl; public: - mbp(ast_manager& m); - + mbp(ast_manager& m, params_ref const& p = params_ref()); + ~mbp(); - + + void updt_params(params_ref const& p); + + static void get_param_descrs(param_descrs & r); + /** \brief - Apply model-based qe on constants provided as vector of variables. - Return the updated formula and updated set of variables that were not eliminated. + Apply model-based qe on constants provided as vector of variables. + Return the updated formula and updated set of variables that were not eliminated. */ void operator()(bool force_elim, app_ref_vector& vars, model& mdl, expr_ref_vector& fmls); @@ -70,17 +90,26 @@ namespace qe { void solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); /** - \brief + \brief Extract literals from formulas based on model. */ void extract_literals(model& model, expr_ref_vector& lits); /** - \brief + \brief Maximize objective t under current model for constraints in fmls. */ opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& ge, expr_ref& gt); + + /** + \brief + Apply spacer friendly MBP. + Use parameters to control behavior. + - reduce_all_selects (false) + - dont_sub (false) + */ + void spacer(app_ref_vector& vars, model& mdl, expr_ref& fml); }; } -#endif +#endif diff --git a/src/qe/qe_sat_tactic.cpp b/src/qe/qe_sat_tactic.cpp index 69ebc1a42..5fbe10d35 100644 --- a/src/qe/qe_sat_tactic.cpp +++ b/src/qe/qe_sat_tactic.cpp @@ -39,14 +39,14 @@ namespace qe { class is_relevant_default : public i_expr_pred { public: - bool operator()(expr* e) { + bool operator()(expr* e) override { return true; } }; class mk_atom_default : public i_nnf_atom { public: - virtual void operator()(expr* e, bool pol, expr_ref& result) { + void operator()(expr* e, bool pol, expr_ref& result) override { if (pol) result = e; else result = result.get_manager().mk_not(e); } @@ -97,7 +97,7 @@ namespace qe { m_fml(m), m_projection_mode_param(true) {} - virtual ~solver_context() { + ~solver_context() override { std::for_each(m_contains_app.begin(), m_contains_app.end(), delete_proc()); } @@ -111,28 +111,28 @@ namespace qe { void set_projection_mode(bool p) { m_projection_mode_param = p; } - ast_manager& get_manager() { return m; } + ast_manager& get_manager() override { return m; } expr* fml() { return m_fml; } // set of atoms in current formula. - virtual atom_set const& pos_atoms() const { return m_pos; } - virtual atom_set const& neg_atoms() const { return m_neg; } + atom_set const& pos_atoms() const override { return m_pos; } + atom_set const& neg_atoms() const override { return m_neg; } // Access current set of variables to solve - virtual unsigned get_num_vars() const { return m_vars.size(); } - virtual app* get_var(unsigned idx) const { return m_vars[idx]; } - virtual app*const* get_vars() const { return m_vars.c_ptr(); } - virtual bool is_var(expr* e, unsigned& idx) const { + unsigned get_num_vars() const override { return m_vars.size(); } + app* get_var(unsigned idx) const override { return m_vars[idx]; } + app_ref_vector const& get_vars() const override { return m_vars; } + bool is_var(expr* e, unsigned& idx) const override { for (unsigned i = 0; i < m_vars.size(); ++i) { if (e == m_vars[i]) return (idx = i, true); } return false; } - virtual contains_app& contains(unsigned idx) { return *m_contains_app[idx]; } + contains_app& contains(unsigned idx) override { return *m_contains_app[idx]; } // callback to replace variable at index 'idx' with definition 'def' and updated formula 'fml' - virtual void elim_var(unsigned idx, expr* fml, expr* def) { + void elim_var(unsigned idx, expr* fml, expr* def) override { m_fml = fml; m_pos.reset(); m_neg.reset(); @@ -143,13 +143,13 @@ namespace qe { } // callback to add new variable to branch. - virtual void add_var(app* x) { + void add_var(app* x) override { m_vars.push_back(x); m_contains_app.push_back(alloc(contains_app, m, x)); } // callback to add constraints in branch. - virtual void add_constraint(bool use_var, expr* l1 = 0, expr* l2 = 0, expr* l3 = 0) { + void add_constraint(bool use_var, expr* l1 = nullptr, expr* l2 = nullptr, expr* l3 = nullptr) override { ptr_buffer args; if (l1) args.push_back(l1); if (l2) args.push_back(l2); @@ -160,7 +160,7 @@ namespace qe { } // eliminate finite domain variable 'var' from fml. - virtual void blast_or(app* var, expr_ref& fml) { + void blast_or(app* var, expr_ref& fml) override { expr_ref result(m); expr_quant_elim qelim(m, m_super.m_fparams); qe::mk_exists(1, &var, fml); @@ -179,7 +179,7 @@ namespace qe { m_super.m_rewriter(m_fml); TRACE("qe", model_v2_pp(tout, *model); tout << "\n"; tout << mk_pp(m_fml, m) << "\n";); - elim_var(i, m_fml, 0); + elim_var(i, m_fml, nullptr); } void project_var_full(unsigned i) { @@ -191,7 +191,7 @@ namespace qe { m_fml = result; m_super.m_rewriter(m_fml); TRACE("qe", tout << mk_pp(m_fml, m) << "\n";); - elim_var(i, m_fml, 0); + elim_var(i, m_fml, nullptr); } void project_var(unsigned i) { @@ -223,21 +223,15 @@ namespace qe { m_fparams.m_model = true; } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(sat_tactic, m); } - virtual ~sat_tactic() { + ~sat_tactic() override { reset(); } - virtual void operator()( - goal_ref const& goal, - goal_ref_buffer& result, - model_converter_ref& mc, - proof_converter_ref & pc, - expr_dependency_ref& core) - { + void operator()(goal_ref const& goal, goal_ref_buffer& result) override { try { checkpoint(); reset(); @@ -260,7 +254,7 @@ namespace qe { else { goal->reset(); // equi-satisfiable. What to do with model? - mc = model2model_converter(&*model); + goal->add(model2model_converter(&*model)); } result.push_back(goal.get()); } @@ -269,25 +263,25 @@ namespace qe { } } - virtual void collect_statistics(statistics & st) const { - for (unsigned i = 0; i < m_solvers.size(); ++i) { - m_solvers[i]->collect_statistics(st); + void collect_statistics(statistics & st) const override { + for (auto const * s : m_solvers) { + s->collect_statistics(st); } m_solver.collect_statistics(st); m_ctx_rewriter.collect_statistics(st); } - virtual void reset_statistics() { - for (unsigned i = 0; i < m_solvers.size(); ++i) { - m_solvers[i]->reset_statistics(); + void reset_statistics() override { + for (auto * s : m_solvers) { + s->reset_statistics(); } m_solver.reset_statistics(); m_ctx_rewriter.reset_statistics(); } - virtual void cleanup() {} + void cleanup() override {} - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_extrapolate_strategy_param = p.get_uint("extrapolate_strategy", m_extrapolate_strategy_param); m_projection_mode_param = p.get_bool("projection_mode", m_projection_mode_param); m_strong_context_simplify_param = p.get_bool("strong_context_simplify", m_strong_context_simplify_param); @@ -296,7 +290,7 @@ namespace qe { m_qe_rw.updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("extrapolate_strategy",CPK_UINT, "(default: 0 trivial extrapolation) 1 - nnf strengthening 2 - smt-test 3 - nnf_weakening"); r.insert("projection_mode", CPK_BOOL, "(default: true - full) false - partial quantifier instantiation"); r.insert("strong_context_simplify", CPK_BOOL, "(default: true) use strong context simplifier on result of quantifier elimination"); @@ -326,11 +320,11 @@ namespace qe { smt::kernel& solver(unsigned i) { return *m_solvers[i]; } - void reset() { + void reset() override { for (unsigned i = 0; i < m_solvers.size(); ++i) { dealloc(m_solvers[i]); } - m_fml = 0; + m_fml = nullptr; m_Ms.reset(); m_fparamv.reset(); m_solvers.reset(); diff --git a/src/qe/qe_solve_plugin.cpp b/src/qe/qe_solve_plugin.cpp new file mode 100644 index 000000000..1f314848f --- /dev/null +++ b/src/qe/qe_solve_plugin.cpp @@ -0,0 +1,401 @@ +/** +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + qe_solve.cpp + +Abstract: + + Light-weight variable solving plugin model for qe-lite and term_graph. + +Author: + + Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 + +Revision History: + + +--*/ + +#include "ast/arith_decl_plugin.h" +#include "ast/datatype_decl_plugin.h" +#include "ast/ast_util.h" +#include "ast/ast_pp.h" +#include "qe/qe_solve_plugin.h" + +namespace qe { + + expr_ref solve_plugin::operator()(expr* lit) { + if (m.is_not(lit, lit)) + return solve(lit, false); + else + return solve(lit, true); + } + + class arith_solve_plugin : public solve_plugin { + arith_util a; + public: + arith_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("arith"), is_var), a(m) {} + + typedef std::pair signed_expr; + + /** + *\brief + * return r * (sum_{(sign,e) \in exprs} sign * e) + */ + expr_ref mk_term(bool is_int, rational const& r, bool sign, svector const& exprs) { + expr_ref_vector result(m); + for (auto const& kv : exprs) { + bool sign2 = kv.first; + expr* e = kv.second; + rational r2(r); + if (sign == sign2) { + r2.neg(); + } + if (!r2.is_one()) { + result.push_back(a.mk_mul(a.mk_numeral(r2, is_int), e)); + } + else { + result.push_back(e); + } + } + return a.mk_add_simplify(result); + } + + /** + * \brief return true if lhs = rhs can be solved as v = t, where v is a variable. + */ + bool solve(expr* lhs, expr* rhs, expr_ref& v, expr_ref& t) { + if (!a.is_int(lhs) && !a.is_real(rhs)) { + return false; + } + rational a_val; + bool is_int = a.is_int(lhs); + svector todo, done; + todo.push_back(std::make_pair(true, lhs)); + todo.push_back(std::make_pair(false, rhs)); + while (!todo.empty()) { + expr* e = todo.back().second; + bool sign = todo.back().first; + todo.pop_back(); + if (a.is_add(e)) { + for (expr* arg : *to_app(e)) { + todo.push_back(std::make_pair(sign, arg)); + } + } + else if (a.is_sub(e)) { + app* sub = to_app(e); + todo.push_back(std::make_pair(sign, sub->get_arg(0))); + for (unsigned i = 1; i < sub->get_num_args(); ++i) { + todo.push_back(std::make_pair(!sign, sub->get_arg(i))); + } + } + else if (a.is_uminus(e)) { + todo.push_back(std::make_pair(!sign, to_app(e)->get_arg(0))); + } + else if (is_invertible_mul(is_int, e, a_val)) { + done.append(todo); + v = e; + a_val = rational(1)/a_val; + t = mk_term(is_int, a_val, sign, done); + TRACE("qe", tout << mk_pp(lhs, m) << " " << mk_pp(rhs, m) << " " << e << " := " << t << "\n";); + return true; + } + else { + done.push_back(std::make_pair(sign, e)); + } + } + return false; + } + + // is x a constant and can we safely divide by x to solve for a variable? + bool is_invertible_const(bool is_int, expr* x, rational& a_val) { + expr* y; + if (a.is_uminus(x, y) && is_invertible_const(is_int, y, a_val)) { + a_val.neg(); + return true; + } + else if (a.is_numeral(x, a_val) && !a_val.is_zero()) { + if (!is_int || a_val.is_one() || a_val.is_minus_one()) { + return true; + } + } + return false; + } + + // is arg of the form a_val * v, where a_val + // is a constant that we can safely divide by. + bool is_invertible_mul(bool is_int, expr*& arg, rational& a_val) { + if (is_variable(arg)) { + a_val = rational(1); + return true; + } + expr* x, *y; + if (a.is_mul(arg, x, y)) { + if (is_variable(x) && is_invertible_const(is_int, y, a_val)) { + arg = x; + return true; + } + if (is_variable(y) && is_invertible_const(is_int, x, a_val)) { + arg = y; + return true; + } + } + return false; + } + + + expr_ref mk_eq_core (expr *e1, expr *e2) { + expr_ref v(m), t(m); + if (solve(e1, e2, v, t)) { + return expr_ref(m.mk_eq(v, t), m); + } + + if (a.is_zero(e1)) { + std::swap(e1, e2); + } + // y + -1*x == 0 --> y = x + expr *a0 = 0, *a1 = 0, *x = 0; + if (a.is_zero(e2) && a.is_add(e1, a0, a1)) { + if (a.is_times_minus_one(a1, x)) { + e1 = a0; + e2 = x; + } + else if (a.is_times_minus_one(a0, x)) { + e1 = a1; + e2 = x; + } + } + return expr_ref(m.mk_eq(e1, e2), m); + } + + app* mk_le_zero(expr *arg) { + expr *e1, *e2, *e3; + if (a.is_add(arg, e1, e2)) { + // e1-e2<=0 --> e1<=e2 + if (a.is_times_minus_one(e2, e3)) { + return a.mk_le(e1, e3); + } + // -e1+e2<=0 --> e2<=e1 + else if (a.is_times_minus_one(e1, e3)) { + return a.mk_le(e2, e3); + } + } + return a.mk_le(arg, mk_zero()); + } + + app* mk_ge_zero(expr *arg) { + expr *e1, *e2, *e3; + if (a.is_add(arg, e1, e2)) { + // e1-e2>=0 --> e1>=e2 + if (a.is_times_minus_one(e2, e3)) { + return a.mk_ge(e1, e3); + } + // -e1+e2>=0 --> e2>=e1 + else if (a.is_times_minus_one(e1, e3)) { + return a.mk_ge(e2, e3); + } + } + return a.mk_ge(arg, mk_zero()); + } + + bool mk_le_core (expr *arg1, expr * arg2, expr_ref &result) { + // t <= -1 ==> t < 0 ==> ! (t >= 0) + rational n; + if (a.is_int (arg1) && a.is_minus_one (arg2)) { + result = m.mk_not (mk_ge_zero (arg1)); + return true; + } + else if (a.is_zero(arg2)) { + result = mk_le_zero(arg1); + return true; + } + else if (a.is_int(arg1) && a.is_numeral(arg2, n) && n < 0) { + // t <= n ==> t < n + 1 ==> ! (t >= n + 1) + result = m.mk_not(a.mk_ge(arg1, a.mk_numeral(n+1, true))); + return true; + } + return false; + } + + expr * mk_zero () { + return a.mk_numeral (rational (0), true); + } + + bool is_one (expr const * n) const { + rational val; + return a.is_numeral (n, val) && val.is_one (); + } + + bool mk_ge_core (expr * arg1, expr * arg2, expr_ref &result) { + // t >= 1 ==> t > 0 ==> ! (t <= 0) + rational n; + if (a.is_int (arg1) && is_one (arg2)) { + result = m.mk_not (mk_le_zero (arg1)); + return true; + } + else if (a.is_zero(arg2)) { + result = mk_ge_zero(arg1); + return true; + } + else if (a.is_int(arg1) && a.is_numeral(arg2, n) && n > 0) { + // t >= n ==> t > n - 1 ==> ! (t <= n - 1) + result = m.mk_not(a.mk_le(arg1, a.mk_numeral(n-1, true))); + return true; + } + return false; + } + + expr_ref solve(expr* atom, bool is_pos) override { + expr *e1, *e2; + + expr_ref res(atom, m); + if (m.is_eq (atom, e1, e2)) { + expr_ref v(m), t(m); + v = e1; t = e2; + // -- attempt to solve using arithmetic + solve(e1, e2, v, t); + // -- normalize equality + res = mk_eq_core(v, t); + } + else if (a.is_le(atom, e1, e2)) { + mk_le_core(e1, e2, res); + } + else if (a.is_ge(atom, e1, e2)) { + mk_ge_core(e1, e2, res); + } + + // restore negation + if (!is_pos) { + res = mk_not(m, res); + } + return res; + } + }; + + class basic_solve_plugin : public solve_plugin { + public: + basic_solve_plugin(ast_manager& m, is_variable_proc& is_var): + solve_plugin(m, m.get_basic_family_id(), is_var) {} + + expr_ref solve(expr *atom, bool is_pos) override { + expr_ref res(atom, m); + expr* lhs = nullptr, *rhs = nullptr, *n = nullptr; + if (m.is_eq(atom, lhs, rhs)) { + if (m.is_not(lhs, n) && is_variable(n)) { + res = m.mk_eq(n, mk_not(m, rhs)); + } + else if (m.is_not(rhs, n) && is_variable(n)) { + res = m.mk_eq(n, mk_not(m, lhs)); + } + else if (is_variable(rhs) && !is_variable(lhs)) { + res = m.mk_eq(rhs, lhs); + } + } + // (ite cond (= VAR t) (= VAR t2)) case + expr* cond = nullptr, *th = nullptr, *el = nullptr; + if (m.is_ite(atom, cond, th, el)) { + expr_ref r1 = solve(th, true); + expr_ref r2 = solve(el, true); + expr* v1 = nullptr, *t1 = nullptr, *v2 = nullptr, *t2 = nullptr; + if (m.is_eq(r1, v1, t1) && m.is_eq(r2, v2, t2) && v1 == v2) { + res = m.mk_eq(v1, m.mk_ite(cond, t1, t2)); + } + } + + if (is_variable(atom) && m.is_bool(atom)) { + res = m.mk_eq(atom, m.mk_bool_val(is_pos)); + return res; + } + + return is_pos ? res : mk_not(res); + } + }; + + class dt_solve_plugin : public solve_plugin { + datatype_util dt; + public: + dt_solve_plugin(ast_manager& m, is_variable_proc& is_var): + solve_plugin(m, m.get_family_id("datatype"), is_var), + dt(m) {} + + expr_ref solve(expr *atom, bool is_pos) override { + expr_ref res(atom, m); + expr* lhs = nullptr, *rhs = nullptr; + if (m.is_eq(atom, lhs, rhs)) { + if (dt.is_constructor(rhs)) { + std::swap(lhs, rhs); + } + if (dt.is_constructor(lhs) && dt.is_constructor(rhs)) { + app* l = to_app(lhs), *r = to_app(rhs); + if (l->get_decl() == r->get_decl()) { + expr_ref_vector eqs(m); + for (unsigned i = 0, sz = l->get_num_args(); i < sz; ++i) { + eqs.push_back(m.mk_eq(l->get_arg(i), r->get_arg(i))); + } + res = mk_and(eqs); + } + else { + res = m.mk_false(); + } + } + else if (dt.is_constructor(lhs)) { + app* l = to_app(lhs); + func_decl* d = l->get_decl(); + expr_ref_vector conjs(m); + conjs.push_back(dt.mk_is(d, rhs)); + ptr_vector const& acc = *dt.get_constructor_accessors(d); + for (unsigned i = 0; i < acc.size(); ++i) { + conjs.push_back(m.mk_eq(l->get_arg(i), m.mk_app(acc[i], rhs))); + } + res = mk_and(conjs); + } + } + // TBD: can also solve for is_nil(x) by x = nil + // + return is_pos ? res : mk_not(res); + } + }; + + class bv_solve_plugin : public solve_plugin { + public: + bv_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("bv"), is_var) {} + expr_ref solve(expr *atom, bool is_pos) override { + expr_ref res(atom, m); + return is_pos ? res : mk_not(res); + } + }; + + class array_solve_plugin : public solve_plugin { + public: + array_solve_plugin(ast_manager& m, is_variable_proc& is_var): solve_plugin(m, m.get_family_id("array"), is_var) {} + expr_ref solve(expr *atom, bool is_pos) override { + expr_ref res(atom, m); + return is_pos ? res : mk_not(res); + } + }; + + solve_plugin* mk_basic_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(basic_solve_plugin, m, is_var); + } + + solve_plugin* mk_arith_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(arith_solve_plugin, m, is_var); + } + + solve_plugin* mk_dt_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(dt_solve_plugin, m, is_var); + } + +#if 0 + solve_plugin* mk_bv_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(bv_solve_plugin, m, is_var); + } + + solve_plugin* mk_array_solve_plugin(ast_manager& m, is_variable_proc& is_var) { + return alloc(array_solve_plugin, m, is_var); + } +#endif + +} diff --git a/src/qe/qe_solve_plugin.h b/src/qe/qe_solve_plugin.h new file mode 100644 index 000000000..571d568f1 --- /dev/null +++ b/src/qe/qe_solve_plugin.h @@ -0,0 +1,54 @@ +/** +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + qe_solve.h + +Abstract: + + Light-weight variable solving plugin model for qe-lite and term_graph. + +Author: + + Nikolaj Bjorner (nbjorner), Arie Gurfinkel 2018-6-8 + +Revision History: + + +--*/ + +#pragma once + +#include "ast/ast.h" +#include "util/plugin_manager.h" +#include "qe/qe_vartest.h" + +namespace qe { + + class solve_plugin { + protected: + ast_manager& m; + family_id m_id; + is_variable_proc& m_is_var; + + virtual expr_ref solve(expr* atom, bool is_pos) = 0; + bool is_variable(expr* e) const { return m_is_var(e); } + public: + solve_plugin(ast_manager& m, family_id fid, is_variable_proc& is_var) : m(m), m_id(fid), m_is_var(is_var) {} + virtual ~solve_plugin() {} + family_id get_family_id() const { return m_id; } + /// Process (and potentially augment) a literal + expr_ref operator() (expr *lit); + }; + + solve_plugin* mk_basic_solve_plugin(ast_manager& m, is_variable_proc& is_var); + + solve_plugin* mk_arith_solve_plugin(ast_manager& m, is_variable_proc& is_var); + + solve_plugin* mk_dt_solve_plugin(ast_manager& m, is_variable_proc& is_var); + + // solve_plugin* mk_bv_solve_plugin(ast_manager& m, is_variable_proc& is_var); + + // solve_plugin* mk_array_solve_plugin(ast_manager& m, is_variable_proc& is_var); +} diff --git a/src/qe/qe_tactic.cpp b/src/qe/qe_tactic.cpp index 5d1a74813..a4e04485b 100644 --- a/src/qe/qe_tactic.cpp +++ b/src/qe/qe_tactic.cpp @@ -17,7 +17,6 @@ Revision History: --*/ #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" #include "util/cooperate.h" #include "qe/qe.h" @@ -51,12 +50,8 @@ class qe_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("qe", *g); m_fparams.m_model = g->models_enabled(); proof_ref new_pr(m); @@ -72,7 +67,7 @@ class qe_tactic : public tactic { if (!has_quantifiers(f)) continue; m_qe(m.mk_true(), f, new_f); - new_pr = 0; + new_pr = nullptr; if (produce_proofs) { new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } @@ -102,46 +97,43 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(qe_tactic, m, m_params); } - virtual ~qe_tactic() { + ~qe_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("qe_nonlinear", CPK_BOOL, "(default: false) enable virtual term substitution."); m_imp->collect_param_descrs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); m_st.reset(); m_imp->collect_statistics(m_st); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.copy(m_st); } - virtual void reset_statistics() { + void reset_statistics() override { m_st.reset(); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m; dealloc(m_imp); m_imp = alloc(imp, m, m_params); diff --git a/src/qe/qe_term_graph.cpp b/src/qe/qe_term_graph.cpp new file mode 100644 index 000000000..510868366 --- /dev/null +++ b/src/qe/qe_term_graph.cpp @@ -0,0 +1,873 @@ +/**++ +Copyright (c) Arie Gurfinkel + +Module Name: + + qe_term_graph.cpp + +Abstract: + + Equivalence graph of terms + +Author: + + Arie Gurfinkel + +Notes: + +--*/ + +#include "util/util.h" +#include "util/uint_set.h" +#include "ast/ast_pp.h" +#include "ast/ast_util.h" +#include "ast/for_each_expr.h" +#include "ast/occurs.h" +#include "qe/qe_term_graph.h" +#include "model/model_evaluator.h" + +namespace qe { + + static expr_ref mk_neq(ast_manager &m, expr *e1, expr *e2) { + expr *t = nullptr; + // x != !x == true + if ((m.is_not(e1, t) && t == e2) || (m.is_not(e2, t) && t == e1)) + return expr_ref(m.mk_true(), m); + else if (m.are_distinct(e1, e2)) + return expr_ref(m.mk_true(), m); + return expr_ref(m.mk_not(m.mk_eq(e1, e2)), m); + } + + namespace { + struct sort_lt_proc { + bool operator()(const expr* a, const expr *b) const { + return get_sort(a)->get_id() < get_sort(b)->get_id(); + } + }; + } + + namespace is_pure_ns { + struct found{}; + struct proc { + is_variable_proc &m_is_var; + proc(is_variable_proc &is_var) : m_is_var(is_var) {} + void operator()(var *n) const {if (m_is_var(n)) throw found();} + void operator()(app const *n) const {if (m_is_var(n)) throw found();} + void operator()(quantifier *n) const {} + }; + } + + bool is_pure(is_variable_proc &is_var, expr *e) { + try { + is_pure_ns::proc v(is_var); + quick_for_each_expr(v, e); + } + catch (is_pure_ns::found) { + return false; + } + return true; + } + + class term { + // -- an app represented by this term + expr_ref m_expr; // NSB: to make usable with exprs + // -- root of the equivalence class + term* m_root; + // -- next element in the equivalence class (cyclic linked list) + term* m_next; + // -- eq class size + unsigned m_class_size; + // -- general purpose mark + unsigned m_mark:1; + // -- general purpose second mark + unsigned m_mark2:1; + // -- is an interpreted constant + unsigned m_interpreted:1; + + // -- terms that contain this term as a child + ptr_vector m_parents; + + // arguments of term. + ptr_vector m_children; + + public: + term(expr_ref const& v, u_map& app2term) : + m_expr(v), + m_root(this), + m_next(this), + m_class_size(1), + m_mark(false), + m_mark2(false), + m_interpreted(false) { + if (!is_app()) return; + for (expr* e : *to_app(m_expr)) { + term* t = app2term[e->get_id()]; + t->get_root().m_parents.push_back(this); + m_children.push_back(t); + } + } + + ~term() {} + + class parents { + term const& t; + public: + parents(term const& _t):t(_t) {} + parents(term const* _t):t(*_t) {} + ptr_vector::const_iterator begin() const { return t.m_parents.begin(); } + ptr_vector::const_iterator end() const { return t.m_parents.end(); } + }; + + class children { + term const& t; + public: + children(term const& _t):t(_t) {} + children(term const* _t):t(*_t) {} + ptr_vector::const_iterator begin() const { return t.m_children.begin(); } + ptr_vector::const_iterator end() const { return t.m_children.end(); } + }; + + // Congruence table hash function is based on + // roots of children and function declaration. + + unsigned get_hash() const { + unsigned a, b, c; + a = b = c = get_decl_id(); + for (term * ch : children(this)) { + a = ch->get_root().get_id(); + mix(a, b, c); + } + return c; + } + + static bool cg_eq(term const * t1, term const * t2) { + if (t1->get_decl_id() != t2->get_decl_id()) return false; + if (t1->m_children.size() != t2->m_children.size()) return false; + for (unsigned i = 0, sz = t1->m_children.size(); i < sz; ++ i) { + if (t1->m_children[i]->get_root().get_id() != t2->m_children[i]->get_root().get_id()) return false; + } + return true; + } + + unsigned get_id() const { return m_expr->get_id();} + + unsigned get_decl_id() const { return is_app() ? get_app()->get_decl()->get_id() : m_expr->get_id(); } + + bool is_marked() const {return m_mark;} + void set_mark(bool v){m_mark = v;} + bool is_marked2() const {return m_mark2;} // NSB: where is this used? + void set_mark2(bool v){m_mark2 = v;} // NSB: where is this used? + + bool is_interpreted() const {return m_interpreted;} + bool is_theory() const { return !is_app() || get_app()->get_family_id() != null_family_id; } + void mark_as_interpreted() {m_interpreted=true;} + expr* get_expr() const {return m_expr;} + bool is_app() const {return ::is_app(m_expr);} + app *get_app() const {return is_app() ? to_app(m_expr) : nullptr;} + unsigned get_num_args() const { return is_app() ? get_app()->get_num_args() : 0; } + + term &get_root() const {return *m_root;} + bool is_root() const {return m_root == this;} + void set_root(term &r) {m_root = &r;} + term &get_next() const {return *m_next;} + void add_parent(term* p) { m_parents.push_back(p); } + + unsigned get_class_size() const {return m_class_size;} + + void merge_eq_class(term &b) { + std::swap(this->m_next, b.m_next); + m_class_size += b.get_class_size(); + // -- reset (useful for debugging) + b.m_class_size = 0; + } + + // -- make this term the root of its equivalence class + void mk_root() { + if (is_root()) return; + + term *curr = this; + do { + if (curr->is_root()) { + // found previous root + SASSERT(curr != this); + m_class_size = curr->get_class_size(); + curr->m_class_size = 0; + } + curr->set_root(*this); + curr = &curr->get_next(); + } + while (curr != this); + } + }; + + + bool term_graph::is_variable_proc::operator()(const expr * e) const { + if (!is_app(e)) return false; + const app *a = ::to_app(e); + return + a->get_family_id() == null_family_id && + !m_solved.contains(a->get_decl()) && + m_exclude == m_decls.contains(a->get_decl()); + } + + bool term_graph::is_variable_proc::operator()(const term &t) const { + return (*this)(t.get_app()); + } + + void term_graph::is_variable_proc::set_decls(const func_decl_ref_vector &decls, bool exclude) { + reset(); + m_exclude = exclude; + for (auto *d : decls) m_decls.insert(d); + } + void term_graph::is_variable_proc::mark_solved(const expr *e) { + if ((*this)(e) && is_app(e)) + m_solved.insert(::to_app(e)->get_decl()); + } + + + unsigned term_graph::term_hash::operator()(term const* t) const { return t->get_hash(); } + + bool term_graph::term_eq::operator()(term const* a, term const* b) const { return term::cg_eq(a, b); } + + term_graph::term_graph(ast_manager &man) : m(man), m_lits(m), m_pinned(m) { + m_plugins.register_plugin(mk_basic_solve_plugin(m, m_is_var)); + m_plugins.register_plugin(mk_arith_solve_plugin(m, m_is_var)); + } + + term_graph::~term_graph() { + reset(); + } + + bool term_graph::is_pure_def(expr *atom, expr*& v) { + expr *e = nullptr; + return m.is_eq(atom, v, e) && m_is_var(v) && is_pure(m_is_var, e); + } + + static family_id get_family_id(ast_manager &m, expr *lit) { + if (m.is_not(lit, lit)) + return get_family_id(m, lit); + + expr *a = nullptr, *b = nullptr; + // deal with equality using sort of range + if (m.is_eq (lit, a, b)) { + return get_sort (a)->get_family_id(); + } + // extract family_id of top level app + else if (is_app(lit)) { + return to_app(lit)->get_decl()->get_family_id(); + } + else { + return null_family_id; + } + } + void term_graph::add_lit(expr *l) { + expr_ref lit(m); + expr_ref_vector lits(m); + lits.push_back(l); + for (unsigned i = 0; i < lits.size(); ++i) { + l = lits.get(i); + family_id fid = get_family_id(m, l); + qe::solve_plugin *pin = m_plugins.get_plugin(fid); + lit = pin ? (*pin)(l) : l; + if (m.is_and(lit)) { + lits.append(::to_app(lit)->get_num_args(), ::to_app(lit)->get_args()); + } + else { + m_lits.push_back(lit); + internalize_lit(lit); + } + } + } + + bool term_graph::is_internalized(expr *a) { + return m_app2term.contains(a->get_id()); + } + + term* term_graph::get_term(expr *a) { + term *res; + return m_app2term.find (a->get_id(), res) ? res : nullptr; + } + + term *term_graph::mk_term(expr *a) { + expr_ref e(a, m); + term * t = alloc(term, e, m_app2term); + if (t->get_num_args() == 0 && m.is_unique_value(a)){ + t->mark_as_interpreted(); + } + + m_terms.push_back(t); + m_app2term.insert(a->get_id(), t); + return t; + } + + term* term_graph::internalize_term(expr *t) { + term* res = get_term(t); + if (res) return res; + ptr_buffer todo; + todo.push_back(t); + while (!todo.empty()) { + t = todo.back(); + res = get_term(t); + if (res) { + todo.pop_back(); + continue; + } + unsigned sz = todo.size(); + if (is_app(t)) { + for (expr * arg : *::to_app(t)) { + if (!get_term(arg)) + todo.push_back(arg); + } + } + if (sz < todo.size()) continue; + todo.pop_back(); + res = mk_term(t); + } + SASSERT(res); + return res; + } + + void term_graph::internalize_eq(expr *a1, expr* a2) { + SASSERT(m_merge.empty()); + merge(*internalize_term(a1), *internalize_term(a2)); + merge_flush(); + SASSERT(m_merge.empty()); + } + + void term_graph::internalize_lit(expr* lit) { + expr *e1 = nullptr, *e2 = nullptr, *v = nullptr; + if (m.is_eq (lit, e1, e2)) { + internalize_eq (e1, e2); + } + else { + internalize_term(lit); + } + if (is_pure_def(lit, v)) { + m_is_var.mark_solved(v); + } + } + + void term_graph::merge_flush() { + while (!m_merge.empty()) { + term* t1 = m_merge.back().first; + term* t2 = m_merge.back().second; + m_merge.pop_back(); + merge(*t1, *t2); + } + } + + void term_graph::merge(term &t1, term &t2) { + // -- merge might invalidate term2app cache + m_term2app.reset(); + m_pinned.reset(); + + term *a = &t1.get_root(); + term *b = &t2.get_root(); + + if (a == b) return; + + if (a->get_class_size() > b->get_class_size()) { + std::swap(a, b); + } + + // Remove parents of it from the cg table. + for (term* p : term::parents(b)) { + if (!p->is_marked()) { + p->set_mark(true); + m_cg_table.erase(p); + } + } + // make 'a' be the root of the equivalence class of 'b' + b->set_root(*a); + for (term *it = &b->get_next(); it != b; it = &it->get_next()) { + it->set_root(*a); + } + + // merge equivalence classes + a->merge_eq_class(*b); + + // Insert parents of b's old equilvalence class into the cg table + for (term* p : term::parents(a)) { + if (p->is_marked()) { + term* p_old = m_cg_table.insert_if_not_there(p); + p->set_mark(false); + a->add_parent(p); + // propagate new equalities. + if (p->get_root().get_id() != p_old->get_root().get_id()) { + m_merge.push_back(std::make_pair(p, p_old)); + } + } + } + } + + expr* term_graph::mk_app_core (expr *e) { + if (is_app(e)) { + expr_ref_buffer kids(m); + app* a = ::to_app(e); + for (expr * arg : *a) { + kids.push_back (mk_app(arg)); + } + app* res = m.mk_app(a->get_decl(), a->get_num_args(), kids.c_ptr()); + m_pinned.push_back(res); + return res; + } + else { + return e; + } + } + + expr_ref term_graph::mk_app(term const &r) { + SASSERT(r.is_root()); + + if (r.get_num_args() == 0) { + return expr_ref(r.get_expr(), m); + } + + expr* res = nullptr; + if (m_term2app.find(r.get_id(), res)) { + return expr_ref(res, m); + } + + res = mk_app_core (r.get_app()); + m_term2app.insert(r.get_id(), res); + return expr_ref(res, m); + + } + + expr_ref term_graph::mk_app(expr *a) { + term *t = get_term(a); + if (!t) + return expr_ref(a, m); + else + return mk_app(t->get_root()); + + } + + void term_graph::mk_equalities(term const &t, expr_ref_vector &out) { + SASSERT(t.is_root()); + expr_ref rep(mk_app(t), m); + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + expr* mem = mk_app_core(it->get_app()); + out.push_back (m.mk_eq (rep, mem)); + } + } + + void term_graph::mk_all_equalities(term const &t, expr_ref_vector &out) { + mk_equalities(t, out); + + for (term *it = &t.get_next(); it != &t; it = &it->get_next ()) { + expr* a1 = mk_app_core (it->get_app()); + for (term *it2 = &it->get_next(); it2 != &t; it2 = &it2->get_next()) { + expr* a2 = mk_app_core(it2->get_app()); + out.push_back (m.mk_eq (a1, a2)); + } + } + } + + void term_graph::reset_marks() { + for (term * t : m_terms) { + t->set_mark(false); + } + } + + /// Order of preference for roots of equivalence classes + /// XXX This should be factored out to let clients control the preference + bool term_graph::term_lt(term const &t1, term const &t2) { + + // prefer constants over applications + // prefer uninterpreted constants over values + // prefer smaller expressions over larger ones + if (t1.get_num_args() == 0 || t2.get_num_args() == 0) { + if (t1.get_num_args() == t2.get_num_args()) { + // t1.get_num_args() == t2.get_num_args() == 0 + if (m.is_value(t1.get_expr()) == m.is_value(t2.get_expr())) + return t1.get_id() < t2.get_id(); + return m.is_value(t2.get_expr()); + } + return t1.get_num_args() < t2.get_num_args(); + } + + unsigned sz1 = get_num_exprs(t1.get_expr()); + unsigned sz2 = get_num_exprs(t1.get_expr()); + return sz1 < sz2; + } + + void term_graph::pick_root (term &t) { + term *r = &t; + for (term *it = &t.get_next(); it != &t; it = &it->get_next()) { + it->set_mark(true); + if (term_lt(*it, *r)) { r = it; } + } + + // -- if found something better, make it the new root + if (r != &t) { + r->mk_root(); + } + } + + /// Choose better roots for equivalence classes + void term_graph::pick_roots() { + for (term* t : m_terms) { + if (!t->is_marked() && t->is_root()) + pick_root(*t); + } + reset_marks(); + } + + void term_graph::display(std::ostream &out) { + for (term * t : m_terms) { + out << mk_pp(t->get_expr(), m) << " is root " << t->is_root() + << " cls sz " << t->get_class_size() + << " term " << t + << "\n"; + } + } + + void term_graph::to_lits (expr_ref_vector &lits, bool all_equalities) { + pick_roots(); + + for (expr * a : m_lits) { + if (is_internalized(a)) { + lits.push_back (::to_app(mk_app(a))); + } + } + + for (term * t : m_terms) { + if (!t->is_root()) + continue; + else if (all_equalities) + mk_all_equalities (*t, lits); + else + mk_equalities(*t, lits); + } + } + + expr_ref term_graph::to_app() { + expr_ref_vector lits(m); + to_lits(lits); + return mk_and(lits); + } + + void term_graph::reset() { + m_term2app.reset(); + m_pinned.reset(); + m_app2term.reset(); + std::for_each(m_terms.begin(), m_terms.end(), delete_proc()); + m_terms.reset(); + m_lits.reset(); + m_cg_table.reset(); + } + + class term_graph::projector { + term_graph &m_tg; + ast_manager &m; + u_map m_term2app; + u_map m_root2rep; + + model_ref m_model; + expr_ref_vector m_pinned; // tracks expr in the maps + + expr* mk_pure(term const& t) { + expr* e = nullptr; + if (m_term2app.find(t.get_id(), e)) return e; + e = t.get_expr(); + if (!is_app(e)) return nullptr; + app* a = ::to_app(e); + expr_ref_buffer kids(m); + for (term* ch : term::children(t)) { + if (!m_root2rep.find(ch->get_root().get_id(), e)) return nullptr; + kids.push_back(e); + } + expr* pure = m.mk_app(a->get_decl(), kids.size(), kids.c_ptr()); + m_pinned.push_back(pure); + m_term2app.insert(t.get_id(), pure); + return pure; + } + + + bool is_better_rep(expr *t1, expr *t2) { + if (!t2) return t1 != nullptr; + return m.is_unique_value(t1) && !m.is_unique_value(t2); + } + + void purify() { + // - propagate representatives up over parents. + // use work-list + marking to propagate. + // - produce equalities over represented classes. + // - produce other literals over represented classes + // (walk disequalities in m_lits and represent + // lhs/rhs over decls or excluding decls) + + ptr_vector worklist; + for (term * t : m_tg.m_terms) { + worklist.push_back(t); + t->set_mark(true); + } + + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; + if (!t->is_theory() && is_projected(*t)) + continue; + + expr* pure = mk_pure(*t); + if (!pure) continue; + + m_term2app.insert(t->get_id(), pure); + expr* rep = nullptr; + // ensure that the root has a representative + m_root2rep.find(t->get_root().get_id(), rep); + + // update rep with pure if it is better + if (pure != rep && is_better_rep(pure, rep)) { + m_root2rep.insert(t->get_root().get_id(), pure); + for (term * p : term::parents(t->get_root())) { + m_term2app.remove(p->get_id()); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); + } + } + } + } + + // Here we could also walk equivalence classes that + // contain interpreted values by sort and extract + // disequalities bewteen non-unique value + // representatives. these disequalities are implied + // and can be mined using other means, such as theory + // aware core minimization + m_tg.reset_marks(); + } + + void solve_core() { + ptr_vector worklist; + for (term * t : m_tg.m_terms) { + // skip pure terms + if (m_term2app.contains(t->get_id())) continue; + worklist.push_back(t); + t->set_mark(true); + } + + while (!worklist.empty()) { + term* t = worklist.back(); + worklist.pop_back(); + t->set_mark(false); + if (m_term2app.contains(t->get_id())) + continue; + + expr* pure = mk_pure(*t); + if (!pure) continue; + + m_term2app.insert(t->get_id(), pure); + expr* rep = nullptr; + // ensure that the root has a representative + m_root2rep.find(t->get_root().get_id(), rep); + + if (!rep) { + m_root2rep.insert(t->get_root().get_id(), pure); + for (term * p : term::parents(t->get_root())) { + SASSERT(!m_term2app.contains(p->get_id())); + if (!p->is_marked()) { + p->set_mark(true); + worklist.push_back(p); + } + } + } + } + m_tg.reset_marks(); + } + + bool find_app(term &t, expr *&res) { + return m_root2rep.find(t.get_root().get_id(), res); + } + + bool find_app(expr *lit, expr *&res) { + return m_root2rep.find(m_tg.get_term(lit)->get_root().get_id(), res); + } + + void mk_lits(expr_ref_vector &res) { + expr *e = nullptr; + for (auto *lit : m_tg.m_lits) { + if (!m.is_eq(lit) && find_app(lit, e)) + res.push_back(e); + } + } + + void mk_pure_equalities(const term &t, expr_ref_vector &res) { + SASSERT(t.is_root()); + expr *rep = nullptr; + if (!m_root2rep.find(t.get_id(), rep)) return; + obj_hashtable members; + members.insert(rep); + term const * r = &t; + do { + expr* member = nullptr; + if (m_term2app.find(r->get_id(), member) && !members.contains(member)) { + res.push_back (m.mk_eq (rep, member)); + members.insert(member); + } + r = &r->get_next(); + } + while (r != &t); + } + + bool is_projected(const term &t) {return m_tg.m_is_var(t);} + + void mk_unpure_equalities(const term &t, expr_ref_vector &res) { + expr *rep = nullptr; + if (!m_root2rep.find(t.get_id(), rep)) return; + obj_hashtable members; + members.insert(rep); + term const * r = &t; + do { + expr* member = mk_pure(*r); + SASSERT(member); + if (!members.contains(member) && + (!is_projected(*r) || !is_solved_eq(rep, member))) { + res.push_back(m.mk_eq(rep, member)); + members.insert(member); + } + r = &r->get_next(); + } + while (r != &t); + } + + void mk_equalities(bool pure, expr_ref_vector &res) { + for (term *t : m_tg.m_terms) { + if (!t->is_root()) continue; + if (!m_root2rep.contains(t->get_id())) continue; + if (pure) + mk_pure_equalities(*t, res); + else + mk_unpure_equalities(*t, res); + } + } + + void mk_pure_equalities(expr_ref_vector &res) { + return mk_equalities(true, res); + } + + void mk_unpure_equalities(expr_ref_vector &res) { + return mk_equalities(false, res); + } + + // TBD: generalize for also the case of a (:var n) + bool is_solved_eq(expr *lhs, expr* rhs) { + return is_uninterp_const(rhs) && !occurs(rhs, lhs); + } + + /// Add equalities and disequalities for all pure representatives + /// based on their equivalence in the model + void model_complete(expr_ref_vector &res) { + if (!m_model) return; + obj_map val2rep; + model_evaluator mev(*m_model); + for (auto &kv : m_root2rep) { + expr *rep = kv.m_value; + expr_ref val(m); + expr *u = nullptr; + if (!mev.eval(rep, val)) continue; + if (val2rep.find(val, u)) { + res.push_back(m.mk_eq(u, rep)); + } + else { + val2rep.insert(val, rep); + } + } + + // TBD: optimize further based on implied values (e.g., + // some literals are forced to be true/false) and based on + // unique_values (e.g., (x=1 & y=1) does not require + // (x!=y) to be added + ptr_buffer reps; + for (auto &kv : val2rep) { + expr *rep = kv.m_value; + if (!m.is_unique_value(rep)) + reps.push_back(kv.m_value); + } + + if (reps.size() <= 1) return; + + // -- sort representatives, call mk_distinct on any range + // -- of the same sort longer than 1 + std::sort(reps.c_ptr(), reps.c_ptr() + reps.size(), sort_lt_proc()); + unsigned i = 0; + unsigned sz = reps.size(); + while (i < sz) { + sort* last_sort = get_sort(reps.get(i)); + unsigned j = i + 1; + while (j < sz && last_sort == get_sort(reps.get(j))) {++j;} + if (j - i == 2) { + expr_ref d(m); + d = mk_neq(m, reps.get(i), reps.get(i+1)); + if (!m.is_true(d)) res.push_back(d); + } + else if (j - i > 2) + res.push_back(m.mk_distinct(j - i, reps.c_ptr() + i)); + i = j; + } + TRACE("qe", tout << "after distinct: " << res << "\n";); + } + + public: + projector(term_graph &tg) : m_tg(tg), m(m_tg.m), m_pinned(m) {} + + void set_model(model &mdl) { m_model = &mdl; } + + void reset() { + m_tg.reset_marks(); + m_term2app.reset(); + m_root2rep.reset(); + m_pinned.reset(); + m_model.reset(); + } + expr_ref_vector project() { + expr_ref_vector res(m); + purify(); + mk_lits(res); + mk_pure_equalities(res); + model_complete(res); + reset(); + return res; + } + expr_ref_vector solve() { + expr_ref_vector res(m); + purify(); + solve_core(); + mk_lits(res); + mk_unpure_equalities(res); + reset(); + return res; + } + }; + + void term_graph::set_vars(func_decl_ref_vector const& decls, bool exclude) { + m_is_var.set_decls(decls, exclude); + } + + expr_ref_vector term_graph::project() { + // reset solved vars so that they are not considered pure by projector + m_is_var.reset_solved(); + term_graph::projector p(*this); + return p.project(); + } + + expr_ref_vector term_graph::project(model &mdl) { + m_is_var.reset_solved(); + term_graph::projector p(*this); + p.set_model(mdl); + return p.project(); + } + + expr_ref_vector term_graph::solve() { + // reset solved vars so that they are not considered pure by projector + m_is_var.reset_solved(); + term_graph::projector p(*this); + return p.solve(); + } + +} diff --git a/src/qe/qe_term_graph.h b/src/qe/qe_term_graph.h new file mode 100644 index 000000000..19b694a76 --- /dev/null +++ b/src/qe/qe_term_graph.h @@ -0,0 +1,119 @@ +/**++ +Copyright (c) Arie Gurfinkel + +Module Name: + + qe_term_graph.h + +Abstract: + + Equivalence graph of terms + +Author: + + Arie Gurfinkel + +Notes: + +--*/ +#ifndef QE_TERM_GRAPH_H__ +#define QE_TERM_GRAPH_H__ + +#include "ast/ast.h" +#include "util/plugin_manager.h" +#include "qe/qe_solve_plugin.h" +#include "qe/qe_vartest.h" +#include "model/model.h" + +namespace qe { + + class term; + + class term_graph { + class projector; + + class is_variable_proc : public ::is_variable_proc { + bool m_exclude; + obj_hashtable m_decls, m_solved; + public: + bool operator()(const expr *e) const override; + bool operator()(const term &t) const; + + void set_decls(const func_decl_ref_vector &decls, bool exclude); + void mark_solved(const expr *e); + void reset_solved() {m_solved.reset();} + void reset() {m_decls.reset(); m_solved.reset(); m_exclude = true;} + }; + + struct term_hash { unsigned operator()(term const* t) const; }; + struct term_eq { bool operator()(term const* a, term const* b) const; }; + ast_manager & m; + ptr_vector m_terms; + expr_ref_vector m_lits; // NSB: expr_ref_vector? + u_map m_app2term; + ast_ref_vector m_pinned; + u_map m_term2app; + plugin_manager m_plugins; + ptr_hashtable m_cg_table; + vector> m_merge; + + term_graph::is_variable_proc m_is_var; + void merge(term &t1, term &t2); + void merge_flush(); + + term *mk_term(expr *t); + term *get_term(expr *t); + + term *internalize_term(expr *t); + void internalize_eq(expr *a1, expr *a2); + void internalize_lit(expr *lit); + + bool is_internalized(expr *a); + + bool term_lt(term const &t1, term const &t2); + void pick_root (term &t); + void pick_roots(); + + void reset_marks(); + + expr* mk_app_core(expr* a); + expr_ref mk_app(term const &t); + expr* mk_pure(term& t); + expr_ref mk_app(expr *a); + void mk_equalities(term const &t, expr_ref_vector &out); + void mk_all_equalities(term const &t, expr_ref_vector &out); + void display(std::ostream &out); + + bool is_pure_def(expr* atom, expr *& v); + + public: + term_graph(ast_manager &m); + ~term_graph(); + + void set_vars(func_decl_ref_vector const& decls, bool exclude); + + ast_manager& get_ast_manager() const { return m;} + + void add_lit(expr *lit); + void add_lits(expr_ref_vector const &lits) { for (expr* e : lits) add_lit(e); } + void add_eq(expr* a, expr* b) { internalize_eq(a, b); } + + void reset(); + + // deprecate? + void to_lits(expr_ref_vector &lits, bool all_equalities = false); + expr_ref to_app(); + + /** + * Return literals obtained by projecting added literals + * onto the vocabulary of decls (if exclude is false) or outside the + * vocabulary of decls (if exclude is true). + */ + expr_ref_vector project(); + expr_ref_vector solve(); + expr_ref_vector project(model &mdl); + + }; + +} +#endif diff --git a/src/qe/qe_vartest.h b/src/qe/qe_vartest.h index 047964825..52609893f 100644 --- a/src/qe/qe_vartest.h +++ b/src/qe/qe_vartest.h @@ -22,9 +22,10 @@ Revision History: #include "ast/ast.h" #include "util/uint_set.h" -class is_variable_proc { +// TBD: move under qe namespace +class is_variable_proc : public std::unary_function { public: - virtual bool operator()(expr* e) const = 0; + virtual bool operator()(const expr* e) const = 0; }; class is_variable_test : public is_variable_proc { @@ -42,7 +43,7 @@ public: m_num_decls(num_decls), m_var_kind(BY_NUM_DECLS) {} - virtual bool operator()(expr* e) const { + bool operator()(const expr* e) const override { if (!is_var(e)) { return false; } diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 2419d1c77..a2e8fd8b1 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -44,11 +44,11 @@ namespace qe { m(m), m_asms(m), m_trail(m), - m_fmc(alloc(filter_model_converter, m)) + m_fmc(alloc(generic_model_converter, m, "qsat")) { } - filter_model_converter* pred_abs::fmc() { + generic_model_converter* pred_abs::fmc() { return m_fmc.get(); } @@ -282,7 +282,7 @@ namespace qe { app_ref pred_abs::fresh_bool(char const* name) { app_ref r(m.mk_fresh_const(name, m.mk_bool_sort()), m); - m_fmc->insert(r->get_decl()); + m_fmc->hide(r); return r; } @@ -516,8 +516,8 @@ namespace qe { expr_ref val_a(m), val_b(m); expr* a = it->m_key; expr* b = it->m_value; - VERIFY(model.eval(a, val_a)); - VERIFY(model.eval(b, val_b)); + val_a = model(a); + val_b = model(b); if (val_a != val_b) { TRACE("qe", tout << mk_pp(a, m) << " := " << val_a << "\n"; @@ -696,7 +696,7 @@ namespace qe { m_level -= num_scopes; } - void reset() { + void reset() override { m_st.reset(); m_fa.s().collect_statistics(m_st); m_ex.s().collect_statistics(m_st); @@ -706,7 +706,7 @@ namespace qe { m_asms.reset(); m_pred_abs.reset(); m_vars.reset(); - m_model = 0; + m_model = nullptr; m_fa.reset(); m_ex.reset(); m_free_vars.reset(); @@ -750,9 +750,7 @@ namespace qe { } void filter_vars(app_ref_vector const& vars) { - for (unsigned i = 0; i < vars.size(); ++i) { - m_pred_abs.fmc()->insert(vars[i]->get_decl()); - } + for (app* v : vars) m_pred_abs.fmc()->hide(v); } void initialize_levels() { @@ -956,7 +954,7 @@ namespace qe { ptr_vector todo; trail.push_back(fml); todo.push_back(fml); - expr* e = 0, *r = 0; + expr* e = nullptr, *r = nullptr; while (!todo.empty()) { check_cancel(); @@ -1062,11 +1060,9 @@ namespace qe { } bool validate_assumptions(model& mdl, expr_ref_vector const& core) { - for (unsigned i = 0; i < core.size(); ++i) { - expr_ref val(m); - VERIFY(mdl.eval(core[i], val)); - if (!m.is_true(val)) { - TRACE("qe", tout << "component of core is not true: " << mk_pp(core[i], m) << "\n";); + for (expr* c : core) { + if (!mdl.is_true(c)) { + TRACE("qe", tout << "component of core is not true: " << mk_pp(c, m) << "\n";); return false; } } @@ -1113,14 +1109,10 @@ namespace qe { bool validate_model(model& mdl, unsigned sz, expr* const* fmls) { expr_ref val(m); for (unsigned i = 0; i < sz; ++i) { - if (!m_model->eval(fmls[i], val) && !m.canceled()) { - TRACE("qe", tout << "Formula does not evaluate in model: " << mk_pp(fmls[i], m) << "\n";); + if (!m_model->is_true(fmls[i]) && !m.canceled()) { + TRACE("qe", tout << "Formula does not evaluate to true in model: " << mk_pp(fmls[i], m) << "\n";); return false; } - if (!m.is_true(val)) { - TRACE("qe", tout << mk_pp(fmls[i], m) << " evaluates to " << val << " in model\n";); - return false; - } } return true; } @@ -1196,38 +1188,32 @@ namespace qe { m_mode(mode), m_avars(m), m_free_vars(m), - m_objective(0), - m_value(0), + m_objective(nullptr), + m_value(nullptr), m_was_sat(false), m_gt(m) { reset(); } - virtual ~qsat() { + ~qsat() override { reset(); } - void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { } - void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { } void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) { + /* out */ goal_ref_buffer & result) override { tactic_report report("qsat-tactic", *in); ptr_vector fmls; expr_ref_vector defs(m); expr_ref fml(m); - mc = 0; pc = 0; core = 0; in->get_formulas(fmls); - - fml = mk_and(m, fmls.size(), fmls.c_ptr()); // for now: @@ -1277,8 +1263,10 @@ namespace qe { in->inc_depth(); result.push_back(in.get()); if (in->models_enabled()) { - mc = model2model_converter(m_model_save.get()); + model_converter_ref mc; + mc = model2model_converter(m_model.get()); mc = concat(m_pred_abs.fmc(), mc.get()); + in->add(mc.get()); } break; case l_undef: @@ -1291,7 +1279,7 @@ namespace qe { } } - void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.copy(m_st); m_fa.s().collect_statistics(st); m_ex.s().collect_statistics(st); @@ -1300,23 +1288,23 @@ namespace qe { m_pred_abs.collect_statistics(st); } - void reset_statistics() { + void reset_statistics() override { m_stats.reset(); m_fa.reset(); m_ex.reset(); } - void cleanup() { + void cleanup() override { reset(); } - void set_logic(symbol const & l) { + void set_logic(symbol const & l) override { } - void set_progress_callback(progress_callback * callback) { + void set_progress_callback(progress_callback * callback) override { } - tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(qsat, m, m_params, m_mode); } diff --git a/src/qe/qsat.h b/src/qe/qsat.h index 85a8181d6..dca7cc00b 100644 --- a/src/qe/qsat.h +++ b/src/qe/qsat.h @@ -22,7 +22,7 @@ Revision History: #define QE_QSAT_H__ #include "tactic/tactic.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "qe/qe_mbp.h" namespace qe { @@ -72,7 +72,7 @@ namespace qe { obj_map m_asm2pred; // maintain map from assumptions to predicates obj_map m_pred2asm; // predicates |-> assumptions expr_ref_vector m_trail; - filter_model_converter_ref m_fmc; + generic_model_converter_ref m_fmc; ptr_vector todo; obj_map m_elevel; obj_map m_flevel; @@ -91,7 +91,7 @@ namespace qe { public: pred_abs(ast_manager& m); - filter_model_converter* fmc(); + generic_model_converter* fmc(); void reset(); max_level compute_level(app* e); void push(); diff --git a/src/sat/CMakeLists.txt b/src/sat/CMakeLists.txt index d88a73708..320a674a2 100644 --- a/src/sat/CMakeLists.txt +++ b/src/sat/CMakeLists.txt @@ -1,22 +1,30 @@ z3_add_component(sat SOURCES + ba_solver.cpp dimacs.cpp sat_asymm_branch.cpp + sat_bdd.cpp + sat_big.cpp sat_clause.cpp sat_clause_set.cpp sat_clause_use_list.cpp sat_cleaner.cpp sat_config.cpp + sat_drat.cpp sat_elim_eqs.cpp + sat_elim_vars.cpp sat_iff3_finder.cpp sat_integrity_checker.cpp + sat_local_search.cpp + sat_lookahead.cpp sat_model_converter.cpp sat_mus.cpp - sat_par.cpp + sat_parallel.cpp sat_probing.cpp sat_scc.cpp sat_simplifier.cpp sat_solver.cpp + sat_unit_walk.cpp sat_watched.cpp COMPONENT_DEPENDENCIES util diff --git a/src/sat/ba_solver.cpp b/src/sat/ba_solver.cpp new file mode 100644 index 000000000..930617301 --- /dev/null +++ b/src/sat/ba_solver.cpp @@ -0,0 +1,4218 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + ba_solver.cpp + +Abstract: + + Extension for cardinality and xr reasoning. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-01-30 + +Revision History: + +--*/ + +#include +#include "sat/ba_solver.h" +#include "sat/sat_types.h" +#include "util/mpz.h" +#include "sat/sat_simplifier_params.hpp" + + +namespace sat { + + static unsigned _bad_id = 11111111; // 2759; // +#define BADLOG(_cmd_) if (p.id() == _bad_id) { _cmd_; } + + ba_solver::card& ba_solver::constraint::to_card() { + SASSERT(is_card()); + return static_cast(*this); + } + + ba_solver::card const& ba_solver::constraint::to_card() const{ + SASSERT(is_card()); + return static_cast(*this); + } + + ba_solver::pb& ba_solver::constraint::to_pb() { + SASSERT(is_pb()); + return static_cast(*this); + } + + ba_solver::pb const& ba_solver::constraint::to_pb() const{ + SASSERT(is_pb()); + return static_cast(*this); + } + + ba_solver::xr& ba_solver::constraint::to_xr() { + SASSERT(is_xr()); + return static_cast(*this); + } + + ba_solver::xr const& ba_solver::constraint::to_xr() const{ + SASSERT(is_xr()); + return static_cast(*this); + } + + std::ostream& operator<<(std::ostream& out, ba_solver::constraint const& cnstr) { + if (cnstr.lit() != null_literal) out << cnstr.lit() << " == "; + switch (cnstr.tag()) { + case ba_solver::card_t: { + ba_solver::card const& c = cnstr.to_card(); + for (literal l : c) { + out << l << " "; + } + out << " >= " << c.k(); + break; + } + case ba_solver::pb_t: { + ba_solver::pb const& p = cnstr.to_pb(); + for (ba_solver::wliteral wl : p) { + if (wl.first != 1) out << wl.first << " * "; + out << wl.second << " "; + } + out << " >= " << p.k(); + break; + } + case ba_solver::xr_t: { + ba_solver::xr const& x = cnstr.to_xr(); + for (unsigned i = 0; i < x.size(); ++i) { + out << x[i] << " "; + if (i + 1 < x.size()) out << "x "; + } + break; + } + default: + UNREACHABLE(); + } + return out; + } + + + // ----------------------- + // pb_base + + bool ba_solver::pb_base::well_formed() const { + uint_set vars; + if (lit() != null_literal) vars.insert(lit().var()); + for (unsigned i = 0; i < size(); ++i) { + bool_var v = get_lit(i).var(); + if (vars.contains(v)) return false; + if (get_coeff(i) > k()) return false; + vars.insert(v); + } + return true; + } + + // ---------------------- + // card + + ba_solver::card::card(unsigned id, literal lit, literal_vector const& lits, unsigned k): + pb_base(card_t, id, lit, lits.size(), get_obj_size(lits.size()), k) { + for (unsigned i = 0; i < size(); ++i) { + m_lits[i] = lits[i]; + } + } + + void ba_solver::card::negate() { + m_lit.neg(); + for (unsigned i = 0; i < m_size; ++i) { + m_lits[i].neg(); + } + m_k = m_size - m_k + 1; + SASSERT(m_size >= m_k && m_k > 0); + } + + bool ba_solver::card::is_watching(literal l) const { + unsigned sz = std::min(k() + 1, size()); + for (unsigned i = 0; i < sz; ++i) { + if ((*this)[i] == l) return true; + } + return false; + } + + // ----------------------------------- + // pb + + ba_solver::pb::pb(unsigned id, literal lit, svector const& wlits, unsigned k): + pb_base(pb_t, id, lit, wlits.size(), get_obj_size(wlits.size()), k), + m_slack(0), + m_num_watch(0), + m_max_sum(0) { + for (unsigned i = 0; i < size(); ++i) { + m_wlits[i] = wlits[i]; + } + update_max_sum(); + } + + void ba_solver::pb::update_max_sum() { + m_max_sum = 0; + for (unsigned i = 0; i < size(); ++i) { + m_wlits[i].first = std::min(k(), m_wlits[i].first); + if (m_max_sum + m_wlits[i].first < m_max_sum) { + throw default_exception("addition of pb coefficients overflows"); + } + m_max_sum += m_wlits[i].first; + } + } + + void ba_solver::pb::negate() { + m_lit.neg(); + unsigned w = 0; + for (unsigned i = 0; i < m_size; ++i) { + m_wlits[i].second.neg(); + VERIFY(w + m_wlits[i].first >= w); + w += m_wlits[i].first; + } + m_k = w - m_k + 1; + VERIFY(w >= m_k && m_k > 0); + } + + bool ba_solver::pb::is_watching(literal l) const { + for (unsigned i = 0; i < m_num_watch; ++i) { + if ((*this)[i].second == l) return true; + } + return false; + } + + + bool ba_solver::pb::is_cardinality() const { + if (size() == 0) return false; + unsigned w = (*this)[0].first; + for (wliteral wl : *this) if (w != wl.first) return false; + return true; + } + + + // ----------------------------------- + // xr + + ba_solver::xr::xr(unsigned id, literal_vector const& lits): + constraint(xr_t, id, null_literal, lits.size(), get_obj_size(lits.size())) { + for (unsigned i = 0; i < size(); ++i) { + m_lits[i] = lits[i]; + } + } + + bool ba_solver::xr::is_watching(literal l) const { + return + l == (*this)[0] || l == (*this)[1] || + ~l == (*this)[0] || ~l == (*this)[1]; + } + + bool ba_solver::xr::well_formed() const { + uint_set vars; + if (lit() != null_literal) vars.insert(lit().var()); + for (literal l : *this) { + bool_var v = l.var(); + if (vars.contains(v)) return false; + vars.insert(v); + } + return true; + } + + // ---------------------------- + // card + + bool ba_solver::init_watch(card& c) { + literal root = c.lit(); + if (root != null_literal && value(root) == l_false) { + clear_watch(c); + c.negate(); + root.neg(); + } + if (root != null_literal) { + if (!is_watched(root, c)) watch_literal(root, c); + if (!c.is_pure() && !is_watched(~root, c)) watch_literal(~root, c); + } + TRACE("ba", display(tout << "init watch: ", c, true);); + SASSERT(root == null_literal || value(root) == l_true); + unsigned j = 0, sz = c.size(), bound = c.k(); + // put the non-false literals into the head. + + if (bound == sz) { + for (literal l : c) assign(c, l); + return false; + } + + for (unsigned i = 0; i < sz; ++i) { + if (value(c[i]) != l_false) { + if (j != i) { + if (c.is_watched() && j <= bound && i > bound) { + unwatch_literal(c[j], c); + watch_literal(c[i], c); + } + c.swap(i, j); + } + ++j; + } + } + DEBUG_CODE( + bool is_false = false; + for (literal l : c) { + SASSERT(!is_false || value(l) == l_false); + is_false = value(l) == l_false; + }); + + // j is the number of non-false, sz - j the number of false. + + if (j < bound) { + if (c.is_watched()) clear_watch(c); + SASSERT(0 < bound && bound < sz); + literal alit = c[j]; + + // + // we need the assignment level of the asserting literal to be maximal. + // such that conflict resolution can use the asserting literal as a starting + // point. + // + + for (unsigned i = bound; i < sz; ++i) { + if (lvl(alit) < lvl(c[i])) { + c.swap(i, j); + alit = c[j]; + } + } + set_conflict(c, alit); + return false; + } + else if (j == bound) { + for (unsigned i = 0; i < bound; ++i) { + assign(c, c[i]); + } + return false; + } + else { + if (c.is_watched()) return true; + clear_watch(c); + for (unsigned i = 0; i <= bound; ++i) { + watch_literal(c[i], c); + } + c.set_watch(); + return true; + } + } + + void ba_solver::clear_watch(card& c) { + if (c.is_clear()) return; + c.clear_watch(); + unsigned sz = std::min(c.k() + 1, c.size()); + for (unsigned i = 0; i < sz; ++i) { + unwatch_literal(c[i], c); + } + } + + // ----------------------- + // constraint + + void ba_solver::set_conflict(constraint& c, literal lit) { + m_stats.m_num_conflicts++; + TRACE("ba", display(tout, c, true); ); + if (!validate_conflict(c)) { + display(std::cout, c, true); + UNREACHABLE(); + } + SASSERT(validate_conflict(c)); + if (c.is_xr() && value(lit) == l_true) lit.neg(); + SASSERT(value(lit) == l_false); + set_conflict(justification::mk_ext_justification(c.index()), ~lit); + SASSERT(inconsistent()); + } + + void ba_solver::assign(constraint& c, literal lit) { + if (inconsistent()) return; + switch (value(lit)) { + case l_true: + break; + case l_false: + set_conflict(c, lit); + break; + default: + m_stats.m_num_propagations++; + m_num_propagations_since_pop++; + //TRACE("ba", tout << "#prop: " << m_stats.m_num_propagations << " - " << c.lit() << " => " << lit << "\n";); + SASSERT(validate_unit_propagation(c, lit)); + if (get_config().m_drat) { + svector ps; + literal_vector lits; + get_antecedents(lit, c, lits); + lits.push_back(lit); + ps.push_back(drat::premise(drat::s_ext(), c.lit())); // null_literal case. + drat_add(lits, ps); + } + assign(lit, justification::mk_ext_justification(c.index())); + break; + } + } + + // ------------------- + // pb_base + + void ba_solver::simplify(pb_base& p) { + SASSERT(s().at_base_lvl()); + if (p.lit() != null_literal && value(p.lit()) == l_false) { + TRACE("ba", tout << "pb: flip sign " << p << "\n";); + IF_VERBOSE(0, verbose_stream() << "sign is flipped " << p << "\n";); + return; + } + bool nullify = p.lit() != null_literal && value(p.lit()) == l_true; + if (nullify) { + IF_VERBOSE(100, display(verbose_stream() << "nullify tracking literal\n", p, true);); + SASSERT(lvl(p.lit()) == 0); + nullify_tracking_literal(p); + init_watch(p); + } + + SASSERT(p.lit() == null_literal || value(p.lit()) != l_false); + + unsigned true_val = 0, slack = 0, num_false = 0; + for (unsigned i = 0; i < p.size(); ++i) { + literal l = p.get_lit(i); + if (s().was_eliminated(l.var())) { + SASSERT(p.learned()); + remove_constraint(p, "contains eliminated"); + return; + } + switch (value(l)) { + case l_true: true_val += p.get_coeff(i); break; + case l_false: ++num_false; break; + default: slack += p.get_coeff(i); break; + } + } + if (p.k() == 1 && p.lit() == null_literal) { + literal_vector lits(p.literals()); + s().mk_clause(lits.size(), lits.c_ptr(), p.learned()); + IF_VERBOSE(100, display(verbose_stream() << "add clause: " << lits << "\n", p, true);); + remove_constraint(p, "implies clause"); + } + else if (true_val == 0 && num_false == 0) { + if (p.lit() == null_literal || value(p.lit()) == l_true) { + init_watch(p); + } + } + else if (true_val >= p.k()) { + if (p.lit() != null_literal) { + IF_VERBOSE(100, display(verbose_stream() << "assign true literal ", p, true);); + s().assign(p.lit(), justification()); + } + remove_constraint(p, "is true"); + } + else if (slack + true_val < p.k()) { + if (p.lit() != null_literal) { + IF_VERBOSE(100, display(verbose_stream() << "assign false literal ", p, true);); + s().assign(~p.lit(), justification()); + } + else { + IF_VERBOSE(0, verbose_stream() << "unsat during simplification\n";); + s().set_conflict(justification()); + } + remove_constraint(p, "is false"); + } + else if (slack + true_val == p.k()) { + literal_vector lits(p.literals()); + assert_unconstrained(p.lit(), lits); + remove_constraint(p, "is tight"); + } + else { + + unsigned sz = p.size(); + clear_watch(p); + unsigned j = 0; + for (unsigned i = 0; i < sz; ++i) { + literal l = p.get_lit(i); + if (value(l) == l_undef) { + if (i != j) p.swap(i, j); + ++j; + } + } + sz = j; + // _bad_id = p.id(); + BADLOG(display(verbose_stream() << "simplify ", p, true)); + + unsigned k = p.k() - true_val; + + if (k == 1 && p.lit() == null_literal) { + literal_vector lits(sz, p.literals().c_ptr()); + s().mk_clause(sz, lits.c_ptr(), p.learned()); + remove_constraint(p, "is clause"); + return; + } + p.set_size(sz); + p.set_k(k); + if (p.lit() == null_literal || value(p.lit()) == l_true) { + init_watch(p); + } + else { + SASSERT(value(p.lit()) == l_undef); + } + BADLOG(display(verbose_stream() << "simplified ", p, true); verbose_stream() << "\n"); + // display(verbose_stream(), c, true); + _bad_id = 11111111; + SASSERT(p.well_formed()); + m_simplify_change = true; + } + } + + /* + \brief split PB constraint into two because root is reused in arguments. + + x <=> a*x + B*y >= k + + x => a*x + By >= k + ~x => a*x + By < k + + k*~x + a*x + By >= k + (B+a-k + 1)*x + a*~x + B*~y >= B + a - k + 1 + + (k - a) * ~x + By >= k - a + k' * x + B'y >= k' + + */ + + void ba_solver::split_root(pb_base& p) { + SASSERT(p.lit() != null_literal); + SASSERT(!p.learned()); + m_weights.resize(2*s().num_vars(), 0); + unsigned k = p.k(); + unsigned w, w1, w2; + literal root = p.lit(); + m_weights[(~root).index()] = k; + for (unsigned i = 0; i < p.size(); ++i) { + m_weights[p.get_lit(i).index()] += p.get_coeff(i); + } + literal_vector lits(p.literals()); + lits.push_back(~root); + + for (literal l : lits) { + w1 = m_weights[l.index()]; + w2 = m_weights[(~l).index()]; + if (w1 >= w2) { + if (w2 >= k) { + for (literal l2 : lits) m_weights[l2.index()] = 0; + // constraint is true + return; + } + k -= w2; + m_weights[(~l).index()] = 0; + m_weights[l.index()] = w1 - w2; + } + } + SASSERT(k > 0); + + // ~root * (k - a) + p >= k - a + + m_wlits.reset(); + for (literal l : lits) { + w = m_weights[l.index()]; + if (w != 0) { + m_wlits.push_back(wliteral(w, l)); + } + m_weights[l.index()] = 0; + } + + add_pb_ge(null_literal, m_wlits, k, false); + } + + + // ------------------- + // pb + + + // watch a prefix of literals, such that the slack of these is >= k + bool ba_solver::init_watch(pb& p) { + clear_watch(p); + if (p.lit() != null_literal && value(p.lit()) == l_false) { + p.negate(); + } + + VERIFY(p.lit() == null_literal || value(p.lit()) == l_true); + unsigned sz = p.size(), bound = p.k(); + + // put the non-false literals into the head. + unsigned slack = 0, slack1 = 0, num_watch = 0, j = 0; + for (unsigned i = 0; i < sz; ++i) { + if (value(p[i].second) != l_false) { + if (j != i) { + p.swap(i, j); + } + if (slack <= bound) { + slack += p[j].first; + ++num_watch; + } + else { + slack1 += p[j].first; + } + ++j; + } + } + BADLOG(verbose_stream() << "watch " << num_watch << " out of " << sz << "\n"); + + DEBUG_CODE( + bool is_false = false; + for (unsigned k = 0; k < sz; ++k) { + SASSERT(!is_false || value(p[k].second) == l_false); + SASSERT((k < j) == (value(p[k].second) != l_false)); + is_false = value(p[k].second) == l_false; + }); + + if (slack < bound) { + literal lit = p[j].second; + VERIFY(value(lit) == l_false); + for (unsigned i = j + 1; i < sz; ++i) { + if (lvl(lit) < lvl(p[i].second)) { + lit = p[i].second; + } + } + set_conflict(p, lit); + return false; + } + else { + for (unsigned i = 0; i < num_watch; ++i) { + watch_literal(p[i], p); + } + p.set_slack(slack); + p.set_num_watch(num_watch); + + SASSERT(validate_watch(p, null_literal)); + + TRACE("ba", display(tout << "init watch: ", p, true);); + + // slack is tight: + if (slack + slack1 == bound) { + SASSERT(slack1 == 0); + SASSERT(j == num_watch); + for (unsigned i = 0; i < j; ++i) { + assign(p, p[i].second); + } + } + return true; + } + } + + /* + Chai Kuhlmann: + Lw - set of watched literals + Lu - set of unwatched literals that are not false + + Lw = Lw \ { alit } + Sw -= value + a_max = max { a | l in Lw u Lu, l = undef } + while (Sw < k + a_max & Lu != 0) { + a_s = max { a | l in Lu } + Sw += a_s + Lw = Lw u {l_s} + Lu = Lu \ {l_s} + } + if (Sw < k) return conflict + for (li in Lw | Sw < k + ai) + assign li + return no-conflict + + a_max index: index of non-false literal with maximal weight. + */ + + void ba_solver::add_index(pb& p, unsigned index, literal lit) { + if (value(lit) == l_undef) { + m_pb_undef.push_back(index); + if (p[index].first > m_a_max) { + m_a_max = p[index].first; + } + } + } + + /* + \brief propagate assignment to alit in constraint p. + + TBD: + - consider reordering literals in watch list so that the search for watched literal takes average shorter time. + - combine with caching literals that are assigned to 'true' to a cold store where they are not being revisited. + Since 'true' literals may be unassigned (unless they are assigned at level 0) the cache has to be backtrack + friendly (and the overhead of backtracking has to be taken into account). + */ + lbool ba_solver::add_assign(pb& p, literal alit) { + BADLOG(display(verbose_stream() << "assign: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true)); + TRACE("ba", display(tout << "assign: " << alit << "\n", p, true);); + SASSERT(!inconsistent()); + unsigned sz = p.size(); + unsigned bound = p.k(); + unsigned num_watch = p.num_watch(); + unsigned slack = p.slack(); + SASSERT(value(alit) == l_false); + SASSERT(p.lit() == null_literal || value(p.lit()) == l_true); + SASSERT(num_watch <= sz); + SASSERT(num_watch > 0); + unsigned index = 0; + m_a_max = 0; + m_pb_undef.reset(); + for (; index < num_watch; ++index) { + literal lit = p[index].second; + if (lit == alit) { + break; + } + add_index(p, index, lit); + } + if (index == num_watch || num_watch == 0) { + _bad_id = p.id(); + BADLOG( + verbose_stream() << "BAD: " << p.id() << "\n"; + display(verbose_stream(), p, true); + verbose_stream() << "alit: " << alit << "\n"; + verbose_stream() << "num watch " << num_watch << "\n"); + UNREACHABLE(); + exit(0); + return l_undef; + } + + SASSERT(validate_watch(p, null_literal)); + // SASSERT(validate_watch(p, null_literal)); + + SASSERT(index < num_watch); + unsigned index1 = index + 1; + for (; m_a_max == 0 && index1 < num_watch; ++index1) { + add_index(p, index1, p[index1].second); + } + + unsigned val = p[index].first; + SASSERT(value(p[index].second) == l_false); + SASSERT(val <= slack); + slack -= val; + + // find literals to swap with: + for (unsigned j = num_watch; j < sz && slack < bound + m_a_max; ++j) { + literal lit = p[j].second; + if (value(lit) != l_false) { + slack += p[j].first; + SASSERT(!is_watched(p[j].second, p)); + watch_literal(p[j], p); + p.swap(num_watch, j); + add_index(p, num_watch, lit); + BADLOG(verbose_stream() << "add watch: " << lit << " num watch: " << num_watch << " max: " << m_a_max << " slack " << slack << "\n"); + ++num_watch; + } + } + + SASSERT(!inconsistent()); + DEBUG_CODE(for (auto idx : m_pb_undef) { SASSERT(value(p[idx].second) == l_undef); }); + + if (slack < bound) { + // maintain watching the literal + slack += val; + p.set_slack(slack); + p.set_num_watch(num_watch); + SASSERT(validate_watch(p, null_literal)); + BADLOG(display(verbose_stream() << "conflict: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true)); + SASSERT(bound <= slack); + TRACE("ba", tout << "conflict " << alit << "\n";); + set_conflict(p, alit); + return l_false; + } + + if (num_watch == 1) { _bad_id = p.id(); } + + BADLOG(verbose_stream() << "size: " << p.size() << " index: " << index << " num watch: " << num_watch << "\n"); + + // swap out the watched literal. + --num_watch; + SASSERT(num_watch > 0); + p.set_slack(slack); + p.set_num_watch(num_watch); + p.swap(num_watch, index); + + + // + // slack >= bound, but slack - w(l) < bound + // l must be true. + // + if (slack < bound + m_a_max) { + BADLOG(verbose_stream() << "slack " << slack << " " << bound << " " << m_a_max << "\n";); + TRACE("ba", tout << p << "\n"; for(auto j : m_pb_undef) tout << j << " "; tout << "\n";); + for (unsigned index1 : m_pb_undef) { + if (index1 == num_watch) { + index1 = index; + } + wliteral wl = p[index1]; + literal lit = wl.second; + SASSERT(value(lit) == l_undef); + if (slack < bound + wl.first) { + BADLOG(verbose_stream() << "Assign " << lit << " " << wl.first << "\n"); + assign(p, lit); + } + } + } + + SASSERT(validate_watch(p, alit)); // except that alit is still watched. + + TRACE("ba", display(tout << "assign: " << alit << "\n", p, true);); + + BADLOG(verbose_stream() << "unwatch " << alit << " watch: " << p.num_watch() << " size: " << p.size() << " slack: " << p.slack() << " " << inconsistent() << "\n"); + + return l_undef; + } + + void ba_solver::watch_literal(wliteral l, pb& p) { + watch_literal(l.second, p); + } + + void ba_solver::clear_watch(pb& p) { + p.clear_watch(); + for (unsigned i = 0; i < p.num_watch(); ++i) { + unwatch_literal(p[i].second, p); + } + p.set_num_watch(0); + + DEBUG_CODE(for (wliteral wl : p) VERIFY(!is_watched(wl.second, p));); + } + + void ba_solver::recompile(pb& p) { + // IF_VERBOSE(2, verbose_stream() << "re: " << p << "\n";); + SASSERT(p.num_watch() == 0); + m_weights.resize(2*s().num_vars(), 0); + for (wliteral wl : p) { + m_weights[wl.second.index()] += wl.first; + } + unsigned k = p.k(); + unsigned sz = p.size(); + bool all_units = true; + unsigned j = 0; + for (unsigned i = 0; i < sz && 0 < k; ++i) { + literal l = p[i].second; + unsigned w1 = m_weights[l.index()]; + unsigned w2 = m_weights[(~l).index()]; + if (w1 == 0 || w1 < w2) { + continue; + } + else if (k <= w2) { + k = 0; + break; + } + else { + SASSERT(w2 <= w1 && w2 < k); + k -= w2; + w1 -= w2; + m_weights[l.index()] = 0; + m_weights[(~l).index()] = 0; + if (w1 == 0) { + continue; + } + else { + p[j] = wliteral(w1, l); + all_units &= w1 == 1; + ++j; + } + } + } + sz = j; + // clear weights + for (wliteral wl : p) { + m_weights[wl.second.index()] = 0; + m_weights[(~wl.second).index()] = 0; + } + + if (k == 0) { + if (p.lit() != null_literal) { + s().assign(p.lit(), justification()); + } + remove_constraint(p, "recompiled to true"); + return; + } + + else if (k == 1 && p.lit() == null_literal) { + literal_vector lits(sz, p.literals().c_ptr()); + s().mk_clause(sz, lits.c_ptr(), p.learned()); + remove_constraint(p, "recompiled to clause"); + return; + } + + else if (all_units) { + literal_vector lits(sz, p.literals().c_ptr()); + add_at_least(p.lit(), lits, k, p.learned()); + remove_constraint(p, "recompiled to cardinality"); + return; + } + else { + p.set_size(sz); + p.update_max_sum(); + if (p.max_sum() < k) { + if (p.lit() == null_literal) { + s().set_conflict(justification()); + } + else { + s().assign(~p.lit(), justification()); + } + remove_constraint(p, "recompiled to false"); + return; + } + p.set_k(k); + SASSERT(p.well_formed()); + + if (clausify(p)) { + return; + } + if (p.lit() == null_literal || value(p.lit()) == l_true) { + init_watch(p); + } + } + } + + void ba_solver::display(std::ostream& out, pb const& p, bool values) const { + if (p.lit() != null_literal) out << p.lit() << " == "; + if (values) { + out << "[watch: " << p.num_watch() << ", slack: " << p.slack() << "]"; + } + if (p.lit() != null_literal && values) { + out << "@(" << value(p.lit()); + if (value(p.lit()) != l_undef) { + out << ":" << lvl(p.lit()); + } + out << "): "; + } + unsigned i = 0; + for (wliteral wl : p) { + literal l = wl.second; + unsigned w = wl.first; + if (i++ == p.num_watch()) out << " | "; + if (w > 1) out << w << " * "; + out << l; + if (values) { + out << "@(" << value(l); + if (value(l) != l_undef) { + out << ":" << lvl(l); + } + out << ") "; + } + else { + out << " "; + } + } + out << ">= " << p.k() << "\n"; + } + + // -------------------- + // xr: + + void ba_solver::clear_watch(xr& x) { + x.clear_watch(); + unwatch_literal(x[0], x); + unwatch_literal(x[1], x); + unwatch_literal(~x[0], x); + unwatch_literal(~x[1], x); + } + + bool ba_solver::parity(xr const& x, unsigned offset) const { + bool odd = false; + unsigned sz = x.size(); + for (unsigned i = offset; i < sz; ++i) { + SASSERT(value(x[i]) != l_undef); + if (value(x[i]) == l_true) { + odd = !odd; + } + } + return odd; + } + + bool ba_solver::init_watch(xr& x) { + clear_watch(x); + if (x.lit() != null_literal && value(x.lit()) == l_false) { + x.negate(); + } + TRACE("ba", display(tout, x, true);); + unsigned sz = x.size(); + unsigned j = 0; + for (unsigned i = 0; i < sz && j < 2; ++i) { + if (value(x[i]) == l_undef) { + x.swap(i, j); + ++j; + } + } + switch (j) { + case 0: + if (!parity(x, 0)) { + unsigned l = lvl(x[0]); + j = 1; + for (unsigned i = 1; i < sz; ++i) { + if (lvl(x[i]) > l) { + j = i; + l = lvl(x[i]); + } + } + SASSERT(x.lit() == null_literal || value(x.lit()) == l_true); + set_conflict(x, x[j]); + } + return false; + case 1: + SASSERT(x.lit() == null_literal || value(x.lit()) == l_true); + assign(x, parity(x, 1) ? ~x[0] : x[0]); + return false; + default: + SASSERT(j == 2); + watch_literal(x[0], x); + watch_literal(x[1], x); + watch_literal(~x[0], x); + watch_literal(~x[1], x); + return true; + } + } + + + lbool ba_solver::add_assign(xr& x, literal alit) { + // literal is assigned + unsigned sz = x.size(); + TRACE("ba", tout << "assign: " << ~alit << "@" << lvl(~alit) << " " << x << "\n"; display(tout, x, true); ); + + SASSERT(x.lit() == null_literal); + SASSERT(value(alit) != l_undef); + unsigned index = 0; + for (; index < 2; ++index) { + if (x[index].var() == alit.var()) break; + } + if (index == 2) { + // literal is no longer watched. + // this can happen as both polarities of literals + // are put in watch lists and they are removed only + // one polarity at a time. + return l_undef; + } + SASSERT(x[index].var() == alit.var()); + + // find a literal to swap with: + for (unsigned i = 2; i < sz; ++i) { + literal lit2 = x[i]; + if (value(lit2) == l_undef) { + x.swap(index, i); + // unwatch_literal(alit, x); + watch_literal(lit2, x); + watch_literal(~lit2, x); + TRACE("ba", tout << "swap in: " << lit2 << " " << x << "\n";); + return l_undef; + } + } + if (index == 0) { + x.swap(0, 1); + } + // alit resides at index 1. + SASSERT(x[1].var() == alit.var()); + if (value(x[0]) == l_undef) { + bool p = parity(x, 1); + assign(x, p ? ~x[0] : x[0]); + } + else if (!parity(x, 0)) { + set_conflict(x, ~x[1]); + } + return inconsistent() ? l_false : l_true; + } + + // --------------------------- + // conflict resolution + + void ba_solver::normalize_active_coeffs() { + reset_active_var_set(); + unsigned i = 0, j = 0, sz = m_active_vars.size(); + for (; i < sz; ++i) { + bool_var v = m_active_vars[i]; + if (!m_active_var_set.contains(v) && get_coeff(v) != 0) { + m_active_var_set.insert(v); + if (j != i) { + m_active_vars[j] = m_active_vars[i]; + } + ++j; + } + } + m_active_vars.shrink(j); + } + + void ba_solver::inc_coeff(literal l, unsigned offset) { + SASSERT(offset > 0); + bool_var v = l.var(); + SASSERT(v != null_bool_var); + m_coeffs.reserve(v + 1, 0); + + int64_t coeff0 = m_coeffs[v]; + if (coeff0 == 0) { + m_active_vars.push_back(v); + } + + int64_t loffset = static_cast(offset); + int64_t inc = l.sign() ? -loffset : loffset; + int64_t coeff1 = inc + coeff0; + m_coeffs[v] = coeff1; + if (coeff1 > INT_MAX || coeff1 < INT_MIN) { + m_overflow = true; + return; + } + + if (coeff0 > 0 && inc < 0) { + inc_bound(std::max((int64_t)0, coeff1) - coeff0); + } + else if (coeff0 < 0 && inc > 0) { + inc_bound(coeff0 - std::min((int64_t)0, coeff1)); + } + int64_t lbound = static_cast(m_bound); + + // reduce coefficient to be no larger than bound. + if (coeff1 > lbound) { + m_coeffs[v] = lbound; + } + else if (coeff1 < 0 && -coeff1 > lbound) { + m_coeffs[v] = -lbound; + } + } + + int64_t ba_solver::get_coeff(bool_var v) const { + return m_coeffs.get(v, 0); + } + + unsigned ba_solver::get_abs_coeff(bool_var v) const { + int64_t c = get_coeff(v); + if (c < INT_MIN+1 || c > UINT_MAX) { + m_overflow = true; + return UINT_MAX; + } + return static_cast(std::abs(c)); + } + + int ba_solver::get_int_coeff(bool_var v) const { + int64_t c = m_coeffs.get(v, 0); + if (c < INT_MIN || c > INT_MAX) { + m_overflow = true; + return 0; + } + return static_cast(c); + } + + void ba_solver::inc_bound(int64_t i) { + if (i < INT_MIN || i > INT_MAX) { + m_overflow = true; + return; + } + int64_t new_bound = m_bound; + new_bound += i; + if (new_bound < 0) { + m_overflow = true; + } + else if (new_bound > UINT_MAX) { + m_overflow = true; + } + else { + m_bound = static_cast(new_bound); + } + } + + void ba_solver::reset_coeffs() { + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + m_coeffs[m_active_vars[i]] = 0; + } + m_active_vars.reset(); + } + + static bool _debug_conflict = false; + static literal _debug_consequent = null_literal; + static unsigned_vector _debug_var2position; + +// #define DEBUG_CODE(_x_) _x_ + + lbool ba_solver::resolve_conflict() { + if (0 == m_num_propagations_since_pop) { + return l_undef; + } + m_overflow = false; + reset_coeffs(); + m_num_marks = 0; + m_bound = 0; + literal consequent = s().m_not_l; + justification js = s().m_conflict; + TRACE("ba", tout << consequent << " " << js << "\n";); + m_conflict_lvl = s().get_max_lvl(consequent, js); + if (consequent != null_literal) { + consequent.neg(); + process_antecedent(consequent, 1); + } + literal_vector const& lits = s().m_trail; + unsigned idx = lits.size() - 1; + unsigned offset = 1; + DEBUG_CODE(active2pb(m_A);); + + do { + + if (m_overflow || offset > (1 << 12)) { + IF_VERBOSE(20, verbose_stream() << "offset: " << offset << "\n"; + DEBUG_CODE(active2pb(m_A); display(verbose_stream(), m_A););); + goto bail_out; + } + + if (offset == 0) { + goto process_next_resolvent; + } + + DEBUG_CODE(TRACE("sat_verbose", display(tout, m_A););); + TRACE("ba", tout << "process consequent: " << consequent << " : "; s().display_justification(tout, js) << "\n";); + SASSERT(offset > 0); + + DEBUG_CODE(justification2pb(js, consequent, offset, m_B);); + + if (_debug_conflict) { + IF_VERBOSE(0, + verbose_stream() << consequent << "\n"; + s().display_justification(verbose_stream(), js); + verbose_stream() << "\n";); + _debug_consequent = consequent; + } + switch(js.get_kind()) { + case justification::NONE: + SASSERT (consequent != null_literal); + inc_bound(offset); + break; + case justification::BINARY: + inc_bound(offset); + SASSERT (consequent != null_literal); + inc_coeff(consequent, offset); + process_antecedent(js.get_literal(), offset); + break; + case justification::TERNARY: + inc_bound(offset); + SASSERT (consequent != null_literal); + inc_coeff(consequent, offset); + process_antecedent(js.get_literal1(), offset); + process_antecedent(js.get_literal2(), offset); + break; + case justification::CLAUSE: { + inc_bound(offset); + clause & c = s().get_clause(js); + unsigned i = 0; + if (consequent != null_literal) { + inc_coeff(consequent, offset); + if (c[0] == consequent) { + i = 1; + } + else { + SASSERT(c[1] == consequent); + process_antecedent(c[0], offset); + i = 2; + } + } + unsigned sz = c.size(); + for (; i < sz; i++) + process_antecedent(c[i], offset); + break; + } + case justification::EXT_JUSTIFICATION: { + constraint& cnstr = index2constraint(js.get_ext_justification_idx()); + ++m_stats.m_num_resolves; + switch (cnstr.tag()) { + case card_t: { + card& c = cnstr.to_card(); + inc_bound(static_cast(offset) * c.k()); + process_card(c, offset); + break; + } + case pb_t: { + pb& p = cnstr.to_pb(); + m_lemma.reset(); + inc_bound(offset); + inc_coeff(consequent, offset); + get_antecedents(consequent, p, m_lemma); + TRACE("ba", display(tout, p, true); tout << m_lemma << "\n";); + if (_debug_conflict) { + verbose_stream() << consequent << " "; + verbose_stream() << "antecedents: " << m_lemma << "\n"; + } + for (literal l : m_lemma) process_antecedent(~l, offset); + break; + } + case xr_t: { + // jus.push_back(js); + m_lemma.reset(); + inc_bound(offset); + inc_coeff(consequent, offset); + get_xr_antecedents(consequent, idx, js, m_lemma); + for (literal l : m_lemma) process_antecedent(~l, offset); + break; + } + default: + UNREACHABLE(); + break; + } + break; + } + default: + UNREACHABLE(); + break; + } + + SASSERT(validate_lemma()); + + DEBUG_CODE( + active2pb(m_C); + VERIFY(validate_resolvent()); + m_A = m_C; + TRACE("ba", display(tout << "conflict: ", m_A););); + + cut(); + + process_next_resolvent: + + // find the next marked variable in the assignment stack + // + bool_var v; + while (true) { + consequent = lits[idx]; + v = consequent.var(); + if (s().is_marked(v)) break; + if (idx == 0) { + IF_VERBOSE(2, verbose_stream() << "did not find marked literal\n";); + goto bail_out; + } + SASSERT(idx > 0); + --idx; + } + + SASSERT(lvl(v) == m_conflict_lvl); + s().reset_mark(v); + --idx; + TRACE("sat_verbose", tout << "Unmark: v" << v << "\n";); + --m_num_marks; + js = s().m_justification[v]; + offset = get_abs_coeff(v); + if (offset > m_bound) { + int64_t bound64 = static_cast(m_bound); + m_coeffs[v] = (get_coeff(v) < 0) ? -bound64 : bound64; + offset = m_bound; + DEBUG_CODE(active2pb(m_A);); + } + SASSERT(value(consequent) == l_true); + } + while (m_num_marks > 0); + + DEBUG_CODE(for (bool_var i = 0; i < static_cast(s().num_vars()); ++i) SASSERT(!s().is_marked(i));); + SASSERT(validate_lemma()); + + normalize_active_coeffs(); + + if (!create_asserting_lemma()) { + goto bail_out; + } + + DEBUG_CODE(VERIFY(validate_conflict(m_lemma, m_A));); + + TRACE("ba", tout << m_lemma << "\n";); + + if (get_config().m_drat) { + svector ps; // TBD fill in + drat_add(m_lemma, ps); + } + + s().m_lemma.reset(); + s().m_lemma.append(m_lemma); + for (unsigned i = 1; i < m_lemma.size(); ++i) { + CTRACE("ba", s().is_marked(m_lemma[i].var()), tout << "marked: " << m_lemma[i] << "\n";); + s().mark(m_lemma[i].var()); + } + + return l_true; + + bail_out: + + m_overflow = false; + + while (m_num_marks > 0) { + bool_var v = lits[idx].var(); + if (s().is_marked(v)) { + s().reset_mark(v); + --m_num_marks; + } + if (idx == 0 && !_debug_conflict) { + _debug_conflict = true; + _debug_var2position.reserve(s().num_vars()); + for (unsigned i = 0; i < lits.size(); ++i) { + _debug_var2position[lits[i].var()] = i; + } + IF_VERBOSE(0, + active2pb(m_A); + uint64_t c = 0; + for (uint64_t c1 : m_A.m_coeffs) c += c1; + verbose_stream() << "sum of coefficients: " << c << "\n"; + display(verbose_stream(), m_A, true); + verbose_stream() << "conflicting literal: " << s().m_not_l << "\n";); + + for (literal l : lits) { + if (s().is_marked(l.var())) { + IF_VERBOSE(0, verbose_stream() << "missing mark: " << l << "\n";); + s().reset_mark(l.var()); + } + } + m_num_marks = 0; + resolve_conflict(); + } + --idx; + } + return l_undef; + } + + bool ba_solver::create_asserting_lemma() { + bool adjusted = false; + + adjust_conflict_level: + int64_t bound64 = m_bound; + int64_t slack = -bound64; + for (bool_var v : m_active_vars) { + slack += get_abs_coeff(v); + } + m_lemma.reset(); + m_lemma.push_back(null_literal); + unsigned num_skipped = 0; + int64_t asserting_coeff = 0; + for (unsigned i = 0; 0 <= slack && i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int64_t coeff = get_coeff(v); + lbool val = value(v); + bool is_true = val == l_true; + bool append = coeff != 0 && val != l_undef && ((coeff < 0) == is_true); + if (append) { + literal lit(v, !is_true); + if (lvl(lit) == m_conflict_lvl) { + if (m_lemma[0] == null_literal) { + asserting_coeff = std::abs(coeff); + slack -= asserting_coeff; + m_lemma[0] = ~lit; + } + else { + ++num_skipped; + if (asserting_coeff < std::abs(coeff)) { + m_lemma[0] = ~lit; + slack -= (std::abs(coeff) - asserting_coeff); + asserting_coeff = std::abs(coeff); + } + } + } + else if (lvl(lit) < m_conflict_lvl) { + slack -= std::abs(coeff); + m_lemma.push_back(~lit); + } + } + } + if (slack >= 0) { + IF_VERBOSE(20, verbose_stream() << "(sat.card slack: " << slack << " skipped: " << num_skipped << ")\n";); + return false; + } + if (m_overflow) { + return false; + } + if (m_lemma[0] == null_literal) { + if (m_lemma.size() == 1) { + s().set_conflict(justification()); + return false; + } + return false; + unsigned old_level = m_conflict_lvl; + m_conflict_lvl = 0; + for (unsigned i = 1; i < m_lemma.size(); ++i) { + m_conflict_lvl = std::max(m_conflict_lvl, lvl(m_lemma[i])); + } + IF_VERBOSE(1, verbose_stream() << "(sat.backjump :new-level " << m_conflict_lvl << " :old-level " << old_level << ")\n";); + adjusted = true; + goto adjust_conflict_level; + } + if (!adjusted) { + active2card(); + } + return true; + } + + /* + \brief compute a cut for current resolvent. + */ + + void ba_solver::cut() { + + // bypass cut if there is a unit coefficient + for (bool_var v : m_active_vars) { + if (1 == get_abs_coeff(v)) return; + } + + unsigned g = 0; + + for (unsigned i = 0; g != 1 && i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + unsigned coeff = get_abs_coeff(v); + if (coeff == 0) { + continue; + } + if (m_bound < coeff) { + int64_t bound64 = m_bound; + if (get_coeff(v) > 0) { + m_coeffs[v] = bound64; + } + else { + m_coeffs[v] = -bound64; + } + coeff = m_bound; + } + SASSERT(0 < coeff && coeff <= m_bound); + if (g == 0) { + g = coeff; + } + else { + g = u_gcd(g, coeff); + } + } + + if (g >= 2) { + normalize_active_coeffs(); + for (bool_var v : m_active_vars) { + m_coeffs[v] /= static_cast(g); + } + m_bound = (m_bound + g - 1) / g; + ++m_stats.m_num_cut; + } + } + + void ba_solver::process_card(card& c, unsigned offset) { + literal lit = c.lit(); + SASSERT(c.k() <= c.size()); + SASSERT(lit == null_literal || value(lit) != l_undef); + SASSERT(0 < offset); + for (unsigned i = c.k(); i < c.size(); ++i) { + process_antecedent(c[i], offset); + } + for (unsigned i = 0; i < c.k(); ++i) { + inc_coeff(c[i], offset); + } + if (lit != null_literal) { + uint64_t offset1 = static_cast(offset) * c.k(); + if (offset1 > UINT_MAX) { + m_overflow = true; + } + if (value(lit) == l_true) { + process_antecedent(~lit, static_cast(offset1)); + } + else { + process_antecedent(lit, static_cast(offset1)); + } + } + } + + void ba_solver::process_antecedent(literal l, unsigned offset) { + SASSERT(value(l) == l_false); + bool_var v = l.var(); + unsigned level = lvl(v); + + if (level > 0 && !s().is_marked(v) && level == m_conflict_lvl) { + s().mark(v); + TRACE("sat_verbose", tout << "Mark: v" << v << "\n";); + ++m_num_marks; + if (_debug_conflict && _debug_consequent != null_literal && _debug_var2position[_debug_consequent.var()] < _debug_var2position[l.var()]) { + IF_VERBOSE(0, verbose_stream() << "antecedent " << l << " is above consequent in stack\n";); + } + } + inc_coeff(l, offset); + } + + literal ba_solver::get_asserting_literal(literal p) { + if (get_abs_coeff(p.var()) != 0) { + return p; + } + unsigned level = 0; + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + if (value(lit) == l_false && lvl(lit) > level) { + p = lit; + level = lvl(lit); + } + } + 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) { + TRACE("ba", tout << this << "\n";); + m_num_propagations_since_pop = 0; + } + + ba_solver::~ba_solver() { + m_stats.reset(); + for (constraint* c : m_constraints) { + m_allocator.deallocate(c->obj_size(), c); + } + for (constraint* c : m_learned) { + m_allocator.deallocate(c->obj_size(), c); + } + } + + void ba_solver::add_at_least(bool_var v, literal_vector const& lits, unsigned k) { + literal lit = v == null_bool_var ? null_literal : literal(v, false); + add_at_least(lit, lits, k, false); + } + + ba_solver::constraint* ba_solver::add_at_least(literal lit, literal_vector const& lits, unsigned k, bool learned) { + if (k == 1 && lit == null_literal) { + literal_vector _lits(lits); + s().mk_clause(_lits.size(), _lits.c_ptr(), learned); + return 0; + } + if (!learned && clausify(lit, lits.size(), lits.c_ptr(), k)) { + return 0; + } + void * mem = m_allocator.allocate(card::get_obj_size(lits.size())); + card* c = new (mem) card(next_id(), lit, lits, k); + c->set_learned(learned); + add_constraint(c); + return c; + } + + void ba_solver::add_constraint(constraint* c) { + literal_vector lits(c->literals()); + if (c->learned()) { + m_learned.push_back(c); + } + else { + SASSERT(!m_solver || s().at_base_lvl()); + m_constraints.push_back(c); + } + literal lit = c->lit(); + if (c->learned() && m_solver && !s().at_base_lvl()) { + SASSERT(lit == null_literal); + // gets initialized after backjump. + m_constraint_to_reinit.push_back(c); + } + else if (lit == null_literal) { + init_watch(*c); + } + else { + if (m_solver) m_solver->set_external(lit.var()); + watch_literal(lit, *c); + watch_literal(~lit, *c); + } + SASSERT(c->well_formed()); + } + + + bool ba_solver::init_watch(constraint& c) { + if (inconsistent()) return false; + switch (c.tag()) { + case card_t: return init_watch(c.to_card()); + case pb_t: return init_watch(c.to_pb()); + case xr_t: return init_watch(c.to_xr()); + } + UNREACHABLE(); + return false; + } + + lbool ba_solver::add_assign(constraint& c, literal l) { + switch (c.tag()) { + case card_t: return add_assign(c.to_card(), l); + case pb_t: return add_assign(c.to_pb(), l); + case xr_t: return add_assign(c.to_xr(), l); + } + UNREACHABLE(); + return l_undef; + } + + ba_solver::constraint* ba_solver::add_pb_ge(literal lit, svector const& wlits, unsigned k, bool learned) { + bool units = true; + for (wliteral wl : wlits) units &= wl.first == 1; + if (k == 0 && lit == null_literal) { + return 0; + } + if (units || k == 1) { + literal_vector lits; + for (wliteral wl : wlits) lits.push_back(wl.second); + return add_at_least(lit, lits, k, learned); + } + void * mem = m_allocator.allocate(pb::get_obj_size(wlits.size())); + pb* p = new (mem) pb(next_id(), lit, wlits, k); + p->set_learned(learned); + add_constraint(p); + return p; + } + + void ba_solver::add_pb_ge(bool_var v, svector const& wlits, unsigned k) { + literal lit = v == null_bool_var ? null_literal : literal(v, false); + add_pb_ge(lit, wlits, k, false); + } + + void ba_solver::add_xr(literal_vector const& lits) { + add_xr(lits, false); + } + + ba_solver::constraint* ba_solver::add_xr(literal_vector const& lits, bool learned) { + void * mem = m_allocator.allocate(xr::get_obj_size(lits.size())); + xr* x = new (mem) xr(next_id(), lits); + x->set_learned(learned); + add_constraint(x); + return x; + } + + /* + \brief return true to keep watching literal. + */ + bool ba_solver::propagate(literal l, ext_constraint_idx idx) { + SASSERT(value(l) == l_true); + constraint& c = index2constraint(idx); + if (c.lit() != null_literal && l.var() == c.lit().var()) { + init_watch(c); + return true; + } + else if (c.lit() != null_literal && value(c.lit()) != l_true) { + // else if (c.lit() != null_literal && value(c.lit()) == l_false) { + return true; + } + else { + return l_undef != add_assign(c, ~l); + } + } + + double ba_solver::get_reward(card const& c, literal_occs_fun& literal_occs) const { + unsigned k = c.k(), slack = 0; + bool do_add = get_config().m_lookahead_reward == heule_schur_reward; + double to_add = do_add ? 0: 1; + for (literal l : c) { + switch (value(l)) { + case l_true: --k; if (k == 0) return 0; + case l_undef: + if (do_add) to_add += literal_occs(l); + ++slack; break; + case l_false: break; + } + } + if (k >= slack) return 1; + return pow(0.5, slack - k + 1) * to_add; + } + + double ba_solver::get_reward(pb const& c, literal_occs_fun& occs) const { + unsigned k = c.k(), slack = 0; + bool do_add = get_config().m_lookahead_reward == heule_schur_reward; + double to_add = do_add ? 0 : 1; + double undefs = 0; + for (wliteral wl : c) { + literal l = wl.second; + unsigned w = wl.first; + switch (value(l)) { + case l_true: if (k <= w) return 0; + case l_undef: + if (do_add) to_add += occs(l); + ++undefs; + slack += w; + break; // TBD multiplier factor on this + case l_false: break; + } + } + if (k >= slack || 0 == undefs) return 0; + double avg = slack / undefs; + return pow(0.5, (slack - k + 1)/avg) * to_add; + } + + double ba_solver::get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const { + constraint const& c = index2constraint(idx); + switch (c.tag()) { + case card_t: return get_reward(c.to_card(), occs); + case pb_t: return get_reward(c.to_pb(), occs); + case xr_t: return 0; + default: UNREACHABLE(); return 0; + } + } + + + void ba_solver::ensure_parity_size(bool_var v) { + if (m_parity_marks.size() <= static_cast(v)) { + m_parity_marks.resize(static_cast(v) + 1, 0); + } + } + + unsigned ba_solver::get_parity(bool_var v) { + return m_parity_marks.get(v, 0); + } + + void ba_solver::inc_parity(bool_var v) { + ensure_parity_size(v); + m_parity_marks[v]++; + } + + void ba_solver::reset_parity(bool_var v) { + ensure_parity_size(v); + m_parity_marks[v] = 0; + } + + /** + \brief perform parity resolution on xr premises. + The idea is to collect premises based on xr resolvents. + Variables that are repeated an even number of times cancel out. + */ + void ba_solver::get_xr_antecedents(literal l, unsigned index, justification js, literal_vector& r) { + unsigned level = lvl(l); + bool_var v = l.var(); + SASSERT(js.get_kind() == justification::EXT_JUSTIFICATION); + TRACE("ba", tout << l << ": " << js << "\n"; + for (unsigned i = 0; i <= index; ++i) tout << s().m_trail[i] << " "; tout << "\n"; + s().display_units(tout); + ); + + unsigned num_marks = 0; + unsigned count = 0; + while (true) { + TRACE("ba", tout << "process: " << l << "\n";); + ++count; + if (js.get_kind() == justification::EXT_JUSTIFICATION) { + constraint& c = index2constraint(js.get_ext_justification_idx()); + TRACE("ba", tout << c << "\n";); + if (!c.is_xr()) { + r.push_back(l); + } + else { + xr& x = c.to_xr(); + if (x[1].var() == l.var()) { + x.swap(0, 1); + } + SASSERT(x[0].var() == l.var()); + for (unsigned i = 1; i < x.size(); ++i) { + literal lit(value(x[i]) == l_true ? x[i] : ~x[i]); + inc_parity(lit.var()); + if (lvl(lit) == level) { + TRACE("ba", tout << "mark: " << lit << "\n";); + ++num_marks; + } + else { + m_parity_trail.push_back(lit); + } + } + } + } + else { + r.push_back(l); + } + bool found = false; + while (num_marks > 0) { + l = s().m_trail[index]; + v = l.var(); + unsigned n = get_parity(v); + if (n > 0) { + reset_parity(v); + num_marks -= n; + if (n % 2 == 1) { + found = true; + break; + } + } + --index; + } + if (!found) { + break; + } + --index; + js = s().m_justification[v]; + } + + // now walk the defined literals + + for (unsigned i = 0; i < m_parity_trail.size(); ++i) { + literal lit = m_parity_trail[i]; + if (get_parity(lit.var()) % 2 == 1) { + r.push_back(lit); + } + else { + // IF_VERBOSE(2, verbose_stream() << "skip even parity: " << lit << "\n";); + } + reset_parity(lit.var()); + } + m_parity_trail.reset(); + TRACE("ba", tout << r << "\n";); + } + + /** + \brief retrieve a sufficient set of literals from p that imply l. + + Find partition: + + - Ax + coeff*l + B*y >= k + - all literals in x are false. + - B < k + + Then x is an explanation for l + + */ + + bool ba_solver::assigned_above(literal above, literal below) { + unsigned l = lvl(above); + SASSERT(l == lvl(below)); + if (l == 0) return false; + unsigned start = s().m_scopes[l-1].m_trail_lim; + literal_vector const& lits = s().m_trail; + +#if 0 + IF_VERBOSE(10, verbose_stream() << "level " << l << " scope level " << s().scope_lvl() << " tail lim start: " + << start << " size of lits: " << lits.size() << " num scopes " << s().m_scopes.size() << "\n";); +#endif + + for (unsigned sz = lits.size(); sz-- > start; ) { + if (lits[sz] == above) return true; + if (lits[sz] == below) return false; + } + UNREACHABLE(); + return false; + } + + void ba_solver::get_antecedents(literal l, pb const& p, literal_vector& r) { + TRACE("ba", display(tout << l << " level: " << s().scope_lvl() << " ", p, true);); + SASSERT(p.lit() == null_literal || value(p.lit()) == l_true); + + if (p.lit() != null_literal) { + r.push_back(p.lit()); + } + + unsigned k = p.k(); + + if (_debug_conflict) { + IF_VERBOSE(0, display(verbose_stream(), p, true); + verbose_stream() << "literal: " << l << " value: " << value(l) << " num-watch: " << p.num_watch() << " slack: " << p.slack() << "\n";); + } + + if (value(l) == l_false) { + // The literal comes from a conflict. + // it is forced true, but assigned to false. + unsigned slack = 0; + for (wliteral wl : p) { + if (value(wl.second) != l_false) { + slack += wl.first; + } + } + SASSERT(slack < k); + for (wliteral wl : p) { + literal lit = wl.second; + if (lit != l && value(lit) == l_false) { + unsigned w = wl.first; + if (slack + w < k) { + slack += w; + } + else { + r.push_back(~lit); + } + } + } + } + else { + // comes from a unit propagation + SASSERT(value(l) == l_true); + unsigned coeff = 0, j = 0; + for (; j < p.size(); ++j) { + if (p[j].second == l) { + coeff = p[j].first; + break; + } + } + + ++j; + if (j < p.num_watch()) { + j = p.num_watch(); + } + CTRACE("ba", coeff == 0, display(tout << l << " coeff: " << coeff << "\n", p, true);); + + if (_debug_conflict) { + std::cout << "coeff " << coeff << "\n"; + } + + SASSERT(coeff > 0); + unsigned slack = p.max_sum() - coeff; + + // we need antecedents to be deeper than alit. + for (; j < p.size(); ++j) { + literal lit = p[j].second; + unsigned w = p[j].first; + if (l_false != value(lit)) { + // skip + } + else if (lvl(lit) > lvl(l)) { + // skip + } + else if (lvl(lit) == lvl(l) && assigned_above(~lit, l)) { + // skip + } + else if (slack + w < k) { + slack += w; + } + else { + r.push_back(~lit); + } + } + } + SASSERT(validate_unit_propagation(p, r, l)); + } + + bool ba_solver::is_extended_binary(ext_justification_idx idx, literal_vector & r) { + constraint const& c = index2constraint(idx); + switch (c.tag()) { + case card_t: { + card const& ca = c.to_card(); + if (ca.size() == ca.k() + 1 && ca.lit() == null_literal) { + r.reset(); + for (literal l : ca) r.push_back(l); + return true; + } + else { + return false; + } + } + default: + return false; + } + } + + void ba_solver::simplify(xr& x) { + // no-op + } + + void ba_solver::get_antecedents(literal l, card const& c, literal_vector& r) { + if (l == ~c.lit()) { + for (unsigned i = c.k() - 1; i < c.size(); ++i) { + VERIFY(value(c[i]) == l_false); + r.push_back(~c[i]); + } + return; + } + DEBUG_CODE( + bool found = false; + for (unsigned i = 0; !found && i < c.k(); ++i) { + found = c[i] == l; + } + SASSERT(found);); + + // IF_VERBOSE(0, if (_debug_conflict) verbose_stream() << "ante " << l << " " << c << "\n"); + VERIFY(c.lit() == null_literal || value(c.lit()) != l_false); + if (c.lit() != null_literal) r.push_back(value(c.lit()) == l_true ? c.lit() : ~c.lit()); + for (unsigned i = c.k(); i < c.size(); ++i) { + SASSERT(value(c[i]) == l_false); + r.push_back(~c[i]); + } + } + + void ba_solver::get_antecedents(literal l, xr const& x, literal_vector& r) { + if (x.lit() != null_literal) r.push_back(x.lit()); + // TRACE("ba", display(tout << l << " ", x, true);); + SASSERT(x.lit() == null_literal || value(x.lit()) == l_true); + SASSERT(x[0].var() == l.var() || x[1].var() == l.var()); + if (x[0].var() == l.var()) { + SASSERT(value(x[1]) != l_undef); + r.push_back(value(x[1]) == l_true ? x[1] : ~x[1]); + } + else { + SASSERT(value(x[0]) != l_undef); + r.push_back(value(x[0]) == l_true ? x[0] : ~x[0]); + } + for (unsigned i = 2; i < x.size(); ++i) { + SASSERT(value(x[i]) != l_undef); + r.push_back(value(x[i]) == l_true ? x[i] : ~x[i]); + } + } + + // ---------------------------- + // constraint generic methods + + void ba_solver::get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) { + get_antecedents(l, index2constraint(idx), r); + } + + bool ba_solver::is_watched(literal lit, constraint const& c) const { + return get_wlist(~lit).contains(watched(c.index())); + } + + void ba_solver::unwatch_literal(literal lit, constraint& c) { + get_wlist(~lit).erase(watched(c.index())); + } + + void ba_solver::watch_literal(literal lit, constraint& c) { + if (c.is_pure() && lit == ~c.lit()) return; + get_wlist(~lit).push_back(watched(c.index())); + } + + void ba_solver::get_antecedents(literal l, constraint const& c, literal_vector& r) { + switch (c.tag()) { + case card_t: get_antecedents(l, c.to_card(), r); break; + case pb_t: get_antecedents(l, c.to_pb(), r); break; + case xr_t: get_antecedents(l, c.to_xr(), r); break; + default: UNREACHABLE(); break; + } + } + + void ba_solver::nullify_tracking_literal(constraint& c) { + if (c.lit() != null_literal) { + unwatch_literal(c.lit(), c); + unwatch_literal(~c.lit(), c); + c.nullify_literal(); + } + } + + void ba_solver::clear_watch(constraint& c) { + switch (c.tag()) { + case card_t: + clear_watch(c.to_card()); + break; + case pb_t: + clear_watch(c.to_pb()); + break; + case xr_t: + clear_watch(c.to_xr()); + break; + default: + UNREACHABLE(); + } + } + + void ba_solver::remove_constraint(constraint& c, char const* reason) { + IF_VERBOSE(21, display(verbose_stream() << "remove " << reason << " ", c, true);); + nullify_tracking_literal(c); + clear_watch(c); + c.set_removed(); + m_constraint_removed = true; + } + + // -------------------------------- + // validation + + bool ba_solver::validate_unit_propagation(constraint const& c, literal l) const { + return true; + switch (c.tag()) { + case card_t: return validate_unit_propagation(c.to_card(), l); + case pb_t: return validate_unit_propagation(c.to_pb(), l); + case xr_t: return true; + default: UNREACHABLE(); break; + } + return false; + } + + bool ba_solver::validate_conflict(constraint const& c) const { + return eval(c) == l_false; + } + + lbool ba_solver::eval(constraint const& c) const { + lbool v1 = c.lit() == null_literal ? l_true : value(c.lit()); + switch (c.tag()) { + case card_t: return eval(v1, eval(c.to_card())); + case pb_t: return eval(v1, eval(c.to_pb())); + case xr_t: return eval(v1, eval(c.to_xr())); + default: UNREACHABLE(); break; + } + return l_undef; + } + + lbool ba_solver::eval(model const& m, constraint const& c) const { + lbool v1 = c.lit() == null_literal ? l_true : value(m, c.lit()); + switch (c.tag()) { + case card_t: return eval(v1, eval(m, c.to_card())); + case pb_t: return eval(v1, eval(m, c.to_pb())); + case xr_t: return eval(v1, eval(m, c.to_xr())); + default: UNREACHABLE(); break; + } + return l_undef; + } + + lbool ba_solver::eval(lbool a, lbool b) const { + if (a == l_undef || b == l_undef) return l_undef; + return (a == b) ? l_true : l_false; + } + + lbool ba_solver::eval(card const& c) const { + unsigned trues = 0, undefs = 0; + for (literal l : c) { + switch (value(l)) { + case l_true: trues++; break; + case l_undef: undefs++; break; + default: break; + } + } + if (trues + undefs < c.k()) return l_false; + if (trues >= c.k()) return l_true; + return l_undef; + } + + lbool ba_solver::eval(model const& m, card const& c) const { + unsigned trues = 0, undefs = 0; + for (literal l : c) { + switch (value(m, l)) { + case l_true: trues++; break; + case l_undef: undefs++; break; + default: break; + } + } + if (trues + undefs < c.k()) return l_false; + if (trues >= c.k()) return l_true; + return l_undef; + } + + lbool ba_solver::eval(model const& m, pb const& p) const { + unsigned trues = 0, undefs = 0; + for (wliteral wl : p) { + switch (value(m, wl.second)) { + case l_true: trues += wl.first; break; + case l_undef: undefs += wl.first; break; + default: break; + } + } + if (trues + undefs < p.k()) return l_false; + if (trues >= p.k()) return l_true; + return l_undef; + } + + lbool ba_solver::eval(pb const& p) const { + unsigned trues = 0, undefs = 0; + for (wliteral wl : p) { + switch (value(wl.second)) { + case l_true: trues += wl.first; break; + case l_undef: undefs += wl.first; break; + default: break; + } + } + if (trues + undefs < p.k()) return l_false; + if (trues >= p.k()) return l_true; + return l_undef; + } + + lbool ba_solver::eval(xr const& x) const { + bool odd = false; + + for (auto l : x) { + switch (value(l)) { + case l_true: odd = !odd; break; + case l_false: break; + default: return l_undef; + } + } + return odd ? l_true : l_false; + } + + lbool ba_solver::eval(model const& m, xr const& x) const { + bool odd = false; + + for (auto l : x) { + switch (value(m, l)) { + case l_true: odd = !odd; break; + case l_false: break; + default: return l_undef; + } + } + return odd ? l_true : l_false; + } + + bool ba_solver::validate() { + if (!validate_watch_literals()) { + return false; + } + for (constraint* c : m_constraints) { + if (!validate_watched_constraint(*c)) + return false; + } + for (constraint* c : m_learned) { + if (!validate_watched_constraint(*c)) + return false; + } + return true; + } + + bool ba_solver::validate_watch_literals() const { + for (unsigned v = 0; v < s().num_vars(); ++v) { + literal lit(v, false); + if (lvl(lit) == 0) continue; + if (!validate_watch_literal(lit)) return false; + if (!validate_watch_literal(~lit)) return false; + } + return true; + } + + bool ba_solver::validate_watch_literal(literal lit) const { + if (lvl(lit) == 0) return true; + for (auto const & w : get_wlist(lit)) { + if (w.get_kind() == watched::EXT_CONSTRAINT) { + constraint const& c = index2constraint(w.get_ext_constraint_idx()); + if (!c.is_watching(~lit) && lit.var() != c.lit().var()) { + IF_VERBOSE(0, display(verbose_stream() << lit << " " << lvl(lit) << " is not watched in " << c << "\n", c, true);); + UNREACHABLE(); + return false; + } + } + } + return true; + } + + bool ba_solver::validate_watched_constraint(constraint const& c) const { + if (c.is_pb() && !validate_watch(c.to_pb(), null_literal)) { + return false; + } + if (c.lit() != null_literal && value(c.lit()) != l_true) return true; + SASSERT(c.lit() == null_literal || lvl(c.lit()) == 0 || (is_watched(c.lit(), c) && is_watched(~c.lit(), c))); + if (eval(c) == l_true) { + return true; + } + literal_vector lits(c.literals()); + for (literal l : lits) { + if (lvl(l) == 0) continue; + bool found = is_watched(l, c); + if (found != c.is_watching(l)) { + + IF_VERBOSE(0, + verbose_stream() << "Discrepancy of watched literal: " << l << " id: " << c.id() + << " clause: " << c << (found?" is watched, but shouldn't be":" not watched, but should be") << "\n"; + s().display_watch_list(verbose_stream() << l << ": ", get_wlist(l)) << "\n"; + s().display_watch_list(verbose_stream() << ~l << ": ", get_wlist(~l)) << "\n"; + verbose_stream() << "value: " << value(l) << " level: " << lvl(l) << "\n"; + display(verbose_stream(), c, true); + if (c.lit() != null_literal) verbose_stream() << value(c.lit()) << "\n";); + + IF_VERBOSE(0, s().display_watches(verbose_stream())); + + UNREACHABLE(); + exit(1); + return false; + } + } + return true; + } + + bool ba_solver::validate_watch(pb const& p, literal alit) const { + for (unsigned i = 0; i < p.size(); ++i) { + literal l = p[i].second; + if (l != alit && lvl(l) != 0 && is_watched(l, p) != (i < p.num_watch())) { + IF_VERBOSE(0, display(verbose_stream(), p, true); + verbose_stream() << "literal " << l << " at position " << i << " " << is_watched(l, p) << "\n";); + UNREACHABLE(); + return false; + } + } + unsigned slack = 0; + for (unsigned i = 0; i < p.num_watch(); ++i) { + slack += p[i].first; + } + if (slack != p.slack()) { + IF_VERBOSE(0, display(verbose_stream(), p, true);); + UNREACHABLE(); + return false; + } + return true; + } + + + /** + \brief Lex on (glue, size) + */ + struct constraint_glue_psm_lt { + bool operator()(ba_solver::constraint const * c1, ba_solver::constraint const * c2) const { + return + (c1->glue() < c2->glue()) || + (c1->glue() == c2->glue() && + (c1->psm() < c2->psm() || + (c1->psm() == c2->psm() && c1->size() < c2->size()))); + } + }; + + void ba_solver::update_psm(constraint& c) const { + unsigned r = 0; + switch (c.tag()) { + case card_t: + for (literal l : c.to_card()) { + if (s().m_phase[l.var()] == (l.sign() ? NEG_PHASE : POS_PHASE)) ++r; + } + break; + case pb_t: + for (wliteral l : c.to_pb()) { + if (s().m_phase[l.second.var()] == (l.second.sign() ? NEG_PHASE : POS_PHASE)) ++r; + } + break; + default: + break; + } + c.set_psm(r); + } + + void ba_solver::gc() { + if (m_learned.size() >= 2 * m_constraints.size()) { + for (auto & c : m_learned) update_psm(*c); + std::stable_sort(m_learned.begin(), m_learned.end(), constraint_glue_psm_lt()); + gc_half("glue-psm"); + cleanup_constraints(m_learned, true); + } + } + + void ba_solver::gc_half(char const* st_name) { + TRACE("ba", tout << "gc\n";); + unsigned sz = m_learned.size(); + unsigned new_sz = sz/2; + unsigned removed = 0; + for (unsigned i = new_sz; i < sz; i++) { + constraint* c = m_learned[i]; + if (!m_constraint_to_reinit.contains(c)) { + remove_constraint(*c, "gc"); + ++removed; + } + } + m_stats.m_num_gc += removed; + m_learned.shrink(new_sz); + IF_VERBOSE(2, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << removed << ")\n";); + + } + + lbool ba_solver::add_assign(card& c, literal alit) { + // literal is assigned to false. + unsigned sz = c.size(); + unsigned bound = c.k(); + TRACE("ba", tout << "assign: " << c.lit() << ": " << ~alit << "@" << lvl(~alit) << "\n";); + + SASSERT(0 < bound && bound <= sz); + if (bound == sz) { + if (c.lit() != null_literal && value(c.lit()) == l_undef) { + assign(c, ~c.lit()); + return inconsistent() ? l_false : l_true; + } + set_conflict(c, alit); + return l_false; + } + SASSERT(value(alit) == l_false); + VERIFY(c.lit() == null_literal || value(c.lit()) != l_false); + unsigned index = 0; + for (index = 0; index <= bound; ++index) { + if (c[index] == alit) { + break; + } + } + if (index == bound + 1) { + // literal is no longer watched. + return l_undef; + } + VERIFY(index <= bound); + VERIFY(c[index] == alit); + + // find a literal to swap with: + for (unsigned i = bound + 1; i < sz; ++i) { + literal lit2 = c[i]; + if (value(lit2) != l_false) { + c.swap(index, i); + watch_literal(lit2, c); + return l_undef; + } + } + + // conflict + if (bound != index && value(c[bound]) == l_false) { + TRACE("ba", tout << "conflict " << c[bound] << " " << alit << "\n";); + if (c.lit() != null_literal && value(c.lit()) == l_undef) { + if (index + 1 < bound) c.swap(index, bound - 1); + assign(c, ~c.lit()); + return inconsistent() ? l_false : l_true; + } + set_conflict(c, alit); + return l_false; + } + + if (index != bound) { + c.swap(index, bound); + } + + // TRACE("ba", tout << "no swap " << index << " " << alit << "\n";); + // there are no literals to swap with, + // prepare for unit propagation by swapping the false literal into + // position bound. Then literals in positions 0..bound-1 have to be + // assigned l_true. + + if (c.lit() != null_literal && value(c.lit()) == l_undef) { + return l_true; + } + + for (unsigned i = 0; i < bound; ++i) { + assign(c, c[i]); + } + + if (c.learned() && c.glue() > 2) { + unsigned glue; + if (s().num_diff_false_levels_below(c.size(), c.begin(), c.glue()-1, glue)) { + c.set_glue(glue); + } + } + + return inconsistent() ? l_false : l_true; + } + + void ba_solver::asserted(literal l) { + } + + + check_result ba_solver::check() { return CR_DONE; } + + void ba_solver::push() { + m_constraint_to_reinit_lim.push_back(m_constraint_to_reinit.size()); + } + + void ba_solver::pop(unsigned n) { + TRACE("sat_verbose", tout << "pop:" << n << "\n";); + unsigned new_lim = m_constraint_to_reinit_lim.size() - n; + m_constraint_to_reinit_last_sz = m_constraint_to_reinit_lim[new_lim]; + m_constraint_to_reinit_lim.shrink(new_lim); + m_num_propagations_since_pop = 0; + } + + void ba_solver::pop_reinit() { + unsigned sz = m_constraint_to_reinit_last_sz; + for (unsigned i = sz; i < m_constraint_to_reinit.size(); ++i) { + constraint* c = m_constraint_to_reinit[i]; + if (!init_watch(*c) && !s().at_base_lvl()) { + m_constraint_to_reinit[sz++] = c; + } + } + m_constraint_to_reinit.shrink(sz); + } + + + void ba_solver::simplify(constraint& c) { + SASSERT(s().at_base_lvl()); + switch (c.tag()) { + case card_t: + simplify(c.to_card()); + break; + case pb_t: + simplify(c.to_pb()); + break; + case xr_t: + simplify(c.to_xr()); + break; + default: + UNREACHABLE(); + } + } + + void ba_solver::simplify() { + if (!s().at_base_lvl()) s().pop_to_base_level(); + unsigned trail_sz; + do { + trail_sz = s().init_trail_size(); + m_simplify_change = false; + m_clause_removed = false; + m_constraint_removed = false; + for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) simplify(*m_constraints[i]); + for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) simplify(*m_learned[i]); + init_use_lists(); + remove_unused_defs(); + set_non_external(); + if (get_config().m_elim_vars) elim_pure(); + for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) subsumption(*m_constraints[i]); + for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) subsumption(*m_learned[i]); + cleanup_clauses(); + cleanup_constraints(); + update_pure(); + } + while (m_simplify_change || trail_sz < s().init_trail_size()); + + IF_VERBOSE(1, verbose_stream() << "(ba.simplify" + << " :vars " << s().num_vars() - trail_sz + << " :constraints " << m_constraints.size() + << " :lemmas " << m_learned.size() + << " :subsumes " << m_stats.m_num_bin_subsumes + + m_stats.m_num_clause_subsumes + + m_stats.m_num_pb_subsumes + << " :gc " << m_stats.m_num_gc + << ")\n";); + + // IF_VERBOSE(0, s().display(verbose_stream())); + // mutex_reduction(); + // if (s().m_clauses.size() < 80000) lp_lookahead_reduction(); + } + + /* + * ~lit does not occur in clauses + * ~lit is only in one constraint use list + * lit == C + * -> ignore assignemnts to ~lit for C + * + * ~lit does not occur in clauses + * lit is only in one constraint use list + * lit == C + * -> negate: ~lit == ~C + */ + void ba_solver::update_pure() { + // return; + for (constraint* cp : m_constraints) { + literal lit = cp->lit(); + if (lit != null_literal && + !cp->is_pure() && + value(lit) == l_undef && + get_wlist(~lit).size() == 1 && + m_clause_use_list.get(lit).empty()) { + clear_watch(*cp); + cp->negate(); + lit.neg(); + } + if (lit != null_literal && + !cp->is_pure() && + m_cnstr_use_list[(~lit).index()].size() == 1 && + get_wlist(lit).size() == 1 && + m_clause_use_list.get(~lit).empty()) { + cp->set_pure(); + get_wlist(~lit).erase(watched(cp->index())); // just ignore assignments to false + } + } + } + + void ba_solver::mutex_reduction() { + literal_vector lits; + for (unsigned v = 0; v < s().num_vars(); ++v) { + lits.push_back(literal(v, false)); + lits.push_back(literal(v, true)); + } + vector mutexes; + s().find_mutexes(lits, mutexes); + for (literal_vector& mux : mutexes) { + if (mux.size() > 2) { + IF_VERBOSE(1, verbose_stream() << "mux: " << mux << "\n";); + for (unsigned i = 0; i < mux.size(); ++i) mux[i].neg(); + add_at_least(null_literal, mux, mux.size() - 1, false); + } + } + } + + // ------------------------- + // sorting networks + literal ba_solver::ba_sort::mk_false() { + return ~mk_true(); + } + + literal ba_solver::ba_sort::mk_true() { + if (m_true == null_literal) { + bool_var v = s.s().mk_var(false, false); + m_true = literal(v, false); + s.s().mk_clause(1,&m_true); + } + VERIFY(m_true != null_literal); + return m_true; + } + + literal ba_solver::ba_sort::mk_not(literal l) { + return ~l; + } + + literal ba_solver::ba_sort::fresh(char const*) { + bool_var v = s.s().mk_var(false, true); + return literal(v, false); + } + + literal ba_solver::ba_sort::mk_max(literal l1, literal l2) { + VERIFY(l1 != null_literal); + VERIFY(l2 != null_literal); + if (l1 == m_true) return l1; + if (l2 == m_true) return l2; + if (l1 == ~m_true) return l2; + if (l2 == ~m_true) return l1; + literal max = fresh("max"); + s.s().mk_clause(~l1, max); + s.s().mk_clause(~l2, max); + s.s().mk_clause(~max, l1, l2); + return max; + } + + literal ba_solver::ba_sort::mk_min(literal l1, literal l2) { + return ~mk_max(~l1, ~l2); + } + + void ba_solver::ba_sort::mk_clause(unsigned n, literal const* lits) { + m_lits.reset(); + m_lits.append(n, lits); + s.s().mk_clause(n, m_lits.c_ptr()); + } + + std::ostream& ba_solver::ba_sort::pp(std::ostream& out, literal l) const { + return out << l; + } + + + // ------------------------------- + // set literals equivalent + + bool ba_solver::set_root(literal l, literal r) { + if (s().is_assumption(l.var())) { + return false; + } + m_root_vars.reserve(s().num_vars(), false); + for (unsigned i = m_roots.size(); i < 2 * s().num_vars(); ++i) { + m_roots.push_back(to_literal(i)); + } + m_roots[l.index()] = r; + m_roots[(~l).index()] = ~r; + m_root_vars[l.var()] = true; + return true; + } + + void ba_solver::flush_roots() { + if (m_roots.empty()) return; + + // validate(); + m_visited.resize(s().num_vars()*2, false); + m_constraint_removed = false; + for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) + flush_roots(*m_constraints[i]); + for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) + flush_roots(*m_learned[i]); + cleanup_constraints(); + // validate(); + } + + void ba_solver::recompile(constraint& c) { + if (c.id() == _bad_id) { + IF_VERBOSE(0, display(verbose_stream() << "recompile\n", c, true);); + } + switch (c.tag()) { + case card_t: + recompile(c.to_card()); + break; + case pb_t: + recompile(c.to_pb()); + break; + case xr_t: + NOT_IMPLEMENTED_YET(); + break; + default: + UNREACHABLE(); + } + } + + void ba_solver::recompile(card& c) { + SASSERT(c.lit() == null_literal || is_watched(c.lit(), c)); + + // pre-condition is that the literals, except c.lit(), in c are unwatched. + if (c.id() == _bad_id) std::cout << "recompile: " << c << "\n"; + // IF_VERBOSE(0, verbose_stream() << c << "\n";); + m_weights.resize(2*s().num_vars(), 0); + for (literal l : c) { + ++m_weights[l.index()]; + } + unsigned k = c.k(); + bool all_units = true; + unsigned sz = c.size(); + unsigned_vector coeffs; + literal_vector lits; + unsigned j = 0; + for (unsigned i = 0; i < sz && 0 < k; ++i) { + literal l = c[i]; + unsigned w = m_weights[l.index()]; + unsigned w2 = m_weights[(~l).index()]; + if (w == 0 || w < w2) { + continue; + } + else if (k <= w2) { + k = 0; + break; + } + else { + SASSERT(w2 <= w && w2 < k); + k -= w2; + w -= w2; + m_weights[(~l).index()] = 0; + m_weights[l.index()] = 0; + if (w == 0) { + continue; + } + else { + all_units &= (w == 1); + coeffs.push_back(w); + c[j++] = l; + } + } + } + sz = j; + + // clear weights + for (literal l : c) { + m_weights[l.index()] = 0; + m_weights[(~l).index()] = 0; + } + + if (k == 0 && c.lit() == null_literal) { + remove_constraint(c, "recompiled to true"); + return; + } + + if (k == 1 && c.lit() == null_literal) { + literal_vector lits(sz, c.literals().c_ptr()); + s().mk_clause(sz, lits.c_ptr(), c.learned()); + remove_constraint(c, "recompiled to clause"); + return; + } + + if (sz == 0) { + if (c.lit() == null_literal) { + if (k > 0) { + s().mk_clause(0, nullptr, true); + } + } + else if (k > 0) { + literal lit = ~c.lit(); + s().mk_clause(1, &lit, c.learned()); + } + else { + literal lit = c.lit(); + s().mk_clause(1, &lit, c.learned()); + } + remove_constraint(c, "recompiled to clause"); + return; + } + if (all_units && sz < k) { + // IF_VERBOSE(0, verbose_stream() << "all units " << sz << " " << k << "\n"); + if (c.lit() == null_literal) { + s().mk_clause(0, nullptr, true); + } + else { + literal lit = ~c.lit(); + s().mk_clause(1, &lit, c.learned()); + } + remove_constraint(c, "recompiled to clause"); + return; + } + // IF_VERBOSE(0, verbose_stream() << "csz: " << c.size() << " ck:" << c.k() << " sz:" << sz << " k:" << k << "\n"); + VERIFY(!all_units || c.size() - c.k() >= sz - k); + c.set_size(sz); + c.set_k(k); + + if (all_units && clausify(c)) { + return; + } + + if (!all_units) { + TRACE("ba", tout << "replacing by pb: " << c << "\n";); + m_wlits.reset(); + for (unsigned i = 0; i < sz; ++i) { + m_wlits.push_back(wliteral(coeffs[i], c[i])); + } + literal root = c.lit(); + remove_constraint(c, "recompiled to pb"); + add_pb_ge(root, m_wlits, k, c.learned()); + } + else { + if (c.lit() == null_literal || value(c.lit()) == l_true) { + init_watch(c); + } + SASSERT(c.lit() == null_literal || is_watched(c.lit(), c)); + SASSERT(c.well_formed()); + } + } + + bool ba_solver::clausify(literal lit, unsigned n, literal const* lits, unsigned k) { + return false; + bool is_def = lit != null_literal; + if ((!is_def || !s().was_eliminated(lit)) && + !std::any_of(lits, lits + n, [&](literal l) { return s().was_eliminated(l); })) { + literal def_lit = m_sort.ge(is_def, k, n, lits); + if (is_def) { + s().mk_clause(~lit, def_lit); + s().mk_clause( lit, ~def_lit); + } + return true; + } + return false; + } + + bool ba_solver::clausify(xr& x) { + return false; + } + + bool ba_solver::clausify(card& c) { + return false; + if (get_config().m_card_solver) + return false; + + // + // TBD: conditions for when to clausify are TBD and + // handling of conditional cardinality as well. + // + if (!c.learned() && clausify(c.lit(), c.size(), c.begin(), c.k())) { + IF_VERBOSE(0, verbose_stream() << "clausify " << c << "\n";); + // compiled + } + remove_constraint(c, "recompiled to clauses"); + return true; + } + + bool ba_solver::clausify(pb& p) { + return false; + if (get_config().m_card_solver) + return false; + + bool ok = !p.learned(); + bool is_def = p.lit() != null_literal; + for (wliteral wl : p) { + ok &= !s().was_eliminated(wl.second); + } + ok &= !is_def || !s().was_eliminated(p.lit()); + if (!ok) { + remove_constraint(p, "recompiled to clauses"); + return true; + } + + if (is_cardinality(p, m_lemma)) { + literal lit = m_sort.ge(is_def, p.k(), m_lemma.size(), m_lemma.c_ptr()); + if (is_def) { + s().mk_clause(p.lit(), ~lit); + s().mk_clause(~p.lit(), lit); + } + remove_constraint(p, "recompiled to clauses"); + return true; + } + return false; + } + + bool ba_solver::is_cardinality(pb const& p, literal_vector& lits) { + lits.reset(); + p.size(); + for (wliteral wl : p) { + if (lits.size() > 2*p.size() + wl.first) { + return false; + } + for (unsigned i = 0; i < wl.first; ++i) { + lits.push_back(wl.second); + } + } + return true; + } + + void ba_solver::split_root(constraint& c) { + switch (c.tag()) { + case card_t: split_root(c.to_card()); break; + case pb_t: split_root(c.to_pb()); break; + case xr_t: NOT_IMPLEMENTED_YET(); break; + } + } + + void ba_solver::flush_roots(constraint& c) { + if (c.lit() != null_literal && !is_watched(c.lit(), c)) { + watch_literal(c.lit(), c); + watch_literal(~c.lit(), c); + } + SASSERT(c.lit() == null_literal || is_watched(c.lit(), c)); + bool found = c.lit() != null_literal && m_root_vars[c.lit().var()]; + for (unsigned i = 0; !found && i < c.size(); ++i) { + found = m_root_vars[c.get_lit(i).var()]; + } + if (!found) return; + clear_watch(c); + + // this could create duplicate literals + for (unsigned i = 0; i < c.size(); ++i) { + literal lit = m_roots[c.get_lit(i).index()]; + c.set_lit(i, lit); + } + + literal root = c.lit(); + if (root != null_literal && m_roots[root.index()] != root) { + root = m_roots[root.index()]; + nullify_tracking_literal(c); + c.update_literal(root); + watch_literal(root, c); + watch_literal(~root, c); + } + + bool found_dup = false; + bool found_root = false; + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c.get_lit(i); + if (is_marked(l)) { + found_dup = true; + break; + } + else { + mark_visited(l); + mark_visited(~l); + } + } + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c.get_lit(i); + unmark_visited(l); + unmark_visited(~l); + found_root |= l.var() == root.var(); + } + + if (found_root) { + split_root(c); + c.negate(); + split_root(c); + remove_constraint(c, "flush roots"); + } + else if (found_dup) { + recompile(c); + } + else { + if (c.lit() == null_literal || value(c.lit()) != l_undef) init_watch(c); + SASSERT(c.well_formed()); + } + } + + unsigned ba_solver::get_num_unblocked_bin(literal l) { + return s().m_simplifier.num_nonlearned_bin(l); + } + + /* + \brief garbage collection. + This entails + - finding pure literals, + - setting literals that are not used in the extension to non-external. + - subsumption + - resolution + - blocked literals + */ + void ba_solver::init_use_lists() { + m_visited.resize(s().num_vars()*2, false); + m_clause_use_list.init(s().num_vars()); + m_cnstr_use_list.reset(); + m_cnstr_use_list.resize(2*s().num_vars()); + for (clause* c : s().m_clauses) { + if (!c->frozen()) + m_clause_use_list.insert(*c); + } + for (constraint* cp : m_constraints) { + literal lit = cp->lit(); + if (lit != null_literal) { + m_cnstr_use_list[lit.index()].push_back(cp); + m_cnstr_use_list[(~lit).index()].push_back(cp); + } + switch (cp->tag()) { + case card_t: { + card& c = cp->to_card(); + for (literal l : c) { + m_cnstr_use_list[l.index()].push_back(&c); + if (lit != null_literal) m_cnstr_use_list[(~l).index()].push_back(&c); + } + break; + } + case pb_t: { + pb& p = cp->to_pb(); + for (wliteral wl : p) { + literal l = wl.second; + m_cnstr_use_list[l.index()].push_back(&p); + if (lit != null_literal) m_cnstr_use_list[(~l).index()].push_back(&p); + } + break; + } + case xr_t: { + xr& x = cp->to_xr(); + for (literal l : x) { + m_cnstr_use_list[l.index()].push_back(&x); + m_cnstr_use_list[(~l).index()].push_back(&x); + } + break; + } + } + } + } + + void ba_solver::remove_unused_defs() { + // remove constraints where indicator literal isn't used. + for (constraint* cp : m_constraints) { + constraint& c = *cp; + literal lit = c.lit(); + switch (c.tag()) { + case card_t: + case pb_t: { + if (lit != null_literal && + value(lit) == l_undef && + use_count(lit) == 1 && + use_count(~lit) == 1 && + get_num_unblocked_bin(lit) == 0 && + get_num_unblocked_bin(~lit) == 0) { + remove_constraint(c, "unused def"); + } + break; + } + default: + break; + } + } + } + + unsigned ba_solver::set_non_external() { + sat_simplifier_params p(s().m_params); + // set variables to be non-external if they are not used in theory constraints. + unsigned ext = 0; + bool incremental_mode = s().get_config().m_incremental && !p.override_incremental(); + incremental_mode |= s().tracking_assumptions(); + for (unsigned v = 0; !incremental_mode && v < s().num_vars(); ++v) { + literal lit(v, false); + if (s().is_external(v) && + m_cnstr_use_list[lit.index()].empty() && + m_cnstr_use_list[(~lit).index()].empty()) { + s().set_non_external(v); + ++ext; + } + } + // ensure that lemmas use only non-eliminated variables + for (constraint* cp : m_learned) { + constraint& c = *cp; + if (c.was_removed()) continue; + SASSERT(c.lit() == null_literal); + for (unsigned i = 0; i < c.size(); ++i) { + bool_var v = c.get_lit(i).var(); + if (s().was_eliminated(v)) { + remove_constraint(c, "contains eliminated var"); + break; + } + } + } + return ext; + } + + bool ba_solver::elim_pure(literal lit) { + if (value(lit) == l_undef && !m_cnstr_use_list[lit.index()].empty() && + use_count(~lit) == 0 && get_num_unblocked_bin(~lit) == 0) { + IF_VERBOSE(100, verbose_stream() << "pure literal: " << lit << "\n";); + s().assign(lit, justification()); + return true; + } + return false; + } + + unsigned ba_solver::elim_pure() { + // eliminate pure literals + unsigned pure_literals = 0; + for (unsigned v = 0; v < s().num_vars(); ++v) { + literal lit(v, false); + if (value(v) != l_undef) continue; + if (m_cnstr_use_list[lit.index()].empty() && + m_cnstr_use_list[(~lit).index()].empty()) continue; + + if (elim_pure(lit) || elim_pure(~lit)) { + ++pure_literals; + } + } + return pure_literals; + } + + void ba_solver::subsumption(constraint& cnstr) { + if (cnstr.was_removed()) return; + switch (cnstr.tag()) { + case card_t: { + card& c = cnstr.to_card(); + if (c.k() > 1) subsumption(c); + break; + } + case pb_t: { + pb& p = cnstr.to_pb(); + if (p.k() > 1) subsumption(p); + break; + } + default: + break; + } + } + + void ba_solver::cleanup_clauses() { + if (!m_clause_removed) return; + // version in simplify first clears + // all watch literals, then reinserts them. + // this ensures linear time cleanup. + clause_vector::iterator it = s().m_clauses.begin(); + clause_vector::iterator end = s().m_clauses.end(); + clause_vector::iterator it2 = it; + for (; it != end; ++it) { + clause* c = *it; + if (c->was_removed() && s().can_delete(*c)) { + s().detach_clause(*c); + s().del_clause(*c); + } + else { + if (it2 != it) { + *it2 = *it; + } + ++it2; + } + } + s().m_clauses.set_end(it2); + } + + void ba_solver::cleanup_constraints() { + if (!m_constraint_removed) return; + cleanup_constraints(m_constraints, false); + cleanup_constraints(m_learned, true); + m_constraint_removed = false; + } + + void ba_solver::cleanup_constraints(ptr_vector& cs, bool learned) { + ptr_vector::iterator it = cs.begin(); + ptr_vector::iterator it2 = it; + ptr_vector::iterator end = cs.end(); + for (; it != end; ++it) { + constraint& c = *(*it); + if (c.was_removed()) { + clear_watch(c); + nullify_tracking_literal(c); + m_allocator.deallocate(c.obj_size(), &c); + } + else if (learned && !c.learned()) { + m_constraints.push_back(&c); + } + else { + if (it != it2) { + *it2 = *it; + } + ++it2; + } + } + cs.set_end(it2); + } + + /* + \brief subsumption between two cardinality constraints + - A >= k subsumes A + B >= k' for k' <= k + - A + A' >= k subsumes A + B >= k' for k' + |A'| <= k + - A + lit >= k self subsumes A + ~lit + B >= k' into A + B >= k' for k' <= k + - version that generalizes self-subsumption to more than one literal + A + ~L + B >= k' => A + B >= k' if A + A' + L >= k and k' + |L| + |A'| <= k + */ + bool ba_solver::subsumes(card& c1, card& c2, literal_vector & comp) { + if (c2.lit() != null_literal) return false; + + unsigned c2_exclusive = 0; + unsigned common = 0; + comp.reset(); + for (literal l : c2) { + if (is_marked(l)) { + ++common; + } + else if (is_marked(~l)) { + comp.push_back(l); + } + else { + ++c2_exclusive; + } + } + + unsigned c1_exclusive = c1.size() - common - comp.size(); + return c1_exclusive + c2.k() + comp.size() <= c1.k(); + } + + /* + \brief L + A >= k subsumes L + C if |A| < k + A + L + B >= k self-subsumes A + ~L + C >= 1 + if k + 1 - |B| - |C| - |L| > 0 + */ + bool ba_solver::subsumes(card& c1, clause& c2, bool& self) { + unsigned common = 0, complement = 0, c2_exclusive = 0; + self = false; + + for (literal l : c2) { + if (is_marked(l)) { + ++common; + } + else if (is_marked(~l)) { + ++complement; + } + else { + ++c2_exclusive; + } + } + unsigned c1_exclusive = c1.size() - common - complement; + if (complement > 0 && c1.k() + 1 > c1_exclusive + c2_exclusive + common) { + self = true; + return true; + } + return c1.size() - common < c1.k(); + } + + /* + \brief Ax >= k subsumes By >= k' if + all coefficients in A are <= B and k >= k' + */ + bool ba_solver::subsumes(pb const& p1, pb_base const& p2) { + if (p1.k() < p2.k() || p1.size() > p2.size()) return false; + unsigned num_sub = 0; + for (unsigned i = 0; i < p2.size(); ++i) { + literal l = p2.get_lit(i); + if (is_marked(l) && m_weights[l.index()] <= p2.get_coeff(i)) { + ++num_sub; + } + if (p1.size() + i > p2.size() + num_sub) return false; + } + return num_sub == p1.size(); + } + + void ba_solver::subsumes(pb& p1, literal lit) { + for (constraint* c : m_cnstr_use_list[lit.index()]) { + if (c == &p1 || c->was_removed()) continue; + bool s = false; + switch (c->tag()) { + case card_t: + s = subsumes(p1, c->to_card()); + break; + case pb_t: + s = subsumes(p1, c->to_pb()); + break; + default: + break; + } + if (s) { + ++m_stats.m_num_pb_subsumes; + p1.set_learned(false); + remove_constraint(*c, "subsumed"); + } + } + } + + literal ba_solver::get_min_occurrence_literal(card const& c) { + unsigned occ_count = UINT_MAX; + literal lit = null_literal; + for (literal l : c) { + unsigned occ_count1 = m_cnstr_use_list[l.index()].size(); + if (occ_count1 < occ_count) { + lit = l; + occ_count = occ_count1; + } + } + return lit; + } + + void ba_solver::card_subsumption(card& c1, literal lit) { + literal_vector slit; + for (constraint* c : m_cnstr_use_list[lit.index()]) { + if (!c->is_card() || c == &c1 || c->was_removed()) { + continue; + } + card& c2 = c->to_card(); + + SASSERT(c1.index() != c2.index()); + if (subsumes(c1, c2, slit)) { + if (slit.empty()) { + TRACE("ba", tout << "subsume cardinality\n" << c1.index() << ":" << c1 << "\n" << c2.index() << ":" << c2 << "\n";); + remove_constraint(c2, "subsumed"); + ++m_stats.m_num_pb_subsumes; + c1.set_learned(false); + } + else { + TRACE("ba", tout << "self subsume cardinality\n";); + IF_VERBOSE(11, + verbose_stream() << "self-subsume cardinality\n"; + verbose_stream() << c1 << "\n"; + verbose_stream() << c2 << "\n";); + clear_watch(c2); + unsigned j = 0; + for (unsigned i = 0; i < c2.size(); ++i) { + if (!is_marked(~c2[i])) { + c2[j++] = c2[i]; + } + } + c2.set_size(j); + init_watch(c2); + m_simplify_change = true; + } + } + } + } + + void ba_solver::clause_subsumption(card& c1, literal lit, clause_vector& removed_clauses) { + SASSERT(!c1.was_removed()); + clause_use_list& occurs = m_clause_use_list.get(lit); + clause_use_list::iterator it = occurs.mk_iterator(); + while (!it.at_end()) { + clause& c2 = it.curr(); + bool self; + if (!c2.was_removed() && subsumes(c1, c2, self)) { + if (self) { + // self-subsumption is TBD + } + else { + TRACE("ba", tout << "remove\n" << c1 << "\n" << c2 << "\n";); + removed_clauses.push_back(&c2); + ++m_stats.m_num_clause_subsumes; + c1.set_learned(false); + } + } + it.next(); + } + } + + void ba_solver::binary_subsumption(card& c1, literal lit) { + if (c1.k() + 1 != c1.size()) return; + SASSERT(is_marked(lit)); + SASSERT(!c1.was_removed()); + watch_list & wlist = get_wlist(~lit); + watch_list::iterator it = wlist.begin(); + watch_list::iterator it2 = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + watched w = *it; + if (w.is_binary_clause() && is_marked(w.get_literal())) { + ++m_stats.m_num_bin_subsumes; + IF_VERBOSE(10, verbose_stream() << c1 << " subsumes (" << lit << " " << w.get_literal() << ")\n";); + if (!w.is_learned()) { + c1.set_learned(false); + } + } + else { + if (it != it2) { + *it2 = *it; + } + ++it2; + } + } + wlist.set_end(it2); + } + + void ba_solver::subsumption(card& c1) { + if (c1.was_removed() || c1.lit() != null_literal) { + return; + } + clause_vector removed_clauses; + for (literal l : c1) mark_visited(l); + for (unsigned i = 0; i < std::min(c1.size(), c1.k() + 1); ++i) { + literal lit = c1[i]; + card_subsumption(c1, lit); + clause_subsumption(c1, lit, removed_clauses); + binary_subsumption(c1, lit); + } + for (literal l : c1) unmark_visited(l); + m_clause_removed |= !removed_clauses.empty(); + for (clause *c : removed_clauses) { + c->set_removed(true); + m_clause_use_list.erase(*c); + } + } + + void ba_solver::subsumption(pb& p1) { + if (p1.was_removed() || p1.lit() != null_literal) { + return; + } + for (wliteral l : p1) { + SASSERT(m_weights[l.second.index()] == 0); + m_weights.setx(l.second.index(), l.first, 0); + mark_visited(l.second); + } + for (unsigned i = 0; i < std::min(10u, p1.num_watch()); ++i) { + unsigned j = s().m_rand() % p1.num_watch(); + subsumes(p1, p1[j].second); + } + for (wliteral l : p1) { + m_weights[l.second.index()] = 0; + unmark_visited(l.second); + } + } + + void ba_solver::clauses_modifed() {} + + lbool ba_solver::get_phase(bool_var v) { return l_undef; } + + /* + \brief lit <=> conjunction of unconstrained lits + */ + void ba_solver::assert_unconstrained(literal lit, literal_vector const& lits) { + if (lit == null_literal) { + for (literal l : lits) { + if (value(l) == l_undef) { + s().assign(l, justification()); + } + } + } + else { + // add clauses for: lit <=> conjunction of undef literals + SASSERT(value(lit) == l_undef); + literal_vector cl; + cl.push_back(lit); + for (literal l : lits) { + if (value(l) == l_undef) { + s().mk_clause(~lit, l); + cl.push_back(~l); + } + } + s().mk_clause(cl); + } + } + + extension* ba_solver::copy(solver* s) { + ba_solver* result = alloc(ba_solver); + result->set_solver(s); + copy_core(result, false); + return result; + } + + extension* ba_solver::copy(lookahead* s, bool learned) { + ba_solver* result = alloc(ba_solver); + result->set_lookahead(s); + copy_core(result, learned); + return result; + } + + void ba_solver::copy_core(ba_solver* result, bool learned) { + copy_constraints(result, m_constraints); + if (learned) copy_constraints(result, m_learned); + } + + void ba_solver::copy_constraints(ba_solver* result, ptr_vector const& constraints) { + literal_vector lits; + svector wlits; + for (constraint* cp : constraints) { + switch (cp->tag()) { + case card_t: { + card const& c = cp->to_card(); + lits.reset(); + for (literal l : c) lits.push_back(l); + result->add_at_least(c.lit(), lits, c.k(), c.learned()); + break; + } + case pb_t: { + pb const& p = cp->to_pb(); + wlits.reset(); + for (wliteral w : p) { + wlits.push_back(w); + } + result->add_pb_ge(p.lit(), wlits, p.k(), p.learned()); + break; + } + case xr_t: { + xr const& x = cp->to_xr(); + lits.reset(); + for (literal l : x) lits.push_back(l); + result->add_xr(lits, x.learned()); + break; + } + default: + UNREACHABLE(); + } + } + } + + void ba_solver::init_use_list(ext_use_list& ul) { + ul.init(s().num_vars()); + for (constraint const* cp : m_constraints) { + ext_constraint_idx idx = cp->index(); + if (cp->lit() != null_literal) { + ul.insert(cp->lit(), idx); + ul.insert(~cp->lit(), idx); + } + switch (cp->tag()) { + case card_t: { + card const& c = cp->to_card(); + for (literal l : c) { + ul.insert(l, idx); + } + break; + } + case pb_t: { + pb const& p = cp->to_pb(); + for (wliteral w : p) { + ul.insert(w.second, idx); + } + break; + } + case xr_t: { + xr const& x = cp->to_xr(); + for (literal l : x) { + ul.insert(l, idx); + ul.insert(~l, idx); + } + break; + } + default: + UNREACHABLE(); + } + } + } + + // + // literal is used in a clause (C or l), it + // it occurs negatively in constraint c. + // all literals in C are marked + // + bool ba_solver::is_blocked(literal l, ext_constraint_idx idx) { + constraint const& c = index2constraint(idx); + simplifier& sim = s().m_simplifier; + if (c.lit() != null_literal) return false; + switch (c.tag()) { + case card_t: { + card const& ca = c.to_card(); + unsigned weight = 0; + for (literal l2 : ca) { + if (sim.is_marked(~l2)) ++weight; + } + return weight >= ca.k(); + } + case pb_t: { + pb const& p = c.to_pb(); + unsigned weight = 0, offset = 0; + for (wliteral l2 : p) { + if (~l2.second == l) { + offset = l2.first; + break; + } + } + SASSERT(offset != 0); + for (wliteral l2 : p) { + if (sim.is_marked(~l2.second)) { + weight += std::min(offset, l2.first); + } + } + return weight >= p.k(); + } + default: + break; + } + return false; + } + + + void ba_solver::find_mutexes(literal_vector& lits, vector & mutexes) { + literal_set slits(lits); + bool change = false; + for (constraint* cp : m_constraints) { + if (!cp->is_card()) continue; + card const& c = cp->to_card(); + if (c.size() == c.k() + 1) { + literal_vector mux; + for (literal lit : c) { + if (slits.contains(~lit)) { + mux.push_back(~lit); + } + } + if (mux.size() <= 1) { + continue; + } + + for (literal m : mux) { + slits.remove(m); + } + change = true; + mutexes.push_back(mux); + } + } + if (!change) return; + lits.reset(); + for (literal l : slits) { + lits.push_back(l); + } + } + + void ba_solver::display(std::ostream& out, ineq& ineq, bool values) const { + for (unsigned i = 0; i < ineq.m_lits.size(); ++i) { + out << ineq.m_coeffs[i] << "*" << ineq.m_lits[i] << " "; + if (values) out << value(ineq.m_lits[i]) << " "; + } + out << ">= " << ineq.m_k << "\n"; + } + + void ba_solver::display(std::ostream& out, xr const& x, bool values) const { + out << "xr: "; + for (literal l : x) { + out << l; + if (values) { + out << "@(" << value(l); + if (value(l) != l_undef) { + out << ":" << lvl(l); + } + out << ") "; + } + else { + out << " "; + } + } + out << "\n"; + } + + void ba_solver::display_lit(std::ostream& out, literal lit, unsigned sz, bool values) const { + if (lit != null_literal) { + if (values) { + out << lit << "[" << sz << "]"; + out << "@(" << value(lit); + if (value(lit) != l_undef) { + out << ":" << lvl(lit); + } + out << "): "; + } + else { + out << lit << " == "; + } + } + } + + void ba_solver::display(std::ostream& out, card const& c, bool values) const { + display_lit(out, c.lit(), c.size(), values); + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c[i]; + out << l; + if (values) { + out << "@(" << value(l); + if (value(l) != l_undef) { + out << ":" << lvl(l); + } + out << ") "; + } + else { + out << " "; + } + } + out << ">= " << c.k() << "\n"; + } + + std::ostream& ba_solver::display(std::ostream& out) const { + for (constraint const* c : m_constraints) { + out << (*c) << "\n"; + } + if (!m_learned.empty()) { + out << "learned:\n"; + } + for (constraint const* c : m_learned) { + out << (*c) << "\n"; + } + return out; + } + + std::ostream& ba_solver::display_justification(std::ostream& out, ext_justification_idx idx) const { + return out << index2constraint(idx); + } + + void ba_solver::display(std::ostream& out, constraint const& c, bool values) const { + switch (c.tag()) { + case card_t: display(out, c.to_card(), values); break; + case pb_t: display(out, c.to_pb(), values); break; + case xr_t: display(out, c.to_xr(), values); break; + default: UNREACHABLE(); break; + } + } + + void ba_solver::collect_statistics(statistics& st) const { + st.update("ba propagations", m_stats.m_num_propagations); + st.update("ba conflicts", m_stats.m_num_conflicts); + st.update("ba resolves", m_stats.m_num_resolves); + st.update("ba cuts", m_stats.m_num_cut); + st.update("ba gc", m_stats.m_num_gc); + } + + bool ba_solver::validate_unit_propagation(card const& c, literal alit) const { + (void) alit; + if (c.lit() != null_literal && value(c.lit()) != l_true) return false; + for (unsigned i = c.k(); i < c.size(); ++i) { + if (value(c[i]) != l_false) return false; + } + return true; + } + + bool ba_solver::validate_unit_propagation(pb const& p, literal alit) const { + if (p.lit() != null_literal && value(p.lit()) != l_true) { + return false; + } + + unsigned sum = 0; + TRACE("ba", display(tout << "validate: " << alit << "\n", p, true);); + for (wliteral wl : p) { + literal lit = wl.second; + lbool val = value(lit); + if (val != l_false && lit != alit) { + sum += wl.first; + } + } + return sum < p.k(); + } + + bool ba_solver::validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const { + // all elements of r are true, + for (literal l : r) { + if (value(l) != l_true) { + IF_VERBOSE(0, verbose_stream() << "value of " << l << " is " << value(l) << "\n"; + display(verbose_stream(), p, true);); + return false; + } + if (value(alit) == l_true && lvl(l) > lvl(alit)) { + IF_VERBOSE(0, + verbose_stream() << "level of premise " << l << " is " << lvl(l) << "\n"; + verbose_stream() << "level of asserting literal " << alit << " is " << lvl(alit) << "\n"; + display(verbose_stream(), p, true);); + return false; + } + // if (value(alit) == l_true && lvl(l) == lvl(alit)) { + // std::cout << "same level " << alit << " " << l << "\n"; + // } + } + // the sum of elements not in r or alit add up to less than k. + unsigned sum = 0; + // + // a*x + b*alit + c*r >= k + // sum a < k + // val(r) = false + // hence alit has to be true. + for (wliteral wl : p) { + literal lit = wl.second; + if (lit != alit && !r.contains(~lit)) { + sum += wl.first; + } + } + if (sum >= p.k()) { + IF_VERBOSE(0, + verbose_stream() << "sum is " << sum << " >= " << p.k() << "\n"; + display(verbose_stream(), p, true); + verbose_stream() << "id: " << p.id() << "\n"; + sum = 0; + for (wliteral wl : p) sum += wl.first; + verbose_stream() << "overall sum " << sum << "\n"; + verbose_stream() << "asserting literal: " << alit << "\n"; + verbose_stream() << "reason: " << r << "\n";); + return false; + } + for (wliteral wl : p) { + if (alit == wl.second) { + return true; + } + } + IF_VERBOSE(0, verbose_stream() << alit << " not found among literals\n";); + return false; + } + + bool ba_solver::validate_unit_propagation(xr const& x, literal alit) const { + if (value(x.lit()) != l_true) return false; + for (unsigned i = 1; i < x.size(); ++i) { + if (value(x[i]) == l_undef) return false; + } + return true; + } + + bool ba_solver::validate_lemma() { + int64_t bound64 = m_bound; + int64_t val = -bound64; + reset_active_var_set(); + for (bool_var v : m_active_vars) { + if (m_active_var_set.contains(v)) continue; + int64_t coeff = get_coeff(v); + if (coeff == 0) continue; + m_active_var_set.insert(v); + literal lit(v, false); + if (coeff < 0 && value(lit) != l_true) { + val -= coeff; + } + else if (coeff > 0 && value(lit) != l_false) { + val += coeff; + } + } + CTRACE("ba", val >= 0, active2pb(m_A); display(tout, m_A);); + return val < 0; + } + + void ba_solver::reset_active_var_set() { + while (!m_active_var_set.empty()) m_active_var_set.erase(); + } + + void ba_solver::active2pb(ineq& p) { + reset_active_var_set(); + p.reset(m_bound); + for (bool_var v : m_active_vars) { + if (m_active_var_set.contains(v)) continue; + int64_t coeff = get_coeff(v); + if (coeff == 0) continue; + m_active_var_set.insert(v); + literal lit(v, coeff < 0); + p.m_lits.push_back(lit); + p.m_coeffs.push_back(std::abs(coeff)); + } + } + + ba_solver::constraint* ba_solver::active2constraint() { + reset_active_var_set(); + m_wlits.reset(); + uint64_t sum = 0; + if (m_bound == 1) return 0; + if (m_overflow) return 0; + + for (bool_var v : m_active_vars) { + int coeff = get_int_coeff(v); + if (m_active_var_set.contains(v) || coeff == 0) continue; + m_active_var_set.insert(v); + literal lit(v, coeff < 0); + m_wlits.push_back(wliteral(get_abs_coeff(v), lit)); + sum += get_abs_coeff(v); + } + + if (m_overflow || sum >= UINT_MAX/2) { + return 0; + } + else { + return add_pb_ge(null_literal, m_wlits, m_bound, true); + } + } + + /* + Chai Kuhlmann: + + a1*l1 + ... + a_n*l_n >= k + s.t. + a1 >= a2 >= .. >= a_n + + let m be such that + + sum_{i = 1}^{m-1} a_i < k <= sum_{i = 1}^{m} + + then + + l1 + ... + l_n >= m + + furthermore, for the largest n' <= n, such that + + sum_{i = n'+1}^n a_i + sum_{i = 1}^{m-1} a_i < k + + then + + l1 + ... + l_n' >= m + + */ + struct compare_wlit { + bool operator()(ba_solver::wliteral l1, ba_solver::wliteral l2) const { + return l1.first > l2.first; + } + }; + + + ba_solver::constraint* ba_solver::active2card() { + normalize_active_coeffs(); + m_wlits.reset(); + for (bool_var v : m_active_vars) { + int coeff = get_int_coeff(v); + m_wlits.push_back(std::make_pair(get_abs_coeff(v), literal(v, coeff < 0))); + } + std::sort(m_wlits.begin(), m_wlits.end(), compare_wlit()); + unsigned k = 0; + uint64_t sum = 0, sum0 = 0; + for (wliteral wl : m_wlits) { + if (sum >= m_bound) break; + sum0 = sum; + sum += wl.first; + ++k; + } + if (k == 1) { + return 0; + } + while (!m_wlits.empty()) { + wliteral wl = m_wlits.back(); + if (wl.first + sum0 >= m_bound) break; + m_wlits.pop_back(); + sum0 += wl.first; + } + + unsigned slack = 0; + unsigned max_level = 0; + unsigned num_max_level = 0; + for (wliteral wl : m_wlits) { + if (value(wl.second) != l_false) ++slack; + unsigned level = lvl(wl.second); + if (level > max_level) { + max_level = level; + num_max_level = 1; + } + else if (max_level == level) { + ++num_max_level; + } + } + if (m_overflow) return 0; + + if (slack >= k) { +#if 0 + return active2constraint(); + active2pb(m_A); + std::cout << "not asserting\n"; + display(std::cout, m_A, true); +#endif + return 0; + } + + // produce asserting cardinality constraint + literal_vector lits; + for (wliteral wl : m_wlits) { + lits.push_back(wl.second); + } + constraint* c = add_at_least(null_literal, lits, k, true); + + if (c) { + // IF_VERBOSE(0, verbose_stream() << *c << "\n";); + lits.reset(); + for (wliteral wl : m_wlits) { + if (value(wl.second) == l_false) lits.push_back(wl.second); + } + unsigned glue = s().num_diff_levels(lits.size(), lits.c_ptr()); + c->set_glue(glue); + } + return c; + } + + + void ba_solver::justification2pb(justification const& js, literal lit, unsigned offset, ineq& ineq) { + switch (js.get_kind()) { + case justification::NONE: + ineq.reset(offset); + ineq.push(lit, offset); + break; + case justification::BINARY: + ineq.reset(offset); + ineq.push(lit, offset); + ineq.push(js.get_literal(), offset); + break; + case justification::TERNARY: + ineq.reset(offset); + ineq.push(lit, offset); + ineq.push(js.get_literal1(), offset); + ineq.push(js.get_literal2(), offset); + break; + case justification::CLAUSE: { + ineq.reset(offset); + clause & c = s().get_clause(js); + for (literal l : c) ineq.push(l, offset); + break; + } + case justification::EXT_JUSTIFICATION: { + ext_justification_idx index = js.get_ext_justification_idx(); + constraint& cnstr = index2constraint(index); + switch (cnstr.tag()) { + case card_t: { + card& c = cnstr.to_card(); + ineq.reset(offset*c.k()); + for (literal l : c) ineq.push(l, offset); + if (c.lit() != null_literal) ineq.push(~c.lit(), offset*c.k()); + break; + } + case pb_t: { + pb& p = cnstr.to_pb(); + ineq.reset(p.k()); + for (wliteral wl : p) ineq.push(wl.second, wl.first); + if (p.lit() != null_literal) ineq.push(~p.lit(), p.k()); + break; + } + case xr_t: { + xr& x = cnstr.to_xr(); + literal_vector ls; + get_antecedents(lit, x, ls); + ineq.reset(offset); + for (literal l : ls) ineq.push(~l, offset); + literal lxr = x.lit(); + if (lxr != null_literal) ineq.push(~lxr, offset); + break; + } + default: + UNREACHABLE(); + break; + } + break; + } + default: + UNREACHABLE(); + break; + } + } + + + // validate that m_A & m_B implies m_C + + bool ba_solver::validate_resolvent() { + return true; + u_map coeffs; + uint64_t k = m_A.m_k + m_B.m_k; + for (unsigned i = 0; i < m_A.m_lits.size(); ++i) { + uint64_t coeff = m_A.m_coeffs[i]; + SASSERT(!coeffs.contains(m_A.m_lits[i].index())); + coeffs.insert(m_A.m_lits[i].index(), coeff); + } + for (unsigned i = 0; i < m_B.m_lits.size(); ++i) { + uint64_t coeff1 = m_B.m_coeffs[i], coeff2; + literal lit = m_B.m_lits[i]; + if (coeffs.find((~lit).index(), coeff2)) { + if (coeff1 == coeff2) { + coeffs.remove((~lit).index()); + k += coeff1; + } + else if (coeff1 < coeff2) { + coeffs.insert((~lit).index(), coeff2 - coeff1); + k += coeff1; + } + else { + SASSERT(coeff2 < coeff1); + coeffs.remove((~lit).index()); + coeffs.insert(lit.index(), coeff1 - coeff2); + k += coeff2; + } + } + else if (coeffs.find(lit.index(), coeff2)) { + coeffs.insert(lit.index(), coeff1 + coeff2); + } + else { + coeffs.insert(lit.index(), coeff1); + } + } + // C is above the sum of A and B + for (unsigned i = 0; i < m_C.m_lits.size(); ++i) { + literal lit = m_C.m_lits[i]; + uint64_t coeff; + if (coeffs.find(lit.index(), coeff)) { + if (coeff > m_C.m_coeffs[i] && m_C.m_coeffs[i] < m_C.m_k) { + goto violated; + } + coeffs.remove(lit.index()); + } + } + if (!coeffs.empty()) goto violated; + if (m_C.m_k > k) goto violated; + SASSERT(coeffs.empty()); + SASSERT(m_C.m_k <= k); + return true; + + violated: + // last ditch effort by translating to SAT. + solver s0(s().m_params, s().rlimit()); + u_map translation; + literal l1 = translate_to_sat(s0, translation, m_A); + if (l1 == null_literal) return true; + literal l2 = translate_to_sat(s0, translation, m_B); + if (l2 == null_literal) return true; + ineq notC = negate(m_B); + literal l3 = translate_to_sat(s0, translation, notC); + if (l3 == null_literal) return true; + s0.assign(l1, justification()); + s0.assign(l2, justification()); + s0.assign(l3, justification()); + lbool is_sat = s0.check(); + TRACE("ba", s0.display(tout << "trying sat encoding");); + if (is_sat == l_false) return true; + + IF_VERBOSE(0, + display(verbose_stream(), m_A); + display(verbose_stream(), m_B); + display(verbose_stream(), m_C); + for (auto& e : coeffs) { + verbose_stream() << to_literal(e.m_key) << ": " << e.m_value << "\n"; + }); + + UNREACHABLE(); + return false; + } + + /** + \brief translate PB inequality to SAT formula. + */ + literal ba_solver::translate_to_sat(solver& s, u_map& translation, ineq const& pb) { + SASSERT(pb.m_k > 0); + if (pb.m_lits.size() > 1) { + ineq a, b; + a.reset(pb.m_k); + b.reset(pb.m_k); + for (unsigned i = 0; i < pb.m_lits.size()/2; ++i) { + a.push(pb.m_lits[i], pb.m_coeffs[i]); + } + for (unsigned i = pb.m_lits.size()/2; i < pb.m_lits.size(); ++i) { + b.push(pb.m_lits[i], pb.m_coeffs[i]); + } + bool_var v = s.mk_var(); + literal lit(v, false); + literal_vector lits; + lits.push_back(~lit); + push_lit(lits, translate_to_sat(s, translation, a)); + push_lit(lits, translate_to_sat(s, translation, b)); + push_lit(lits, translate_to_sat(s, translation, a, b)); + s.mk_clause(lits); + return lit; + } + if (pb.m_coeffs[0] >= pb.m_k) { + return translate_to_sat(s, translation, pb.m_lits[0]); + } + else { + return null_literal; + } + } + + /* + \brief encode the case where Sum(a) >= k-1 & Sum(b) >= 1 \/ ... \/ Sum(a) >= 1 & Sum(b) >= k-1 + */ + literal ba_solver::translate_to_sat(solver& s, u_map& translation, ineq& a, ineq& b) { + uint64_t k0 = a.m_k; + literal_vector lits; + for (unsigned k = 1; k < a.m_k - 1; ++k) { + a.m_k = k; b.m_k = k0 - k; + literal lit1 = translate_to_sat(s, translation, a); + literal lit2 = translate_to_sat(s, translation, b); + if (lit1 != null_literal && lit2 != null_literal) { + bool_var v = s.mk_var(); + literal lit(v, false); + s.mk_clause(~lit, lit1); + s.mk_clause(~lit, lit2); + lits.push_back(lit); + } + } + a.m_k = k0; + b.m_k = k0; + switch (lits.size()) { + case 0: return null_literal; + case 1: return lits[0]; + default: { + bool_var v = s.mk_var(); + literal lit(v, false); + lits.push_back(~lit); + s.mk_clause(lits); + return lit; + } + } + } + + literal ba_solver::translate_to_sat(solver& s, u_map& translation, literal lit) { + bool_var v; + if (!translation.find(lit.var(), v)) { + v = s.mk_var(); + translation.insert(lit.var(), v); + } + return literal(v, lit.sign()); + } + + ba_solver::ineq ba_solver::negate(ineq const& a) const { + ineq result; + uint64_t sum = 0; + for (unsigned i = 0; i < a.m_lits.size(); ++i) { + result.push(~a.m_lits[i], a.m_coeffs[i]); + sum += a.m_coeffs[i]; + } + SASSERT(sum >= a.m_k + 1); + result.m_k = sum + 1 - a.m_k; + return result; + } + + void ba_solver::push_lit(literal_vector& lits, literal lit) { + if (lit != null_literal) { + lits.push_back(lit); + } + } + + bool ba_solver::validate_conflict(literal_vector const& lits, ineq& p) { + for (literal l : lits) { + if (value(l) != l_false) { + TRACE("ba", tout << "literal " << l << " is not false\n";); + return false; + } + if (!p.m_lits.contains(l)) { + TRACE("ba", tout << "lemma contains literal " << l << " not in inequality\n";); + return false; + } + } + uint64_t value = 0; + for (unsigned i = 0; i < p.m_lits.size(); ++i) { + uint64_t coeff = p.m_coeffs[i]; + if (!lits.contains(p.m_lits[i])) { + value += coeff; + } + } + CTRACE("ba", value >= p.m_k, tout << "slack: " << value << " bound " << p.m_k << "\n"; + display(tout, p); + tout << lits << "\n";); + return value < p.m_k; + } + + bool ba_solver::check_model(model const& m) const { + bool ok = true; + for (constraint const* c : m_constraints) { + if (c->is_pure() && c->lit() != null_literal && m[c->lit().var()] == (c->lit().sign() ? l_true : l_false)) { + continue; + } + switch (eval(m, *c)) { + case l_false: + IF_VERBOSE(0, verbose_stream() << "failed checking " << c->id() << ": " << *c << "\n";); + ok = false; + break; + case l_true: + break; + case l_undef: + IF_VERBOSE(0, verbose_stream() << "undef " << c->id() << ": " << *c << "\n";); + break; + } + } + return ok; + } + + +}; + diff --git a/src/sat/ba_solver.h b/src/sat/ba_solver.h new file mode 100644 index 000000000..e947cee96 --- /dev/null +++ b/src/sat/ba_solver.h @@ -0,0 +1,531 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + ba_solver.h + +Abstract: + + Cardinality extensions, + Pseudo Booleans, + Xors + +Author: + + Nikolaj Bjorner (nbjorner) 2017-01-30 + +Revision History: + +--*/ +#ifndef BA_SOLVER_H_ +#define BA_SOLVER_H_ + +#include "sat/sat_extension.h" +#include "sat/sat_solver.h" +#include "sat/sat_lookahead.h" +#include "sat/sat_unit_walk.h" +#include "util/scoped_ptr_vector.h" +#include "util/sorting_network.h" + +namespace sat { + + class ba_solver : public extension { + + friend class local_search; + + struct stats { + unsigned m_num_propagations; + unsigned m_num_conflicts; + unsigned m_num_resolves; + unsigned m_num_bin_subsumes; + unsigned m_num_clause_subsumes; + unsigned m_num_pb_subsumes; + unsigned m_num_cut; + unsigned m_num_gc; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + public: + enum tag_t { + card_t, + pb_t, + xr_t + }; + + class card; + class pb; + class xr; + + class constraint { + protected: + tag_t m_tag; + bool m_removed; + literal m_lit; + literal m_watch; + unsigned m_glue; + unsigned m_psm; + unsigned m_size; + size_t m_obj_size; + bool m_learned; + unsigned m_id; + bool m_pure; // is the constraint pure (only positive occurrences) + public: + constraint(tag_t t, unsigned id, literal l, unsigned sz, size_t osz): + m_tag(t), m_removed(false), m_lit(l), m_watch(null_literal), m_glue(0), m_psm(0), m_size(sz), m_obj_size(osz), m_learned(false), m_id(id), m_pure(false) {} + ext_constraint_idx index() const { return reinterpret_cast(this); } + unsigned id() const { return m_id; } + tag_t tag() const { return m_tag; } + literal lit() const { return m_lit; } + unsigned size() const { return m_size; } + void set_size(unsigned sz) { SASSERT(sz <= m_size); m_size = sz; } + void update_literal(literal l) { m_lit = l; } + bool was_removed() const { return m_removed; } + void set_removed() { m_removed = true; } + void nullify_literal() { m_lit = null_literal; } + unsigned glue() const { return m_glue; } + void set_glue(unsigned g) { m_glue = g; } + unsigned psm() const { return m_psm; } + void set_psm(unsigned p) { m_psm = p; } + void set_learned(bool f) { m_learned = f; } + bool learned() const { return m_learned; } + bool is_watched() const { return m_watch == m_lit && m_lit != null_literal; } + void set_watch() { m_watch = m_lit; } + void clear_watch() { m_watch = null_literal; } + bool is_clear() const { return m_watch == null_literal && m_lit != null_literal; } + bool is_pure() const { return m_pure; } + void set_pure() { m_pure = true; } + + size_t obj_size() const { return m_obj_size; } + card& to_card(); + pb& to_pb(); + xr& to_xr(); + card const& to_card() const; + pb const& to_pb() const; + xr const& to_xr() const; + bool is_card() const { return m_tag == card_t; } + bool is_pb() const { return m_tag == pb_t; } + bool is_xr() const { return m_tag == xr_t; } + + virtual bool is_watching(literal l) const { UNREACHABLE(); return false; }; + virtual literal_vector literals() const { UNREACHABLE(); return literal_vector(); } + virtual void swap(unsigned i, unsigned j) { UNREACHABLE(); } + virtual literal get_lit(unsigned i) const { UNREACHABLE(); return null_literal; } + virtual void set_lit(unsigned i, literal l) { UNREACHABLE(); } + virtual bool well_formed() const { return true; } + virtual void negate() { UNREACHABLE(); } + }; + + friend std::ostream& operator<<(std::ostream& out, constraint const& c); + + // base class for pb and cardinality constraints + class pb_base : public constraint { + protected: + unsigned m_k; + public: + pb_base(tag_t t, unsigned id, literal l, unsigned sz, size_t osz, unsigned k): constraint(t, id, l, sz, osz), m_k(k) { VERIFY(k < 4000000000); } + 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; + }; + + class card : public pb_base { + literal m_lits[0]; + public: + static size_t get_obj_size(unsigned num_lits) { return sizeof(card) + num_lits * sizeof(literal); } + card(unsigned id, literal lit, literal_vector const& lits, unsigned k); + literal operator[](unsigned i) const { return m_lits[i]; } + 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; } + }; + + + typedef std::pair wliteral; + + class pb : public pb_base { + unsigned m_slack; + unsigned m_num_watch; + unsigned m_max_sum; + wliteral m_wlits[0]; + public: + static size_t get_obj_size(unsigned num_lits) { return sizeof(pb) + num_lits * sizeof(wliteral); } + pb(unsigned id, literal lit, svector const& wlits, unsigned k); + literal lit() const { return m_lit; } + wliteral operator[](unsigned i) const { return m_wlits[i]; } + wliteral& operator[](unsigned i) { return m_wlits[i]; } + wliteral const* begin() const { return m_wlits; } + wliteral const* end() const { return begin() + m_size; } + + unsigned slack() const { return m_slack; } + void set_slack(unsigned s) { m_slack = s; } + unsigned num_watch() const { return m_num_watch; } + unsigned max_sum() const { return m_max_sum; } + 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; } + }; + + class xr : public constraint { + literal m_lits[0]; + public: + static size_t get_obj_size(unsigned num_lits) { return sizeof(xr) + num_lits * sizeof(literal); } + xr(unsigned id, literal_vector const& lits); + 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; + }; + + + protected: + + struct ineq { + literal_vector m_lits; + svector m_coeffs; + uint64_t m_k; + ineq(): m_k(0) {} + void reset(uint64_t k) { m_lits.reset(); m_coeffs.reset(); m_k = k; } + void push(literal l, uint64_t c) { m_lits.push_back(l); m_coeffs.push_back(c); } + }; + + solver* m_solver; + lookahead* m_lookahead; + unit_walk* m_unit_walk; + stats m_stats; + small_object_allocator m_allocator; + + + ptr_vector m_constraints; + ptr_vector m_learned; + ptr_vector m_constraint_to_reinit; + unsigned_vector m_constraint_to_reinit_lim; + unsigned m_constraint_to_reinit_last_sz; + unsigned m_constraint_id; + + // conflict resolution + unsigned m_num_marks; + unsigned m_conflict_lvl; + svector m_coeffs; + svector m_active_vars; + unsigned m_bound; + tracked_uint_set m_active_var_set; + literal_vector m_lemma; + literal_vector m_skipped; + unsigned m_num_propagations_since_pop; + unsigned_vector m_parity_marks; + literal_vector m_parity_trail; + + unsigned_vector m_pb_undef; + + struct ba_sort { + typedef sat::literal pliteral; + typedef sat::literal_vector pliteral_vector; + + ba_solver& s; + pliteral m_true; + pliteral_vector m_lits; + + + ba_sort(ba_solver& s): s(s), m_true(null_literal) {} + pliteral mk_false(); + pliteral mk_true(); + pliteral mk_not(pliteral l); + pliteral fresh(char const*); + pliteral mk_max(pliteral l1, pliteral l2); + pliteral mk_min(pliteral l1, pliteral l2); + void mk_clause(unsigned n, literal const* lits); + std::ostream& pp(std::ostream& out, pliteral l) const; + }; + ba_sort m_ba; + psort_nw m_sort; + + void ensure_parity_size(bool_var v); + unsigned get_parity(bool_var v); + void inc_parity(bool_var v); + void reset_parity(bool_var v); + + solver& s() const { return *m_solver; } + + + // simplification routines + + svector m_visited; + vector> m_cnstr_use_list; + use_list m_clause_use_list; + bool m_simplify_change; + bool m_clause_removed; + bool m_constraint_removed; + literal_vector m_roots; + svector m_root_vars; + unsigned_vector m_weights; + svector m_wlits; + bool subsumes(card& c1, card& c2, literal_vector& comp); + bool subsumes(card& c1, clause& c2, bool& self); + bool subsumed(card& c1, literal l1, literal l2); + bool subsumes(pb const& p1, pb_base const& p2); + void subsumes(pb& p1, literal lit); + void subsumption(pb& p1); + void binary_subsumption(card& c1, literal lit); + void clause_subsumption(card& c1, literal lit, clause_vector& removed_clauses); + void card_subsumption(card& c1, literal lit); + void mark_visited(literal l) { m_visited[l.index()] = true; } + void unmark_visited(literal l) { m_visited[l.index()] = false; } + bool is_marked(literal l) const { return m_visited[l.index()] != 0; } + unsigned get_num_unblocked_bin(literal l); + literal get_min_occurrence_literal(card const& c); + void init_use_lists(); + void remove_unused_defs(); + unsigned set_non_external(); + unsigned elim_pure(); + bool elim_pure(literal lit); + void subsumption(constraint& c1); + void subsumption(card& c1); + void gc_half(char const* _method); + void update_psm(constraint& c) const; + void mutex_reduction(); + void update_pure(); + + unsigned use_count(literal lit) const { return m_cnstr_use_list[lit.index()].size() + m_clause_use_list.get(lit).size(); } + + void cleanup_clauses(); + void cleanup_constraints(); + void cleanup_constraints(ptr_vector& cs, bool learned); + void remove_constraint(constraint& c, char const* reason); + + // constraints + constraint& index2constraint(size_t idx) const { return *reinterpret_cast(idx); } + void pop_constraint(); + void unwatch_literal(literal w, constraint& c); + void watch_literal(literal w, constraint& c); + void watch_literal(wliteral w, pb& p); + bool is_watched(literal l, constraint const& c) const; + void add_constraint(constraint* c); + bool init_watch(constraint& c); + void init_watch(bool_var v); + void clear_watch(constraint& c); + lbool add_assign(constraint& c, literal l); + void simplify(constraint& c); + void nullify_tracking_literal(constraint& c); + void set_conflict(constraint& c, literal lit); + void assign(constraint& c, literal lit); + bool assigned_above(literal above, literal below); + void get_antecedents(literal l, constraint const& c, literal_vector & r); + bool validate_conflict(constraint const& c) const; + bool validate_unit_propagation(constraint const& c, literal alit) const; + void attach_constraint(constraint const& c); + void detach_constraint(constraint const& c); + lbool eval(constraint const& c) const; + lbool eval(model const& m, constraint const& c) const; + lbool eval(lbool a, lbool b) const; + void assert_unconstrained(literal lit, literal_vector const& lits); + void flush_roots(constraint& c); + void recompile(constraint& c); + void split_root(constraint& c); + unsigned next_id() { return m_constraint_id++; } + + + // cardinality + bool init_watch(card& c); + lbool add_assign(card& c, literal lit); + void clear_watch(card& c); + void reset_coeffs(); + void reset_marked_literals(); + void get_antecedents(literal l, card const& c, literal_vector & r); + void flush_roots(card& c); + void recompile(card& c); + bool clausify(card& c); + bool clausify(literal lit, unsigned n, literal const* lits, unsigned k); + lbool eval(card const& c) const; + lbool eval(model const& m, card const& c) const; + double get_reward(card const& c, literal_occs_fun& occs) const; + + + // xr specific functionality + void clear_watch(xr& x); + bool init_watch(xr& x); + bool parity(xr const& x, unsigned offset) const; + lbool add_assign(xr& x, literal alit); + void get_xr_antecedents(literal l, unsigned index, justification js, literal_vector& r); + void get_antecedents(literal l, xr const& x, literal_vector & r); + void simplify(xr& x); + bool clausify(xr& x); + void flush_roots(xr& x); + lbool eval(xr const& x) const; + lbool eval(model const& m, xr const& x) const; + + // pb functionality + unsigned m_a_max; + bool init_watch(pb& p); + lbool add_assign(pb& p, literal alit); + void add_index(pb& p, unsigned index, literal lit); + void clear_watch(pb& p); + void get_antecedents(literal l, pb const& p, literal_vector & r); + void split_root(pb_base& p); + void simplify(pb_base& p); + void simplify2(pb& p); + bool is_cardinality(pb const& p); + void flush_roots(pb& p); + void recompile(pb& p); + bool clausify(pb& p); + bool is_cardinality(pb const& p, literal_vector& lits); + lbool eval(pb const& p) const; + lbool eval(model const& m, pb const& p) const; + double get_reward(pb const& p, literal_occs_fun& occs) const; + + // access solver + inline lbool value(bool_var v) const { return value(literal(v, false)); } + inline lbool value(literal lit) const { return m_lookahead ? m_lookahead->value(lit) : m_solver->value(lit); } + inline lbool value(model const& m, literal l) const { return l.sign() ? ~m[l.var()] : m[l.var()]; } + + inline unsigned lvl(literal lit) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(lit); } + inline unsigned lvl(bool_var v) const { return m_lookahead || m_unit_walk ? 0 : m_solver->lvl(v); } + inline bool inconsistent() const { + if (m_lookahead) return m_lookahead->inconsistent(); + if (m_unit_walk) return m_unit_walk->inconsistent(); + return m_solver->inconsistent(); + } + inline watch_list& get_wlist(literal l) { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); } + inline watch_list const& get_wlist(literal l) const { return m_lookahead ? m_lookahead->get_wlist(l) : m_solver->get_wlist(l); } + inline void assign(literal l, justification j) { + if (m_lookahead) m_lookahead->assign(l); + else if (m_unit_walk) m_unit_walk->assign(l); + else m_solver->assign(l, j); + } + inline void set_conflict(justification j, literal l) { + if (m_lookahead) m_lookahead->set_conflict(); + else if (m_unit_walk) m_unit_walk->set_conflict(); + else m_solver->set_conflict(j, l); + } + inline config const& get_config() const { return m_lookahead ? m_lookahead->get_config() : m_solver->get_config(); } + inline void drat_add(literal_vector const& c, svector const& premises) { if (m_solver) m_solver->m_drat.add(c, premises); } + + + mutable bool m_overflow; + void reset_active_var_set(); + void normalize_active_coeffs(); + void inc_coeff(literal l, unsigned offset); + int64_t get_coeff(bool_var v) const; + unsigned get_abs_coeff(bool_var v) const; + int get_int_coeff(bool_var v) const; + unsigned get_bound() const; + void inc_bound(int64_t i); + + literal get_asserting_literal(literal conseq); + void process_antecedent(literal l, unsigned offset); + void process_card(card& c, unsigned offset); + void cut(); + bool create_asserting_lemma(); + + // validation utilities + bool validate_conflict(card const& c) const; + bool validate_conflict(xr const& x) const; + bool validate_conflict(pb const& p) const; + bool validate_assign(literal_vector const& lits, literal lit); + bool validate_lemma(); + bool validate_unit_propagation(card const& c, literal alit) const; + bool validate_unit_propagation(pb const& p, literal alit) const; + bool validate_unit_propagation(pb const& p, literal_vector const& r, literal alit) const; + bool validate_unit_propagation(xr const& x, literal alit) const; + bool validate_conflict(literal_vector const& lits, ineq& p); + bool validate_watch_literals() const; + bool validate_watch_literal(literal lit) const; + bool validate_watched_constraint(constraint const& c) const; + bool validate_watch(pb const& p, literal alit) const; + bool is_watching(literal lit, constraint const& c) const; + literal translate_to_sat(solver& s, u_map& translation, ineq const& pb); + literal translate_to_sat(solver& s, u_map& translation, ineq& a, ineq& b); + literal translate_to_sat(solver& s, u_map& translation, literal lit); + ineq negate(ineq const& a) const; + void push_lit(literal_vector& lits, literal lit); + + ineq m_A, m_B, m_C; + void active2pb(ineq& p); + constraint* active2constraint(); + constraint* active2card(); + void justification2pb(justification const& j, literal lit, unsigned offset, ineq& p); + bool validate_resolvent(); + + void display(std::ostream& out, ineq& p, bool values = false) const; + void display(std::ostream& out, card const& c, bool values) const; + void display(std::ostream& out, pb const& p, bool values) const; + void display(std::ostream& out, xr const& c, bool values) const; + void display_lit(std::ostream& out, literal l, unsigned sz, bool values) const; + + constraint* add_at_least(literal l, literal_vector const& lits, unsigned k, bool learned); + constraint* add_pb_ge(literal l, svector const& wlits, unsigned k, bool learned); + constraint* add_xr(literal_vector const& lits, bool learned); + + void copy_core(ba_solver* result, bool learned); + void copy_constraints(ba_solver* result, ptr_vector const& constraints); + + 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; } + 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; + + ptr_vector const & constraints() const { return m_constraints; } + void display(std::ostream& out, constraint const& c, bool values) const; + + virtual bool validate(); + + + }; + +}; + +#endif diff --git a/src/sat/dimacs.cpp b/src/sat/dimacs.cpp index 512be5f4b..463418b23 100644 --- a/src/sat/dimacs.cpp +++ b/src/sat/dimacs.cpp @@ -24,10 +24,12 @@ Revision History: class stream_buffer { std::istream & m_stream; int m_val; + unsigned m_line; public: stream_buffer(std::istream & s): - m_stream(s) { + m_stream(s), + m_line(0) { m_val = m_stream.get(); } @@ -37,7 +39,10 @@ public: void operator ++() { m_val = m_stream.get(); + if (m_val == '\n') ++m_line; } + + unsigned line() const { return m_line; } }; template @@ -76,7 +81,7 @@ int parse_int(Buffer & in) { } if (*in < '0' || *in > '9') { - std::cerr << "(error, \"unexpected char: " << *in << "\")\n"; + std::cerr << "(error, \"unexpected char: " << *in << " line: " << in.line() << "\")\n"; exit(3); exit(ERR_PARSER); } diff --git a/src/sat/sat_allocator.h b/src/sat/sat_allocator.h new file mode 100644 index 000000000..06585ebed --- /dev/null +++ b/src/sat/sat_allocator.h @@ -0,0 +1,103 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + sat_allocator.h + +Abstract: + + Small object allocator suitable for clauses + +Author: + + Nikolaj bjorner (nbjorner) 2018-04-26. + +Revision History: +--*/ + +#ifndef SAT_ALLOCATOR_H_ +#define SAT_ALLOCATOR_H_ + +#include "util/vector.h" +#include "util/machine.h" + +class sat_allocator { + static const unsigned CHUNK_SIZE = (1 << 16); + static const unsigned SMALL_OBJ_SIZE = 512; + static const unsigned MASK = ((1 << PTR_ALIGNMENT) - 1); + static const unsigned NUM_FREE = 1 + (SMALL_OBJ_SIZE >> PTR_ALIGNMENT); + struct chunk { + char * m_curr; + char m_data[CHUNK_SIZE]; + chunk():m_curr(m_data) {} + }; + char const * m_id; + size_t m_alloc_size; + ptr_vector m_chunks; + void * m_chunk_ptr; + ptr_vector m_free[NUM_FREE]; + + unsigned align_size(size_t sz) const { + return free_slot_id(sz) << PTR_ALIGNMENT; + } + unsigned free_slot_id(size_t size) const { + return (static_cast(size >> PTR_ALIGNMENT) + ((0 != (size & MASK)) ? 1u : 0u)); + } +public: + sat_allocator(char const * id = "unknown"): m_id(id), m_alloc_size(0), m_chunk_ptr(nullptr) {} + ~sat_allocator() { reset(); } + void reset() { + for (chunk * ch : m_chunks) dealloc(ch); + m_chunks.reset(); + for (unsigned i = 0; i < NUM_FREE; ++i) m_free[i].reset(); + m_alloc_size = 0; + m_chunk_ptr = nullptr; + } + void * allocate(size_t size) { + m_alloc_size += size; + if (size >= SMALL_OBJ_SIZE) { + return memory::allocate(size); + } + unsigned slot_id = free_slot_id(size); + if (!m_free[slot_id].empty()) { + void* result = m_free[slot_id].back(); + m_free[slot_id].pop_back(); + return result; + } + if (m_chunks.empty()) { + m_chunks.push_back(alloc(chunk)); + m_chunk_ptr = m_chunks.back(); + } + + unsigned sz = align_size(size); + if ((char*)m_chunk_ptr + sz > (char*)m_chunks.back() + CHUNK_SIZE) { + m_chunks.push_back(alloc(chunk)); + m_chunk_ptr = m_chunks.back(); + } + void * result = m_chunk_ptr; + m_chunk_ptr = (char*)m_chunk_ptr + sz; + return result; + } + + void deallocate(size_t size, void * p) { + m_alloc_size -= size; + if (size >= SMALL_OBJ_SIZE) { + memory::deallocate(p); + } + else { + m_free[free_slot_id(size)].push_back(p); + } + } + size_t get_allocation_size() const { return m_alloc_size; } + + char const* id() const { return m_id; } +}; + +inline void * operator new(size_t s, sat_allocator & r) { return r.allocate(s); } +inline void * operator new[](size_t s, sat_allocator & r) { return r.allocate(s); } +inline void operator delete(void * p, sat_allocator & r) { UNREACHABLE(); } +inline void operator delete[](void * p, sat_allocator & r) { UNREACHABLE(); } + +#endif /* SAT_ALLOCATOR_H_ */ + diff --git a/src/sat/sat_asymm_branch.cpp b/src/sat/sat_asymm_branch.cpp index 8d5778f0b..941426321 100644 --- a/src/sat/sat_asymm_branch.cpp +++ b/src/sat/sat_asymm_branch.cpp @@ -19,6 +19,7 @@ Revision History: #include "sat/sat_asymm_branch.h" #include "sat/sat_asymm_branch_params.hpp" #include "sat/sat_solver.h" +#include "sat/sat_big.h" #include "util/stopwatch.h" #include "util/trace.h" @@ -26,9 +27,11 @@ namespace sat { asymm_branch::asymm_branch(solver & _s, params_ref const & p): s(_s), + m_params(p), m_counter(0) { updt_params(p); reset_statistics(); + m_calls = 0; } struct clause_size_lt { @@ -39,43 +42,75 @@ namespace sat { asymm_branch & m_asymm_branch; stopwatch m_watch; unsigned m_elim_literals; + unsigned m_elim_learned_literals; + unsigned m_tr; report(asymm_branch & a): m_asymm_branch(a), - m_elim_literals(a.m_elim_literals) { + m_elim_literals(a.m_elim_literals), + m_elim_learned_literals(a.m_elim_learned_literals), + m_tr(a.m_tr) { m_watch.start(); } ~report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-asymm-branch :elim-literals " - << (m_asymm_branch.m_elim_literals - m_elim_literals) + unsigned num_learned = (m_asymm_branch.m_elim_learned_literals - m_elim_learned_literals); + unsigned num_total = (m_asymm_branch.m_elim_literals - m_elim_literals); + verbose_stream() + << " (sat-asymm-branch :elim-literals " << (num_total - num_learned) + << " :elim-learned-literals " << num_learned + << " :hte " << (m_asymm_branch.m_tr - m_tr) << " :cost " << m_asymm_branch.m_counter << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; - - void asymm_branch::operator()(bool force) { - if (!m_asymm_branch) - return; - s.propagate(false); // must propagate, since it uses s.push() - if (s.m_inconsistent) - return; - if (!force && m_counter > 0) - return; - CASSERT("asymm_branch", s.check_invariant()); - TRACE("asymm_branch_detail", s.display(tout);); - report rpt(*this); - svector saved_phase(s.m_phase); - m_counter = 0; // counter is moving down to capture propagate cost. - int limit = -static_cast(m_asymm_branch_limit); - std::stable_sort(s.m_clauses.begin(), s.m_clauses.end(), clause_size_lt()); - m_counter -= s.m_clauses.size(); - SASSERT(s.m_qhead == s.m_trail.size()); - clause_vector::iterator it = s.m_clauses.begin(); + + void asymm_branch::process_bin(big& big) { + m_tr += big.reduce_tr(s); + } + + bool asymm_branch::process(big& big, bool learned) { + unsigned elim0 = m_elim_literals; + unsigned eliml0 = m_elim_learned_literals; + for (unsigned i = 0; i < m_asymm_branch_rounds; ++i) { + unsigned elim = m_elim_literals + m_tr; + big.init(s, learned); + process(&big, s.m_clauses); + process(&big, s.m_learned); + process_bin(big); + s.propagate(false); + if (s.m_inconsistent) + break; + unsigned num_elim = m_elim_literals + m_tr - elim; + IF_VERBOSE(1, verbose_stream() << "(sat-asymm-branch-step :elim " << num_elim << ")\n";); + if (num_elim == 0) + break; + } + IF_VERBOSE(1, if (m_elim_learned_literals > eliml0) + verbose_stream() << "(sat-asymm-branch :elim " << m_elim_learned_literals - eliml0 << ")\n";); + return m_elim_literals > elim0; + } + + bool asymm_branch::process(bool learned) { + unsigned eliml0 = m_elim_learned_literals; + unsigned elim = m_elim_literals; + process(nullptr, s.m_clauses); + s.propagate(false); + IF_VERBOSE(1, if (m_elim_learned_literals > eliml0) + verbose_stream() << "(sat-asymm-branch :elim " << m_elim_learned_literals - eliml0 << ")\n";); + return m_elim_literals > elim; + } + + + void asymm_branch::process(big* big, clause_vector& clauses) { + int64_t limit = -m_asymm_branch_limit; + std::stable_sort(clauses.begin(), clauses.end(), clause_size_lt()); + m_counter -= clauses.size(); + clause_vector::iterator it = clauses.begin(); clause_vector::iterator it2 = it; - clause_vector::iterator end = s.m_clauses.end(); + clause_vector::iterator end = clauses.end(); try { for (; it != end; ++it) { if (s.inconsistent()) { @@ -84,44 +119,339 @@ namespace sat { } break; } - SASSERT(s.m_qhead == s.m_trail.size()); - if (m_counter < limit || s.inconsistent()) { + clause & c = *(*it); + if (m_counter < limit || s.inconsistent() || c.was_removed()) { *it2 = *it; ++it2; continue; } - s.checkpoint(); - clause & c = *(*it); - m_counter -= c.size(); - if (!process(c)) + s.checkpoint(); + if (big ? !process_sampled(*big, c) : !process(c)) { continue; // clause was removed + } *it2 = *it; ++it2; } - s.m_clauses.set_end(it2); + clauses.set_end(it2); } catch (solver_exception & ex) { // put m_clauses in a consistent state... for (; it != end; ++it, ++it2) { *it2 = *it; } - s.m_clauses.set_end(it2); + clauses.set_end(it2); m_counter = -m_counter; throw ex; } - m_counter = -m_counter; - s.m_phase = saved_phase; + } + + + void asymm_branch::operator()(bool force) { + ++m_calls; + if (m_calls <= m_asymm_branch_delay) + return; + if (!m_asymm_branch && !m_asymm_branch_all && !m_asymm_branch_sampled) + return; + s.propagate(false); // must propagate, since it uses s.push() + if (s.m_inconsistent) + return; + if (!force && m_counter > 0) { + m_counter /= 100; + return; + } CASSERT("asymm_branch", s.check_invariant()); + TRACE("asymm_branch_detail", s.display(tout);); + report rpt(*this); + svector saved_phase(s.m_phase); + + bool change = true; + unsigned counter = 0; + while (change && counter < 2) { + ++counter; + change = false; + if (m_asymm_branch_sampled) { + big big(s.m_rand); + if (process(big, true)) change = true; + } + if (m_asymm_branch_sampled) { + big big(s.m_rand); + if (process(big, false)) change = true; + } + if (m_asymm_branch) { + m_counter = 0; + if (process(true)) change = true; + m_counter = -m_counter; + } + } + + s.m_phase = saved_phase; + m_asymm_branch_limit *= 2; + if (m_asymm_branch_limit > UINT_MAX) + m_asymm_branch_limit = UINT_MAX; + + CASSERT("asymm_branch", s.check_invariant()); + } + + /** + \brief try asymmetric branching on all literals in clause. + */ + + bool asymm_branch::process_all(clause & c) { + scoped_detach scoped_d(s, c); // clause must not be used for propagation + unsigned sz = c.size(); + SASSERT(sz > 0); + unsigned i = 0, new_sz = sz; + for (i = sz; i-- > 0; ) { + if (flip_literal_at(c, i, new_sz)) + return cleanup(scoped_d, c, i, new_sz); + } + return true; + } + + struct asymm_branch::compare_left { + big& s; + compare_left(big& s): s(s) {} + bool operator()(literal u, literal v) const { + return s.get_left(u) < s.get_left(v); + } + }; + + void asymm_branch::sort(big& big, clause const& c) { + sort(big, c.begin(), c.end()); + } + + void asymm_branch::radix_sort(big& big, literal_vector& lits) { + const unsigned d = 4; + const unsigned w = 20; // 1M variable cap + unsigned sz = lits.size(); + m_tmp.reserve(sz); + for (unsigned p = 0; p < w/d; ++p) { + unsigned on[16]; + memset(on, 0, 16*sizeof(unsigned)); + for (literal l : lits) on[(big.get_left(l) >> 4*p) & 15]++; + for (unsigned i = 1; i < 16; ++i) on[i] += on[i-1]; + for (unsigned i = sz; i-- > 0; ) + m_tmp[--on[(big.get_left(lits[i]) >> 4*p) & 15]] = lits[i]; + for (unsigned i = sz; i-- > 0; ) lits[i] = m_tmp[i]; + } + } + + void asymm_branch::sort(big& big, literal const* begin, literal const* end) { + m_pos.reset(); m_neg.reset(); + for (; begin != end; ++begin) { + literal l = *begin; + m_pos.push_back(l); + m_neg.push_back(~l); + } + compare_left cmp(big); + std::sort(m_pos.begin(), m_pos.end(), cmp); + std::sort(m_neg.begin(), m_neg.end(), cmp); + + // alternative: worse + // radix_sort(big, m_pos); + // radix_sort(big, m_neg); + + IF_VERBOSE(100, + for (literal l : m_pos) verbose_stream() << big.get_left(l) << " "; + verbose_stream() << "\n"; + for (literal l : m_neg) verbose_stream() << big.get_left(l) << " "; + verbose_stream() << "\n"; + ); + } + + bool asymm_branch::uhte(big& big, clause & c) { + unsigned pindex = 0, nindex = 0; + literal lpos = m_pos[pindex++]; + literal lneg = m_neg[nindex++]; + while (true) { + if (big.get_left(lneg) > big.get_left(lpos)) { + if (pindex == m_pos.size()) return false; + lpos = m_pos[pindex++]; + } + else if (big.get_right(lneg) < big.get_right(lpos) || + (m_pos.size() == 2 && (lpos == ~lneg || big.get_parent(lpos) == lneg))) { + if (nindex == m_neg.size()) return false; + lneg = m_neg[nindex++]; + } + else { + return true; + } + } + return false; + } + + void asymm_branch::minimize(big& big, literal_vector& lemma) { + big.ensure_big(s, true); + sort(big, lemma.begin(), lemma.end()); + uhle(big); + if (!m_to_delete.empty()) { + unsigned j = 0; + for (unsigned i = 0; i < lemma.size(); ++i) { + literal l = lemma[i]; + if (!m_to_delete.contains(l)) { + lemma[j++] = l; + } + } + lemma.shrink(j); + } + } + + void asymm_branch::uhle(big& big) { + m_to_delete.reset(); + if (m_to_delete.empty()) { + int right = big.get_right(m_pos.back()); + for (unsigned i = m_pos.size() - 1; i-- > 0; ) { + literal lit = m_pos[i]; + int right2 = big.get_right(lit); + if (right2 > right) { + // lit => last, so lit can be deleted + m_to_delete.push_back(lit); + } + else { + right = right2; + } + } + } + if (m_to_delete.empty()) { + int right = big.get_right(m_neg[0]); + for (unsigned i = 1; i < m_neg.size(); ++i) { + literal lit = m_neg[i]; + int right2 = big.get_right(lit); + if (right > right2) { + // ~first => ~lit + m_to_delete.push_back(~lit); + } + else { + right = right2; + } + } + } + } + + bool asymm_branch::uhle(scoped_detach& scoped_d, big& big, clause & c) { + uhle(big); + if (!m_to_delete.empty()) { + unsigned j = 0; + for (unsigned i = 0; i < c.size(); ++i) { + literal lit = c[i]; + switch (s.value(lit)) { + case l_true: + scoped_d.del_clause(); + return false; + case l_false: + break; + default: + if (!m_to_delete.contains(lit)) { + c[j++] = lit; + } + break; + } + } + return re_attach(scoped_d, c, j); + } + else { + return true; + } + } + + + bool asymm_branch::propagate_literal(clause const& c, literal l) { + SASSERT(!s.inconsistent()); + TRACE("asymm_branch_detail", tout << "assigning: " << l << "\n";); + s.assign(l, justification()); + s.propagate_core(false); // must not use propagate(), since check_missed_propagation may fail for c + return s.inconsistent(); + } + + bool asymm_branch::flip_literal_at(clause const& c, unsigned flip_index, unsigned& new_sz) { + VERIFY(s.m_trail.size() == s.m_qhead); + bool found_conflict = false; + unsigned i = 0, sz = c.size(); + s.push(); + for (i = 0; !found_conflict && i < sz; i++) { + if (i == flip_index) continue; + found_conflict = propagate_literal(c, ~c[i]); + } + if (!found_conflict) { + SASSERT(sz == i); + found_conflict = propagate_literal(c, c[flip_index]); + } + s.pop(1); + new_sz = i; + return found_conflict; + } + + bool asymm_branch::cleanup(scoped_detach& scoped_d, clause& c, unsigned skip_idx, unsigned new_sz) { + unsigned j = 0; + for (unsigned i = 0; i < new_sz; i++) { + if (skip_idx == i) continue; + literal l = c[i]; + switch (s.value(l)) { + case l_undef: + if (i != j) { + std::swap(c[i], c[j]); + } + j++; + break; + case l_false: + break; + case l_true: + UNREACHABLE(); + break; + } + } + new_sz = j; + return re_attach(scoped_d, c, new_sz); + } + + bool asymm_branch::re_attach(scoped_detach& scoped_d, clause& c, unsigned new_sz) { + VERIFY(s.m_trail.size() == s.m_qhead); + m_elim_literals += c.size() - new_sz; + if (c.is_learned()) { + m_elim_learned_literals += c.size() - new_sz; + } + + switch(new_sz) { + case 0: + s.set_conflict(justification()); + return false; + case 1: + TRACE("asymm_branch", tout << "produced unit clause: " << c[0] << "\n";); + s.assign(c[0], justification()); + s.propagate_core(false); + scoped_d.del_clause(); + return false; // check_missed_propagation() may fail, since m_clauses is not in a consistent state. + case 2: + SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); + VERIFY(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); + s.mk_bin_clause(c[0], c[1], c.is_learned()); + if (s.m_trail.size() > s.m_qhead) s.propagate_core(false); + scoped_d.del_clause(); + return false; + default: + c.shrink(new_sz); + if (s.m_config.m_drat) s.m_drat.add(c, true); + // if (s.m_config.m_drat) s.m_drat.del(c0); // TBD + return true; + } + } + + bool asymm_branch::process_sampled(big& big, clause & c) { + scoped_detach scoped_d(s, c); + sort(big, c); + if (uhte(big, c)) { + // don't touch hidden tautologies. + // ATE takes care of them. + return true; + } + return uhle(scoped_d, big, c); } bool asymm_branch::process(clause & c) { TRACE("asymm_branch_detail", tout << "processing: " << c << "\n";); SASSERT(s.scope_lvl() == 0); - SASSERT(s.m_qhead == s.m_trail.size()); -#ifdef Z3DEBUG - unsigned trail_sz = s.m_trail.size(); -#endif SASSERT(!s.inconsistent()); + unsigned sz = c.size(); SASSERT(sz > 0); unsigned i; @@ -133,83 +463,38 @@ namespace sat { return false; } } + m_counter -= c.size(); + + if (m_asymm_branch_all) return process_all(c); + // try asymmetric branching // clause must not be used for propagation - solver::scoped_detach scoped_d(s, c); - s.push(); - for (i = 0; i < sz - 1; i++) { - literal l = c[i]; - SASSERT(!s.inconsistent()); - TRACE("asymm_branch_detail", tout << "assigning: " << ~l << "\n";); - s.assign(~l, justification()); - s.propagate_core(false); // must not use propagate(), since check_missed_propagation may fail for c - if (s.inconsistent()) - break; - } - s.pop(1); + scoped_detach scoped_d(s, c); + unsigned new_sz = c.size(); + unsigned flip_position = m_rand(c.size()); + bool found_conflict = flip_literal_at(c, flip_position, new_sz); SASSERT(!s.inconsistent()); SASSERT(s.scope_lvl() == 0); - SASSERT(trail_sz == s.m_trail.size()); - SASSERT(s.m_qhead == s.m_trail.size()); - if (i == sz - 1) { + if (!found_conflict) { // clause size can't be reduced. return true; } - // clause can be reduced - unsigned new_sz = i+1; - SASSERT(new_sz >= 1); - SASSERT(new_sz < sz); - TRACE("asymm_branch", tout << c << "\nnew_size: " << new_sz << "\n"; - for (unsigned i = 0; i < c.size(); i++) tout << static_cast(s.value(c[i])) << " "; tout << "\n";); - // cleanup reduced clause - unsigned j = 0; - for (i = 0; i < new_sz; i++) { - literal l = c[i]; - switch (s.value(l)) { - case l_undef: - c[j] = l; - j++; - break; - case l_false: - break; - case l_true: - UNREACHABLE(); - break; - } - } - new_sz = j; - m_elim_literals += sz - new_sz; - switch(new_sz) { - case 0: - s.set_conflict(justification()); - return false; - case 1: - TRACE("asymm_branch", tout << "produced unit clause: " << c[0] << "\n";); - s.assign(c[0], justification()); - s.propagate_core(false); - scoped_d.del_clause(); - SASSERT(s.inconsistent() || s.m_qhead == s.m_trail.size()); - return false; // check_missed_propagation() may fail, since m_clauses is not in a consistent state. - case 2: - SASSERT(s.value(c[0]) == l_undef && s.value(c[1]) == l_undef); - s.mk_bin_clause(c[0], c[1], false); - scoped_d.del_clause(); - SASSERT(s.m_qhead == s.m_trail.size()); - return false; - default: - c.shrink(new_sz); - SASSERT(s.m_qhead == s.m_trail.size()); - return true; + else { + // clause can be reduced + return cleanup(scoped_d, c, flip_position, new_sz); } } void asymm_branch::updt_params(params_ref const & _p) { sat_asymm_branch_params p(_p); - m_asymm_branch = p.asymm_branch(); - m_asymm_branch_rounds = p.asymm_branch_rounds(); - m_asymm_branch_limit = p.asymm_branch_limit(); - if (m_asymm_branch_limit > INT_MAX) - m_asymm_branch_limit = INT_MAX; + m_asymm_branch = p.asymm_branch(); + m_asymm_branch_rounds = p.asymm_branch_rounds(); + m_asymm_branch_delay = p.asymm_branch_delay(); + m_asymm_branch_sampled = p.asymm_branch_sampled(); + m_asymm_branch_limit = p.asymm_branch_limit(); + m_asymm_branch_all = p.asymm_branch_all(); + if (m_asymm_branch_limit > UINT_MAX) + m_asymm_branch_limit = UINT_MAX; } void asymm_branch::collect_param_descrs(param_descrs & d) { @@ -218,10 +503,13 @@ namespace sat { void asymm_branch::collect_statistics(statistics & st) const { st.update("elim literals", m_elim_literals); + st.update("tr", m_tr); } void asymm_branch::reset_statistics() { m_elim_literals = 0; + m_elim_learned_literals = 0; + m_tr = 0; } }; diff --git a/src/sat/sat_asymm_branch.h b/src/sat/sat_asymm_branch.h index 9e28d1600..4d032022c 100644 --- a/src/sat/sat_asymm_branch.h +++ b/src/sat/sat_asymm_branch.h @@ -20,31 +20,79 @@ Revision History: #define SAT_ASYMM_BRANCH_H_ #include "sat/sat_types.h" +#include "sat/sat_big.h" #include "util/statistics.h" #include "util/params.h" namespace sat { class solver; + class scoped_detach; class asymm_branch { struct report; - solver & s; - int m_counter; - + solver & s; + params_ref m_params; + int64_t m_counter; + random_gen m_rand; + unsigned m_calls; + // config - bool m_asymm_branch; - unsigned m_asymm_branch_rounds; - unsigned m_asymm_branch_limit; + bool m_asymm_branch; + unsigned m_asymm_branch_rounds; + unsigned m_asymm_branch_delay; + bool m_asymm_branch_sampled; + bool m_asymm_branch_all; + int64_t m_asymm_branch_limit; // stats - unsigned m_elim_literals; + unsigned m_elim_literals; + unsigned m_elim_learned_literals; + unsigned m_tr; + + literal_vector m_pos, m_neg; // literals (complements of literals) in clauses sorted by discovery time (m_left in BIG). + svector> m_pos1, m_neg1; + literal_vector m_to_delete; + literal_vector m_tmp; + + struct compare_left; + + void sort(big& big, literal const* begin, literal const* end); + void sort(big & big, clause const& c); + void radix_sort(big & big, literal_vector& lits); + + bool uhle(scoped_detach& scoped_d, big & big, clause & c); + + void uhle(big & big); + + bool uhte(big & big, clause & c); + + bool re_attach(scoped_detach& scoped_d, clause& c, unsigned new_sz); + + bool process(bool learned); + + bool process(big& big, bool learned); bool process(clause & c); + + bool process_sampled(big& big, clause & c); + + void process(big* big, clause_vector & c); + + bool process_all(clause & c); + + void process_bin(big& big); + + bool flip_literal_at(clause const& c, unsigned flip_index, unsigned& new_sz); + + bool cleanup(scoped_detach& scoped_d, clause& c, unsigned skip_index, unsigned new_sz); + + bool propagate_literal(clause const& c, literal l); + public: asymm_branch(solver & s, params_ref const & p); - void operator()(bool force = false); + void operator()(bool force); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); @@ -52,7 +100,11 @@ namespace sat { void collect_statistics(statistics & st) const; void reset_statistics(); - void dec(unsigned c) { m_counter -= c; } + void minimize(big& big, literal_vector& lemma); + + void init_search() { m_calls = 0; } + + inline void dec(unsigned c) { m_counter -= c; } }; }; diff --git a/src/sat/sat_asymm_branch_params.pyg b/src/sat/sat_asymm_branch_params.pyg index 8940c64a6..5ee140ab9 100644 --- a/src/sat/sat_asymm_branch_params.pyg +++ b/src/sat/sat_asymm_branch_params.pyg @@ -2,5 +2,8 @@ def_module_params(module_name='sat', class_name='sat_asymm_branch_params', export=True, params=(('asymm_branch', BOOL, True, 'asymmetric branching'), - ('asymm_branch.rounds', UINT, 32, 'maximum number of rounds of asymmetric branching'), - ('asymm_branch.limit', UINT, 100000000, 'approx. maximum number of literals visited during asymmetric branching'))) + ('asymm_branch.rounds', UINT, 2, 'maximal number of rounds to run asymmetric branch simplifications if progress is made'), + ('asymm_branch.delay', UINT, 1, 'number of simplification rounds to wait until invoking asymmetric branch simplification'), + ('asymm_branch.sampled', BOOL, True, 'use sampling based asymmetric branching based on binary implication graph'), + ('asymm_branch.limit', UINT, 100000000, 'approx. maximum number of literals visited during asymmetric branching'), + ('asymm_branch.all', BOOL, False, 'asymmetric branching on all literals per clause'))) diff --git a/src/sat/sat_bdd.cpp b/src/sat/sat_bdd.cpp new file mode 100644 index 000000000..bd1745765 --- /dev/null +++ b/src/sat/sat_bdd.cpp @@ -0,0 +1,894 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_bdd.cpp + +Abstract: + + Simple BDD package modeled after BuDDy, which is modeled after CUDD. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-13 + +Revision History: + +--*/ + +#include "sat/sat_bdd.h" +#include "util/trace.h" +#include "util/stopwatch.h" + +namespace sat { + + bdd_manager::bdd_manager(unsigned num_vars) { + m_cost_metric = bdd_cost; + m_cost_bdd = 0; + for (BDD a = 0; a < 2; ++a) { + for (BDD b = 0; b < 2; ++b) { + for (unsigned op = bdd_and_op; op < bdd_not_op; ++op) { + unsigned index = a + 2*b + 4*op; + m_apply_const.reserve(index+1); + m_apply_const[index] = apply_const(a, b, static_cast(op)); + } + } + } + + // add dummy nodes for operations, and true, false bdds. + for (unsigned i = 0; i <= bdd_no_op + 2; ++i) { + m_nodes.push_back(bdd_node(0,0,0)); + m_nodes.back().m_refcount = max_rc; + m_nodes.back().m_index = m_nodes.size()-1; + } + + m_spare_entry = nullptr; + m_max_num_bdd_nodes = 1 << 24; // up to 16M nodes + m_mark_level = 0; + alloc_free_nodes(1024 + num_vars); + m_disable_gc = false; + m_is_new_node = false; + + // add variables + for (unsigned i = 0; i < num_vars; ++i) { + reserve_var(i); + } + } + + bdd_manager::~bdd_manager() { + if (m_spare_entry) { + m_alloc.deallocate(sizeof(*m_spare_entry), m_spare_entry); + } + for (auto* e : m_op_cache) { + SASSERT(e != m_spare_entry); + m_alloc.deallocate(sizeof(*e), e); + } + } + + bdd_manager::BDD bdd_manager::apply_const(BDD a, BDD b, bdd_op op) { + SASSERT(is_const(a) && is_const(b)); + switch (op) { + case bdd_and_op: + return (a == true_bdd && b == true_bdd) ? true_bdd : false_bdd; + case bdd_or_op: + return (a == true_bdd || b == true_bdd) ? true_bdd : false_bdd; + case bdd_xor_op: + return (a == b) ? false_bdd : true_bdd; + default: + return false_bdd; + } + } + + bdd_manager::BDD bdd_manager::apply(BDD arg1, BDD arg2, bdd_op op) { + bool first = true; + SASSERT(well_formed()); + while (true) { + try { + return apply_rec(arg1, arg2, op); + } + catch (mem_out) { + try_reorder(); + if (!first) throw; + first = false; + } + } + SASSERT(well_formed()); + } + + + bdd bdd_manager::mk_true() { return bdd(true_bdd, this); } + bdd bdd_manager::mk_false() { return bdd(false_bdd, this); } + bdd bdd_manager::mk_and(bdd const& a, bdd const& b) { return bdd(apply(a.root, b.root, bdd_and_op), this); } + bdd bdd_manager::mk_or(bdd const& a, bdd const& b) { return bdd(apply(a.root, b.root, bdd_or_op), this); } + bdd bdd_manager::mk_xor(bdd const& a, bdd const& b) { return bdd(apply(a.root, b.root, bdd_xor_op), this); } + bdd bdd_manager::mk_exists(unsigned v, bdd const& b) { return mk_exists(1, &v, b); } + bdd bdd_manager::mk_forall(unsigned v, bdd const& b) { return mk_forall(1, &v, b); } + + + bool bdd_manager::check_result(op_entry*& e1, op_entry const* e2, BDD a, BDD b, BDD c) { + if (e1 != e2) { + SASSERT(e2->m_result != null_bdd); + push_entry(e1); + e1 = nullptr; + return true; + } + else { + e1->m_bdd1 = a; + e1->m_bdd2 = b; + e1->m_op = c; + SASSERT(e1->m_result == null_bdd); + return false; + } + } + + bdd_manager::BDD bdd_manager::apply_rec(BDD a, BDD b, bdd_op op) { + switch (op) { + case bdd_and_op: + if (a == b) return a; + if (is_false(a) || is_false(b)) return false_bdd; + if (is_true(a)) return b; + if (is_true(b)) return a; + break; + case bdd_or_op: + if (a == b) return a; + if (is_false(a)) return b; + if (is_false(b)) return a; + if (is_true(a) || is_true(b)) return true_bdd; + break; + case bdd_xor_op: + if (a == b) return false_bdd; + if (is_false(a)) return b; + if (is_false(b)) return a; + break; + default: + UNREACHABLE(); + break; + } + if (is_const(a) && is_const(b)) { + return m_apply_const[a + 2*b + 4*op]; + } + op_entry * e1 = pop_entry(a, b, op); + op_entry const* e2 = m_op_cache.insert_if_not_there(e1); + if (check_result(e1, e2, a, b, op)) { + SASSERT(!m_free_nodes.contains(e2->m_result)); + return e2->m_result; + } + // SASSERT(well_formed()); + BDD r; + if (level(a) == level(b)) { + push(apply_rec(lo(a), lo(b), op)); + push(apply_rec(hi(a), hi(b), op)); + r = make_node(level(a), read(2), read(1)); + } + else if (level(a) > level(b)) { + push(apply_rec(lo(a), b, op)); + push(apply_rec(hi(a), b, op)); + r = make_node(level(a), read(2), read(1)); + } + else { + push(apply_rec(a, lo(b), op)); + push(apply_rec(a, hi(b), op)); + r = make_node(level(b), read(2), read(1)); + } + pop(2); + e1->m_result = r; + // SASSERT(well_formed()); + SASSERT(!m_free_nodes.contains(r)); + return r; + } + + void bdd_manager::push(BDD b) { + m_bdd_stack.push_back(b); + } + + void bdd_manager::pop(unsigned num_scopes) { + m_bdd_stack.shrink(m_bdd_stack.size() - num_scopes); + } + + bdd_manager::BDD bdd_manager::read(unsigned index) { + return m_bdd_stack[m_bdd_stack.size() - index]; + } + + bdd_manager::op_entry* bdd_manager::pop_entry(BDD l, BDD r, BDD op) { + op_entry* result = nullptr; + if (m_spare_entry) { + result = m_spare_entry; + m_spare_entry = nullptr; + result->m_bdd1 = l; + result->m_bdd2 = r; + result->m_op = op; + } + else { + void * mem = m_alloc.allocate(sizeof(op_entry)); + result = new (mem) op_entry(l, r, op); + } + result->m_result = null_bdd; + return result; + } + + void bdd_manager::push_entry(op_entry* e) { + SASSERT(!m_spare_entry); + m_spare_entry = e; + } + + bdd_manager::BDD bdd_manager::make_node(unsigned lvl, BDD l, BDD h) { + m_is_new_node = false; + if (l == h) { + return l; + } + SASSERT(is_const(l) || level(l) < lvl); + SASSERT(is_const(h) || level(h) < lvl); + + bdd_node n(lvl, l, h); + node_table::entry* e = m_node_table.insert_if_not_there2(n); + if (e->get_data().m_index != 0) { + unsigned result = e->get_data().m_index; + return result; + } + e->get_data().m_refcount = 0; + bool do_gc = m_free_nodes.empty(); + if (do_gc && !m_disable_gc) { + gc(); + e = m_node_table.insert_if_not_there2(n); + e->get_data().m_refcount = 0; + } + if (do_gc && m_free_nodes.size()*3 < m_nodes.size()) { + if (m_nodes.size() > m_max_num_bdd_nodes) { + throw mem_out(); + } + alloc_free_nodes(m_nodes.size()/2); + } + + SASSERT(!m_free_nodes.empty()); + unsigned result = m_free_nodes.back(); + m_free_nodes.pop_back(); + e->get_data().m_index = result; + m_nodes[result] = e->get_data(); + m_is_new_node = true; + SASSERT(!m_free_nodes.contains(result)); + SASSERT(m_nodes[result].m_index == result); + return result; + } + + void bdd_manager::try_cnf_reorder(bdd const& b) { + m_cost_bdd = b.root; + m_cost_metric = cnf_cost; + try_reorder(); + m_cost_metric = bdd_cost; + m_cost_bdd = 0; + } + + void bdd_manager::try_reorder() { + gc(); + for (auto* e : m_op_cache) { + m_alloc.deallocate(sizeof(*e), e); + } + m_op_cache.reset(); + init_reorder(); + for (unsigned i = 0; i < m_var2level.size(); ++i) { + sift_var(i); + } + SASSERT(m_op_cache.empty()); + SASSERT(well_formed()); + } + + double bdd_manager::current_cost() { + switch (m_cost_metric) { + case bdd_cost: + return m_nodes.size() - m_free_nodes.size(); + case cnf_cost: + return cnf_size(m_cost_bdd); + case dnf_cost: + return dnf_size(m_cost_bdd); + default: + UNREACHABLE(); + return 0; + } + } + + bool bdd_manager::is_bad_cost(double current_cost, double best_cost) const { + return current_cost > 1.1 * best_cost; + } + + void bdd_manager::sift_var(unsigned v) { + unsigned lvl = m_var2level[v]; + unsigned start = lvl; + double best_cost = current_cost(); + bool first = true; + unsigned max_lvl = m_level2nodes.size()-1; + if (lvl*2 < max_lvl) { + goto go_down; + } + go_up: + TRACE("bdd", tout << "sift up " << lvl << "\n";); + while (lvl < max_lvl) { + sift_up(lvl++); + double cost = current_cost(); + if (is_bad_cost(cost, best_cost)) break; + best_cost = std::min(cost, best_cost); + } + if (first) { + first = false; + while (lvl != start) { + sift_up(--lvl); + } + goto go_down; + } + else { + while (current_cost() != best_cost) { + sift_up(--lvl); + } + return; + } + go_down: + TRACE("bdd", tout << "sift down " << lvl << "\n";); + while (lvl > 0) { + sift_up(--lvl); + double cost = current_cost(); + if (is_bad_cost(cost, best_cost)) break; + best_cost = std::min(cost, best_cost); + } + if (first) { + first = false; + while (lvl != start) { + sift_up(lvl++); + } + goto go_up; + } + else { + while (current_cost() != best_cost) { + sift_up(lvl++); + } + return; + } + } + + void bdd_manager::sift_up(unsigned lvl) { + if (m_level2nodes[lvl].empty()) return; + // SASSERT(well_formed()); + // exchange level and level + 1. + m_S.reset(); + m_T.reset(); + m_to_free.reset(); + m_disable_gc = true; + + for (unsigned n : m_level2nodes[lvl + 1]) { + BDD l = lo(n); + BDD h = hi(n); + if (l == 0 && h == 0) continue; + if ((is_const(l) || level(l) != lvl) && + (is_const(h) || level(h) != lvl)) { + m_S.push_back(n); + } + else { + reorder_decref(l); + reorder_decref(h); + m_T.push_back(n); + } + TRACE("bdd", tout << "remove " << n << "\n";); + m_node_table.remove(m_nodes[n]); + } + m_level2nodes[lvl + 1].reset(); + m_level2nodes[lvl + 1].append(m_T); + + for (unsigned n : m_level2nodes[lvl]) { + bdd_node& node = m_nodes[n]; + m_node_table.remove(node); + node.m_level = lvl + 1; + if (m_reorder_rc[n] == 0) { + m_to_free.push_back(n); + } + else { + TRACE("bdd", tout << "set level " << n << " to " << lvl + 1 << "\n";); + m_node_table.insert(node); + m_level2nodes[lvl + 1].push_back(n); + } + } + m_level2nodes[lvl].reset(); + m_level2nodes[lvl].append(m_S); + + for (unsigned n : m_S) { + m_nodes[n].m_level = lvl; + m_node_table.insert(m_nodes[n]); + } + + for (unsigned n : m_T) { + BDD l = lo(n); + BDD h = hi(n); + if (l == 0 && h == 0) continue; + BDD a, b, c, d; + if (level(l) == lvl + 1) { + a = lo(l); + b = hi(l); + } + else { + a = b = l; + } + if (level(h) == lvl + 1) { + c = lo(h); + d = hi(h); + } + else { + c = d = h; + } + + unsigned ac = make_node(lvl, a, c); + if (is_new_node()) { + m_level2nodes[lvl].push_back(ac); + m_reorder_rc.reserve(ac+1); + reorder_incref(a); + reorder_incref(c); + } + unsigned bd = make_node(lvl, b, d); + if (is_new_node()) { + m_level2nodes[lvl].push_back(bd); + m_reorder_rc.reserve(bd+1); + reorder_incref(b); + reorder_incref(d); + } + m_nodes[n].m_lo = ac; + m_nodes[n].m_hi = bd; + reorder_incref(ac); + reorder_incref(bd); + TRACE("bdd", tout << "transform " << n << " " << " " << a << " " << b << " " << c << " " << d << " " << ac << " " << bd << "\n";); + m_node_table.insert(m_nodes[n]); + } + unsigned v = m_level2var[lvl]; + unsigned w = m_level2var[lvl+1]; + std::swap(m_level2var[lvl], m_level2var[lvl+1]); + std::swap(m_var2level[v], m_var2level[w]); + m_disable_gc = false; + + // add orphaned nodes to free-list + for (unsigned i = 0; i < m_to_free.size(); ++i) { + unsigned n = m_to_free[i]; + bdd_node& node = m_nodes[n]; + if (!node.is_internal()) { + SASSERT(!m_free_nodes.contains(n)); + SASSERT(node.m_refcount == 0); + m_free_nodes.push_back(n); + m_node_table.remove(node); + BDD l = lo(n); + BDD h = hi(n); + node.set_internal(); + + reorder_decref(l); + if (!m_nodes[l].is_internal() && m_reorder_rc[l] == 0) { + m_to_free.push_back(l); + } + reorder_decref(h); + if (!m_nodes[h].is_internal() && m_reorder_rc[h] == 0) { + m_to_free.push_back(h); + } + } + } + TRACE("bdd", tout << "sift " << lvl << "\n"; display(tout); ); + DEBUG_CODE( + for (unsigned i = 0; i < m_level2nodes.size(); ++i) { + for (unsigned n : m_level2nodes[i]) { + bdd_node const& node = m_nodes[n]; + SASSERT(node.m_level == i); + } + }); + + TRACE("bdd", + for (unsigned i = 0; i < m_nodes.size(); ++i) { + if (m_reorder_rc[i] != 0) { + tout << i << " " << m_reorder_rc[i] << "\n"; + }}); + + // SASSERT(well_formed()); + } + + void bdd_manager::init_reorder() { + m_level2nodes.reset(); + unsigned sz = m_nodes.size(); + m_reorder_rc.fill(sz, 0); + for (unsigned i = 0; i < sz; ++i) { + if (m_nodes[i].m_refcount > 0) + m_reorder_rc[i] = UINT_MAX; + } + for (unsigned i = 0; i < sz; ++i) { + bdd_node const& n = m_nodes[i]; + if (n.is_internal()) continue; + unsigned lvl = n.m_level; + SASSERT(i == m_nodes[i].m_index); + m_level2nodes.reserve(lvl + 1); + m_level2nodes[lvl].push_back(i); + reorder_incref(n.m_lo); + reorder_incref(n.m_hi); + } + TRACE("bdd", + display(tout); + for (unsigned i = 0; i < sz; ++i) { + bdd_node const& n = m_nodes[i]; + if (n.is_internal()) continue; + unsigned lvl = n.m_level; + tout << i << " lvl: " << lvl << " rc: " << m_reorder_rc[i] << " lo " << n.m_lo << " hi " << n.m_hi << "\n"; + } + ); + } + + void bdd_manager::reorder_incref(unsigned n) { + if (m_reorder_rc[n] != UINT_MAX) m_reorder_rc[n]++; + } + + void bdd_manager::reorder_decref(unsigned n) { + if (m_reorder_rc[n] != UINT_MAX) m_reorder_rc[n]--; + } + + void bdd_manager::reserve_var(unsigned i) { + while (m_var2level.size() <= i) { + unsigned v = m_var2level.size(); + m_var2bdd.push_back(make_node(v, false_bdd, true_bdd)); + m_var2bdd.push_back(make_node(v, true_bdd, false_bdd)); + m_nodes[m_var2bdd[2*v]].m_refcount = max_rc; + m_nodes[m_var2bdd[2*v+1]].m_refcount = max_rc; + m_var2level.push_back(v); + m_level2var.push_back(v); + } + } + + bdd bdd_manager::mk_var(unsigned i) { + reserve_var(i); + return bdd(m_var2bdd[2*i], this); + } + + bdd bdd_manager::mk_nvar(unsigned i) { + reserve_var(i); + return bdd(m_var2bdd[2*i+1], this); + } + + bdd bdd_manager::mk_not(bdd b) { + bool first = true; + while (true) { + try { + return bdd(mk_not_rec(b.root), this); + } + catch (mem_out) { + try_reorder(); + if (!first) throw; + first = false; + } + } + } + + bdd_manager::BDD bdd_manager::mk_not_rec(BDD b) { + if (is_true(b)) return false_bdd; + if (is_false(b)) return true_bdd; + op_entry* e1 = pop_entry(b, b, bdd_not_op); + op_entry const* e2 = m_op_cache.insert_if_not_there(e1); + if (check_result(e1, e2, b, b, bdd_not_op)) + return e2->m_result; + push(mk_not_rec(lo(b))); + push(mk_not_rec(hi(b))); + BDD r = make_node(level(b), read(2), read(1)); + pop(2); + e1->m_result = r; + return r; + } + + bdd bdd_manager::mk_ite(bdd const& c, bdd const& t, bdd const& e) { + bool first = true; + while (true) { + try { + return bdd(mk_ite_rec(c.root, t.root, e.root), this); + } + catch (mem_out) { + try_reorder(); + if (!first) throw; + first = false; + } + } + } + + bdd_manager::BDD bdd_manager::mk_ite_rec(BDD a, BDD b, BDD c) { + if (is_true(a)) return b; + if (is_false(a)) return c; + if (b == c) return b; + if (is_true(b)) return apply(a, c, bdd_or_op); + if (is_false(c)) return apply(a, b, bdd_and_op); + if (is_false(b)) return apply(mk_not_rec(a), c, bdd_and_op); + if (is_true(c)) return apply(mk_not_rec(a), b, bdd_or_op); + SASSERT(!is_const(a) && !is_const(b) && !is_const(c)); + op_entry * e1 = pop_entry(a, b, c); + op_entry const* e2 = m_op_cache.insert_if_not_there(e1); + if (check_result(e1, e2, a, b, c)) + return e2->m_result; + unsigned la = level(a), lb = level(b), lc = level(c); + BDD r; + BDD a1, b1, c1, a2, b2, c2; + unsigned lvl = la; + if (la >= lb && la >= lc) { + a1 = lo(a), a2 = hi(a); + lvl = la; + } + else { + a1 = a, a2 = a; + } + if (lb >= la && lb >= lc) { + b1 = lo(b), b2 = hi(b); + lvl = lb; + } + else { + b1 = b, b2 = b; + } + if (lc >= la && lc >= lb) { + c1 = lo(c), c2 = hi(c); + lvl = lc; + } + else { + c1 = c, c2 = c; + } + push(mk_ite_rec(a1, b1, c1)); + push(mk_ite_rec(a2, b2, c2)); + r = make_node(lvl, read(2), read(1)); + pop(2); + e1->m_result = r; + return r; + } + + bdd bdd_manager::mk_exists(unsigned n, unsigned const* vars, bdd const& b) { + // SASSERT(well_formed()); + return bdd(mk_quant(n, vars, b.root, bdd_or_op), this); + } + + bdd bdd_manager::mk_forall(unsigned n, unsigned const* vars, bdd const& b) { + return bdd(mk_quant(n, vars, b.root, bdd_and_op), this); + } + + bdd_manager::BDD bdd_manager::mk_quant(unsigned n, unsigned const* vars, BDD b, bdd_op op) { + BDD result = b; + for (unsigned i = 0; i < n; ++i) { + result = mk_quant_rec(m_var2level[vars[i]], result, op); + } + return result; + } + + bdd_manager::BDD bdd_manager::mk_quant_rec(unsigned l, BDD b, bdd_op op) { + unsigned lvl = level(b); + BDD r; + if (is_const(b)) { + r = b; + } + else if (lvl == l) { + r = apply(lo(b), hi(b), op); + } + else if (lvl < l) { + r = b; + } + else { + BDD a = level2bdd(l); + bdd_op q_op = op == bdd_and_op ? bdd_and_proj_op : bdd_or_proj_op; + op_entry * e1 = pop_entry(a, b, q_op); + op_entry const* e2 = m_op_cache.insert_if_not_there(e1); + if (check_result(e1, e2, a, b, q_op)) { + r = e2->m_result; + } + else { + SASSERT(e1->m_result == null_bdd); + push(mk_quant_rec(l, lo(b), op)); + push(mk_quant_rec(l, hi(b), op)); + r = make_node(lvl, read(2), read(1)); + pop(2); + e1->m_result = r; + } + } + SASSERT(r != UINT_MAX); + return r; + } + + double bdd_manager::count(BDD b, unsigned z) { + init_mark(); + m_count.resize(m_nodes.size()); + m_count[0] = z; + m_count[1] = 1-z; + set_mark(0); + set_mark(1); + m_todo.push_back(b); + while (!m_todo.empty()) { + BDD r = m_todo.back(); + if (is_marked(r)) { + m_todo.pop_back(); + } + else if (!is_marked(lo(r))) { + SASSERT (is_const(r) || r != lo(r)); + m_todo.push_back(lo(r)); + } + else if (!is_marked(hi(r))) { + SASSERT (is_const(r) || r != hi(r)); + m_todo.push_back(hi(r)); + } + else { + m_count[r] = m_count[lo(r)] + m_count[hi(r)]; + set_mark(r); + m_todo.pop_back(); + } + } + return m_count[b]; + } + + unsigned bdd_manager::bdd_size(bdd const& b) { + init_mark(); + set_mark(0); + set_mark(1); + unsigned sz = 0; + m_todo.push_back(b.root); + while (!m_todo.empty()) { + BDD r = m_todo.back(); + m_todo.pop_back(); + if (!is_marked(r)) { + ++sz; + set_mark(r); + if (!is_marked(lo(r))) { + m_todo.push_back(lo(r)); + } + if (!is_marked(hi(r))) { + m_todo.push_back(hi(r)); + } + } + } + return sz; + } + + void bdd_manager::alloc_free_nodes(unsigned n) { + for (unsigned i = 0; i < n; ++i) { + m_free_nodes.push_back(m_nodes.size()); + m_nodes.push_back(bdd_node()); + m_nodes.back().m_index = m_nodes.size() - 1; + } + m_free_nodes.reverse(); + } + + void bdd_manager::gc() { + m_free_nodes.reset(); + IF_VERBOSE(13, verbose_stream() << "(bdd :gc " << m_nodes.size() << ")\n";); + svector reachable(m_nodes.size(), false); + for (unsigned i = m_bdd_stack.size(); i-- > 0; ) { + reachable[m_bdd_stack[i]] = true; + m_todo.push_back(m_bdd_stack[i]); + } + for (unsigned i = m_nodes.size(); i-- > 2; ) { + if (m_nodes[i].m_refcount > 0) { + reachable[i] = true; + m_todo.push_back(i); + } + } + while (!m_todo.empty()) { + BDD b = m_todo.back(); + m_todo.pop_back(); + SASSERT(reachable[b]); + if (is_const(b)) continue; + if (!reachable[lo(b)]) { + reachable[lo(b)] = true; + m_todo.push_back(lo(b)); + } + if (!reachable[hi(b)]) { + reachable[hi(b)] = true; + m_todo.push_back(hi(b)); + } + } + for (unsigned i = m_nodes.size(); i-- > 2; ) { + if (!reachable[i]) { + m_nodes[i].set_internal(); + SASSERT(m_nodes[i].m_refcount == 0); + m_free_nodes.push_back(i); + } + } + // sort free nodes so that adjacent nodes are picked in order of use + std::sort(m_free_nodes.begin(), m_free_nodes.end()); + m_free_nodes.reverse(); + + ptr_vector to_delete, to_keep; + for (auto* e : m_op_cache) { + if (e->m_result != null_bdd) { + to_delete.push_back(e); + } + else { + to_keep.push_back(e); + } + } + m_op_cache.reset(); + for (op_entry* e : to_delete) { + m_alloc.deallocate(sizeof(*e), e); + } + for (op_entry* e : to_keep) { + m_op_cache.insert(e); + } + + m_node_table.reset(); + // re-populate node cache + for (unsigned i = m_nodes.size(); i-- > 2; ) { + if (reachable[i]) { + SASSERT(m_nodes[i].m_index == i); + m_node_table.insert(m_nodes[i]); + } + } + SASSERT(well_formed()); + } + + void bdd_manager::init_mark() { + m_mark.resize(m_nodes.size()); + ++m_mark_level; + if (m_mark_level == 0) { + m_mark.fill(0); + ++m_mark_level; + } + } + + std::ostream& bdd_manager::display(std::ostream& out, bdd const& b) { + init_mark(); + m_todo.push_back(b.root); + m_reorder_rc.reserve(m_nodes.size()); + while (!m_todo.empty()) { + BDD r = m_todo.back(); + if (is_marked(r)) { + m_todo.pop_back(); + } + else if (lo(r) == 0 && hi(r) == 0) { + set_mark(r); + m_todo.pop_back(); + } + else if (!is_marked(lo(r))) { + m_todo.push_back(lo(r)); + } + else if (!is_marked(hi(r))) { + m_todo.push_back(hi(r)); + } + else { + out << r << " : " << var(r) << " @ " << level(r) << " " << lo(r) << " " << hi(r) << " " << m_reorder_rc[r] << "\n"; + set_mark(r); + m_todo.pop_back(); + } + } + return out; + } + + bool bdd_manager::well_formed() { + bool ok = true; + for (unsigned n : m_free_nodes) { + ok &= (lo(n) == 0 && hi(n) == 0 && m_nodes[n].m_refcount == 0); + if (!ok) { + IF_VERBOSE(0, + verbose_stream() << "free node is not internal " << n << " " << lo(n) << " " << hi(n) << " " << m_nodes[n].m_refcount << "\n"; + display(verbose_stream());); + UNREACHABLE(); + return false; + } + } + for (bdd_node const& n : m_nodes) { + if (n.is_internal()) continue; + unsigned lvl = n.m_level; + BDD lo = n.m_lo; + BDD hi = n.m_hi; + ok &= is_const(lo) || level(lo) < lvl; + ok &= is_const(hi) || level(hi) < lvl; + ok &= is_const(lo) || !m_nodes[lo].is_internal(); + ok &= is_const(hi) || !m_nodes[hi].is_internal(); + if (!ok) { + IF_VERBOSE(0, display(verbose_stream() << n.m_index << " lo " << lo << " hi " << hi << "\n");); + UNREACHABLE(); + return false; + } + } + return ok; + } + + std::ostream& bdd_manager::display(std::ostream& out) { + m_reorder_rc.reserve(m_nodes.size()); + for (unsigned i = 0; i < m_nodes.size(); ++i) { + bdd_node const& n = m_nodes[i]; + if (n.is_internal()) continue; + out << i << " : v" << m_level2var[n.m_level] << " " << n.m_lo << " " << n.m_hi << " rc " << m_reorder_rc[i] << "\n"; + } + for (unsigned i = 0; i < m_level2nodes.size(); ++i) { + out << "level: " << i << " : " << m_level2nodes[i] << "\n"; + } + return out; + } + + bdd& bdd::operator=(bdd const& other) { unsigned r1 = root; root = other.root; m->inc_ref(root); m->dec_ref(r1); return *this; } + std::ostream& operator<<(std::ostream& out, bdd const& b) { return b.display(out); } + +} diff --git a/src/sat/sat_bdd.h b/src/sat/sat_bdd.h new file mode 100644 index 000000000..70f6960fe --- /dev/null +++ b/src/sat/sat_bdd.h @@ -0,0 +1,260 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_bdd + +Abstract: + + Simple BDD package modeled after BuDDy, which is modeled after CUDD. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-13 + +Revision History: + +--*/ +#ifndef SAT_BDD_H_ +#define SAT_BDD_H_ + +#include "util/vector.h" +#include "util/map.h" +#include "util/small_object_allocator.h" + +namespace sat { + + class bdd; + + class bdd_manager { + friend bdd; + + typedef unsigned BDD; + + const BDD null_bdd = UINT_MAX; + + enum bdd_op { + bdd_and_op = 2, + bdd_or_op = 3, + bdd_xor_op = 4, + bdd_not_op = 5, + bdd_and_proj_op = 6, + bdd_or_proj_op = 7, + bdd_no_op = 8, + }; + + struct bdd_node { + bdd_node(unsigned level, BDD lo, BDD hi): + m_refcount(0), + m_level(level), + m_lo(lo), + m_hi(hi), + m_index(0) + {} + bdd_node(): m_refcount(0), m_level(0), m_lo(0), m_hi(0), m_index(0) {} + unsigned m_refcount : 10; + unsigned m_level : 22; + BDD m_lo; + BDD m_hi; + unsigned m_index; + unsigned hash() const { return mk_mix(m_level, m_lo, m_hi); } + bool is_internal() const { return m_lo == 0 && m_hi == 0; } + void set_internal() { m_lo = 0; m_hi = 0; } + }; + + enum cost_metric { + cnf_cost, + dnf_cost, + bdd_cost + }; + + struct hash_node { + unsigned operator()(bdd_node const& n) const { return n.hash(); } + }; + + struct eq_node { + bool operator()(bdd_node const& a, bdd_node const& b) const { + return a.m_lo == b.m_lo && a.m_hi == b.m_hi && a.m_level == b.m_level; + } + }; + + typedef hashtable node_table; + + struct op_entry { + op_entry(BDD l, BDD r, BDD op): + m_bdd1(l), + m_bdd2(r), + m_op(op), + m_result(0) + {} + + BDD m_bdd1; + BDD m_bdd2; + BDD m_op; + BDD m_result; + unsigned hash() const { return mk_mix(m_bdd1, m_bdd2, m_op); } + }; + + struct hash_entry { + unsigned operator()(op_entry* e) const { return e->hash(); } + }; + + struct eq_entry { + bool operator()(op_entry * a, op_entry * b) const { + return a->m_bdd1 == b->m_bdd2 && a->m_bdd2 == b->m_bdd2 && a->m_op == b->m_op; + } + }; + + typedef ptr_hashtable op_table; + + svector m_nodes; + op_table m_op_cache; + node_table m_node_table; + unsigned_vector m_apply_const; + svector m_bdd_stack; + op_entry* m_spare_entry; + svector m_var2bdd; + unsigned_vector m_var2level, m_level2var; + unsigned_vector m_free_nodes; + small_object_allocator m_alloc; + mutable svector m_mark; + mutable unsigned m_mark_level; + mutable svector m_count; + mutable svector m_todo; + bool m_disable_gc; + bool m_is_new_node; + unsigned m_max_num_bdd_nodes; + unsigned_vector m_S, m_T, m_to_free; // used for reordering + vector m_level2nodes; + unsigned_vector m_reorder_rc; + cost_metric m_cost_metric; + BDD m_cost_bdd; + + BDD make_node(unsigned level, BDD l, BDD r); + bool is_new_node() const { return m_is_new_node; } + + BDD apply_const(BDD a, BDD b, bdd_op op); + BDD apply(BDD arg1, BDD arg2, bdd_op op); + BDD mk_quant(unsigned n, unsigned const* vars, BDD b, bdd_op op); + + BDD apply_rec(BDD arg1, BDD arg2, bdd_op op); + BDD mk_not_rec(BDD b); + BDD mk_ite_rec(BDD a, BDD b, BDD c); + BDD mk_quant_rec(unsigned lvl, BDD b, bdd_op op); + + void push(BDD b); + void pop(unsigned num_scopes); + BDD read(unsigned index); + + op_entry* pop_entry(BDD l, BDD r, BDD op); + void push_entry(op_entry* e); + bool check_result(op_entry*& e1, op_entry const* e2, BDD a, BDD b, BDD c); + + double count(BDD b, unsigned z); + + void alloc_free_nodes(unsigned n); + void init_mark(); + void set_mark(unsigned i) { m_mark[i] = m_mark_level; } + bool is_marked(unsigned i) { return m_mark[i] == m_mark_level; } + + void init_reorder(); + void reorder_incref(unsigned n); + void reorder_decref(unsigned n); + void sift_up(unsigned level); + void sift_var(unsigned v); + double current_cost(); + bool is_bad_cost(double new_cost, double best_cost) const; + + static const BDD false_bdd = 0; + static const BDD true_bdd = 1; + static const unsigned max_rc = (1 << 10) - 1; + + inline bool is_true(BDD b) const { return b == true_bdd; } + inline bool is_false(BDD b) const { return b == false_bdd; } + inline bool is_const(BDD b) const { return b <= 1; } + inline unsigned level(BDD b) const { return m_nodes[b].m_level; } + inline unsigned var(BDD b) const { return m_level2var[level(b)]; } + inline BDD lo(BDD b) const { return m_nodes[b].m_lo; } + inline BDD hi(BDD b) const { return m_nodes[b].m_hi; } + inline void inc_ref(BDD b) { if (m_nodes[b].m_refcount != max_rc) m_nodes[b].m_refcount++; VERIFY(!m_free_nodes.contains(b)); } + inline void dec_ref(BDD b) { if (m_nodes[b].m_refcount != max_rc) m_nodes[b].m_refcount--; VERIFY(!m_free_nodes.contains(b)); } + inline BDD level2bdd(unsigned l) const { return m_var2bdd[m_level2var[l]]; } + + double dnf_size(BDD b) { return count(b, 0); } + double cnf_size(BDD b) { return count(b, 1); } + unsigned bdd_size(bdd const& b); + + bdd mk_not(bdd b); + bdd mk_and(bdd const& a, bdd const& b); + bdd mk_or(bdd const& a, bdd const& b); + bdd mk_xor(bdd const& a, bdd const& b); + + void reserve_var(unsigned v); + bool well_formed(); + + public: + struct mem_out {}; + + bdd_manager(unsigned nodes); + ~bdd_manager(); + + void set_max_num_nodes(unsigned n) { m_max_num_bdd_nodes = n; } + + bdd mk_var(unsigned i); + bdd mk_nvar(unsigned i); + + bdd mk_true(); + bdd mk_false(); + + bdd mk_exists(unsigned n, unsigned const* vars, bdd const & b); + bdd mk_forall(unsigned n, unsigned const* vars, bdd const & b); + bdd mk_exists(unsigned v, bdd const& b); + bdd mk_forall(unsigned v, bdd const& b); + bdd mk_ite(bdd const& c, bdd const& t, bdd const& e); + + std::ostream& display(std::ostream& out); + std::ostream& display(std::ostream& out, bdd const& b); + + void gc(); + void try_reorder(); + void try_cnf_reorder(bdd const& b); + }; + + class bdd { + friend class bdd_manager; + unsigned root; + bdd_manager* m; + bdd(unsigned root, bdd_manager* m): root(root), m(m) { m->inc_ref(root); } + public: + bdd(bdd & other): root(other.root), m(other.m) { m->inc_ref(root); } + bdd(bdd && other): root(0), m(other.m) { std::swap(root, other.root); } + bdd& operator=(bdd const& other); + ~bdd() { m->dec_ref(root); } + bdd lo() const { return bdd(m->lo(root), m); } + bdd hi() const { return bdd(m->hi(root), m); } + unsigned var() const { return m->var(root); } + + bool is_true() const { return root == bdd_manager::true_bdd; } + bool is_false() const { return root == bdd_manager::false_bdd; } + + bdd operator!() { return m->mk_not(*this); } + bdd operator&&(bdd const& other) { return m->mk_and(*this, other); } + bdd operator||(bdd const& other) { return m->mk_or(*this, other); } + bdd operator^(bdd const& other) { return m->mk_xor(*this, other); } + bdd operator|=(bdd const& other) { return *this = *this || other; } + bdd operator&=(bdd const& other) { return *this = *this && other; } + std::ostream& display(std::ostream& out) const { return m->display(out, *this); } + bool operator==(bdd const& other) const { return root == other.root; } + bool operator!=(bdd const& other) const { return root != other.root; } + double cnf_size() const { return m->cnf_size(root); } + double dnf_size() const { return m->dnf_size(root); } + unsigned bdd_size() const { return m->bdd_size(*this); } + }; + + std::ostream& operator<<(std::ostream& out, bdd const& b); + +} + + +#endif diff --git a/src/sat/sat_big.cpp b/src/sat/sat_big.cpp new file mode 100644 index 000000000..35898a110 --- /dev/null +++ b/src/sat/sat_big.cpp @@ -0,0 +1,289 @@ +/*++ +Copyright (c) 2011 Microsoft Corporation + +Module Name: + + sat_big.cpp + +Abstract: + + binary implication graph structure. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-12-13. + +Revision History: + +--*/ +#include "sat/sat_big.h" +#include "sat/sat_solver.h" + +namespace sat { + + big::big(random_gen& rand): + m_rand(rand) { + } + + void big::init(solver& s, bool learned) { + init_adding_edges(s.num_vars(), learned); + unsigned num_lits = m_num_vars * 2; + literal_vector lits, r; + SASSERT(num_lits == m_dag.size() && num_lits == m_roots.size()); + size_t_map seen_idx; + for (unsigned l_idx = 0; l_idx < num_lits; l_idx++) { + literal u = to_literal(l_idx); + if (s.was_eliminated(u.var())) + continue; + auto& edges = m_dag[l_idx]; + for (watched const& w : s.get_wlist(l_idx)) { + if (learned ? w.is_binary_clause() : w.is_binary_non_learned_clause()) { + literal v = w.get_literal(); + m_roots[v.index()] = false; + edges.push_back(v); + } +#if 0 + if (w.is_ext_constraint() && + s.m_ext && + learned && + !seen_idx.contains(w.get_ext_constraint_idx()) && + s.m_ext->is_extended_binary(w.get_ext_constraint_idx(), r)) { + seen_idx.insert(w.get_ext_constraint_idx(), true); + for (unsigned i = 0; i < r.size(); ++i) { + literal u = r[i]; + for (unsigned j = i + 1; j < r.size(); ++j) { + // add ~r[i] -> r[j] + literal v = r[j]; + literal u = ~r[j]; + m_roots[v.index()] = false; + m_dag[u.index()].push_back(v); + // add ~r[j] -> r[i] + v.neg(); + u.neg(); + m_roots[u.index()] = false; + m_dag[v.index()].push_back(u); + } + } + } +#endif + } + } + done_adding_edges(); + } + + void big::reinit() { + done_adding_edges(); + } + + void big::init_adding_edges(unsigned num_vars, bool learned) { + m_learned = learned; + m_num_vars = num_vars; + unsigned num_lits = m_num_vars * 2; + m_dag.reset(); + m_roots.reset(); + m_dag.resize(num_lits, 0); + m_roots.resize(num_lits, true); + } + + void big::add_edge(literal u, literal v) { + m_dag[u.index()].push_back(v); + } + + void big::done_adding_edges() { + for (auto& edges : m_dag) { + shuffle(edges.size(), edges.c_ptr(), m_rand); + } + init_dfs_num(); + } + + + struct big::pframe { + literal m_parent; + literal m_child; + pframe(literal p, literal c): + m_parent(p), m_child(c) {} + literal child() const { return m_child; } + literal parent() const { return m_parent; } + }; + + void big::init_dfs_num() { + unsigned num_lits = m_num_vars * 2; + m_left.reset(); + m_right.reset(); + m_root.reset(); + m_parent.reset(); + m_left.resize(num_lits, 0); + m_right.resize(num_lits, -1); + m_root.resize(num_lits, null_literal); + m_parent.resize(num_lits, null_literal); + for (unsigned i = 0; i < num_lits; ++i) { + m_root[i] = to_literal(i); + m_parent[i] = to_literal(i); + } + svector todo; + // retrieve literals that have no predecessors + for (unsigned l_idx = 0; l_idx < num_lits; l_idx++) { + literal u(to_literal(l_idx)); + if (m_roots[u.index()]) { + todo.push_back(pframe(null_literal, u)); + } + } + shuffle(todo.size(), todo.c_ptr(), m_rand); + int dfs_num = 0; + while (!todo.empty()) { + literal u = todo.back().child(); + if (m_left[u.index()] > 0) { + // already visited + if (m_right[u.index()] < 0) { + m_right[u.index()] = ++dfs_num; + } + todo.pop_back(); + } + else { + SASSERT(m_left[u.index()] == 0); + m_left[u.index()] = ++dfs_num; + literal p = todo.back().parent(); + if (p != null_literal) { + m_root[u.index()] = m_root[p.index()]; + m_parent[u.index()] = p; + } + for (literal v : m_dag[u.index()]) { + if (m_left[v.index()] == 0) { + todo.push_back(pframe(u, v)); + } + } + } + } + for (unsigned i = 0; i < num_lits; ++i) { + if (m_right[i] < 0) { + VERIFY(m_left[i] == 0); + m_left[i] = ++dfs_num; + m_right[i] = ++dfs_num; + } + } + DEBUG_CODE(for (unsigned i = 0; i < num_lits; ++i) { VERIFY(m_left[i] < m_right[i]);}); + } + + // svector> big::s_del_bin; + + bool big::in_del(literal u, literal v) const { + if (u.index() > v.index()) std::swap(u, v); + return m_del_bin.contains(std::make_pair(u, v)); + } + + void big::add_del(literal u, literal v) { + if (u.index() > v.index()) std::swap(u, v); + m_del_bin.push_back(std::make_pair(u, v)); + } + + unsigned big::reduce_tr(solver& s) { + unsigned idx = 0; + unsigned elim = 0; + m_del_bin.reset(); + for (watch_list & wlist : s.m_watches) { + if (s.inconsistent()) break; + literal u = to_literal(idx++); + watch_list::iterator it = wlist.begin(); + watch_list::iterator itprev = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + watched& w = *it; + if (learned() ? w.is_binary_learned_clause() : w.is_binary_clause()) { + literal v = w.get_literal(); + if (u != get_parent(v) && safe_reach(u, v)) { + ++elim; + add_del(~u, v); + if (s.get_config().m_drat) s.m_drat.del(~u, v); + s.m_mc.stackv().reset(); // TBD: brittle code + s.add_ate(~u, v); + if (find_binary_watch(wlist, ~v)) { + IF_VERBOSE(10, verbose_stream() << "binary: " << ~u << "\n"); + s.assign(~u, justification()); + } + // could turn non-learned non-binary tautology into learned binary. + s.get_wlist(~v).erase(watched(~u, w.is_learned())); + continue; + } + } + *itprev = *it; + itprev++; + } + wlist.set_end(itprev); + } + +#if 0 + s_del_bin.append(m_del_bin); + IF_VERBOSE(1, + display(verbose_stream() << "Learned: " << learned() << ":"); + verbose_stream() << "del-bin\n"; + for (auto p : m_del_bin) { + verbose_stream() << p.first << " " << p.second << "\n"; + if (safe_reach(~p.first, p.second)) { + display_path(verbose_stream(), ~p.first, p.second) << " " << "\n"; + } + else { + display_path(verbose_stream(), ~p.second, p.first) << " " << "\n"; + } + } + ); +#endif + s.propagate(false); + return elim; + } + + bool big::safe_reach(literal u, literal v) { + if (!reaches(u, v)) return false; + while (u != v) { + literal w = next(u, v); + if (in_del(~u, w)) { + return false; + } + u = w; + } + return true; + } + + literal big::next(literal u, literal v) const { + SASSERT(reaches(u, v)); + literal result = null_literal; + int left = m_right[u.index()]; + // identify the path from the reachability graph + for (literal w : m_dag[u.index()]) { + if (reaches(u, w) && + (w == v || reaches(w, v)) && + m_left[w.index()] < left) { + left = m_left[w.index()]; + result = w; + } + } + SASSERT(result != null_literal); + return result; + } + + std::ostream& big::display_path(std::ostream& out, literal u, literal v) const { + while (u != v) { + out << u << " -> "; + u = next(u, v); + } + return out << v; + } + + void big::display(std::ostream& out) const { + unsigned idx = 0; + for (auto& next : m_dag) { + if (!next.empty()) { + out << to_literal(idx) << " : " << m_left[idx] << ":" << m_right[idx] << " -> " << next << "\n"; +#if 0 + for (literal n : next) { + out << n << "[" << m_left[n.index()] << ":" << m_right[n.index()] << "] "; + } + out << "\n"; +#endif + } + ++idx; + } + } + + + +}; diff --git a/src/sat/sat_big.h b/src/sat/sat_big.h new file mode 100644 index 000000000..898ddd1e8 --- /dev/null +++ b/src/sat/sat_big.h @@ -0,0 +1,88 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_big.h + +Abstract: + + binary implication graph structure. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-12-13. + +Revision History: + +--*/ +#ifndef SAT_BIG_H_ +#define SAT_BIG_H_ + +#include "sat/sat_types.h" +#include "util/statistics.h" +#include "util/params.h" + +namespace sat { + class solver; + + class big { + random_gen& m_rand; + unsigned m_num_vars; + vector m_dag; + svector m_roots; + svector m_left, m_right; + literal_vector m_root, m_parent; + bool m_learned; + + svector> m_del_bin; + + + void init_dfs_num(); + struct pframe; + + bool safe_reach(literal u, literal v); + literal next(literal u, literal v) const; + + std::ostream& display_path(std::ostream& out, literal u, literal v) const; + + void add_del(literal u, literal v); + bool in_del(literal u, literal v) const; + + public: + + // static svector> s_del_bin; + + big(random_gen& rand); + /** + \brief initialize a BIG from a solver. + */ + void init(solver& s, bool learned); + + void reinit(); + + /** + \brief initialize a BIG externally by adding implications. + */ + void init_adding_edges(unsigned num_vars, bool learned); + void add_edge(literal u, literal v); + void done_adding_edges(); + + void ensure_big(solver& s, bool learned) { if (m_left.empty()) init(s, learned); } + + unsigned reduce_tr(solver& s); + + // does it include learned binaries? + bool learned() const { return m_learned; } + int get_left(literal l) const { return m_left[l.index()]; } + int get_right(literal l) const { return m_right[l.index()]; } + literal get_parent(literal l) const { return m_parent[l.index()]; } + literal get_root(literal l) const { return m_root[l.index()]; } + bool reaches(literal u, literal v) const { return m_left[u.index()] < m_left[v.index()] && m_right[v.index()] < m_right[u.index()]; } + bool connected(literal u, literal v) const { return reaches(u, v) || reaches(~v, ~u); } + void display(std::ostream& out) const; + + }; +}; + +#endif diff --git a/src/sat/sat_clause.cpp b/src/sat/sat_clause.cpp index c5829ae4e..3cbd3015b 100644 --- a/src/sat/sat_clause.cpp +++ b/src/sat/sat_clause.cpp @@ -32,7 +32,9 @@ namespace sat { m_used(false), m_frozen(false), m_reinit_stack(false), - m_inact_rounds(0) { + m_inact_rounds(0), + m_glue(255), + m_psm(255) { memcpy(m_lits, lits, sizeof(literal) * sz); mark_strengthened(); SASSERT(check_approx()); @@ -59,15 +61,15 @@ namespace sat { } bool clause::contains(literal l) const { - for (unsigned i = 0; i < m_size; i++) - if (m_lits[i] == l) + for (literal l2 : *this) + if (l2 == l) return true; return false; } bool clause::contains(bool_var v) const { - for (unsigned i = 0; i < m_size; i++) - if (m_lits[i].var() == v) + for (literal l : *this) + if (l.var() == v) return true; return false; } @@ -85,9 +87,16 @@ namespace sat { mark_strengthened(); } + void clause::shrink(unsigned num_lits) { + SASSERT(num_lits <= m_size); + if (num_lits < m_size) { + m_size = num_lits; + mark_strengthened(); + } + } + bool clause::satisfied_by(model const & m) const { - for (unsigned i = 0; i < m_size; i++) { - literal l = m_lits[i]; + for (literal l : *this) { if (l.sign()) { if (m[l.var()] == l_false) return true; @@ -100,10 +109,31 @@ namespace sat { return false; } + clause_offset clause::get_new_offset() const { + unsigned o1 = m_lits[0].index(); +#if defined(_AMD64_) || defined(_M_IA64) + if (sizeof(clause_offset) == 8) { + unsigned o2 = m_lits[1].index(); + return (clause_offset)o1 + (((clause_offset)o2) << 32); + } +#endif + return (clause_offset)o1; + } + + void clause::set_new_offset(clause_offset offset) { + m_lits[0] = to_literal(static_cast(offset)); +#if defined(_AMD64_) || defined(_M_IA64) + if (sizeof(offset) == 8) { + m_lits[1] = to_literal(static_cast(offset >> 32)); + } +#endif + } + + void tmp_clause::set(unsigned num_lits, literal const * lits, bool learned) { if (m_clause && m_clause->m_capacity < num_lits) { dealloc_svect(m_clause); - m_clause = 0; + m_clause = nullptr; } if (!m_clause) { void * mem = alloc_svect(char, clause::get_obj_size(num_lits)); @@ -123,94 +153,46 @@ namespace sat { clause_allocator::clause_allocator(): m_allocator("clause-allocator") { -#if defined(_AMD64_) - m_num_segments = 0; -#endif + } + + void clause_allocator::finalize() { + m_allocator.reset(); } clause * clause_allocator::get_clause(clause_offset cls_off) const { -#if defined(_AMD64_) -#if defined (Z3DEBUG) - clause const* result; - if (((cls_off & c_alignment_mask) == c_last_segment)) { - unsigned id = cls_off >> c_cls_alignment; - bool check = m_last_seg_id2cls.find(id, result); - SASSERT(check); - return const_cast(result); - } -#endif - return reinterpret_cast(m_segments[cls_off & c_alignment_mask] + (static_cast(cls_off) & ~c_alignment_mask)); -#else + SASSERT(cls_off == reinterpret_cast(reinterpret_cast(cls_off))); return reinterpret_cast(cls_off); -#endif } -#if defined(_AMD64_) - unsigned clause_allocator::get_segment(clause const* cls) { - size_t ptr = reinterpret_cast(cls); - - SASSERT((ptr & c_alignment_mask) == 0); - ptr &= 0xFFFFFFFF00000000ull; // Keep only high part - unsigned i = 0; - for (i = 0; i < m_num_segments; ++i) - if (m_segments[i] == ptr) - return i; - i = m_num_segments; - SASSERT(i <= c_last_segment); -#if defined(Z3DEBUG) - if (i == c_last_segment) { - if (!m_last_seg_id2cls.contains(cls->id())) - m_last_seg_id2cls.insert(cls->id(), cls); - } - else { - ++m_num_segments; - m_segments[i] = ptr; - } -#else - if (i == c_last_segment) { - throw default_exception("segment out of range"); - } - m_segments[i] = ptr; - ++m_num_segments; -#endif - - return i; - } -#endif - clause_offset clause_allocator::get_offset(clause const * cls) const { -#if defined(_AMD64_) - unsigned segment = const_cast(this)->get_segment(cls); -#if defined(Z3DEBUG) - SASSERT(segment <= c_last_segment); - if (segment == c_last_segment) { - SASSERT(m_last_seg_id2cls.contains(cls->id())); - return (cls->id() << c_cls_alignment) | c_last_segment; - } -#endif - return static_cast(reinterpret_cast(cls)) + segment; -#else + SASSERT(cls == reinterpret_cast(reinterpret_cast(cls))); return reinterpret_cast(cls); -#endif } clause * clause_allocator::mk_clause(unsigned num_lits, literal const * lits, bool learned) { size_t size = clause::get_obj_size(num_lits); void * mem = m_allocator.allocate(size); clause * cls = new (mem) clause(m_id_gen.mk(), num_lits, lits, learned); - TRACE("sat", tout << "alloc: " << cls->id() << " " << *cls << " " << (learned?"l":"a") << "\n";); + TRACE("sat_clause", tout << "alloc: " << cls->id() << " " << *cls << " " << (learned?"l":"a") << "\n";); SASSERT(!learned || cls->is_learned()); return cls; } + clause * clause_allocator::copy_clause(clause const& other) { + size_t size = clause::get_obj_size(other.size()); + void * mem = m_allocator.allocate(size); + clause * cls = new (mem) clause(m_id_gen.mk(), other.size(), other.m_lits, other.is_learned()); + cls->m_reinit_stack = other.on_reinit_stack(); + cls->m_glue = other.glue(); + cls->m_psm = other.psm(); + cls->m_frozen = other.frozen(); + cls->m_approx = other.approx(); + return cls; + } + void clause_allocator::del_clause(clause * cls) { - TRACE("sat", tout << "delete: " << cls->id() << " " << *cls << "\n";); + TRACE("sat_clause", tout << "delete: " << cls->id() << " " << *cls << "\n";); m_id_gen.recycle(cls->id()); -#if defined(_AMD64_) -#if defined(Z3DEBUG) - m_last_seg_id2cls.remove(cls->id()); -#endif -#endif size_t size = clause::get_obj_size(cls->m_capacity); cls->~clause(); m_allocator.deallocate(size, cls); @@ -230,10 +212,8 @@ namespace sat { } std::ostream & operator<<(std::ostream & out, clause_vector const & cs) { - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - out << *(*it) << "\n"; + for (clause *cp : cs) { + out << *cp << "\n"; } return out; } @@ -255,12 +235,12 @@ namespace sat { } std::ostream & operator<<(std::ostream & out, clause_wrapper const & c) { - out << "("; - for (unsigned i = 0; i < c.size(); i++) { - if (i > 0) out << " "; - out << c[i]; + if (c.is_binary()) { + out << "(" << c[0] << " " << c[1] << ")"; + } + else { + out << c.get_clause()->id() << ": " << *c.get_clause(); } - out << ")"; return out; } diff --git a/src/sat/sat_clause.h b/src/sat/sat_clause.h index 5ad08e52a..f95696b3d 100644 --- a/src/sat/sat_clause.h +++ b/src/sat/sat_clause.h @@ -19,10 +19,11 @@ Revision History: #ifndef SAT_CLAUSE_H_ #define SAT_CLAUSE_H_ -#include "sat/sat_types.h" #include "util/small_object_allocator.h" #include "util/id_gen.h" #include "util/map.h" +#include "sat/sat_types.h" +#include "sat/sat_allocator.h" #ifdef _MSC_VER #pragma warning(disable : 4200) @@ -33,6 +34,10 @@ namespace sat { class clause_allocator; + class clause; + + std::ostream & operator<<(std::ostream & out, clause const & c); + class clause { friend class clause_allocator; friend class tmp_clause; @@ -57,11 +62,12 @@ namespace sat { public: unsigned id() const { return m_id; } unsigned size() const { return m_size; } + unsigned capacity() const { return m_capacity; } literal & operator[](unsigned idx) { SASSERT(idx < m_size); return m_lits[idx]; } literal const & operator[](unsigned idx) const { SASSERT(idx < m_size); return m_lits[idx]; } bool is_learned() const { return m_learned; } - void unset_learned() { SASSERT(is_learned()); m_learned = false; } - void shrink(unsigned num_lits) { SASSERT(num_lits <= m_size); if (num_lits < m_size) { m_size = num_lits; mark_strengthened(); } } + void set_learned(bool l) { SASSERT(is_learned() != l); m_learned = l; } + void shrink(unsigned num_lits); bool strengthened() const { return m_strengthened; } void mark_strengthened() { m_strengthened = true; update_approx(); } void unmark_strengthened() { m_strengthened = false; } @@ -73,6 +79,8 @@ namespace sat { bool check_approx() const; // for debugging literal * begin() { return m_lits; } literal * end() { return m_lits + m_size; } + literal const * begin() const { return m_lits; } + literal const * end() const { return m_lits + m_size; } bool contains(literal l) const; bool contains(bool_var v) const; bool satisfied_by(model const & m) const; @@ -90,12 +98,13 @@ namespace sat { unsigned glue() const { return m_glue; } void set_psm(unsigned psm) { m_psm = psm > 255 ? 255 : psm; } unsigned psm() const { return m_psm; } + clause_offset get_new_offset() const; + void set_new_offset(clause_offset off); bool on_reinit_stack() const { return m_reinit_stack; } void set_reinit_stack(bool f) { m_reinit_stack = f; } }; - std::ostream & operator<<(std::ostream & out, clause const & c); std::ostream & operator<<(std::ostream & out, clause_vector const & cs); class bin_clause { @@ -115,7 +124,7 @@ namespace sat { class tmp_clause { clause * m_clause; public: - tmp_clause():m_clause(0) {} + tmp_clause():m_clause(nullptr) {} ~tmp_clause() { if (m_clause) dealloc_svect(m_clause); } clause * get() const { return m_clause; } void set(unsigned num_lits, literal const * lits, bool learned); @@ -127,24 +136,16 @@ namespace sat { \brief Simple clause allocator that allows uint (32bit integers) to be used to reference clauses (even in 64bit machines). */ class clause_allocator { - small_object_allocator m_allocator; - id_gen m_id_gen; -#if defined(_AMD64_) - unsigned get_segment(clause const* cls); - static const unsigned c_cls_alignment = 3; - static const unsigned c_last_segment = (1ull << c_cls_alignment) - 1ull; - static const size_t c_alignment_mask = (1ull << c_cls_alignment) - 1ull; - unsigned m_num_segments; - size_t m_segments[c_last_segment]; -#if defined(Z3DEBUG) - u_map m_last_seg_id2cls; -#endif -#endif + sat_allocator m_allocator; + id_gen m_id_gen; public: clause_allocator(); + void finalize(); + size_t get_allocation_size() const { return m_allocator.get_allocation_size(); } clause * get_clause(clause_offset cls_off) const; clause_offset get_offset(clause const * ptr) const; clause * mk_clause(unsigned num_lits, literal const * lits, bool learned); + clause * copy_clause(clause const& other); void del_clause(clause * cls); }; @@ -160,8 +161,18 @@ namespace sat { }; unsigned m_l2_idx; public: - clause_wrapper(literal l1, literal l2):m_l1_idx(l1.to_uint()), m_l2_idx(l2.to_uint()) {} - clause_wrapper(clause & c):m_cls(&c), m_l2_idx(null_literal.to_uint()) {} + explicit clause_wrapper(literal l1, literal l2):m_l1_idx(l1.to_uint()), m_l2_idx(l2.to_uint()) {} + explicit clause_wrapper(clause & c):m_cls(&c), m_l2_idx(null_literal.to_uint()) {} + clause_wrapper& operator=(clause_wrapper const& other) { + if (other.is_binary()) { + m_l1_idx = other.m_l1_idx; + } + else { + m_cls = other.m_cls; + } + m_l2_idx = other.m_l2_idx; + return *this; + } bool is_binary() const { return m_l2_idx != null_literal.to_uint(); } unsigned size() const { return is_binary() ? 2 : m_cls->size(); } @@ -176,6 +187,7 @@ namespace sat { bool contains(literal l) const; bool contains(bool_var v) const; clause * get_clause() const { SASSERT(!is_binary()); return m_cls; } + bool was_removed() const { return !is_binary() && get_clause()->was_removed(); } }; typedef svector clause_wrapper_vector; diff --git a/src/sat/sat_clause_use_list.cpp b/src/sat/sat_clause_use_list.cpp index 5ee5b4cda..7ca0aa2c6 100644 --- a/src/sat/sat_clause_use_list.cpp +++ b/src/sat/sat_clause_use_list.cpp @@ -22,17 +22,20 @@ Revision History: namespace sat { bool clause_use_list::check_invariant() const { -#ifdef LAZY_USE_LIST unsigned sz = 0; - for (unsigned i = 0; i < m_clauses.size(); i++) - if (!m_clauses[i]->was_removed()) + for (clause* c : m_clauses) + if (!c->was_removed()) sz++; SASSERT(sz == m_size); -#endif + unsigned redundant = 0; + for (clause* c : m_clauses) + if (c->is_learned()) + redundant++; + SASSERT(redundant == m_num_redundant); + return true; } -#ifdef LAZY_USE_LIST void clause_use_list::iterator::consume() { while (true) { if (m_i == m_size) @@ -44,14 +47,11 @@ namespace sat { m_i++; } } -#endif clause_use_list::iterator::~iterator() { -#ifdef LAZY_USE_LIST while (m_i < m_size) next(); m_clauses.shrink(m_j); -#endif } }; diff --git a/src/sat/sat_clause_use_list.h b/src/sat/sat_clause_use_list.h index 121345f21..90c2d7779 100644 --- a/src/sat/sat_clause_use_list.h +++ b/src/sat/sat_clause_use_list.h @@ -24,30 +24,30 @@ Revision History: namespace sat { -#define LAZY_USE_LIST - /** \brief Clause use list with delayed deletion. */ class clause_use_list { clause_vector m_clauses; -#ifdef LAZY_USE_LIST unsigned m_size; -#endif + unsigned m_num_redundant; public: clause_use_list() { STRACE("clause_use_list_bug", tout << "[cul_created] " << this << "\n";); -#ifdef LAZY_USE_LIST m_size = 0; -#endif + m_num_redundant = 0; } unsigned size() const { -#ifdef LAZY_USE_LIST return m_size; -#else - return m_clauses.size(); -#endif + } + + unsigned num_redundant() const { + return m_num_redundant; + } + + unsigned num_irredundant() const { + return m_size - m_num_redundant; } bool empty() const { return size() == 0; } @@ -57,58 +57,59 @@ namespace sat { SASSERT(!m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.push_back(&c); -#ifdef LAZY_USE_LIST m_size++; -#endif + if (c.is_learned()) ++m_num_redundant; } void erase_not_removed(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_erase_not_removed] " << this << " " << &c << "\n";); -#ifdef LAZY_USE_LIST SASSERT(m_clauses.contains(&c)); SASSERT(!c.was_removed()); m_clauses.erase(&c); m_size--; -#else - m_clauses.erase(&c); -#endif + if (c.is_learned()) --m_num_redundant; } void erase(clause & c) { STRACE("clause_use_list_bug", tout << "[cul_erase] " << this << " " << &c << "\n";); -#ifdef LAZY_USE_LIST SASSERT(m_clauses.contains(&c)); SASSERT(c.was_removed()); m_size--; -#else - m_clauses.erase(&c); -#endif + if (c.is_learned()) --m_num_redundant; + } + + void block(clause const& c) { + SASSERT(c.is_learned()); + ++m_num_redundant; + SASSERT(check_invariant()); + } + + void unblock(clause const& c) { + SASSERT(!c.is_learned()); + --m_num_redundant; + SASSERT(check_invariant()); } void reset() { m_clauses.finalize(); -#ifdef LAZY_USE_LIST m_size = 0; -#endif + m_num_redundant = 0; } bool check_invariant() const; // iterate & compress - class iterator { + class iterator { clause_vector & m_clauses; unsigned m_size; unsigned m_i; -#ifdef LAZY_USE_LIST unsigned m_j; void consume(); -#endif + public: iterator(clause_vector & v):m_clauses(v), m_size(v.size()), m_i(0) { -#ifdef LAZY_USE_LIST m_j = 0; consume(); -#endif } ~iterator(); bool at_end() const { return m_i == m_size; } @@ -117,14 +118,21 @@ namespace sat { SASSERT(!at_end()); SASSERT(!m_clauses[m_i]->was_removed()); m_i++; -#ifdef LAZY_USE_LIST m_j++; consume(); -#endif } }; iterator mk_iterator() const { return iterator(const_cast(this)->m_clauses); } + + std::ostream& display(std::ostream& out) const { + iterator it = mk_iterator(); + while (!it.at_end()) { + out << it.curr() << "\n"; + it.next(); + } + return out; + } }; }; diff --git a/src/sat/sat_cleaner.cpp b/src/sat/sat_cleaner.cpp index 9dd53d8f6..c0c6fabe4 100644 --- a/src/sat/sat_cleaner.cpp +++ b/src/sat/sat_cleaner.cpp @@ -99,7 +99,9 @@ namespace sat { m_elim_literals++; break; case l_undef: - c[j] = c[i]; + if (i != j) { + std::swap(c[j], c[i]); + } j++; break; } @@ -115,19 +117,13 @@ namespace sat { else { unsigned new_sz = j; CTRACE("sat_cleaner_bug", new_sz < 2, tout << "new_sz: " << new_sz << "\n"; - if (c.size() > 0) tout << "unit: " << c[0] << "\n";); - SASSERT(c.frozen() || new_sz >= 2); + if (c.size() > 0) tout << "unit: " << c[0] << "\n"; + s.display_watches(tout);); if (new_sz == 0) { - // It can only happen with frozen clauses. - // active clauses would have signed the conflict. - SASSERT(c.frozen()); s.set_conflict(justification()); s.del_clause(c); } else if (new_sz == 1) { - // It can only happen with frozen clauses. - // active clauses would have propagated the literal - SASSERT(c.frozen()); s.assign(c[0], justification()); s.del_clause(c); } @@ -142,11 +138,13 @@ namespace sat { c.shrink(new_sz); *it2 = *it; it2++; - if (!c.frozen()) { - if (new_sz == 3) - s.attach_ter_clause(c); - else - s.attach_nary_clause(c); + if (!c.frozen()) { + s.attach_clause(c); + } + if (s.m_config.m_drat) { + // for optimization, could also report deletion + // of previous version of clause. + s.m_drat.add(c, true); } } } @@ -184,6 +182,7 @@ namespace sat { CASSERT("cleaner_bug", s.check_invariant()); unsigned trail_sz = s.m_trail.size(); s.propagate(false); // make sure that everything was propagated. + TRACE("sat_cleaner_bug", s.display(tout); s.display_watches(tout);); if (s.m_inconsistent) return false; if (m_last_num_units == trail_sz) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index c67511275..c7f2377c9 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -19,23 +19,13 @@ Revision History: #include "sat/sat_config.h" #include "sat/sat_types.h" #include "sat/sat_params.hpp" +#include "sat/sat_simplifier_params.hpp" + namespace sat { - config::config(params_ref const & p): - m_restart_max(0), - m_always_true("always_true"), - m_always_false("always_false"), - m_caching("caching"), - m_random("random"), - m_geometric("geometric"), - m_luby("luby"), - m_dyn_psm("dyn_psm"), - m_psm("psm"), - m_glue("glue"), - m_glue_psm("glue_psm"), - m_psm_glue("psm_glue") { - m_num_parallel = 1; + config::config(params_ref const & p) { + m_incremental = false; // ad-hoc parameter updt_params(p); } @@ -44,31 +34,42 @@ namespace sat { m_max_memory = megabytes_to_bytes(p.max_memory()); symbol s = p.restart(); - if (s == m_luby) + if (s == symbol("luby")) m_restart = RS_LUBY; - else if (s == m_geometric) + else if (s == symbol("geometric")) m_restart = RS_GEOMETRIC; + else if (s == symbol("ema")) + m_restart = RS_EMA; + else if (s == symbol("static")) + m_restart = RS_STATIC; else throw sat_param_exception("invalid restart strategy"); + m_fast_glue_avg = p.restart_emafastglue(); + m_slow_glue_avg = p.restart_emaslowglue(); + m_restart_margin = p.restart_margin(); + m_restart_fast = p.restart_fast(); s = p.phase(); - if (s == m_always_false) + if (s == symbol("always_false")) m_phase = PS_ALWAYS_FALSE; - else if (s == m_always_true) + else if (s == symbol("always_true")) m_phase = PS_ALWAYS_TRUE; - else if (s == m_caching) + else if (s == symbol("caching")) m_phase = PS_CACHING; - else if (s == m_random) + else if (s == symbol("random")) m_phase = PS_RANDOM; else throw sat_param_exception("invalid phase selection strategy"); m_phase_caching_on = p.phase_caching_on(); m_phase_caching_off = p.phase_caching_off(); + m_phase_sticky = p.phase_sticky(); m_restart_initial = p.restart_initial(); m_restart_factor = p.restart_factor(); m_restart_max = p.restart_max(); + m_propagate_prefetch = p.propagate_prefetch(); + m_inprocess_max = p.inprocess_max(); m_random_freq = p.random_freq(); m_random_seed = p.random_seed(); @@ -78,43 +79,127 @@ namespace sat { m_burst_search = p.burst_search(); m_max_conflicts = p.max_conflicts(); - m_num_parallel = p.parallel_threads(); - + m_num_threads = p.threads(); + m_local_search = p.local_search(); + m_local_search_threads = p.local_search_threads(); + if (p.local_search_mode() == symbol("gsat")) + m_local_search_mode = local_search_mode::gsat; + else + m_local_search_mode = local_search_mode::wsat; + m_unit_walk = p.unit_walk(); + m_unit_walk_threads = p.unit_walk_threads(); + m_lookahead_simplify = p.lookahead_simplify(); + m_lookahead_simplify_bca = p.lookahead_simplify_bca(); + if (p.lookahead_reward() == symbol("heule_schur")) + m_lookahead_reward = heule_schur_reward; + else if (p.lookahead_reward() == symbol("heuleu")) + m_lookahead_reward = heule_unit_reward; + else if (p.lookahead_reward() == symbol("ternary")) + m_lookahead_reward = ternary_reward; + else if (p.lookahead_reward() == symbol("unit")) + m_lookahead_reward = unit_literal_reward; + else if (p.lookahead_reward() == symbol("march_cu")) + m_lookahead_reward = march_cu_reward; + else + throw sat_param_exception("invalid reward type supplied: accepted heuristics are 'ternary', 'heuleu', 'unit' or 'heule_schur'"); + + if (p.lookahead_cube_cutoff() == symbol("depth")) + m_lookahead_cube_cutoff = depth_cutoff; + else if (p.lookahead_cube_cutoff() == symbol("freevars")) + m_lookahead_cube_cutoff = freevars_cutoff; + else if (p.lookahead_cube_cutoff() == symbol("psat")) + m_lookahead_cube_cutoff = psat_cutoff; + else if (p.lookahead_cube_cutoff() == symbol("adaptive_freevars")) + m_lookahead_cube_cutoff = adaptive_freevars_cutoff; + else if (p.lookahead_cube_cutoff() == symbol("adaptive_psat")) + m_lookahead_cube_cutoff = adaptive_psat_cutoff; + else + throw sat_param_exception("invalid cutoff type supplied: accepted cutoffs are 'depth', 'freevars', 'psat', 'adaptive_freevars' and 'adaptive_psat'"); + m_lookahead_cube_fraction = p.lookahead_cube_fraction(); + m_lookahead_cube_depth = p.lookahead_cube_depth(); + m_lookahead_cube_freevars = p.lookahead_cube_freevars(); + m_lookahead_cube_psat_var_exp = p.lookahead_cube_psat_var_exp(); + m_lookahead_cube_psat_clause_base = p.lookahead_cube_psat_clause_base(); + m_lookahead_cube_psat_trigger = p.lookahead_cube_psat_trigger(); + m_lookahead_global_autarky = p.lookahead_global_autarky(); + m_lookahead_use_learned = p.lookahead_use_learned(); + + // These parameters are not exposed - m_simplify_mult1 = _p.get_uint("simplify_mult1", 300); + m_next_simplify1 = _p.get_uint("next_simplify", 30000); m_simplify_mult2 = _p.get_double("simplify_mult2", 1.5); m_simplify_max = _p.get_uint("simplify_max", 500000); // -------------------------------- + m_simplify_delay = p.simplify_delay(); s = p.gc(); - if (s == m_dyn_psm) { - m_gc_strategy = GC_DYN_PSM; - m_gc_initial = p.gc_initial(); - m_gc_increment = p.gc_increment(); - m_gc_small_lbd = p.gc_small_lbd(); - m_gc_k = p.gc_k(); - if (m_gc_k > 255) - m_gc_k = 255; - } - else { - if (s == m_glue_psm) - m_gc_strategy = GC_GLUE_PSM; - else if (s == m_glue) - m_gc_strategy = GC_GLUE; - else if (s == m_psm) - m_gc_strategy = GC_PSM; - else if (s == m_psm_glue) - m_gc_strategy = GC_PSM_GLUE; - else - throw sat_param_exception("invalid gc strategy"); - m_gc_initial = p.gc_initial(); - m_gc_increment = p.gc_increment(); - } + if (s == symbol("dyn_psm")) + m_gc_strategy = GC_DYN_PSM; + else if (s == symbol("glue_psm")) + m_gc_strategy = GC_GLUE_PSM; + else if (s == symbol("glue")) + m_gc_strategy = GC_GLUE; + else if (s == symbol("psm")) + m_gc_strategy = GC_PSM; + else if (s == symbol("psm_glue")) + m_gc_strategy = GC_PSM_GLUE; + else + throw sat_param_exception("invalid gc strategy"); + m_gc_initial = p.gc_initial(); + m_gc_increment = p.gc_increment(); + m_gc_small_lbd = p.gc_small_lbd(); + m_gc_k = std::min(255u, p.gc_k()); + m_gc_burst = p.gc_burst(); + m_gc_defrag = p.gc_defrag(); + m_minimize_lemmas = p.minimize_lemmas(); m_core_minimize = p.core_minimize(); m_core_minimize_partial = p.core_minimize_partial(); + m_drat_check_unsat = p.drat_check_unsat(); + m_drat_check_sat = p.drat_check_sat(); + m_drat_file = p.drat_file(); + m_drat = (m_drat_check_unsat || m_drat_file != symbol("") || m_drat_check_sat) && p.threads() == 1; m_dyn_sub_res = p.dyn_sub_res(); - m_dimacs_display = p.dimacs_display(); + + // Parameters used in Liang, Ganesh, Poupart, Czarnecki AAAI 2016. + m_branching_heuristic = BH_VSIDS; + if (p.branching_heuristic() == symbol("vsids")) + m_branching_heuristic = BH_VSIDS; + else if (p.branching_heuristic() == symbol("chb")) + m_branching_heuristic = BH_CHB; + else if (p.branching_heuristic() == symbol("lrb")) + m_branching_heuristic = BH_LRB; + else + throw sat_param_exception("invalid branching heuristic: accepted heuristics are 'vsids', 'lrb' or 'chb'"); + + m_anti_exploration = p.branching_anti_exploration(); + m_step_size_init = 0.40; + m_step_size_dec = 0.000001; + m_step_size_min = 0.06; + m_reward_multiplier = 0.9; + m_reward_offset = 1000000.0; + + m_variable_decay = p.variable_decay(); + + // PB parameters + s = p.pb_solver(); + if (s == symbol("circuit")) + m_pb_solver = PB_CIRCUIT; + else if (s == symbol("sorting")) + m_pb_solver = PB_SORTING; + else if (s == symbol("totalizer")) + m_pb_solver = PB_TOTALIZER; + else if (s == symbol("solver")) + m_pb_solver = PB_SOLVER; + else if (s == symbol("segmented")) + m_pb_solver = PB_SEGMENTED; + else + throw sat_param_exception("invalid PB solver: solver, totalizer, circuit, sorting, segmented"); + + m_card_solver = p.cardinality_solver(); + + sat_simplifier_params sp(_p); + m_elim_vars = sp.elim_vars(); } void config::collect_param_descrs(param_descrs & r) { diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index 36f22e83f..37efe69ed 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -33,7 +33,9 @@ namespace sat { enum restart_strategy { RS_GEOMETRIC, - RS_LUBY + RS_LUBY, + RS_EMA, + RS_STATIC }; enum gc_strategy { @@ -44,51 +46,121 @@ namespace sat { GC_PSM_GLUE }; + enum branching_heuristic { + BH_VSIDS, + BH_CHB, + BH_LRB + }; + + enum pb_solver { + PB_SOLVER, + PB_CIRCUIT, + PB_SORTING, + PB_TOTALIZER, + PB_SEGMENTED + }; + + enum reward_t { + ternary_reward, + unit_literal_reward, + heule_schur_reward, + heule_unit_reward, + march_cu_reward + }; + + enum cutoff_t { + depth_cutoff, + freevars_cutoff, + psat_cutoff, + adaptive_freevars_cutoff, + adaptive_psat_cutoff + }; + + enum local_search_mode { + gsat, + wsat + }; + struct config { unsigned long long m_max_memory; phase_selection m_phase; unsigned m_phase_caching_on; unsigned m_phase_caching_off; + bool m_phase_sticky; + bool m_propagate_prefetch; restart_strategy m_restart; + bool m_restart_fast; unsigned m_restart_initial; double m_restart_factor; // for geometric case + double m_restart_margin; // for ema unsigned m_restart_max; + double m_fast_glue_avg; + double m_slow_glue_avg; + unsigned m_inprocess_max; double m_random_freq; unsigned m_random_seed; unsigned m_burst_search; unsigned m_max_conflicts; - unsigned m_num_parallel; + unsigned m_num_threads; + unsigned m_local_search_threads; + bool m_local_search; + local_search_mode m_local_search_mode; + unsigned m_unit_walk_threads; + bool m_unit_walk; + bool m_lookahead_simplify; + bool m_lookahead_simplify_bca; + cutoff_t m_lookahead_cube_cutoff; + double m_lookahead_cube_fraction; + unsigned m_lookahead_cube_depth; + double m_lookahead_cube_freevars; + double m_lookahead_cube_psat_var_exp; + double m_lookahead_cube_psat_clause_base; + double m_lookahead_cube_psat_trigger; + reward_t m_lookahead_reward; + bool m_lookahead_global_autarky; + bool m_lookahead_use_learned; - unsigned m_simplify_mult1; + bool m_incremental; + unsigned m_next_simplify1; double m_simplify_mult2; unsigned m_simplify_max; + unsigned m_simplify_delay; + + unsigned m_variable_decay; gc_strategy m_gc_strategy; unsigned m_gc_initial; unsigned m_gc_increment; unsigned m_gc_small_lbd; unsigned m_gc_k; + bool m_gc_burst; + bool m_gc_defrag; + bool m_minimize_lemmas; bool m_dyn_sub_res; bool m_core_minimize; bool m_core_minimize_partial; - - bool m_dimacs_display; - - symbol m_always_true; - symbol m_always_false; - symbol m_caching; - symbol m_random; - symbol m_geometric; - symbol m_luby; + bool m_drat; + symbol m_drat_file; + bool m_drat_check_unsat; + bool m_drat_check_sat; - symbol m_dyn_psm; - symbol m_psm; - symbol m_glue; - symbol m_glue_psm; - symbol m_psm_glue; + pb_solver m_pb_solver; + bool m_card_solver; + // branching heuristic settings. + branching_heuristic m_branching_heuristic; + bool m_anti_exploration; + double m_step_size_init; + double m_step_size_dec; + double m_step_size_min; + double m_reward_multiplier; + double m_reward_offset; + + // simplifier configurations used outside of sat_simplifier + bool m_elim_vars; + config(params_ref const & p); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); diff --git a/src/sat/sat_drat.cpp b/src/sat/sat_drat.cpp new file mode 100644 index 000000000..00a3fa076 --- /dev/null +++ b/src/sat/sat_drat.cpp @@ -0,0 +1,502 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_drat.cpp + +Abstract: + + Produce DRAT proofs. + + Check them using a very simple forward checker + that interacts with external plugins. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-2-3 + +Notes: + +--*/ +#include "sat_solver.h" +#include "sat_drat.h" + + +namespace sat { + drat::drat(solver& s): + s(s), + m_out(0), + m_inconsistent(false), + m_check_unsat(false), + m_check_sat(false), + m_check(false) + { + if (s.m_config.m_drat && s.m_config.m_drat_file != symbol()) { + m_out = alloc(std::ofstream, s.m_config.m_drat_file.str().c_str()); + } + } + + drat::~drat() { + dealloc(m_out); + for (unsigned i = 0; i < m_proof.size(); ++i) { + clause* c = m_proof[i]; + if (c && (c->size() == 2 || m_status[i] == status::deleted || m_status[i] == status::external)) { + s.dealloc_clause(c); + } + } + } + + void drat::updt_config() { + m_check_unsat = s.m_config.m_drat_check_unsat; + m_check_sat = s.m_config.m_drat_check_sat; + m_check = m_check_unsat || m_check_sat; + } + + std::ostream& operator<<(std::ostream& out, drat::status st) { + switch (st) { + case drat::status::learned: return out << "l"; + case drat::status::asserted: return out << "a"; + case drat::status::deleted: return out << "d"; + case drat::status::external: return out << "e"; + default: return out; + } + } + + void drat::dump(unsigned n, literal const* c, status st) { + switch (st) { + case status::asserted: return; + case status::external: return; // requires extension to drat format. + case status::learned: break; + case status::deleted: (*m_out) << "d "; break; + } + for (unsigned i = 0; i < n; ++i) (*m_out) << c[i] << " "; + (*m_out) << "0\n"; + } + + bool drat::is_cleaned(clause& c) const { + literal last = null_literal; + unsigned n = c.size(); + for (unsigned i = 0; i < n; ++i) { + if (c[i] == last) return true; + last = c[i]; + } + return false; + } + + void drat::trace(std::ostream& out, unsigned n, literal const* c, status st) { + out << st << " "; + literal last = null_literal; + for (unsigned i = 0; i < n; ++i) { + if (c[i] != last) { + out << c[i] << " "; + last = c[i]; + } + } + out << "\n"; + } + + void drat::append(literal l, status st) { + IF_VERBOSE(20, trace(verbose_stream(), 1, &l, st);); + if (st == status::learned) { + verify(1, &l); + } + if (st == status::deleted) { + return; + } + assign_propagate(l); + } + + void drat::append(literal l1, literal l2, status st) { + literal lits[2] = { l1, l2 }; + IF_VERBOSE(20, trace(verbose_stream(), 2, lits, st);); + if (st == status::deleted) { + // noop + // don't record binary as deleted. + } + else { + if (st == status::learned) { + verify(2, lits); + } + clause* c = s.alloc_clause(2, lits, st == status::learned); + m_proof.push_back(c); + m_status.push_back(st); + unsigned idx = m_watched_clauses.size(); + m_watched_clauses.push_back(watched_clause(c, l1, l2)); + m_watches[(~l1).index()].push_back(idx); + m_watches[(~l2).index()].push_back(idx); + + if (value(l1) == l_false && value(l2) == l_false) { + m_inconsistent = true; + } + else if (value(l1) == l_false) { + assign_propagate(l2); + } + else if (value(l2) == l_false) { + assign_propagate(l1); + } + } + } + + void drat::append(clause& c, status st) { + unsigned n = c.size(); + IF_VERBOSE(20, trace(verbose_stream(), n, c.begin(), st);); + + if (st == status::learned) { + verify(n, c.begin()); + } + + m_status.push_back(st); + m_proof.push_back(&c); + if (st == status::deleted) { + del_watch(c, c[0]); + del_watch(c, c[1]); + return; + } + unsigned num_watch = 0; + literal l1, l2; + for (unsigned i = 0; i < n; ++i) { + if (value(c[i]) != l_false) { + if (num_watch == 0) { + l1 = c[i]; + ++num_watch; + } + else { + l2 = c[i]; + ++num_watch; + break; + } + } + } + switch (num_watch) { + case 0: + m_inconsistent = true; + break; + case 1: + assign_propagate(l1); + break; + default: { + SASSERT(num_watch == 2); + unsigned idx = m_watched_clauses.size(); + m_watched_clauses.push_back(watched_clause(&c, l1, l2)); + m_watches[(~l1).index()].push_back(idx); + m_watches[(~l2).index()].push_back(idx); + break; + } + } + } + + void drat::del_watch(clause& c, literal l) { + watch& w = m_watches[(~l).index()]; + for (unsigned i = 0; i < w.size(); ++i) { + if (m_watched_clauses[w[i]].m_clause == &c) { + w[i] = w.back(); + w.pop_back(); + break; + } + } + } + + void drat::declare(literal l) { + unsigned n = static_cast(l.var()); + while (m_assignment.size() <= n) { + m_assignment.push_back(l_undef); + m_watches.push_back(watch()); + m_watches.push_back(watch()); + } + } + + bool drat::is_drup(unsigned n, literal const* c) { + if (m_inconsistent || n == 0) return true; + unsigned num_units = m_units.size(); + for (unsigned i = 0; !m_inconsistent && i < n; ++i) { + assign_propagate(~c[i]); + } + if (!m_inconsistent) { + DEBUG_CODE(validate_propagation();); + } + for (unsigned i = 0; i < m_units.size(); ++i) { + SASSERT(m_assignment[m_units[i].var()] != l_undef); + } + + for (unsigned i = num_units; i < m_units.size(); ++i) { + m_assignment[m_units[i].var()] = l_undef; + } + m_units.resize(num_units); + bool ok = m_inconsistent; + m_inconsistent = false; + return ok; + } + + bool drat::is_drat(unsigned n, literal const* c) { + if (m_inconsistent || n == 0) return true; + for (unsigned i = 0; i < n; ++i) { + if (is_drat(n, c, i)) return true; + } + return false; + } + + void drat::validate_propagation() const { + for (unsigned i = 0; i < m_proof.size(); ++i) { + status st = m_status[i]; + if (m_proof[i] && st != status::deleted) { + clause& c = *m_proof[i]; + unsigned num_undef = 0, num_true = 0; + for (unsigned j = 0; j < c.size(); ++j) { + switch (value(c[j])) { + case l_false: break; + case l_true: num_true++; break; + case l_undef: num_undef++; break; + } + } + CTRACE("sat", num_true == 0 && num_undef == 1, display(tout);); + SASSERT(num_true != 0 || num_undef != 1); + } + } + } + + bool drat::is_drat(unsigned n, literal const* c, unsigned pos) { + SASSERT(pos < n); + literal l = c[pos]; + literal_vector lits(n, c); + SASSERT(lits.size() == n); + for (unsigned i = 0; i < m_proof.size(); ++i) { + status st = m_status[i]; + if (m_proof[i] && (st == status::asserted || st == status::external)) { + clause& c = *m_proof[i]; + unsigned j = 0; + for (; j < c.size() && c[j] != ~l; ++j) {} + if (j != c.size()) { + lits.append(j, c.begin()); + lits.append(c.size() - j - 1, c.begin() + j + 1); + if (!is_drup(lits.size(), lits.c_ptr())) return false; + lits.resize(n); + } + } + } + return true; + + } + + void drat::verify(unsigned n, literal const* c) { + if (m_check_unsat && !is_drup(n, c) && !is_drat(n, c)) { + std::cout << "Verification failed\n"; + UNREACHABLE(); + //display(std::cout); + TRACE("sat", + tout << literal_vector(n, c) << "\n"; + display(tout); + s.display(tout);); + UNREACHABLE(); + exit(0); + } + } + + void drat::display(std::ostream& out) const { + out << "units: " << m_units << "\n"; + for (unsigned i = 0; i < m_assignment.size(); ++i) { + lbool v = value(literal(i, false)); + if (v != l_undef) out << i << ": " << v << "\n"; + } + for (unsigned i = 0; i < m_proof.size(); ++i) { + clause* c = m_proof[i]; + if (m_status[i] != status::deleted && c) { + unsigned num_true = 0; + unsigned num_undef = 0; + for (unsigned j = 0; j < c->size(); ++j) { + switch (value((*c)[j])) { + case l_true: num_true++; break; + case l_undef: num_undef++; break; + default: break; + } + } + if (num_true == 0 && num_undef == 0) { + out << "False "; + } + if (num_true == 0 && num_undef == 1) { + out << "Unit "; + } + out << m_status[i] << " " << i << ": " << *c << "\n"; + } + } + for (unsigned i = 0; i < m_assignment.size(); ++i) { + watch const& w1 = m_watches[2*i]; + watch const& w2 = m_watches[2*i + 1]; + if (!w1.empty()) { + out << i << " |-> "; + for (unsigned i = 0; i < w1.size(); ++i) out << *(m_watched_clauses[w1[i]].m_clause) << " "; + out << "\n"; + } + if (!w2.empty()) { + out << "-" << i << " |-> "; + for (unsigned i = 0; i < w2.size(); ++i) out << *(m_watched_clauses[w2[i]].m_clause) << " "; + out << "\n"; + } + } + } + + lbool drat::value(literal l) const { + lbool val = m_assignment.get(l.var(), l_undef); + return val == l_undef || !l.sign() ? val : ~val; + } + + void drat::assign(literal l) { + lbool new_value = l.sign() ? l_false : l_true; + lbool old_value = value(l); +// TRACE("sat", tout << "assign " << l << " := " << new_value << " from " << old_value << "\n";); + switch (old_value) { + case l_false: + m_inconsistent = true; + break; + case l_true: + break; + case l_undef: + m_assignment.setx(l.var(), new_value, l_undef); + m_units.push_back(l); + break; + } + } + + void drat::assign_propagate(literal l) { + unsigned num_units = m_units.size(); + assign(l); + for (unsigned i = num_units; !m_inconsistent && i < m_units.size(); ++i) { + propagate(m_units[i]); + } + } + + void drat::propagate(literal l) { + watch& clauses = m_watches[l.index()]; + watch::iterator it = clauses.begin(); + watch::iterator it2 = it; + watch::iterator end = clauses.end(); + for (; it != end; ++it) { + unsigned idx = *it; + watched_clause& wc = m_watched_clauses[idx]; + clause& c = *wc.m_clause; + + //TRACE("sat", tout << "Propagate " << l << " " << c << " watch: " << wc.m_l1 << " " << wc.m_l2 << "\n";); + if (wc.m_l1 == ~l) { + std::swap(wc.m_l1, wc.m_l2); + } + + SASSERT(wc.m_l2 == ~l); + if (value(wc.m_l1) == l_true) { + *it2 = *it; + it2++; + } + else { + bool done = false; + for (unsigned i = 0; !done && i < c.size(); ++i) { + literal lit = c[i]; + if (lit != wc.m_l1 && lit != wc.m_l2 && value(lit) != l_false) { + wc.m_l2 = lit; + m_watches[(~lit).index()].push_back(idx); + done = true; + } + } + if (done) { + continue; + } + else if (value(wc.m_l1) == l_false) { + m_inconsistent = true; + goto end_process_watch; + } + else { + *it2 = *it; + it2++; + assign(wc.m_l1); + } + } + } + end_process_watch: + for (; it != end; ++it, ++it2) + *it2 = *it; + clauses.set_end(it2); + } + + drat::status drat::get_status(bool learned) const { + return learned || s.m_searching ? status::learned : status::asserted; + } + + void drat::add() { + if (m_out) (*m_out) << "0\n"; + if (m_check_unsat) { + SASSERT(m_inconsistent); + } + } + void drat::add(literal l, bool learned) { + declare(l); + status st = get_status(learned); + if (m_out) dump(1, &l, st); + if (m_check) append(l, st); + } + void drat::add(literal l1, literal l2, bool learned) { + declare(l1); + declare(l2); + literal ls[2] = {l1, l2}; + status st = get_status(learned); + if (m_out) dump(2, ls, st); + if (m_check) append(l1, l2, st); + } + void drat::add(clause& c, bool learned) { + TRACE("sat", tout << "add: " << c << "\n";); + for (unsigned i = 0; i < c.size(); ++i) declare(c[i]); + status st = get_status(learned); + if (m_out) dump(c.size(), c.begin(), st); + if (m_check_unsat) append(c, get_status(learned)); + } + void drat::add(literal_vector const& lits, svector const& premises) { + if (m_check) { + switch (lits.size()) { + case 0: add(); break; + case 1: append(lits[0], status::external); break; + default: { + clause* c = s.alloc_clause(lits.size(), lits.c_ptr(), true); + append(*c, status::external); + break; + } + } + } + } + void drat::add(literal_vector const& c) { + for (unsigned i = 0; i < c.size(); ++i) declare(c[i]); + if (m_out) dump(c.size(), c.begin(), status::learned); + if (m_check) { + switch (c.size()) { + case 0: add(); break; + case 1: append(c[0], status::learned); break; + default: { + verify(c.size(), c.begin()); + clause* cl = s.alloc_clause(c.size(), c.c_ptr(), true); + append(*cl, status::external); + break; + } + } + } + } + + void drat::del(literal l) { + if (m_out) dump(1, &l, status::deleted); + if (m_check_unsat) append(l, status::deleted); + } + void drat::del(literal l1, literal l2) { + literal ls[2] = {l1, l2}; + if (m_out) dump(2, ls, status::deleted); + if (m_check) + append(l1, l2, status::deleted); + } + void drat::del(clause& c) { + TRACE("sat", tout << "del: " << c << "\n";); + if (m_out) dump(c.size(), c.begin(), status::deleted); + if (m_check) { + clause* c1 = s.alloc_clause(c.size(), c.begin(), c.is_learned()); + append(*c1, status::deleted); + } + } + + void drat::check_model(model const& m) { + std::cout << "check model on " << m_proof.size() << "\n"; + } + +} diff --git a/src/sat/sat_drat.h b/src/sat/sat_drat.h new file mode 100644 index 000000000..64d796839 --- /dev/null +++ b/src/sat/sat_drat.h @@ -0,0 +1,101 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_drat.h + +Abstract: + + Produce DRAT proofs. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-2-3 + +Notes: + +--*/ +#ifndef SAT_DRAT_H_ +#define SAT_DRAT_H_ + +namespace sat { + class drat { + public: + struct s_ext {}; + struct s_unit {}; + struct premise { + enum { t_clause, t_unit, t_ext } m_type; + union { + clause* m_clause; + unsigned m_literal; + }; + premise(s_ext, literal l): m_type(t_ext), m_literal(l.index()) {} + premise(s_unit, literal l): m_type(t_unit), m_literal(l.index()) {} + premise(clause* c): m_type(t_clause), m_clause(c) {} + }; + private: + enum status { asserted, learned, deleted, external }; + struct watched_clause { + clause* m_clause; + literal m_l1, m_l2; + watched_clause(clause* c, literal l1, literal l2): + m_clause(c), m_l1(l1), m_l2(l2) {} + }; + svector m_watched_clauses; + typedef svector watch; + solver& s; + std::ostream* m_out; + ptr_vector m_proof; + svector m_status; + literal_vector m_units; + vector m_watches; + svector m_assignment; + bool m_inconsistent; + bool m_check_unsat, m_check_sat, m_check; + + void dump(unsigned n, literal const* c, status st); + void append(literal l, status st); + void append(literal l1, literal l2, status st); + void append(clause& c, status st); + + friend std::ostream& operator<<(std::ostream & out, status st); + status get_status(bool learned) const; + + void declare(literal l); + void assign(literal l); + void propagate(literal l); + void assign_propagate(literal l); + void del_watch(clause& c, literal l); + void verify(unsigned n, literal const* c); + bool is_drup(unsigned n, literal const* c); + bool is_drat(unsigned n, literal const* c); + bool is_drat(unsigned n, literal const* c, unsigned pos); + lbool value(literal l) const; + void trace(std::ostream& out, unsigned n, literal const* c, status st); + void display(std::ostream& out) const; + void validate_propagation() const; + + public: + drat(solver& s); + ~drat(); + + void updt_config(); + void add(); + void add(literal l, bool learned); + void add(literal l1, literal l2, bool learned); + void add(clause& c, bool learned); + void add(literal_vector const& c, svector const& premises); + void add(literal_vector const& c); // add learned clause + + bool is_cleaned(clause& c) const; + void del(literal l); + void del(literal l1, literal l2); + void del(clause& c); + + void check_model(model const& m); + }; + +}; + +#endif diff --git a/src/sat/sat_elim_eqs.cpp b/src/sat/sat_elim_eqs.cpp index 4cb2fa8ae..870aa7fe2 100644 --- a/src/sat/sat_elim_eqs.cpp +++ b/src/sat/sat_elim_eqs.cpp @@ -33,19 +33,18 @@ namespace sat { return roots[l.var()]; } - void elim_eqs::cleanup_bin_watches(literal_vector const & roots) { - vector::iterator it = m_solver.m_watches.begin(); - vector::iterator end = m_solver.m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - watch_list & wlist = *it; - literal l1 = ~to_literal(l_idx); + void elim_eqs::cleanup_bin_watches(literal_vector const & roots) { + unsigned l_idx = 0; + m_new_bin.reset(); + for (watch_list & wlist : m_solver.m_watches) { + literal l1 = ~to_literal(l_idx++); literal r1 = norm(roots, l1); - watch_list::iterator it2 = wlist.begin(); - watch_list::iterator itprev = it2; - watch_list::iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause()) { - literal l2 = it2->get_literal(); + watch_list::iterator it = wlist.begin(); + watch_list::iterator itprev = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + if (it->is_binary_clause()) { + literal l2 = it->get_literal(); literal r2 = norm(roots, l2); if (r1 == r2) { m_solver.assign(r1, justification()); @@ -58,18 +57,33 @@ namespace sat { // consume tautology continue; } +#if 0 if (l1 != r1) { // add half r1 => r2, the other half ~r2 => ~r1 is added when traversing l2 - m_solver.m_watches[(~r1).index()].push_back(watched(r2, it2->is_learned())); + m_solver.m_watches[(~r1).index()].push_back(watched(r2, it->is_learned())); continue; } - it2->set_literal(r2); // keep it + it->set_literal(r2); // keep it. +#else + if (l1 != r1 || l2 != r2) { + if (r1.index() < r2.index()) { + m_new_bin.push_back(bin(r1, r2, it->is_learned())); + } + continue; + } + // keep it +#endif } - *itprev = *it2; + *itprev = *it; itprev++; } wlist.set_end(itprev); } + + for (auto const& b : m_new_bin) { + m_solver.mk_bin_clause(b.l1, b.l2, b.learned); + } + m_new_bin.reset(); } void elim_eqs::cleanup_clauses(literal_vector const & roots, clause_vector & cs) { @@ -78,7 +92,7 @@ namespace sat { clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); - TRACE("elim_eqs", tout << "processing: " << c << "\n";); + TRACE("sats", tout << "processing: " << c << "\n";); unsigned sz = c.size(); unsigned i; for (i = 0; i < sz; i++) { @@ -96,12 +110,20 @@ namespace sat { if (!c.frozen()) m_solver.detach_clause(c); // apply substitution - for (i = 0; i < sz; i++) { - SASSERT(!m_solver.was_eliminated(c[i].var())); - c[i] = norm(roots, c[i]); + for (i = 0; i < sz; i++) { + literal lit = c[i]; + c[i] = norm(roots, lit); + VERIFY(c[i] == norm(roots, c[i])); + VERIFY(!m_solver.was_eliminated(c[i].var()) || lit == c[i]); } std::sort(c.begin(), c.end()); - TRACE("elim_eqs", tout << "after normalization/sorting: " << c << "\n";); + for (literal l : c) VERIFY(l == norm(roots, l)); + TRACE("sats", tout << "after normalization/sorting: " << c << "\n"; tout.flush();); + DEBUG_CODE({ + for (literal l : c) { + CTRACE("sat", l != norm(roots, l), tout << l << " " << norm(roots, l) << "\n"; tout.flush();); + SASSERT(l == norm(roots, l)); + } }); // remove duplicates, and check if it is a tautology literal l_prev = null_literal; unsigned j = 0; @@ -117,11 +139,11 @@ namespace sat { break; // clause was satisfied if (val == l_false) continue; // skip - c[j] = l; + c[j] = l; j++; } if (i < sz) { - // clause is a tautology or was simplified + // clause is a tautology or was simplified to true m_solver.del_clause(c); continue; } @@ -136,16 +158,7 @@ namespace sat { return; } TRACE("elim_eqs", tout << "after removing duplicates: " << c << " j: " << j << "\n";); - if (j < sz) - c.shrink(j); - else - c.update_approx(); - SASSERT(c.size() == j); - DEBUG_CODE({ - for (unsigned i = 0; i < c.size(); i++) { - SASSERT(c[i] == norm(roots, c[i])); - } - }); + SASSERT(j >= 1); switch (j) { case 1: @@ -158,10 +171,21 @@ namespace sat { break; default: SASSERT(*it == &c); + if (j < sz) { + if (m_solver.m_config.m_drat) m_solver.m_drat.del(c); + c.shrink(j); + if (m_solver.m_config.m_drat) m_solver.m_drat.add(c, true); + } + else + c.update_approx(); + + DEBUG_CODE(for (literal l : c) VERIFY(l == norm(roots, l));); + *it2 = *it; it2++; - if (!c.frozen()) + if (!c.frozen()) { m_solver.attach_clause(c); + } break; } } @@ -170,14 +194,12 @@ namespace sat { void elim_eqs::save_elim(literal_vector const & roots, bool_var_vector const & to_elim) { model_converter & mc = m_solver.m_mc; - bool_var_vector::const_iterator it = to_elim.begin(); - bool_var_vector::const_iterator end = to_elim.end(); - for (; it != end; ++it) { - bool_var v = *it; + for (bool_var v : to_elim) { literal l(v, false); literal r = roots[v]; SASSERT(v != r.var()); - if (m_solver.is_external(v)) { + bool root_ok = !m_solver.is_external(v) || m_solver.set_root(l, r); + if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !root_ok))) { // cannot really eliminate v, since we have to notify extension of future assignments m_solver.mk_bin_clause(~l, r, false); m_solver.mk_bin_clause(l, ~r, false); @@ -190,28 +212,34 @@ namespace sat { mc.insert(e, l, ~r); } } + m_solver.flush_roots(); } - bool elim_eqs::check_clauses(literal_vector const & roots) const { - clause_vector * vs[2] = { &m_solver.m_clauses, &m_solver.m_learned }; - for (unsigned i = 0; i < 2; i++) { - clause_vector & cs = *(vs[i]); - clause_vector::iterator it = cs.begin(); - clause_vector::iterator end = cs.end(); - for (; it != end; ++it) { - clause & c = *(*it); - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - CTRACE("elim_eqs_bug", m_solver.was_eliminated(c[i].var()), tout << "lit: " << c[i] << " " << norm(roots, c[i]) << "\n"; - tout << c << "\n";); - SASSERT(!m_solver.was_eliminated(c[i].var())); - } + bool elim_eqs::check_clause(clause const& c, literal_vector const& roots) const { + for (literal l : c) { + CTRACE("elim_eqs_bug", m_solver.was_eliminated(l.var()), tout << "lit: " << l << " " << norm(roots, l) << "\n"; + tout << c << "\n";); + if (m_solver.was_eliminated(l.var())) { + IF_VERBOSE(0, verbose_stream() << c << " contains eliminated literal " << l << " " << norm(roots, l) << "\n";); + UNREACHABLE(); } } return true; } + + bool elim_eqs::check_clauses(literal_vector const & roots) const { + for (clause * cp : m_solver.m_clauses) + if (!check_clause(*cp, roots)) + return false; + for (clause * cp : m_solver.m_learned) + if (!check_clause(*cp, roots)) + return false; + return true; + } + void elim_eqs::operator()(literal_vector const & roots, bool_var_vector const & to_elim) { + TRACE("elim_eqs", tout << "before bin cleanup\n"; m_solver.display(tout);); cleanup_bin_watches(roots); TRACE("elim_eqs", tout << "after bin cleanup\n"; m_solver.display(tout);); cleanup_clauses(roots, m_solver.m_clauses); @@ -221,5 +249,6 @@ namespace sat { save_elim(roots, to_elim); m_solver.propagate(false); SASSERT(check_clauses(roots)); + TRACE("elim_eqs", tout << "after full cleanup\n"; m_solver.display(tout);); } }; diff --git a/src/sat/sat_elim_eqs.h b/src/sat/sat_elim_eqs.h index 0422b60df..143fcbb3f 100644 --- a/src/sat/sat_elim_eqs.h +++ b/src/sat/sat_elim_eqs.h @@ -25,11 +25,18 @@ namespace sat { class solver; class elim_eqs { + struct bin { + literal l1, l2; + bool learned; + bin(literal l1, literal l2, bool learned): l1(l1), l2(l2), learned(learned) {} + }; + svector m_new_bin; solver & m_solver; void save_elim(literal_vector const & roots, bool_var_vector const & to_elim); void cleanup_clauses(literal_vector const & roots, clause_vector & cs); void cleanup_bin_watches(literal_vector const & roots); bool check_clauses(literal_vector const & roots) const; + bool check_clause(clause const& c, literal_vector const& roots) const; public: elim_eqs(solver & s); void operator()(literal_vector const & roots, bool_var_vector const & to_elim); diff --git a/src/sat/sat_elim_vars.cpp b/src/sat/sat_elim_vars.cpp new file mode 100644 index 000000000..299fbace1 --- /dev/null +++ b/src/sat/sat_elim_vars.cpp @@ -0,0 +1,336 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_elim_vars.cpp + +Abstract: + + Helper class for eliminating variables + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-14 + +Revision History: + +--*/ + +#include "sat/sat_simplifier.h" +#include "sat/sat_elim_vars.h" +#include "sat/sat_solver.h" + +namespace sat{ + + elim_vars::elim_vars(simplifier& s) : simp(s), s(s.s), m(20) { + m_mark_lim = 0; + m_max_literals = 11; + m_miss = 0; + m_hit1 = 0; + m_hit2 = 0; + } + + bool elim_vars::operator()(bool_var v) { + if (s.value(v) != l_undef) + return false; + + literal pos_l(v, false); + literal neg_l(v, true); + unsigned num_bin_pos = simp.num_nonlearned_bin(pos_l); + if (num_bin_pos > m_max_literals) return false; + unsigned num_bin_neg = simp.num_nonlearned_bin(neg_l); + if (num_bin_neg > m_max_literals) return false; + clause_use_list & pos_occs = simp.m_use_list.get(pos_l); + clause_use_list & neg_occs = simp.m_use_list.get(neg_l); + unsigned clause_size = num_bin_pos + num_bin_neg + pos_occs.num_irredundant() + neg_occs.num_irredundant(); + if (clause_size == 0) { + return false; + } + reset_mark(); + mark_var(v); + if (!mark_literals(pos_occs)) return false; + if (!mark_literals(neg_occs)) return false; + if (!mark_literals(pos_l)) return false; + if (!mark_literals(neg_l)) return false; + + // associate index with each variable. + sort_marked(); + bdd b1 = elim_var(v); + double sz1 = b1.cnf_size(); + if (sz1 > 2*clause_size) { + ++m_miss; + return false; + } + if (sz1 <= clause_size) { + ++m_hit1; + return elim_var(v, b1); + } + m.try_cnf_reorder(b1); + sz1 = b1.cnf_size(); + if (sz1 <= clause_size) { + ++m_hit2; + return elim_var(v, b1); + } + ++m_miss; + return false; + } + + bool elim_vars::elim_var(bool_var v, bdd const& b) { + literal pos_l(v, false); + literal neg_l(v, true); + clause_use_list & pos_occs = simp.m_use_list.get(pos_l); + clause_use_list & neg_occs = simp.m_use_list.get(neg_l); + + // eliminate variable + simp.m_pos_cls.reset(); + simp.m_neg_cls.reset(); + simp.collect_clauses(pos_l, simp.m_pos_cls); + simp.collect_clauses(neg_l, simp.m_neg_cls); + VERIFY(!simp.is_external(v)); + + model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); + simp.save_clauses(mc_entry, simp.m_pos_cls); + simp.save_clauses(mc_entry, simp.m_neg_cls); + s.m_eliminated[v] = true; + ++s.m_stats.m_elim_var_bdd; + simp.remove_bin_clauses(pos_l); + simp.remove_bin_clauses(neg_l); + simp.remove_clauses(pos_occs, pos_l); + simp.remove_clauses(neg_occs, neg_l); + pos_occs.reset(); + neg_occs.reset(); + literal_vector lits; + add_clauses(v, b, lits); + return true; + } + + bdd elim_vars::elim_var(bool_var v) { + unsigned index = 0; + for (bool_var w : m_vars) { + m_var2index[w] = index++; + } + literal pos_l(v, false); + literal neg_l(v, true); + clause_use_list & pos_occs = simp.m_use_list.get(pos_l); + clause_use_list & neg_occs = simp.m_use_list.get(neg_l); + + bdd b1 = make_clauses(pos_l); + bdd b2 = make_clauses(neg_l); + bdd b3 = make_clauses(pos_occs); + bdd b4 = make_clauses(neg_occs); + bdd b0 = b1 && b2 && b3 && b4; + bdd b = m.mk_exists(m_var2index[v], b0); + TRACE("elim_vars", + tout << "eliminate " << v << "\n"; + for (watched const& w : simp.get_wlist(~pos_l)) { + if (w.is_binary_non_learned_clause()) { + tout << pos_l << " " << w.get_literal() << "\n"; + } + } + m.display(tout, b1); + for (watched const& w : simp.get_wlist(~neg_l)) { + if (w.is_binary_non_learned_clause()) { + tout << neg_l << " " << w.get_literal() << "\n"; + } + } + m.display(tout, b2); + clause_use_list::iterator itp = pos_occs.mk_iterator(); + while (!itp.at_end()) { + clause const& c = itp.curr(); + tout << c << "\n"; + itp.next(); + } + m.display(tout, b3); + clause_use_list::iterator itn = neg_occs.mk_iterator(); + while (!itn.at_end()) { + clause const& c = itn.curr(); + tout << c << "\n"; + itn.next(); + } + m.display(tout, b4); + tout << "eliminated:\n"; + tout << b << "\n"; + tout << b.cnf_size() << "\n"; + ); + + return b; + } + + void elim_vars::add_clauses(bool_var v0, bdd const& b, literal_vector& lits) { + if (b.is_true()) { + // no-op + } + else if (b.is_false()) { + SASSERT(lits.size() > 0); + literal_vector c(lits); + if (simp.cleanup_clause(c)) + return; + + if (v0 == 39063) IF_VERBOSE(0, verbose_stream() << "bdd: " << c << "\n"); + switch (c.size()) { + case 0: + s.set_conflict(justification()); + break; + case 1: + simp.propagate_unit(c[0]); + break; + case 2: + s.m_stats.m_mk_bin_clause++; + simp.add_non_learned_binary_clause(c[0], c[1]); + simp.back_subsumption1(c[0], c[1], false); + break; + default: { + if (c.size() == 3) + s.m_stats.m_mk_ter_clause++; + else + s.m_stats.m_mk_clause++; + clause* cp = s.alloc_clause(c.size(), c.c_ptr(), false); + s.m_clauses.push_back(cp); + simp.m_use_list.insert(*cp); + if (simp.m_sub_counter > 0) + simp.back_subsumption1(*cp); + else + simp.back_subsumption0(*cp); + break; + } + } + } + else { + unsigned v = m_vars[b.var()]; + lits.push_back(literal(v, false)); + add_clauses(v0, b.lo(), lits); + lits.pop_back(); + lits.push_back(literal(v, true)); + add_clauses(v0, b.hi(), lits); + lits.pop_back(); + } + } + + + void elim_vars::get_clauses(bdd const& b, literal_vector & lits, clause_vector& clauses, literal_vector& units) { + if (b.is_true()) { + return; + } + if (b.is_false()) { + if (lits.size() > 1) { + clause* c = s.alloc_clause(lits.size(), lits.c_ptr(), false); + clauses.push_back(c); + } + else { + units.push_back(lits.back()); + } + return; + } + + // if (v hi lo) + // (v | lo) & (!v | hi) + // if (v T lo) -> (v | lo) + unsigned v = m_vars[b.var()]; + lits.push_back(literal(v, false)); + get_clauses(b.lo(), lits, clauses, units); + lits.pop_back(); + lits.push_back(literal(v, true)); + get_clauses(b.hi(), lits, clauses, units); + lits.pop_back(); + } + + void elim_vars::reset_mark() { + m_vars.reset(); + m_mark.resize(s.num_vars()); + m_var2index.resize(s.num_vars()); + m_occ.resize(s.num_vars()); + ++m_mark_lim; + if (m_mark_lim == 0) { + ++m_mark_lim; + m_mark.fill(0); + } + } + + class elim_vars::compare_occ { + elim_vars& ev; + public: + compare_occ(elim_vars& ev):ev(ev) {} + + bool operator()(bool_var v1, bool_var v2) const { + return ev.m_occ[v1] < ev.m_occ[v2]; + } + }; + + void elim_vars::sort_marked() { + std::sort(m_vars.begin(), m_vars.end(), compare_occ(*this)); + } + + void elim_vars::shuffle_vars() { + unsigned sz = m_vars.size(); + for (unsigned i = 0; i < sz; ++i) { + unsigned x = m_rand(sz); + unsigned y = m_rand(sz); + std::swap(m_vars[x], m_vars[y]); + } + } + + void elim_vars::mark_var(bool_var v) { + if (m_mark[v] != m_mark_lim) { + m_mark[v] = m_mark_lim; + m_vars.push_back(v); + m_occ[v] = 1; + } + else { + ++m_occ[v]; + } + } + + bool elim_vars::mark_literals(clause_use_list & occs) { + clause_use_list::iterator it = occs.mk_iterator(); + while (!it.at_end()) { + clause const& c = it.curr(); + for (literal l : c) { + mark_var(l.var()); + } + if (num_vars() > m_max_literals) return false; + it.next(); + } + return true; + } + + bool elim_vars::mark_literals(literal lit) { + watch_list& wl = simp.get_wlist(lit); + for (watched const& w : wl) { + if (w.is_binary_non_learned_clause()) { + mark_var(w.get_literal().var()); + } + } + return num_vars() <= m_max_literals; + } + + bdd elim_vars::make_clauses(clause_use_list & occs) { + bdd result = m.mk_true(); + for (auto it = occs.mk_iterator(); !it.at_end(); it.next()) { + clause const& c = it.curr(); + bdd cl = m.mk_false(); + for (literal l : c) { + cl |= mk_literal(l); + } + result &= cl; + } + return result; + } + + bdd elim_vars::make_clauses(literal lit) { + bdd result = m.mk_true(); + watch_list& wl = simp.get_wlist(~lit); + for (watched const& w : wl) { + if (w.is_binary_non_learned_clause()) { + result &= (mk_literal(lit) || mk_literal(w.get_literal())); + } + } + return result; + } + + bdd elim_vars::mk_literal(literal l) { + return l.sign() ? m.mk_nvar(m_var2index[l.var()]) : m.mk_var(m_var2index[l.var()]); + } + +}; + diff --git a/src/sat/sat_elim_vars.h b/src/sat/sat_elim_vars.h new file mode 100644 index 000000000..b62fdc5db --- /dev/null +++ b/src/sat/sat_elim_vars.h @@ -0,0 +1,74 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_elim_vars.h + +Abstract: + + Helper class for eliminating variables + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-14 + +Revision History: + +--*/ +#ifndef SAT_ELIM_VARS_H_ +#define SAT_ELIM_VARS_H_ + +#include "sat/sat_types.h" +#include "sat/sat_bdd.h" + +namespace sat { + class solver; + class simplifier; + + class elim_vars { + class compare_occ; + + simplifier& simp; + solver& s; + bdd_manager m; + random_gen m_rand; + + + svector m_vars; + unsigned_vector m_mark; + unsigned m_mark_lim; + unsigned_vector m_var2index; + unsigned_vector m_occ; + unsigned m_miss; + unsigned m_hit1; + unsigned m_hit2; + + unsigned m_max_literals; + + unsigned num_vars() const { return m_vars.size(); } + void reset_mark(); + void mark_var(bool_var v); + void sort_marked(); + void shuffle_vars(); + bool mark_literals(clause_use_list & occs); + bool mark_literals(literal lit); + bdd make_clauses(clause_use_list & occs); + bdd make_clauses(literal lit); + bdd mk_literal(literal l); + void get_clauses(bdd const& b, literal_vector& lits, clause_vector& clauses, literal_vector& units); + void add_clauses(bool_var v, bdd const& b, literal_vector& lits); + bool elim_var(bool_var v, bdd const& b); + bdd elim_var(bool_var v); + + public: + elim_vars(simplifier& s); + bool operator()(bool_var v); + unsigned hit2() const { return m_hit1; } // first round bdd construction is minimal + unsigned hit1() const { return m_hit2; } // minimal after reshufling + unsigned miss() const { return m_miss; } // not-minimal + }; + +}; + +#endif diff --git a/src/sat/sat_extension.h b/src/sat/sat_extension.h index 80144e00d..e687ab2b0 100644 --- a/src/sat/sat_extension.h +++ b/src/sat/sat_extension.h @@ -21,6 +21,7 @@ Revision History: #include "sat/sat_types.h" #include "util/params.h" +#include "util/statistics.h" namespace sat { @@ -28,17 +29,57 @@ namespace sat { CR_DONE, CR_CONTINUE, CR_GIVEUP }; + class literal_occs_fun { + public: + virtual double operator()(literal l) = 0; + }; + + + typedef svector ext_constraint_list; + + class ext_use_list { + vector m_use_list; + public: + void init(unsigned num_vars) { m_use_list.reset(); m_use_list.resize(num_vars*2); } + void insert(literal l, ext_constraint_idx idx) { get(l).push_back(idx); } + ext_constraint_list & get(literal l) { return m_use_list[l.index()]; } + ext_constraint_list const & get(literal l) const { return m_use_list[l.index()]; } + void finalize() { m_use_list.finalize(); } + }; + class extension { public: - virtual void propagate(literal l, ext_constraint_idx idx, bool & keep) = 0; + virtual ~extension() {} + virtual void set_solver(solver* s) = 0; + virtual void set_lookahead(lookahead* s) = 0; + virtual void set_unit_walk(unit_walk* u) = 0; + virtual bool propagate(literal l, ext_constraint_idx idx) = 0; + virtual double get_reward(literal l, ext_constraint_idx idx, literal_occs_fun& occs) const = 0; virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) = 0; + virtual bool is_extended_binary(ext_justification_idx idx, literal_vector & r) = 0; virtual void asserted(literal l) = 0; virtual check_result check() = 0; + virtual lbool resolve_conflict() { return l_undef; } // stores result in sat::solver::m_lemma virtual void push() = 0; virtual void pop(unsigned n) = 0; virtual void simplify() = 0; + // have a way to replace l by r in all constraints + virtual bool set_root(literal l, literal r) { return false; } + virtual void flush_roots() {} virtual void clauses_modifed() = 0; virtual lbool get_phase(bool_var v) = 0; + virtual std::ostream& display(std::ostream& out) const = 0; + virtual std::ostream& display_justification(std::ostream& out, ext_justification_idx idx) const = 0; + virtual void collect_statistics(statistics& st) const = 0; + virtual extension* copy(solver* s) = 0; + virtual extension* copy(lookahead* s, bool learned) = 0; + virtual void find_mutexes(literal_vector& lits, vector & mutexes) = 0; + virtual void gc() = 0; + virtual void pop_reinit() = 0; + virtual bool validate() = 0; + virtual void init_use_list(ext_use_list& ul) = 0; + virtual bool is_blocked(literal l, ext_constraint_idx) = 0; + virtual bool check_model(model const& m) const = 0; }; }; diff --git a/src/sat/sat_iff3_finder.cpp b/src/sat/sat_iff3_finder.cpp index e889af164..32bf70414 100644 --- a/src/sat/sat_iff3_finder.cpp +++ b/src/sat/sat_iff3_finder.cpp @@ -136,9 +136,9 @@ namespace sat { TRACE("iff3_finder", tout << "visiting: " << x << "\n"; tout << "pos:\n"; - display(tout, s.m_cls_allocator, pos_wlist); + s.display_watch_list(tout, pos_wlist); tout << "\nneg:\n"; - display(tout, s.m_cls_allocator, neg_wlist); + s.display_watch_list(tout, neg_wlist); tout << "\n--------------\n";); // traverse the ternary clauses x \/ l1 \/ l2 bool_var curr_v1 = null_bool_var; diff --git a/src/sat/sat_integrity_checker.cpp b/src/sat/sat_integrity_checker.cpp index 08a6072b6..32feaa2c7 100644 --- a/src/sat/sat_integrity_checker.cpp +++ b/src/sat/sat_integrity_checker.cpp @@ -34,13 +34,11 @@ namespace sat { // for nary clauses static bool contains_watched(watch_list const & wlist, clause const & c, clause_offset cls_off) { - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (it->is_clause()) { - if (it->get_clause_offset() == cls_off) { + for (watched const& w : wlist) { + if (w.is_clause()) { + if (w.get_clause_offset() == cls_off) { // the blocked literal must be in the clause. - SASSERT(c.contains(it->get_blocked_literal())); + VERIFY(c.contains(w.get_blocked_literal())); return true; } } @@ -52,12 +50,12 @@ namespace sat { bool integrity_checker::check_clause(clause const & c) const { SASSERT(!c.was_removed()); for (unsigned i = 0; i < c.size(); i++) { - SASSERT(c[i].var() <= s.num_vars()); + VERIFY(c[i].var() <= s.num_vars()); CTRACE("sat_bug", s.was_eliminated(c[i].var()), tout << "l: " << c[i].var() << "\n"; tout << "c: " << c << "\n"; s.display(tout);); - SASSERT(!s.was_eliminated(c[i].var())); + VERIFY(!s.was_eliminated(c[i].var())); } SASSERT(c.check_approx()); @@ -68,7 +66,7 @@ namespace sat { if (c.size() == 3) { CTRACE("sat_ter_watch_bug", !contains_watched(s.get_wlist(~c[0]), c[1], c[2]), tout << c << "\n"; tout << "watch_list:\n"; - sat::display(tout, s.m_cls_allocator, s.get_wlist(~c[0])); + s.display_watch_list(tout, s.get_wlist(~c[0])); tout << "\n";); VERIFY(contains_watched(s.get_wlist(~c[0]), c[1], c[2])); VERIFY(contains_watched(s.get_wlist(~c[1]), c[0], c[2])); @@ -90,7 +88,7 @@ namespace sat { CTRACE("sat_bug", s.value(c[i]) != l_false, tout << c << " status: " << s.status(c) << "\n"; for (unsigned i = 0; i < c.size(); i++) tout << "val(" << i << "): " << s.value(c[i]) << "\n";); - SASSERT(s.value(c[i]) == l_false); + VERIFY(s.value(c[i]) == l_false); } } } @@ -102,9 +100,9 @@ namespace sat { return true; } - bool integrity_checker::check_clauses(clause * const * begin, clause * const * end) const { + bool integrity_checker::check_clauses(clause * const * begin, clause * const * end) const { for (clause * const * it = begin; it != end; ++it) { - SASSERT(check_clause(*(*it))); + VERIFY(check_clause(*(*it))); } return true; } @@ -130,97 +128,94 @@ namespace sat { } bool integrity_checker::check_bool_vars() const { - SASSERT(s.m_watches.size() == s.num_vars() * 2); - SASSERT(s.m_assignment.size() == s.num_vars() * 2); - SASSERT(s.m_lit_mark.size() == s.num_vars() * 2); - SASSERT(s.m_justification.size() == s.num_vars()); - SASSERT(s.m_decision.size() == s.num_vars()); - SASSERT(s.m_eliminated.size() == s.num_vars()); - SASSERT(s.m_external.size() == s.num_vars()); - SASSERT(s.m_level.size() == s.num_vars()); - SASSERT(s.m_mark.size() == s.num_vars()); - SASSERT(s.m_activity.size() == s.num_vars()); - SASSERT(s.m_phase.size() == s.num_vars()); - SASSERT(s.m_prev_phase.size() == s.num_vars()); - SASSERT(s.m_assigned_since_gc.size() == s.num_vars()); + VERIFY(s.m_watches.size() == s.num_vars() * 2); + VERIFY(s.m_assignment.size() == s.num_vars() * 2); + VERIFY(s.m_lit_mark.size() == s.num_vars() * 2); + VERIFY(s.m_justification.size() == s.num_vars()); + VERIFY(s.m_decision.size() == s.num_vars()); + VERIFY(s.m_eliminated.size() == s.num_vars()); + VERIFY(s.m_external.size() == s.num_vars()); + VERIFY(s.m_level.size() == s.num_vars()); + VERIFY(s.m_mark.size() == s.num_vars()); + VERIFY(s.m_activity.size() == s.num_vars()); + VERIFY(s.m_phase.size() == s.num_vars()); + VERIFY(s.m_prev_phase.size() == s.num_vars()); + VERIFY(s.m_assigned_since_gc.size() == s.num_vars()); for (bool_var v = 0; v < s.num_vars(); v++) { if (s.was_eliminated(v)) { - SASSERT(s.get_wlist(literal(v, false)).empty()); - SASSERT(s.get_wlist(literal(v, true)).empty()); + VERIFY(s.get_wlist(literal(v, false)).empty()); + VERIFY(s.get_wlist(literal(v, true)).empty()); + } + } + return true; + } + + bool integrity_checker::check_watches(literal l) const { + return check_watches(l, s.get_wlist(~l)); + } + + bool integrity_checker::check_watches(literal l, watch_list const& wlist) const { + for (watched const& w : wlist) { + switch (w.get_kind()) { + case watched::BINARY: + VERIFY(!s.was_eliminated(w.get_literal().var())); + CTRACE("sat_watched_bug", !s.get_wlist(~(w.get_literal())).contains(watched(l, w.is_learned())), + tout << "l: " << l << " l2: " << w.get_literal() << "\n"; + tout << "was_eliminated1: " << s.was_eliminated(l.var()); + tout << " was_eliminated2: " << s.was_eliminated(w.get_literal().var()); + tout << " learned: " << w.is_learned() << "\n"; + s.display_watch_list(tout, wlist); + tout << "\n"; + s.display_watch_list(tout, s.get_wlist(~(w.get_literal()))); + tout << "\n";); + VERIFY(find_binary_watch(s.get_wlist(~(w.get_literal())), l)); + break; + case watched::TERNARY: + VERIFY(!s.was_eliminated(w.get_literal1().var())); + VERIFY(!s.was_eliminated(w.get_literal2().var())); + VERIFY(w.get_literal1().index() < w.get_literal2().index()); + break; + case watched::CLAUSE: + VERIFY(!s.get_clause(w.get_clause_offset()).was_removed()); + break; + default: + break; } } return true; } bool integrity_checker::check_watches() const { - DEBUG_CODE( - vector::const_iterator it = s.m_watches.begin(); - vector::const_iterator end = s.m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; + unsigned l_idx = 0; + for (watch_list const& wlist : s.m_watches) { + literal l = ~to_literal(l_idx++); CTRACE("sat_bug", s.was_eliminated(l.var()) && !wlist.empty(), tout << "l: " << l << "\n"; s.display_watches(tout); s.display(tout);); - SASSERT(!s.was_eliminated(l.var()) || wlist.empty()); - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - switch (it2->get_kind()) { - case watched::BINARY: - SASSERT(!s.was_eliminated(it2->get_literal().var())); - CTRACE("sat_watched_bug", !s.get_wlist(~(it2->get_literal())).contains(watched(l, it2->is_learned())), - tout << "l: " << l << " l2: " << it2->get_literal() << "\n"; - tout << "was_eliminated1: " << s.was_eliminated(l.var()); - tout << " was_eliminated2: " << s.was_eliminated(it2->get_literal().var()); - tout << " learned: " << it2->is_learned() << "\n"; - sat::display(tout, s.m_cls_allocator, wlist); - tout << "\n"; - sat::display(tout, s.m_cls_allocator, s.get_wlist(~(it2->get_literal()))); - tout << "\n";); - SASSERT(s.get_wlist(~(it2->get_literal())).contains(watched(l, it2->is_learned()))); - break; - case watched::TERNARY: - SASSERT(!s.was_eliminated(it2->get_literal1().var())); - SASSERT(!s.was_eliminated(it2->get_literal2().var())); - SASSERT(it2->get_literal1().index() < it2->get_literal2().index()); - break; - case watched::CLAUSE: - SASSERT(!s.m_cls_allocator.get_clause(it2->get_clause_offset())->was_removed()); - break; - default: - break; - } - } - }); + VERIFY(!s.was_eliminated(l.var()) || wlist.empty()); + if (!check_watches(l, wlist)) + return false; + } return true; } bool integrity_checker::check_reinit_stack() const { - clause_wrapper_vector::const_iterator it = s.m_clauses_to_reinit.begin(); - clause_wrapper_vector::const_iterator end = s.m_clauses_to_reinit.end(); - for (; it != end; ++it) { - if (it->is_binary()) - continue; - SASSERT(it->get_clause()->on_reinit_stack()); + for (auto const& c : s.m_clauses_to_reinit) { + VERIFY(c.is_binary() || c.get_clause()->on_reinit_stack()); } return true; } bool integrity_checker::check_disjoint_clauses() const { uint_set ids; - clause_vector::const_iterator it = s.m_clauses.begin(); - clause_vector::const_iterator end = s.m_clauses.end(); - for (; it != end; ++it) { - ids.insert((*it)->id()); + for (clause* cp : s.m_clauses) { + ids.insert(cp->id()); } - it = s.m_learned.begin(); - end = s.m_learned.end(); - for (; it != end; ++it) { - if (ids.contains((*it)->id())) { - TRACE("sat", tout << "Repeated clause: " << (*it)->id() << "\n";); + for (clause* cp : s.m_learned) { + if (ids.contains(cp->id())) { + TRACE("sat", tout << "Repeated clause: " << cp->id() << "\n";); return false; } } @@ -230,12 +225,12 @@ namespace sat { bool integrity_checker::operator()() const { if (s.inconsistent()) return true; - SASSERT(check_clauses()); - SASSERT(check_learned_clauses()); - SASSERT(check_watches()); - SASSERT(check_bool_vars()); - SASSERT(check_reinit_stack()); - SASSERT(check_disjoint_clauses()); + VERIFY(check_clauses()); + VERIFY(check_learned_clauses()); + VERIFY(check_watches()); + VERIFY(check_bool_vars()); + VERIFY(check_reinit_stack()); + VERIFY(check_disjoint_clauses()); return true; } }; diff --git a/src/sat/sat_integrity_checker.h b/src/sat/sat_integrity_checker.h index 640fce068..10fd2203c 100644 --- a/src/sat/sat_integrity_checker.h +++ b/src/sat/sat_integrity_checker.h @@ -21,6 +21,7 @@ Revision History: #define SAT_INTEGRITY_CHECKER_H_ #include "sat/sat_types.h" +#include "sat/sat_watched.h" namespace sat { class integrity_checker { @@ -35,6 +36,8 @@ namespace sat { bool check_assignment() const; bool check_bool_vars() const; bool check_watches() const; + bool check_watches(literal l, watch_list const& wlist) const; + bool check_watches(literal l) const; bool check_reinit_stack() const; bool check_disjoint_clauses() const; bool operator()() const; diff --git a/src/sat/sat_justification.h b/src/sat/sat_justification.h index 00da9f30e..497d636c8 100644 --- a/src/sat/sat_justification.h +++ b/src/sat/sat_justification.h @@ -25,25 +25,26 @@ namespace sat { public: enum kind { NONE, BINARY, TERNARY, CLAUSE, EXT_JUSTIFICATION }; private: - unsigned m_val1; + size_t m_val1; unsigned m_val2; justification(ext_justification_idx idx, kind k):m_val1(idx), m_val2(k) {} + unsigned val1() const { return static_cast(m_val1); } public: justification():m_val1(0), m_val2(NONE) {} explicit justification(literal l):m_val1(l.to_uint()), m_val2(BINARY) {} justification(literal l1, literal l2):m_val1(l1.to_uint()), m_val2(TERNARY + (l2.to_uint() << 3)) {} explicit justification(clause_offset cls_off):m_val1(cls_off), m_val2(CLAUSE) {} - justification mk_ext_justification(ext_justification_idx idx) { return justification(idx, EXT_JUSTIFICATION); } + static justification mk_ext_justification(ext_justification_idx idx) { return justification(idx, EXT_JUSTIFICATION); } kind get_kind() const { return static_cast(m_val2 & 7); } bool is_none() const { return m_val2 == NONE; } bool is_binary_clause() const { return m_val2 == BINARY; } - literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(m_val1); } + literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(val1()); } bool is_ternary_clause() const { return get_kind() == TERNARY; } - literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(m_val1); } + literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(val1()); } literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 3); } bool is_clause() const { return m_val2 == CLAUSE; } diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp new file mode 100644 index 000000000..af2447fc2 --- /dev/null +++ b/src/sat/sat_local_search.cpp @@ -0,0 +1,1092 @@ +/*++ + Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_local_search.cpp + +Abstract: + + Local search module for cardinality clauses. + +Author: + + Sixue Liu 2017-2-21 + +Notes: + +--*/ + +#include "sat/sat_local_search.h" +#include "sat/sat_solver.h" +#include "sat/ba_solver.h" +#include "sat/sat_params.hpp" +#include "util/timer.h" + +namespace sat { + + void local_search::init() { + + for (unsigned i = 0; i < m_assumptions.size(); ++i) { + add_clause(1, m_assumptions.c_ptr() + i); + } + + // add sentinel variable. + m_vars.push_back(var_info()); + + if (m_config.phase_sticky()) { + for (var_info& vi : m_vars) + if (!vi.m_unit) + vi.m_value = vi.m_bias < 100; + } + else { + for (var_info& vi : m_vars) + if (!vi.m_unit) + vi.m_value = (0 == (m_rand() % 2)); + } + + m_best_solution.resize(num_vars() + 1, false); + m_index_in_unsat_stack.resize(num_constraints(), 0); + coefficient_in_ob_constraint.resize(num_vars() + 1, 0); + + if (m_config.mode() == local_search_mode::gsat) { + uint_set is_neighbor; + for (bool_var v = 0; v < num_vars(); ++v) { + is_neighbor.reset(); + bool pol = true; + var_info& vi = m_vars[v]; + for (unsigned k = 0; k < 2; pol = !pol, k++) { + for (auto const& wi : m_vars[v].m_watch[pol]) { + constraint const& c = m_constraints[wi.m_constraint_id]; + for (literal lit : c) { + bool_var w = lit.var(); + if (w == v || is_neighbor.contains(w)) continue; + is_neighbor.insert(w); + vi.m_neighbors.push_back(w); + } + } + } + } + } + + for (auto const& c : ob_constraint) { + coefficient_in_ob_constraint[c.var_id] = c.coefficient; + } + + set_parameters(); + } + + void local_search::init_cur_solution() { + for (var_info& vi : m_vars) { + // use bias with a small probability + if (!vi.m_unit) { + if (m_rand() % 10 < 5 || m_config.phase_sticky()) { + vi.m_value = ((unsigned)(m_rand() % 100) < vi.m_bias); + } + else { + vi.m_value = (m_rand() % 2) == 0; + } + } + } + } + + // figure out slack, and init unsat stack + void local_search::init_slack() { + for (unsigned v = 0; v < num_vars(); ++v) { + bool is_true = cur_solution(v); + coeff_vector& truep = m_vars[v].m_watch[is_true]; + for (auto const& coeff : truep) { + unsigned c = coeff.m_constraint_id; + constraint& cn = m_constraints[c]; + cn.m_slack -= coeff.m_coeff; + } + } + for (unsigned c = 0; c < num_constraints(); ++c) { + // violate the at-most-k constraint + if (m_constraints[c].m_slack < 0) + unsat(c); + } + } + + // figure out variables scores and slack_scores + void local_search::init_scores() { + for (unsigned v = 0; v < num_vars(); ++v) { + bool is_true = cur_solution(v); + coeff_vector& truep = m_vars[v].m_watch[is_true]; + coeff_vector& falsep = m_vars[v].m_watch[!is_true]; + for (auto const& coeff : falsep) { + constraint& c = m_constraints[coeff.m_constraint_id]; + //SASSERT(falsep[i].m_coeff == 1); + // will --slack + if (c.m_slack <= 0) { + dec_slack_score(v); + if (c.m_slack == 0) + dec_score(v); + } + } + for (auto const& coeff : truep) { + //SASSERT(coeff.m_coeff == 1); + constraint& c = m_constraints[coeff.m_constraint_id]; + // will --true_terms_count[c] + // will ++slack + if (c.m_slack <= -1) { + inc_slack_score(v); + if (c.m_slack == -1) + inc_score(v); + } + } + } + } + + // init goodvars + void local_search::init_goodvars() { + m_goodvar_stack.reset(); + for (unsigned v = 0; v < num_vars(); ++v) { + if (score(v) > 0) { // && conf_change[v] == true + m_vars[v].m_in_goodvar_stack = true; + m_goodvar_stack.push_back(v); + } + } + } + + void local_search::reinit() { + + IF_VERBOSE(1, verbose_stream() << "(sat-local-search reinit)\n";); + if (true || !m_is_pb) { + // + // the following methods does NOT converge for pseudo-boolean + // can try other way to define "worse" and "better" + // the current best noise is below 1000 + // + if (m_best_unsat_rate > m_last_best_unsat_rate) { + // worse + m_noise -= m_noise * 2 * m_noise_delta; + m_best_unsat_rate *= 1000.0; + } + else { + // better + m_noise += (10000 - m_noise) * m_noise_delta; + } + } + + for (constraint & c : m_constraints) { + c.m_slack = c.m_k; + } + + // init unsat stack + m_is_unsat = false; + m_unsat_stack.reset(); + + // init solution using the bias + init_cur_solution(); + + // init variable information + // the last variable is the virtual variable + + m_vars.back().m_score = INT_MIN; + m_vars.back().m_conf_change = false; + m_vars.back().m_slack_score = INT_MIN; + m_vars.back().m_cscc = 0; + m_vars.back().m_time_stamp = m_max_steps + 1; + for (unsigned i = 0; i < num_vars(); ++i) { + m_vars[i].m_time_stamp = 0; + m_vars[i].m_cscc = 1; + m_vars[i].m_conf_change = true; + m_vars[i].m_in_goodvar_stack = false; + m_vars[i].m_score = 0; + m_vars[i].m_slack_score = 0; + } + init_slack(); + init_scores(); + init_goodvars(); + set_best_unsat(); + + for (bool_var v : m_units) { + propagate(literal(v, !cur_solution(v))); + if (m_is_unsat) break; + } + if (m_is_unsat) { + IF_VERBOSE(0, verbose_stream() << "unsat during reinit\n"); + } + } + + bool local_search::propagate(literal lit) { + bool unit = is_unit(lit); + VERIFY(is_true(lit)); + m_prop_queue.reset(); + add_propagation(lit); + for (unsigned i = 0; i < m_prop_queue.size() && i < m_vars.size(); ++i) { + literal lit2 = m_prop_queue[i]; + if (!is_true(lit2)) { + if (is_unit(lit2)) { + return false; + } + flip_walksat(lit2.var()); + add_propagation(lit2); + } + } + if (m_prop_queue.size() >= m_vars.size()) { + IF_VERBOSE(0, verbose_stream() << "propagation loop\n"); + return false; + } + if (unit) { + for (literal lit : m_prop_queue) { + VERIFY(is_true(lit)); + add_unit(lit); + } + } + return true; + } + + void local_search::add_propagation(literal l) { + VERIFY(is_true(l)); + for (literal lit : m_vars[l.var()].m_bin[l.sign()]) { + if (!is_true(lit)) { + m_prop_queue.push_back(lit); + } + } + } + + void local_search::set_best_unsat() { + m_best_unsat = m_unsat_stack.size(); + if (m_best_unsat == 1) { + constraint const& c = m_constraints[m_unsat_stack[0]]; + IF_VERBOSE(2, display(verbose_stream() << "single unsat:", c)); + } + } + + void local_search::calculate_and_update_ob() { + unsigned i, v; + int objective_value = 0; + for (i = 0; i < ob_constraint.size(); ++i) { + v = ob_constraint[i].var_id; + if (cur_solution(v)) + objective_value += ob_constraint[i].coefficient; + } + if (objective_value > m_best_objective_value) { + m_best_solution.reset(); + for (unsigned v = 0; v < num_vars(); ++v) { + m_best_solution.push_back(cur_solution(v)); + } + m_best_objective_value = objective_value; + } + } + + bool local_search::all_objectives_are_met() const { + for (unsigned i = 0; i < ob_constraint.size(); ++i) { + bool_var v = ob_constraint[i].var_id; + if (!cur_solution(v)) return false; + } + return true; + } + + void local_search::verify_solution() const { + IF_VERBOSE(0, verbose_stream() << "verifying solution\n"); + for (constraint const& c : m_constraints) + verify_constraint(c); + } + + void local_search::verify_unsat_stack() const { + for (unsigned i : m_unsat_stack) { + constraint const& c = m_constraints[i]; + VERIFY(c.m_k < constraint_value(c)); + } + } + + unsigned local_search::constraint_coeff(constraint const& c, literal l) const { + for (auto const& pb : m_vars[l.var()].m_watch[is_pos(l)]) { + if (pb.m_constraint_id == c.m_id) return pb.m_coeff; + } + UNREACHABLE(); + return 0; + } + + + unsigned local_search::constraint_value(constraint const& c) const { + unsigned value = 0; + for (literal t : c) { + if (is_true(t)) { + value += constraint_coeff(c, t); + } + } + return value; + } + + void local_search::verify_constraint(constraint const& c) const { + unsigned value = constraint_value(c); + IF_VERBOSE(11, display(verbose_stream() << "verify ", c);); + TRACE("sat", display(verbose_stream() << "verify ", c);); + if (c.m_k < value) { + IF_VERBOSE(0, display(verbose_stream() << "violated constraint: ", c) << "value: " << value << "\n";); + } + } + + void local_search::add_clause(unsigned sz, literal const* c) { + add_cardinality(sz, c, sz - 1); + } + + // ~c <= k + void local_search::add_cardinality(unsigned sz, literal const* c, unsigned k) { + if (sz == 1 && k == 0) { + add_unit(c[0]); + return; + } + if (k == 1 && sz == 2) { + // IF_VERBOSE(0, verbose_stream() << "bin: " << ~c[0] << " + " << ~c[1] << " <= 1\n"); + for (unsigned i = 0; i < 2; ++i) { + literal t(c[i]), s(c[1-i]); + m_vars.reserve(t.var() + 1); + m_vars[t.var()].m_bin[is_pos(t)].push_back(s); + } + } + unsigned id = m_constraints.size(); + m_constraints.push_back(constraint(k, id)); + for (unsigned i = 0; i < sz; ++i) { + m_vars.reserve(c[i].var() + 1); + literal t(~c[i]); + m_vars[t.var()].m_watch[is_pos(t)].push_back(pbcoeff(id, 1)); + m_constraints.back().push(t); + } + + } + + // c * coeffs <= k + void local_search::add_pb(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k) { + if (sz == 1 && k == 0) { + add_unit(~c[0]); + return; + } + unsigned id = m_constraints.size(); + m_constraints.push_back(constraint(k, id)); + for (unsigned i = 0; i < sz; ++i) { + m_vars.reserve(c[i].var() + 1); + literal t(c[i]); + m_vars[t.var()].m_watch[is_pos(t)].push_back(pbcoeff(id, coeffs[i])); + m_constraints.back().push(t); + } + } + + void local_search::add_unit(literal lit) { + bool_var v = lit.var(); + if (is_unit(lit)) return; + VERIFY(!m_units.contains(v)); + m_vars[v].m_bias = lit.sign() ? 0 : 100; + m_vars[v].m_value = !lit.sign(); + m_vars[v].m_unit = true; + m_units.push_back(v); + verify_unsat_stack(); + } + + local_search::local_search() : + m_is_unsat(false), + m_par(nullptr) { + } + + void local_search::import(solver& s, bool _init) { + m_is_pb = false; + m_vars.reset(); + m_constraints.reset(); + m_units.reset(); + m_unsat_stack.reset(); + + m_vars.reserve(s.num_vars()); + if (m_config.phase_sticky()) { + unsigned v = 0; + for (var_info& vi : m_vars) { + if (!vi.m_unit) + vi.m_bias = s.m_phase[v] == POS_PHASE ? 100 : 0; + ++v; + } + } + + // copy units + unsigned trail_sz = s.init_trail_size(); + for (unsigned i = 0; i < trail_sz; ++i) { + add_clause(1, s.m_trail.c_ptr() + i); + } + + // copy binary clauses + { + unsigned sz = s.m_watches.size(); + for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { + literal l1 = ~to_literal(l_idx); + watch_list const & wlist = s.m_watches[l_idx]; + for (watched const& w : wlist) { + if (!w.is_binary_non_learned_clause()) + continue; + literal l2 = w.get_literal(); + if (l1.index() > l2.index()) + continue; + literal ls[2] = { l1, l2 }; + add_clause(2, ls); + } + } + } + + // copy clauses + for (clause* c : s.m_clauses) { + add_clause(c->size(), c->begin()); + } + m_num_non_binary_clauses = s.m_clauses.size(); + + // copy cardinality clauses + ba_solver* ext = dynamic_cast(s.get_extension()); + if (ext) { + unsigned_vector coeffs; + literal_vector lits; + for (ba_solver::constraint* cp : ext->m_constraints) { + switch (cp->tag()) { + case ba_solver::card_t: { + ba_solver::card const& c = cp->to_card(); + unsigned n = c.size(); + unsigned k = c.k(); + + if (c.lit() == null_literal) { + // c.lits() >= k + // <=> + // ~c.lits() <= n - k + lits.reset(); + for (unsigned j = 0; j < n; ++j) lits.push_back(c[j]); + add_cardinality(lits.size(), lits.c_ptr(), n - k); + } + else { + // + // c.lit() <=> c.lits() >= k + // + // (c.lits() < k) or c.lit() + // = (c.lits() + (n - k + 1)*~c.lit()) <= n + // + // ~c.lit() or (c.lits() >= k) + // = ~c.lit() or (~c.lits() <= n - k) + // = k*c.lit() + ~c.lits() <= n + // + m_is_pb = true; + lits.reset(); + coeffs.reset(); + for (literal l : c) lits.push_back(l), coeffs.push_back(1); + lits.push_back(~c.lit()); coeffs.push_back(n - k + 1); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), n); + + lits.reset(); + coeffs.reset(); + for (literal l : c) lits.push_back(~l), coeffs.push_back(1); + lits.push_back(c.lit()); coeffs.push_back(k); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), n); + } + break; + } + case ba_solver::pb_t: { + ba_solver::pb const& p = cp->to_pb(); + lits.reset(); + coeffs.reset(); + m_is_pb = true; + unsigned sum = 0; + for (ba_solver::wliteral wl : p) sum += wl.first; + + if (p.lit() == null_literal) { + // w1 + .. + w_n >= k + // <=> + // ~wl + ... + ~w_n <= sum_of_weights - k + for (ba_solver::wliteral wl : p) lits.push_back(~(wl.second)), coeffs.push_back(wl.first); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), sum - p.k()); + } + else { + // lit <=> w1 + .. + w_n >= k + // <=> + // lit or w1 + .. + w_n <= k - 1 + // ~lit or w1 + .. + w_n >= k + // <=> + // (sum - k + 1)*~lit + w1 + .. + w_n <= sum + // k*lit + ~wl + ... + ~w_n <= sum + lits.push_back(p.lit()), coeffs.push_back(p.k()); + for (ba_solver::wliteral wl : p) lits.push_back(~(wl.second)), coeffs.push_back(wl.first); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), sum); + + lits.reset(); + coeffs.reset(); + lits.push_back(~p.lit()), coeffs.push_back(sum + 1 - p.k()); + for (ba_solver::wliteral wl : p) lits.push_back(wl.second), coeffs.push_back(wl.first); + add_pb(lits.size(), lits.c_ptr(), coeffs.c_ptr(), sum); + } + break; + } + case ba_solver::xr_t: + NOT_IMPLEMENTED_YET(); + break; + } + } + } + if (_init) { + init(); + } + } + + local_search::~local_search() { + } + + void local_search::add_soft(bool_var v, int weight) { + ob_constraint.push_back(ob_term(v, weight)); + } + + lbool local_search::check() { + return check(0, 0); + } + +#define PROGRESS(tries, flips) \ + if (tries % 10 == 0 || m_unsat_stack.empty()) { \ + IF_VERBOSE(1, verbose_stream() << "(sat-local-search" \ + << " :flips " << flips \ + << " :noise " << m_noise \ + << " :unsat " << /*m_unsat_stack.size()*/ m_best_unsat \ + << " :constraints " << m_constraints.size() \ + << " :time " << (timer.get_seconds() < 0.001 ? 0.0 : timer.get_seconds()) << ")\n";); \ + } + + void local_search::walksat() { + m_best_unsat_rate = 1; + m_last_best_unsat_rate = 1; + + reinit(); + timer timer; + timer.start(); + unsigned step = 0, total_flips = 0, tries = 0; + PROGRESS(tries, total_flips); + + for (tries = 1; !m_unsat_stack.empty() && m_limit.inc(); ++tries) { + for (step = 0; step < m_max_steps && !m_unsat_stack.empty(); ++step) { + pick_flip_walksat(); + if (m_unsat_stack.size() < m_best_unsat) { + set_best_unsat(); + m_last_best_unsat_rate = m_best_unsat_rate; + m_best_unsat_rate = (double)m_unsat_stack.size() / num_constraints(); + } + if (m_is_unsat) return; + } + total_flips += step; + PROGRESS(tries, total_flips); + if (m_par && m_par->get_phase(*this)) { + reinit(); + } + if (tries % 10 == 0 && !m_unsat_stack.empty()) { + reinit(); + } + } + } + + void local_search::gsat() { + reinit(); + bool_var flipvar; + timer timer; + timer.start(); + unsigned tries, step = 0, total_flips = 0; + for (tries = 1; m_limit.inc() && !m_unsat_stack.empty(); ++tries) { + reinit(); + for (step = 1; step <= m_max_steps; ) { + // feasible + if (m_unsat_stack.empty()) { + calculate_and_update_ob(); + if (m_best_objective_value >= m_best_known_value) { + break; + } + } + if (m_unsat_stack.size() < m_best_unsat) { + set_best_unsat(); + } + flipvar = pick_var_gsat(); + flip_gsat(flipvar); + m_vars[flipvar].m_time_stamp = step++; + } + total_flips += step; + PROGRESS(tries, total_flips); + + // tell the SAT solvers about the phase of variables. + if (m_par && tries % 10 == 0) { + m_par->get_phase(*this); + } + } + } + + lbool local_search::check(unsigned sz, literal const* assumptions, parallel* p) { + flet _p(m_par, p); + m_model.reset(); + m_assumptions.reset(); + m_assumptions.append(sz, assumptions); + init(); + + switch (m_config.mode()) { + case local_search_mode::gsat: + gsat(); + break; + case local_search_mode::wsat: + walksat(); + break; + } + + + // remove unit clauses from assumptions. + m_constraints.shrink(num_constraints() - sz); + + TRACE("sat", display(tout);); + + lbool result; + if (m_is_unsat) { + // result = l_false; + result = l_undef; + } + else if (m_unsat_stack.empty() && all_objectives_are_met()) { + verify_solution(); + extract_model(); + result = l_true; + } + else { + result = l_undef; + } + IF_VERBOSE(1, verbose_stream() << "(sat-local-search " << result << ")\n";); + IF_VERBOSE(20, display(verbose_stream());); + return result; + } + + + void local_search::sat(unsigned c) { + unsigned last_unsat_constraint = m_unsat_stack.back(); + int index = m_index_in_unsat_stack[c]; + m_unsat_stack[index] = last_unsat_constraint; + m_index_in_unsat_stack[last_unsat_constraint] = index; + m_unsat_stack.pop_back(); + } + + // swap the deleted one with the last one and pop + void local_search::unsat(unsigned c) { + m_index_in_unsat_stack[c] = m_unsat_stack.size(); + m_unsat_stack.push_back(c); + } + + void local_search::pick_flip_walksat() { + reflip: + bool_var best_var = null_bool_var; + unsigned n = 1; + bool_var v = null_bool_var; + unsigned num_unsat = m_unsat_stack.size(); + constraint const& c = m_constraints[m_unsat_stack[m_rand() % m_unsat_stack.size()]]; + // VERIFY(c.m_k < constraint_value(c)); + unsigned reflipped = 0; + bool is_core = m_unsat_stack.size() <= 10; + // TBD: dynamic noise strategy + //if (m_rand() % 100 < 98) { + if (m_rand() % 10000 <= m_noise) { + // take this branch with 98% probability. + // find the first one, to fast break the rest + unsigned best_bsb = 0; + literal_vector::const_iterator cit = c.m_literals.begin(), cend = c.m_literals.end(); + literal l; + for (; (cit != cend) && (!is_true(*cit) || is_unit(*cit)); ++cit) { } + if (cit == cend) { + if (c.m_k < constraint_value(c)) { + IF_VERBOSE(0, display(verbose_stream() << "unsat clause\n", c)); + m_is_unsat = true; + return; + } + goto reflip; + } + l = *cit; + best_var = v = l.var(); + bool tt = cur_solution(v); + coeff_vector const& falsep = m_vars[v].m_watch[!tt]; + for (pbcoeff const& pbc : falsep) { + int slack = constraint_slack(pbc.m_constraint_id); + if (slack < 0) + ++best_bsb; + else if (slack < static_cast(pbc.m_coeff)) + best_bsb += num_unsat; + } + ++cit; + for (; cit != cend; ++cit) { + l = *cit; + if (is_true(l) && !is_unit(l)) { + v = l.var(); + unsigned bsb = 0; + coeff_vector const& falsep = m_vars[v].m_watch[!cur_solution(v)]; + auto it = falsep.begin(), end = falsep.end(); + for (; it != end; ++it) { + int slack = constraint_slack(it->m_constraint_id); + if (slack < 0) { + if (bsb == best_bsb) { + break; + } + else { + ++bsb; + } + } + else if (slack < static_cast(it->m_coeff)) { + bsb += num_unsat; + if (bsb > best_bsb) { + break; + } + } + } + if (it == end) { + if (bsb < best_bsb) { + best_bsb = bsb; + best_var = v; + n = 1; + } + else {// if (bsb == best_bb) + ++n; + if (m_rand() % n == 0) { + best_var = v; + } + } + } + } + } + } + else { + for (literal l : c) { + if (is_true(l) && !is_unit(l)) { + if (m_rand() % n == 0) { + best_var = l.var(); + } + ++n; + } + } + } + if (best_var == null_bool_var) { + IF_VERBOSE(1, verbose_stream() << "(sat.local_search :unsat)\n"); + return; + } + if (is_unit(best_var)) { + goto reflip; + } + flip_walksat(best_var); + literal lit(best_var, !cur_solution(best_var)); + if (!propagate(lit)) { + if (is_true(lit)) { + flip_walksat(best_var); + } + add_unit(~lit); + if (!propagate(~lit)) { + IF_VERBOSE(0, verbose_stream() << "unsat\n"); + m_is_unsat = true; + return; + } + goto reflip; + } + + if (false && is_core && c.m_k < constraint_value(c)) { + ++reflipped; + goto reflip; + } + } + + void local_search::flip_walksat(bool_var flipvar) { + VERIFY(!is_unit(flipvar)); + m_vars[flipvar].m_value = !cur_solution(flipvar); + + bool flip_is_true = cur_solution(flipvar); + coeff_vector const& truep = m_vars[flipvar].m_watch[flip_is_true]; + coeff_vector const& falsep = m_vars[flipvar].m_watch[!flip_is_true]; + + for (auto const& pbc : truep) { + unsigned ci = pbc.m_constraint_id; + constraint& c = m_constraints[ci]; + int old_slack = c.m_slack; + c.m_slack -= pbc.m_coeff; + if (c.m_slack < 0 && old_slack >= 0) { // from non-negative to negative: sat -> unsat + unsat(ci); + } + } + for (auto const& pbc : falsep) { + unsigned ci = pbc.m_constraint_id; + constraint& c = m_constraints[ci]; + int old_slack = c.m_slack; + c.m_slack += pbc.m_coeff; + if (c.m_slack >= 0 && old_slack < 0) { // from negative to non-negative: unsat -> sat + sat(ci); + } + } + + // verify_unsat_stack(); + } + + void local_search::flip_gsat(bool_var flipvar) { + // already changed truth value!!!! + m_vars[flipvar].m_value = !cur_solution(flipvar); + + unsigned v; + int org_flipvar_score = score(flipvar); + int org_flipvar_slack_score = slack_score(flipvar); + + bool flip_is_true = cur_solution(flipvar); + coeff_vector& truep = m_vars[flipvar].m_watch[flip_is_true]; + coeff_vector& falsep = m_vars[flipvar].m_watch[!flip_is_true]; + + // update related clauses and neighbor vars + for (unsigned i = 0; i < truep.size(); ++i) { + constraint & c = m_constraints[truep[i].m_constraint_id]; + //++true_terms_count[c]; + --c.m_slack; + switch (c.m_slack) { + case -2: // from -1 to -2 + for (literal l : c) { + v = l.var(); + // flipping the slack increasing var will no longer satisfy this constraint + if (is_true(l)) { + //score[v] -= constraint_weight[c]; + dec_score(v); + } + } + break; + case -1: // from 0 to -1: sat -> unsat + for (literal l : c) { + v = l.var(); + inc_cscc(v); + //score[v] += constraint_weight[c]; + inc_score(v); + // slack increasing var + if (is_true(l)) + inc_slack_score(v); + } + unsat(truep[i].m_constraint_id); + break; + case 0: // from 1 to 0 + for (literal l : c) { + v = l.var(); + // flip the slack decreasing var will falsify this constraint + if (is_false(l)) { + // score[v] -= constraint_weight[c]; + dec_score(v); + dec_slack_score(v); + } + } + break; + default: + break; + } + } + for (pbcoeff const& f : falsep) { + constraint& c = m_constraints[f.m_constraint_id]; + //--true_terms_count[c]; + ++c.m_slack; + switch (c.m_slack) { + case 1: // from 0 to 1 + for (literal l : c) { + v = l.var(); + // flip the slack decreasing var will no long falsify this constraint + if (is_false(l)) { + //score[v] += constraint_weight[c]; + inc_score(v); + inc_slack_score(v); + } + } + break; + case 0: // from -1 to 0: unsat -> sat + for (literal l : c) { + v = l.var(); + inc_cscc(v); + //score[v] -= constraint_weight[c]; + dec_score(v); + // slack increasing var no longer sat this var + if (is_true(l)) + dec_slack_score(v); + } + sat(f.m_constraint_id); + break; + case -1: // from -2 to -1 + for (literal l : c) { + v = l.var(); + // flip the slack increasing var will satisfy this constraint + if (is_true(l)) { + //score[v] += constraint_weight[c]; + inc_score(v); + } + } + break; + default: + break; + } + } + m_vars[flipvar].m_score = -org_flipvar_score; + m_vars[flipvar].m_slack_score = -org_flipvar_slack_score; + m_vars[flipvar].m_conf_change = false; + m_vars[flipvar].m_cscc = 0; + + /* update CCD */ + // remove the vars no longer goodvar in goodvar stack + for (unsigned i = m_goodvar_stack.size(); i > 0;) { + --i; + v = m_goodvar_stack[i]; + if (score(v) <= 0) { + m_goodvar_stack[i] = m_goodvar_stack.back(); + m_goodvar_stack.pop_back(); + m_vars[v].m_in_goodvar_stack = false; + } + } + // update all flipvar's neighbor's conf_change to true, add goodvar/okvar + + var_info& vi = m_vars[flipvar]; + for (auto v : vi.m_neighbors) { + m_vars[v].m_conf_change = true; + if (score(v) > 0 && !already_in_goodvar_stack(v)) { + m_goodvar_stack.push_back(v); + m_vars[v].m_in_goodvar_stack = true; + } + } + } + + bool local_search::tie_breaker_sat(bool_var v, bool_var best_var) { + // most improvement on objective value + int v_imp = cur_solution(v) ? -coefficient_in_ob_constraint.get(v, 0) : coefficient_in_ob_constraint.get(v, 0); + int b_imp = cur_solution(best_var) ? -coefficient_in_ob_constraint.get(best_var, 0) : coefficient_in_ob_constraint.get(best_var, 0); + // std::cout << v_imp << "\n"; + // break tie 1: max imp + // break tie 2: conf_change + // break tie 3: time_stamp + + return + (v_imp > b_imp) || + ((v_imp == b_imp) && + ((conf_change(v) && !conf_change(best_var)) || + ((conf_change(v) == conf_change(best_var)) && + (time_stamp(v) < time_stamp(best_var))))); + } + + bool local_search::tie_breaker_ccd(bool_var v, bool_var best_var) { + // break tie 1: max score + // break tie 2: max slack_score + // break tie 3: cscc + // break tie 4: oldest one + return + ((score(v) > score(best_var)) || + ((score(v) == score(best_var)) && + ((slack_score(v) > slack_score(best_var)) || + ((slack_score(v) == slack_score(best_var)) && + ((cscc(v) > cscc(best_var)) || + ((cscc(v) == cscc(best_var)) && + (time_stamp(v) < time_stamp(best_var)))))))); + } + + bool_var local_search::pick_var_gsat() { + bool_var best_var = m_vars.size()-1; // sentinel variable + // SAT Mode + if (m_unsat_stack.empty()) { + //std::cout << "as\t"; + for (auto const& c : ob_constraint) { + bool_var v = c.var_id; + if (tie_breaker_sat(v, best_var)) + best_var = v; + } + return best_var; + } + + // Unsat Mode: CCD > RD + // CCD mode + if (!m_goodvar_stack.empty()) { + //++ccd; + best_var = m_goodvar_stack[0]; + for (bool_var v : m_goodvar_stack) { + if (tie_breaker_ccd(v, best_var)) + best_var = v; + } + return best_var; + } + + // Diversification Mode + constraint const& c = m_constraints[m_unsat_stack[m_rand() % m_unsat_stack.size()]]; // a random unsat constraint + // Within c, from all slack increasing var, choose the oldest one + for (literal l : c) { + bool_var v = l.var(); + if (is_true(l) && time_stamp(v) < time_stamp(best_var)) + best_var = v; + } + return best_var; + } + + void local_search::set_parameters() { + m_rand.set_seed(m_config.random_seed()); + m_best_known_value = m_config.best_known_value(); + + switch (m_config.mode()) { + case local_search_mode::gsat: + m_max_steps = 2 * num_vars(); + break; + case local_search_mode::wsat: + m_max_steps = std::min(static_cast(20 * num_vars()), static_cast(1 << 17)); // cut steps off at 100K + break; + } + + TRACE("sat", + tout << "seed:\t" << m_config.random_seed() << '\n'; + tout << "best_known_value:\t" << m_config.best_known_value() << '\n'; + tout << "max_steps:\t" << m_max_steps << '\n'; + ); + } + + void local_search::print_info(std::ostream& out) { + for (unsigned v = 0; v < num_vars(); ++v) { + out << "v" << v << "\t" + << m_vars[v].m_neighbors.size() << '\t' + << cur_solution(v) << '\t' + << conf_change(v) << '\t' + << score(v) << '\t' + << slack_score(v) << '\n'; + } + } + + void local_search::extract_model() { + m_model.reset(); + for (unsigned v = 0; v < num_vars(); ++v) { + m_model.push_back(cur_solution(v) ? l_true : l_false); + } + } + + std::ostream& local_search::display(std::ostream& out) const { + for (constraint const& c : m_constraints) { + display(out, c); + } + for (bool_var v = 0; v < num_vars(); ++v) { + display(out, v, m_vars[v]); + } + return out; + } + + std::ostream& local_search::display(std::ostream& out, constraint const& c) const { + for (literal l : c) { + unsigned coeff = constraint_coeff(c, l); + if (coeff > 1) out << coeff << " * "; + out << l << " "; + } + return out << " <= " << c.m_k << " lhs value: " << constraint_value(c) << "\n"; + } + + std::ostream& local_search::display(std::ostream& out, unsigned v, var_info const& vi) const { + return out << "v" << v << " := " << (vi.m_value?"true":"false") << " bias: " << vi.m_bias << "\n"; + } + + bool local_search::check_goodvar() { + unsigned g = 0; + for (unsigned v = 0; v < num_vars(); ++v) { + if (conf_change(v) && score(v) > 0) { + ++g; + if (!already_in_goodvar_stack(v)) + std::cout << "3\n"; + } + } + if (g == m_goodvar_stack.size()) + return true; + else { + if (g < m_goodvar_stack.size()) + std::cout << "1\n"; + else + std::cout << "2\n"; // delete too many + return false; + } + } + + void local_search::set_phase(bool_var v, lbool f) { + unsigned& bias = m_vars[v].m_bias; + if (f == l_true && bias < 100) bias++; + if (f == l_false && bias > 0) bias--; + // f == l_undef ? + } + +} diff --git a/src/sat/sat_local_search.h b/src/sat/sat_local_search.h new file mode 100644 index 000000000..8a63898c3 --- /dev/null +++ b/src/sat/sat_local_search.h @@ -0,0 +1,299 @@ +/*++ + Copyright (c) 2017 Microsoft Corporation + + Module Name: + + sat_local_search.h + + Abstract: + + Local search module for cardinality clauses. + + Author: + + Sixue Liu 2017-2-21 + + Notes: + + --*/ +#ifndef _SAT_LOCAL_SEARCH_H_ +#define _SAT_LOCAL_SEARCH_H_ + +#include "util/vector.h" +#include "sat/sat_types.h" +#include "sat/sat_config.h" +#include "util/rlimit.h" + +namespace sat { + + class parallel; + + class local_search_config { + unsigned m_random_seed; + int m_best_known_value; + local_search_mode m_mode; + bool m_phase_sticky; + public: + local_search_config() { + m_random_seed = 0; + m_best_known_value = INT_MAX; + m_mode = local_search_mode::wsat; + m_phase_sticky = false; + } + + unsigned random_seed() const { return m_random_seed; } + unsigned best_known_value() const { return m_best_known_value; } + local_search_mode mode() const { return m_mode; } + bool phase_sticky() const { return m_phase_sticky; } + + void set_random_seed(unsigned s) { m_random_seed = s; } + void set_best_known_value(unsigned v) { m_best_known_value = v; } + + void set_config(config const& cfg) { + m_mode = cfg.m_local_search_mode; + m_random_seed = cfg.m_random_seed; + m_phase_sticky = cfg.m_phase_sticky; + } + }; + + + class local_search { + + struct pbcoeff { + unsigned m_constraint_id; + unsigned m_coeff; + pbcoeff(unsigned id, unsigned coeff): + m_constraint_id(id), m_coeff(coeff) {} + }; + typedef svector bool_vector; + typedef svector coeff_vector; + + // data structure for a term in objective function + struct ob_term { + bool_var var_id; // variable id, begin with 1 + int coefficient; // non-zero integer + ob_term(bool_var v, int c): var_id(v), coefficient(c) {} + }; + + struct var_info { + bool m_value; // current solution + unsigned m_bias; // bias for current solution in percentage. + // if bias is 0, then value is always false, if 100, then always true + bool m_unit; // is this a unit literal + bool m_conf_change; // whether its configure changes since its last flip + bool m_in_goodvar_stack; + int m_score; + int m_slack_score; + int m_time_stamp; // the flip time stamp + int m_cscc; // how many times its constraint state configure changes since its last flip + bool_var_vector m_neighbors; // neighborhood variables + coeff_vector m_watch[2]; + literal_vector m_bin[2]; + var_info(): + m_value(true), + m_bias(50), + m_unit(false), + m_conf_change(true), + m_in_goodvar_stack(false), + m_score(0), + m_slack_score(0), + m_cscc(0) + {} + }; + + struct constraint { + unsigned m_id; + unsigned m_k; + int m_slack; + unsigned m_size; + literal_vector m_literals; + constraint(unsigned k, unsigned id) : m_id(id), m_k(k), m_slack(0), m_size(0) {} + void push(literal l) { m_literals.push_back(l); ++m_size; } + unsigned size() const { return m_size; } + literal const& operator[](unsigned idx) const { return m_literals[idx]; } + literal const* begin() const { return m_literals.begin(); } + literal const* end() const { return m_literals.end(); } + }; + + local_search_config m_config; + + // objective function: maximize + svector ob_constraint; // the objective function *constraint*, sorted in decending order + + // information about the variable + int_vector coefficient_in_ob_constraint; // var! initialized to be 0 + + + vector m_vars; + svector m_units; + + inline int score(bool_var v) const { return m_vars[v].m_score; } + inline void inc_score(bool_var v) { m_vars[v].m_score++; } + inline void dec_score(bool_var v) { m_vars[v].m_score--; } + + inline int slack_score(bool_var v) const { return m_vars[v].m_slack_score; } + inline void inc_slack_score(bool_var v) { m_vars[v].m_slack_score++; } + inline void dec_slack_score(bool_var v) { m_vars[v].m_slack_score--; } + + inline bool already_in_goodvar_stack(bool_var v) const { return m_vars[v].m_in_goodvar_stack; } + inline bool conf_change(bool_var v) const { return m_vars[v].m_conf_change; } + inline int time_stamp(bool_var v) const { return m_vars[v].m_time_stamp; } + inline int cscc(bool_var v) const { return m_vars[v].m_cscc; } + inline void inc_cscc(bool_var v) { m_vars[v].m_cscc++; } + + inline bool cur_solution(bool_var v) const { return m_vars[v].m_value; } + + inline void set_best_unsat(); + /* TBD: other scores */ + + + vector m_constraints; + + literal_vector m_assumptions; + literal_vector m_prop_queue; + + unsigned m_num_non_binary_clauses; + bool m_is_pb; + + inline bool is_pos(literal t) const { return !t.sign(); } + inline bool is_true(bool_var v) const { return cur_solution(v); } + inline bool is_true(literal l) const { return cur_solution(l.var()) != l.sign(); } + inline bool is_false(literal l) const { return cur_solution(l.var()) == l.sign(); } + inline bool is_unit(bool_var v) const { return m_vars[v].m_unit; } + inline bool is_unit(literal l) const { return m_vars[l.var()].m_unit; } + + unsigned num_constraints() const { return m_constraints.size(); } // constraint index from 1 to num_constraint + + + unsigned constraint_slack(unsigned ci) const { return m_constraints[ci].m_slack; } + + // unsat constraint stack + bool m_is_unsat; + unsigned_vector m_unsat_stack; // store all the unsat constraits + unsigned_vector m_index_in_unsat_stack; // which position is a contraint in the unsat_stack + + // configuration changed decreasing variables (score>0 and conf_change==true) + bool_var_vector m_goodvar_stack; + + + // information about solution + unsigned m_best_unsat; + double m_best_unsat_rate; + double m_last_best_unsat_rate; + int m_objective_value; // the objective function value corresponds to the current solution + bool_vector m_best_solution; // !var: the best solution so far + int m_best_objective_value = -1; // the objective value corresponds to the best solution so far + // for non-known instance, set as maximal + int m_best_known_value = INT_MAX; // best known value for this instance + + unsigned m_max_steps = (1 << 30); + + // dynamic noise + double m_noise = 9800; // normalized by 10000 + double m_noise_delta = 0.05; + + reslimit m_limit; + random_gen m_rand; + parallel* m_par; + model m_model; + + void init(); + void reinit(); + void reinit_orig(); + void init_cur_solution(); + void init_slack(); + void init_scores(); + void init_goodvars(); + + bool_var pick_var_gsat(); + + void flip_gsat(bool_var v); + + void pick_flip_walksat(); + + void flip_walksat(bool_var v); + + bool propagate(literal lit); + + void add_propagation(literal lit); + + void walksat(); + + void gsat(); + + void unsat(unsigned c); + + void sat(unsigned c); + + bool tie_breaker_sat(bool_var v1, bool_var v2); + + bool tie_breaker_ccd(bool_var v1, bool_var v2); + + void set_parameters(); + + void calculate_and_update_ob(); + + bool all_objectives_are_met() const; + + void verify_solution() const; + + void verify_unsat_stack() const; + + void verify_constraint(constraint const& c) const; + + unsigned constraint_value(constraint const& c) const; + + unsigned constraint_coeff(constraint const& c, literal l) const; + + void print_info(std::ostream& out); + + void extract_model(); + + bool check_goodvar(); + + void add_clause(unsigned sz, literal const* c); + + void add_unit(literal lit); + + std::ostream& display(std::ostream& out) const; + + std::ostream& display(std::ostream& out, constraint const& c) const; + + std::ostream& display(std::ostream& out, unsigned v, var_info const& vi) const; + + public: + + local_search(); + + reslimit& rlimit() { return m_limit; } + + ~local_search(); + + void add_soft(bool_var v, int weight); + + void add_cardinality(unsigned sz, literal const* c, unsigned k); + + void add_pb(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k); + + lbool check(); + + lbool check(unsigned sz, literal const* assumptions, parallel* p = 0); + + local_search_config& config() { return m_config; } + + unsigned num_vars() const { return m_vars.size() - 1; } // var index from 1 to num_vars + + unsigned num_non_binary_clauses() const { return m_num_non_binary_clauses; } + + void import(solver& s, bool init); + + void set_phase(bool_var v, lbool f); + + bool get_phase(bool_var v) const { return is_true(v); } + + model& get_model() { return m_model; } + + }; +} + +#endif diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp new file mode 100644 index 000000000..3833e2a52 --- /dev/null +++ b/src/sat/sat_lookahead.cpp @@ -0,0 +1,2494 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_lookahead.h + +Abstract: + + Lookahead SAT solver in the style of March. + Thanks also to the presentation in sat11.w. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-2-11 + +Notes: + +--*/ + +#include +#include "sat/sat_solver.h" +#include "sat/sat_extension.h" +#include "sat/sat_lookahead.h" +#include "sat/sat_scc.h" +#include "util/union_find.h" + +namespace sat { + lookahead::scoped_ext::scoped_ext(lookahead& p): p(p) { + if (p.m_s.m_ext) p.m_s.m_ext->set_lookahead(&p); + } + + lookahead::scoped_ext::~scoped_ext() { + if (p.m_s.m_ext) p.m_s.m_ext->set_lookahead(0); + } + + lookahead::scoped_assumptions::scoped_assumptions(lookahead& p, literal_vector const& lits): p(p), lits(lits) { + for (auto l : lits) { + p.push(l, p.c_fixed_truth); + } + } + lookahead::scoped_assumptions::~scoped_assumptions() { + for (auto l : lits) { + (void)l; + p.pop(); + } + } + + void lookahead::flip_prefix() { + if (m_trail_lim.size() < 64) { + uint64_t mask = (1ull << m_trail_lim.size()); + m_prefix = mask | (m_prefix & (mask - 1)); + } + } + + void lookahead::prune_prefix() { + if (m_trail_lim.size() < 64) { + m_prefix &= (1ull << m_trail_lim.size()) - 1; + } + } + + void lookahead::update_prefix(literal l) { + bool_var x = l.var(); + unsigned p = m_vprefix[x].m_prefix; + unsigned pl = m_vprefix[x].m_length; + unsigned mask = (1 << std::min(31u, pl)) - 1; + if (pl >= m_trail_lim.size() || (p & mask) != (m_prefix & mask)) { + m_vprefix[x].m_length = m_trail_lim.size(); + m_vprefix[x].m_prefix = static_cast(m_prefix); + } + } + + bool lookahead::active_prefix(bool_var x) { + unsigned lvl = m_trail_lim.size(); + unsigned p = m_vprefix[x].m_prefix; + unsigned l = m_vprefix[x].m_length; + if (l > lvl) return false; + if (l == lvl || l >= 31) return m_prefix == p; + unsigned mask = ((1 << std::min(l,31u)) - 1); + return (m_prefix & mask) == (p & mask); + } + + void lookahead::add_binary(literal l1, literal l2) { + TRACE("sat", tout << "binary: " << l1 << " " << l2 << "\n";); + SASSERT(l1 != l2); + // don't add tautologies and don't add already added binaries + if (~l1 == l2) return; + if (!m_binary[(~l1).index()].empty() && m_binary[(~l1).index()].back() == l2) return; + m_binary[(~l1).index()].push_back(l2); + m_binary[(~l2).index()].push_back(l1); + m_binary_trail.push_back((~l1).index()); + ++m_stats.m_add_binary; + if (m_s.m_config.m_drat) validate_binary(l1, l2); + } + + void lookahead::del_binary(unsigned idx) { + // TRACE("sat", display(tout << "Delete " << to_literal(idx) << "\n");); + literal_vector & lits = m_binary[idx]; + SASSERT(!lits.empty()); + literal l = lits.back(); + lits.pop_back(); + SASSERT(!m_binary[(~l).index()].empty()); + IF_VERBOSE(0, if (m_binary[(~l).index()].back() != ~to_literal(idx)) verbose_stream() << "pop bad literal: " << idx << " " << (~l).index() << "\n";); + SASSERT(m_binary[(~l).index()].back() == ~to_literal(idx)); + m_binary[(~l).index()].pop_back(); + ++m_stats.m_del_binary; + } + + + void lookahead::validate_binary(literal l1, literal l2) { + if (m_search_mode == lookahead_mode::searching) { + m_assumptions.push_back(l1); + m_assumptions.push_back(l2); + m_drat.add(m_assumptions); + m_assumptions.pop_back(); + m_assumptions.pop_back(); + } + } + + void lookahead::inc_bstamp() { + ++m_bstamp_id; + if (m_bstamp_id == 0) { + ++m_bstamp_id; + m_bstamp.fill(0); + } + } + + void lookahead::inc_istamp() { + ++m_istamp_id; + if (m_istamp_id == 0) { + ++m_istamp_id; + for (unsigned i = 0; i < m_lits.size(); ++i) { + m_lits[i].m_double_lookahead = 0; + } + } + } + + void lookahead::set_bstamps(literal l) { + inc_bstamp(); + set_bstamp(l); + literal_vector const& conseq = m_binary[l.index()]; + for (literal l : conseq) { + set_bstamp(l); + } + } + + /** + \brief add one-step transitive closure of binary implications + return false if we learn a unit literal. + \pre all implicants of ~u are stamped. + u \/ v is true + **/ + + bool lookahead::add_tc1(literal u, literal v) { + unsigned sz = m_binary[v.index()].size(); + for (unsigned i = 0; i < sz; ++i) { + literal w = m_binary[v.index()][i]; + // ~v \/ w + if (!is_fixed(w)) { + if (is_stamped(~w)) { + // u \/ v, ~v \/ w, u \/ ~w => u is unit + TRACE("sat", tout << "tc1: " << u << "\n";); + propagated(u); + return false; + } + if (m_num_tc1 < m_config.m_tc1_limit) { + ++m_num_tc1; + IF_VERBOSE(30, verbose_stream() << "tc1: " << u << " " << w << "\n";); + add_binary(u, w); + } + } + } + return true; + } + + + /** + \brief main routine for adding a new binary clause dynamically. + */ + void lookahead::try_add_binary(literal u, literal v) { + SASSERT(m_search_mode == lookahead_mode::searching); + SASSERT(u.var() != v.var()); + if (!is_undef(u) || !is_undef(v)) { + IF_VERBOSE(0, verbose_stream() << "adding assigned binary " << v << " " << u << "\n";); + } + set_bstamps(~u); + if (is_stamped(~v)) { + TRACE("sat", tout << "try_add_binary: " << u << "\n";); + propagated(u); // u \/ ~v, u \/ v => u is a unit literal + } + else if (!is_stamped(v) && add_tc1(u, v)) { + // u \/ v is not in index + set_bstamps(~v); + if (is_stamped(~u)) { + TRACE("sat", tout << "try_add_binary: " << v << "\n";); + propagated(v); // v \/ ~u, u \/ v => v is a unit literal + } + else if (add_tc1(v, u)) { + update_prefix(u); + update_prefix(v); + add_binary(u, v); + } + } + } + + // ------------------------------------- + // pre-selection + // see also 91 - 102 sat11.w + + + void lookahead::pre_select() { + IF_VERBOSE(10, verbose_stream() << "(sat-lookahead :freevars " << m_freevars.size() << ")\n";); + m_lookahead.reset(); + for (bool_var x : m_freevars) { // tree lookahead leaves literals fixed in lower truth levels + literal l(x, false); + set_undef(l); + set_undef(~l); + } + if (select(scope_lvl())) { + get_scc(); + if (inconsistent()) return; + find_heights(); + construct_lookahead_table(); + } + } + + + bool lookahead::select(unsigned level) { + init_pre_selection(level); + unsigned level_cand = std::max(m_config.m_level_cand, m_freevars.size() / 50); + unsigned max_num_cand = (level > 0 && m_config.m_preselect) ? level_cand / level : m_freevars.size(); + max_num_cand = std::max(m_config.m_min_cutoff, max_num_cand); + + double sum = 0; + for (bool newbies = false; ; newbies = true) { + sum = init_candidates(level, newbies); + if (!m_candidates.empty()) break; + if (is_sat()) { + return false; + } + } + SASSERT(!m_candidates.empty()); + // cut number of candidates down to max_num_cand. + // step 1. cut it to at most 2*max_num_cand. + // step 2. use a heap to sift through the rest. + bool progress = true; + while (progress && m_candidates.size() >= max_num_cand * 2) { + progress = false; + double mean = sum / (double)(m_candidates.size() + 0.0001); + sum = 0; + for (unsigned i = 0; i < m_candidates.size() && m_candidates.size() >= max_num_cand * 2; ++i) { + if (m_candidates[i].m_rating >= mean) { + sum += m_candidates[i].m_rating; + } + else { + m_candidates[i] = m_candidates.back(); + m_candidates.pop_back(); + --i; + progress = true; + } + } + } + TRACE("sat", display_candidates(tout);); + SASSERT(!m_candidates.empty()); + heap_sort(); + while (m_candidates.size() > max_num_cand) { + m_candidates.pop_back(); + } + SASSERT(!m_candidates.empty() && m_candidates.size() <= max_num_cand); + TRACE("sat", display_candidates(tout);); + return true; + } + + void lookahead::heap_sort() { + if (m_candidates.size() > 1) { + heapify(); + for (unsigned i = m_candidates.size() - 1; i > 0; --i) { + candidate c = m_candidates[i]; + m_candidates[i] = m_candidates[0]; + m_candidates[0] = c; + sift_down(0, i); + } + } + SASSERT(validate_heap_sort()); + } + + void lookahead::heapify() { + unsigned i = 1 + (m_candidates.size() - 2) / 2; + while(i > 0) { + sift_down(--i, m_candidates.size()); + } + } + + void lookahead::sift_down(unsigned j, unsigned sz) { + unsigned i = j; + candidate c = m_candidates[j]; + for (unsigned k = 2 * j + 1; k < sz; i = k, k = 2 * k + 1) { + // pick smallest child + if (k + 1 < sz && m_candidates[k].m_rating > m_candidates[k + 1].m_rating) { + ++k; + } + if (c.m_rating <= m_candidates[k].m_rating) break; + m_candidates[i] = m_candidates[k]; + } + if (i > j) m_candidates[i] = c; + } + + /** + * \brief validate that the result of heap sort sorts the candidates + * in descending order of their rating. + */ + bool lookahead::validate_heap_sort() { + for (unsigned i = 0; i + 1 < m_candidates.size(); ++i) + if (m_candidates[i].m_rating < m_candidates[i + 1].m_rating) + return false; + return true; + } + + double lookahead::init_candidates(unsigned level, bool newbies) { + m_candidates.reset(); + double sum = 0; + unsigned skip_candidates = 0; + bool autarky = get_config().m_lookahead_global_autarky; + for (bool_var x : m_freevars) { + SASSERT(is_undef(x)); + if (!m_select_lookahead_vars.empty()) { + if (m_select_lookahead_vars.contains(x)) { + if (!autarky || newbies || in_reduced_clause(x)) { + m_candidates.push_back(candidate(x, m_rating[x])); + sum += m_rating[x]; + } + else { + skip_candidates++; + } + } + } + else if (newbies || active_prefix(x)) { + m_candidates.push_back(candidate(x, m_rating[x])); + sum += m_rating[x]; + } + } + TRACE("sat", display_candidates(tout << "sum: " << sum << "\n");); + if (skip_candidates > 0) { + IF_VERBOSE(1, verbose_stream() << "(sat-lookahead :candidates " << m_candidates.size() << " :skipped " << skip_candidates << ")\n";); + } + return sum; + } + + + std::ostream& lookahead::display_candidates(std::ostream& out) const { + for (unsigned i = 0; i < m_candidates.size(); ++i) { + out << "var: " << m_candidates[i].m_var << " rating: " << m_candidates[i].m_rating << "\n"; + } + return out; + } + + bool lookahead::is_unsat() const { + for (unsigned idx = 0; idx < m_binary.size(); ++idx) { + literal l = to_literal(idx); + for (literal lit : m_binary[idx]) { + if (is_true(l) && is_false(lit)) + return true; + } + } + // check if there is a clause whose literals are false. + // every clause is terminated by a null-literal. + for (nary* n : m_nary_clauses) { + bool all_false = true; + for (literal l : *n) { + all_false &= is_false(l); + } + if (all_false) return true; + } + // check if there is a ternary whose literals are false. + for (unsigned idx = 0; idx < m_ternary.size(); ++idx) { + literal lit = to_literal(idx); + if (is_false(lit)) { + unsigned sz = m_ternary_count[lit.index()]; + for (binary const& b : m_ternary[lit.index()]) { + if (sz-- == 0) break; + if (is_false(b.m_u) && is_false(b.m_v)) + return true; + } + } + } + return false; + } + + bool lookahead::is_sat() const { + for (bool_var x : m_freevars) { + literal l(x, false); + literal_vector const& lits1 = m_binary[l.index()]; + for (literal lit1 : lits1) { + if (!is_true(lit1)) { + TRACE("sat", tout << l << " " << lit1 << "\n";); + return false; + } + } + l.neg(); + literal_vector const& lits2 = m_binary[l.index()]; + for (literal lit2 : lits2) { + if (!is_true(lit2)) { + TRACE("sat", tout << l << " " << lit2 << "\n";); + return false; + } + } + } + // check if there is a clause whose literals are false. + // every clause is terminated by a null-literal. + for (nary * n : m_nary_clauses) { + bool no_true = true; + for (literal l : *n) { + no_true &= !is_true(l); + } + if (no_true) return false; + } + // check if there is a ternary whose literals are false. + for (unsigned idx = 0; idx < m_ternary.size(); ++idx) { + literal lit = to_literal(idx); + if (!is_true(lit)) { + unsigned sz = m_ternary_count[lit.index()]; + for (binary const& b : m_ternary[lit.index()]) { + if (sz-- == 0) break; + if (!is_true(b.m_u) && !is_true(b.m_v)) + return false; + } + } + } + return true; + } + + bool lookahead::missed_propagation() const { + if (inconsistent()) return false; + for (literal l1 : m_trail) { + SASSERT(is_true(l1)); + for (literal l2 : m_binary[l1.index()]) { + VERIFY(is_true(l2)); + if (is_undef(l2)) return true; + } + unsigned sz = m_ternary_count[(~l1).index()]; + for (binary b : m_ternary[(~l1).index()]) { + if (sz-- == 0) break; + if (!(is_true(b.m_u) || is_true(b.m_v) || (is_undef(b.m_v) && is_undef(b.m_u)))) { + IF_VERBOSE(0, verbose_stream() << b.m_u << " " << b.m_v << "\n" + << get_level(b.m_u) << " " << get_level(b.m_v) << " level: " << m_level << "\n";); + UNREACHABLE(); + } + if ((is_false(b.m_u) && is_undef(b.m_v)) || (is_false(b.m_v) && is_undef(b.m_u))) + return true; + } + } + for (nary * n : m_nary_clauses) { + if (n->size() == 1 && !is_true(n->get_head())) { + for (literal lit : *n) { + VERIFY(!is_undef(lit)); + if (is_undef(lit)) return true; + } + } + } + return false; + } + + bool lookahead::missed_conflict() const { + if (inconsistent()) return false; + for (literal l1 : m_trail) { + for (literal l2 : m_binary[l1.index()]) { + if (is_false(l2)) + return true; + } + unsigned sz = m_ternary_count[(~l1).index()]; + for (binary b : m_ternary[(~l1).index()]) { + if (sz-- == 0) break; + if ((is_false(b.m_u) && is_false(b.m_v))) + return true; + } + } + for (nary * n : m_nary_clauses) { + if (n->size() == 0) + return true; + } + return false; + } + + void lookahead::init_pre_selection(unsigned level) { + switch (m_config.m_reward_type) { + case ternary_reward: { + unsigned max_level = m_config.m_max_hlevel; + if (level <= 1) { + ensure_H(2); + h_scores(m_H[0], m_H[1]); + for (unsigned j = 0; j < 2; ++j) { + for (unsigned i = 0; i < 2; ++i) { + h_scores(m_H[i + 1], m_H[(i + 2) % 3]); + } + } + m_heur = &m_H[1]; + } + else if (level < max_level) { + ensure_H(level); + h_scores(m_H[level-1], m_H[level]); + m_heur = &m_H[level]; + } + else { + ensure_H(max_level); + h_scores(m_H[max_level-1], m_H[max_level]); + m_heur = &m_H[max_level]; + } + break; + } + case heule_schur_reward: + heule_schur_scores(); + break; + case heule_unit_reward: + heule_unit_scores(); + break; + case march_cu_reward: + march_cu_scores(); + break; + case unit_literal_reward: + heule_schur_scores(); + break; + } + } + + void lookahead::heule_schur_scores() { + if (m_rating_throttle++ % 10 != 0) return; + for (bool_var x : m_freevars) { + literal l(x, false); + m_rating[l.var()] = heule_schur_score(l) * heule_schur_score(~l); + } + } + + double lookahead::heule_schur_score(literal l) { + double sum = 0; + for (literal lit : m_binary[l.index()]) { + if (is_undef(lit)) sum += literal_occs(lit) / 4.0; + } + unsigned sz = m_ternary_count[(~l).index()]; + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + sum += (literal_occs(b.m_u) + literal_occs(b.m_v)) / 8.0; + } + sz = m_nary_count[(~l).index()]; + for (nary * n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + double to_add = 0; + for (literal lit : *n) { + if (!is_fixed(lit) && lit != ~l) { + to_add += literal_occs(lit); + } + } + unsigned len = n->size(); + sum += pow(0.5, len) * to_add / len; + } + return sum; + } + + void lookahead::heule_unit_scores() { + if (m_rating_throttle++ % 10 != 0) return; + for (bool_var x : m_freevars) { + literal l(x, false); + m_rating[l.var()] = heule_unit_score(l) * heule_unit_score(~l); + } + } + + double lookahead::heule_unit_score(literal l) { + double sum = 0; + for (literal lit : m_binary[l.index()]) { + if (is_undef(lit)) sum += 0.5; + } + sum += 0.25 * m_ternary_count[(~l).index()]; + unsigned sz = m_nary_count[(~l).index()]; + for (nary * n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + sum += pow(0.5, n->size()); + } + return sum; + } + + void lookahead::march_cu_scores() { + for (bool_var x : m_freevars) { + literal l(x, false); + double pos = march_cu_score(l), neg = march_cu_score(~l); + m_rating[l.var()] = 1024 * pos * neg + pos + neg + 1; + } + } + + double lookahead::march_cu_score(literal l) { + double sum = 1.0 + literal_big_occs(~l); + for (literal lit : m_binary[l.index()]) { + if (is_undef(lit)) sum += literal_big_occs(lit); + } + return sum; + } + + void lookahead::ensure_H(unsigned level) { + while (m_H.size() <= level) { + m_H.push_back(svector()); + m_H.back().resize(m_num_vars * 2, 0); + } + } + + void lookahead::h_scores(svector& h, svector& hp) { + double sum = 0; + for (bool_var x : m_freevars) { + literal l(x, false); + sum += h[l.index()] + h[(~l).index()]; + } + if (sum == 0) sum = 0.0001; + double factor = 2 * m_freevars.size() / sum; + double sqfactor = factor * factor; + double afactor = factor * m_config.m_alpha; + for (bool_var x : m_freevars) { + literal l(x, false); + double pos = l_score(l, h, factor, sqfactor, afactor); + double neg = l_score(~l, h, factor, sqfactor, afactor); + hp[l.index()] = pos; + hp[(~l).index()] = neg; + m_rating[l.var()] = pos * neg; + } + } + + double lookahead::l_score(literal l, svector const& h, double factor, double sqfactor, double afactor) { + double sum = 0, tsum = 0; + for (literal lit : m_binary[l.index()]) { + if (is_undef(lit)) sum += h[lit.index()]; + // if (m_freevars.contains(lit.var())) sum += h[lit.index()]; + } + unsigned sz = m_ternary_count[(~l).index()]; + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + tsum += h[b.m_u.index()] * h[b.m_v.index()]; + } + // std::cout << "sum: " << sum << " afactor " << afactor << " sqfactor " << sqfactor << " tsum " << tsum << "\n"; + sum = (double)(0.1 + afactor*sum + sqfactor*tsum); + // std::cout << "sum: " << sum << " max score " << m_config.m_max_score << "\n"; + return std::min(m_config.m_max_score, sum); + } + + // ------------------------------------ + // Implication graph + // Compute implication ordering and strongly connected components. + // sat11.w 103 - 114. + + void lookahead::get_scc() { + unsigned num_candidates = m_candidates.size(); + init_scc(); + for (unsigned i = 0; i < num_candidates && !inconsistent(); ++i) { + literal lit(m_candidates[i].m_var, false); + if (get_rank(lit) == 0) get_scc(lit); + if (get_rank(~lit) == 0) get_scc(~lit); + } + TRACE("sat", display_scc(tout);); + } + void lookahead::init_scc() { + inc_bstamp(); + for (unsigned i = 0; i < m_candidates.size(); ++i) { + literal lit(m_candidates[i].m_var, false); + init_dfs_info(lit); + init_dfs_info(~lit); + } + for (unsigned i = 0; i < m_candidates.size(); ++i) { + literal lit(m_candidates[i].m_var, false); + init_arcs(lit); + init_arcs(~lit); + } + m_rank = 0; + m_rank_max = UINT_MAX; + m_active = null_literal; + m_settled = null_literal; + TRACE("sat", display_dfs(tout);); + } + void lookahead::init_dfs_info(literal l) { + unsigned idx = l.index(); + m_dfs[idx].reset(); + set_bstamp(l); + } + // arcs are added in the opposite direction of implications. + // So for implications l => u we add arcs u -> l + void lookahead::init_arcs(literal l) { + literal_vector lits; + literal_vector const& succ = m_binary[l.index()]; + for (literal u : succ) { + SASSERT(u != l); + // l => u + // NB. u.index() > l.index() iff u.index() > (~l).index(). + // since indices for the same boolean variables occupy + // two adjacent numbers. + if (u.index() > l.index() && is_stamped(u) && ~l != u) { + add_arc(~l, ~u); + add_arc( u, l); + } + } + for (auto w : m_watches[l.index()]) { + lits.reset(); + if (w.is_ext_constraint() && m_s.m_ext->is_extended_binary(w.get_ext_constraint_idx(), lits)) { + for (literal u : lits) { + // u is positive in lits, l is negative: + if (~l != u && u.index() > l.index() && is_stamped(u)) { + add_arc(~l, ~u); + add_arc( u, l); + } + } + } + } + } + + void lookahead::add_arc(literal u, literal v) { + auto & lst = m_dfs[u.index()].m_next; + if (lst.empty() || lst.back() != v) lst.push_back(v); + } + + void lookahead::get_scc(literal v) { + TRACE("scc", tout << v << "\n";); + set_parent(v, null_literal); + activate_scc(v); + do { + literal ll = get_min(v); + if (has_arc(v)) { + literal u = pop_arc(v); + unsigned r = get_rank(u); + if (r > 0) { + // u was processed before ll + if (r < get_rank(ll)) set_min(v, u); + } + else { + // process u in dfs order, add v to dfs stack for u + set_parent(u, v); + v = u; + activate_scc(v); + } + } + else { + literal u = get_parent(v); + if (v == ll) { + found_scc(v); + } + else if (get_rank(ll) < get_rank(get_min(u))) { + set_min(u, ll); + } + // walk back in the dfs stack + v = u; + } + } + while (v != null_literal && !inconsistent()); + } + + void lookahead::activate_scc(literal l) { + SASSERT(get_rank(l) == 0); + set_rank(l, ++m_rank); + set_link(l, m_active); + set_min(l, l); + m_active = l; + } + // make v root of the scc equivalence class + // set vcomp to be the highest rated literal + void lookahead::found_scc(literal v) { + literal t = m_active; + m_active = get_link(v); + literal best = v; + double best_rating = get_rating(v); + set_rank(v, m_rank_max); + set_link(v, m_settled); m_settled = t; + while (t != v) { + if (t == ~v) { + TRACE("sat", display_scc(tout << "found contradiction during scc search\n");); + set_conflict(); + break; + } + set_rank(t, m_rank_max); + set_parent(t, v); + double t_rating = get_rating(t); + if (t_rating > best_rating) { + best = t; + best_rating = t_rating; + } + t = get_link(t); + } + set_parent(v, v); + set_vcomp(v, best); + if (maxed_rank(~v)) { + set_vcomp(v, ~get_vcomp(get_parent(~v))); + } + } + + std::ostream& lookahead::display_dfs(std::ostream& out) const { + for (unsigned i = 0; i < m_candidates.size(); ++i) { + literal l(m_candidates[i].m_var, false); + display_dfs(out, l); + display_dfs(out, ~l); + } + return out; + } + + std::ostream& lookahead::display_dfs(std::ostream& out, literal l) const { + arcs const& a1 = get_arcs(l); + if (!a1.empty()) { + out << l << " -> " << a1 << "\n"; + } + return out; + } + + std::ostream& lookahead::display_scc(std::ostream& out) const { + display_dfs(out); + for (unsigned i = 0; i < m_candidates.size(); ++i) { + literal l(m_candidates[i].m_var, false); + display_scc(out, l); + display_scc(out, ~l); + } + return out; + } + + std::ostream& lookahead::display_scc(std::ostream& out, literal l) const { + out << l << " := " << get_parent(l) + << " min: " << get_min(l) + << " rank: " << get_rank(l) + << " height: " << get_height(l) + << " link: " << get_link(l) + << " child: " << get_child(l) + << " vcomp: " << get_vcomp(l) << "\n"; + return out; + } + + + // ------------------------------------ + // lookahead forest + // sat11.w 115-121 + + literal lookahead::get_child(literal u) const { + if (u == null_literal) return m_root_child; + return m_dfs[u.index()].m_min; + } + + void lookahead::set_child(literal v, literal u) { + if (v == null_literal) m_root_child = u; + else m_dfs[v.index()].m_min = u; + } + + /* + \brief Assign heights to the nodes. + Nodes within the same strongly connected component are given the same height. + The code assumes that m_settled is topologically sorted such that + 1. nodes in the same equivalence class come together + 2. the equivalence class representative is last + + */ + void lookahead::find_heights() { + m_root_child = null_literal; + literal pp = null_literal; + unsigned h = 0; + literal w, uu; + TRACE("sat", + for (literal u = m_settled; u != null_literal; u = get_link(u)) { + tout << u << " "; + } + tout << "\n";); + for (literal u = m_settled; u != null_literal; u = uu) { + TRACE("sat", tout << "process: " << u << "\n";); + uu = get_link(u); + literal p = get_parent(u); + if (p != pp) { + // new equivalence class + h = 0; + w = null_literal; + pp = p; + } + // traverse nodes in order of implication + unsigned sz = num_next(~u); + for (unsigned j = 0; j < sz; ++j) { + literal v = ~get_next(~u, j); + TRACE("sat", tout << "child " << v << " link: " << get_link(v) << "\n";); + literal pv = get_parent(v); + // skip nodes in same equivalence, they will all be processed + if (pv == p) continue; + unsigned hh = get_height(pv); + // update the maximal height descendant + if (hh >= h) { + h = hh + 1; + w = pv; + } + } + if (p == u) { + // u is an equivalence class representative + // it is processed last + literal v = get_child(w); + set_height(u, h); + set_child(u, null_literal); + set_link(u, v); + set_child(w, u); + TRACE("sat", tout << "child(" << w << ") = " << u << " link(" << u << ") = " << v << "\n";); + } + } + TRACE("sat", + display_forest(tout << "forest: ", get_child(null_literal)); + tout << "\n"; + display_scc(tout); ); + } + std::ostream& lookahead::display_forest(std::ostream& out, literal l) { + for (literal u = l; u != null_literal; u = get_link(u)) { + out << u << " "; + l = get_child(u); + if (l != null_literal) { + out << "("; + display_forest(out, l); + out << ") "; + } + } + return out; + } + + void lookahead::display_search_string() { + printf("\r"); + uint64_t q = m_prefix; + unsigned depth = m_trail_lim.size(); + unsigned d = std::min(63u, depth); + unsigned new_prefix_length = d; + for (unsigned i = 0; i <= d; ++i) { + printf((0 != (q & (1ull << i)))? "1" : "0"); + } + if (d < depth) { + printf(" d: %d", depth); + new_prefix_length += 10; + } + for (unsigned i = new_prefix_length; i < m_last_prefix_length; ++i) { + printf(" "); + } + m_last_prefix_length = new_prefix_length; + fflush(stdout); + } + + void lookahead::construct_lookahead_table() { + literal u = get_child(null_literal), v = null_literal; + unsigned offset = 0; + SASSERT(m_lookahead.empty()); + while (u != null_literal) { + set_rank(u, m_lookahead.size()); + set_lookahead(get_vcomp(u)); + if (null_literal != get_child(u)) { + set_parent(u, v); + v = u; + u = get_child(u); + } + else { + while (true) { + set_offset(get_rank(u), offset); + offset += 2; + set_parent(u, v == null_literal ? v : get_vcomp(v)); + u = get_link(u); + if (u == null_literal && v != null_literal) { + u = v; + v = get_parent(u); + } + else { + break; + } + } + } + } + SASSERT(2*m_lookahead.size() == offset); + TRACE("sat", for (unsigned i = 0; i < m_lookahead.size(); ++i) + tout << m_lookahead[i].m_lit << " : " << m_lookahead[i].m_offset << "\n";); + } + + + + // ------------------------------------ + // initialization + + void lookahead::init_var(bool_var v) { + m_binary.push_back(literal_vector()); + m_binary.push_back(literal_vector()); + m_watches.push_back(watch_list()); + m_watches.push_back(watch_list()); + m_ternary.push_back(svector()); + m_ternary.push_back(svector()); + m_ternary_count.push_back(0); + m_ternary_count.push_back(0); + m_nary.push_back(ptr_vector()); + m_nary.push_back(ptr_vector()); + m_nary_count.push_back(0); + m_nary_count.push_back(0); + m_bstamp.push_back(0); + m_bstamp.push_back(0); + m_stamp.push_back(0); + m_dfs.push_back(dfs_info()); + m_dfs.push_back(dfs_info()); + m_lits.push_back(lit_info()); + m_lits.push_back(lit_info()); + m_rating.push_back(0); + m_vprefix.push_back(prefix()); + if (!m_s.was_eliminated(v)) + m_freevars.insert(v); + } + + void lookahead::init(bool learned) { + m_delta_trigger = 0.0; + m_delta_decrease = 0.0; + m_config.m_dl_success = 0.8; + m_inconsistent = false; + m_qhead = 0; + m_bstamp_id = 0; + + for (unsigned i = 0; i < m_num_vars; ++i) { + init_var(i); + } + + // copy binary clauses + unsigned sz = m_s.m_watches.size(); + for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { + literal l = ~to_literal(l_idx); + if (m_s.was_eliminated(l.var())) continue; + watch_list const & wlist = m_s.m_watches[l_idx]; + for (auto& w : wlist) { + if (!w.is_binary_clause()) + continue; + if (!learned && w.is_learned()) + continue; + literal l2 = w.get_literal(); + if (l.index() < l2.index() && !m_s.was_eliminated(l2.var())) + add_binary(l, l2); + } + } + + copy_clauses(m_s.m_clauses, false); + if (learned) copy_clauses(m_s.m_learned, true); + + // copy units + unsigned trail_sz = m_s.init_trail_size(); + for (unsigned i = 0; i < trail_sz; ++i) { + literal l = m_s.m_trail[i]; + if (!m_s.was_eliminated(l.var())) { + if (m_s.m_config.m_drat) m_drat.add(l, false); + assign(l); + } + } + + if (m_s.m_ext) { + // m_ext = m_s.m_ext->copy(this, learned); + } + propagate(); + m_qhead = m_trail.size(); + m_init_freevars = m_freevars.size(); + TRACE("sat", m_s.display(tout); display(tout);); + } + + void lookahead::copy_clauses(clause_vector const& clauses, bool learned) { + // copy clauses + for (clause* cp : clauses) { + clause& c = *cp; + if (c.was_removed()) continue; + // enable when there is a non-ternary reward system. + bool was_eliminated = false; + for (unsigned i = 0; !was_eliminated && i < c.size(); ++i) { + was_eliminated = m_s.was_eliminated(c[i].var()); + } + if (was_eliminated) continue; + + switch (c.size()) { + case 0: set_conflict(); break; + case 1: assign(c[0]); break; + case 2: add_binary(c[0],c[1]); break; + case 3: add_ternary(c[0],c[1],c[2]); break; + default: if (!learned) add_clause(c); break; + } + if (m_s.m_config.m_drat) m_drat.add(c, false); + } + } + + // ------------------------------------ + // search + + + void lookahead::push(literal lit, unsigned level) { + SASSERT(m_search_mode == lookahead_mode::searching); + m_binary_trail_lim.push_back(m_binary_trail.size()); + m_trail_lim.push_back(m_trail.size()); + m_num_tc1_lim.push_back(m_num_tc1); + m_qhead_lim.push_back(m_qhead); + scoped_level _sl(*this, level); + m_assumptions.push_back(~lit); + assign(lit); + propagate(); + } + + void lookahead::pop() { + SASSERT(!m_assumptions.empty()); + m_assumptions.pop_back(); + m_inconsistent = false; + SASSERT(m_search_mode == lookahead_mode::searching); + + // m_freevars only for main search + // undo assignments + unsigned old_sz = m_trail_lim.back(); + for (unsigned i = m_trail.size(); i > old_sz; ) { + --i; + literal l = m_trail[i]; + set_undef(l); + TRACE("sat", tout << "inserting free var v" << l.var() << "\n";); + m_freevars.insert(l.var()); + } + + m_num_tc1 = m_num_tc1_lim.back(); + m_num_tc1_lim.pop_back(); + + for (unsigned i = m_qhead; i > m_qhead_lim.back(); ) { + --i; + restore_ternary(m_trail[i]); + restore_clauses(m_trail[i]); + } + + m_trail.shrink(old_sz); // reset assignment. + m_trail_lim.pop_back(); + + + // remove local binary clauses + old_sz = m_binary_trail_lim.back(); + for (unsigned i = m_binary_trail.size(); i > old_sz; ) { + del_binary(m_binary_trail[--i]); + } + m_binary_trail.shrink(old_sz); + m_binary_trail_lim.pop_back(); + + // reset propagation queue + m_qhead = m_qhead_lim.back(); + m_qhead_lim.pop_back(); + } + + bool lookahead::push_lookahead2(literal lit, unsigned level) { + scoped_level _sl(*this, level); + SASSERT(m_search_mode == lookahead_mode::lookahead1); + m_search_mode = lookahead_mode::lookahead2; + lookahead_backtrack(); + assign(lit); + propagate(); + bool unsat = inconsistent(); + SASSERT(m_search_mode == lookahead_mode::lookahead2); + m_search_mode = lookahead_mode::lookahead1; + m_inconsistent = false; + return unsat; + } + + unsigned lookahead::push_lookahead1(literal lit, unsigned level) { + SASSERT(m_search_mode == lookahead_mode::searching); + m_search_mode = lookahead_mode::lookahead1; + scoped_level _sl(*this, level); + lookahead_backtrack(); + unsigned old_sz = m_trail.size(); + assign(lit); + propagate(); + return m_trail.size() - old_sz; + } + + void lookahead::pop_lookahead1(literal lit, unsigned num_units) { + bool unsat = inconsistent(); + SASSERT(m_search_mode == lookahead_mode::lookahead1); + m_inconsistent = false; + m_search_mode = lookahead_mode::searching; + // convert windfalls to binary clauses. + if (!unsat) { + literal nlit = ~lit; + + for (unsigned i = 0; i < m_wstack.size(); ++i) { + literal l2 = m_wstack[i]; + //update_prefix(~lit); + //update_prefix(m_wstack[i]); + TRACE("sat", tout << "windfall: " << nlit << " " << l2 << "\n";); + // if we use try_add_binary, then this may produce new assignments + // these assignments get put on m_trail, and they are cleared by + // lookahead_backtrack. + add_binary(nlit, l2); + } + m_stats.m_windfall_binaries += m_wstack.size(); + } + switch (m_config.m_reward_type) { + case unit_literal_reward: + m_lookahead_reward += num_units; + break; + case heule_unit_reward: + case march_cu_reward: + case heule_schur_reward: + break; + default: + break; + } + m_wstack.reset(); + } + + void lookahead::lookahead_backtrack() { + literal lit = null_literal; + while (!m_trail.empty() && is_undef((lit = m_trail.back()))) { + if (m_qhead == m_trail.size()) { + unsigned sz = m_nary_count[(~lit).index()]; + for (nary* n : m_nary[(~lit).index()]) { + if (sz-- == 0) break; + n->inc_size(); + } + --m_qhead; + } + m_trail.pop_back(); + } + SASSERT(m_trail_lim.empty() || m_trail.size() >= m_trail_lim.back()); + } + + // + // The current version is modeled after CDCL SAT solving data-structures. + // It borrows from the watch list data-structure. The cost tradeoffs are somewhat + // biased towards CDCL search overheads. + // If we walk over the positive occurrences of l, then those clauses can be retired so + // that they don't interfere with calculation of H. Instead of removing clauses from the watch + // list one can swap them to the "back" and adjust a size indicator of the watch list + // Only the size indicator needs to be updated on backtracking. + // + + class lookahead_literal_occs_fun : public literal_occs_fun { + lookahead& lh; + public: + lookahead_literal_occs_fun(lookahead& lh): lh(lh) {} + double operator()(literal l) { return lh.literal_occs(l); } + }; + + // Ternary clause managagement: + + void lookahead::add_ternary(literal u, literal v, literal w) { + SASSERT(u != w && u != v && v != w && ~u != w && ~u != v && ~w != v); + m_ternary[u.index()].push_back(binary(v, w)); + m_ternary[v.index()].push_back(binary(w, u)); + m_ternary[w.index()].push_back(binary(u, v)); + m_ternary_count[u.index()]++; + m_ternary_count[v.index()]++; + m_ternary_count[w.index()]++; + } + + lbool lookahead::propagate_ternary(literal l1, literal l2) { + if (is_fixed(l1)) { + if (is_false(l1)) { + if (is_false(l2)) { + TRACE("sat", tout << l1 << " " << l2 << " " << "\n";); + set_conflict(); + return l_false; + } + else if (is_undef(l2)) { + propagated(l2); + } + return l_true; + } + else { + return l_true; + } + } + + if (is_fixed(l2)) { + if (is_false(l2)) { + propagated(l1); + return l_false; + } + else { + return l_true; + } + } + return l_undef; + } + + void lookahead::propagate_ternary(literal l) { + unsigned sz = m_ternary_count[(~l).index()]; + + switch (m_search_mode) { + case lookahead_mode::searching: { + + // ternary clauses where l is negative become binary + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + // this could create a conflict from propagation, but we complete the transaction. + TRACE("sat", display(tout);); + literal l1 = b.m_u; + literal l2 = b.m_v; + switch (propagate_ternary(l1, l2)) { + case l_undef: + try_add_binary(l1, l2); + break; + default: + // propagated or tautology or conflict + break; + } + remove_ternary(l1, l2, ~l); + remove_ternary(l2, ~l, l1); + } + + sz = m_ternary_count[l.index()]; + // ternary clauses where l is positive are tautologies + for (binary const& b : m_ternary[l.index()]) { + if (sz-- == 0) break; + remove_ternary(b.m_u, b.m_v, l); + remove_ternary(b.m_v, l, b.m_u); + } + break; + } + case lookahead_mode::lookahead1: + // this could create a conflict from propagation, but we complete the loop. + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + literal l1 = b.m_u; + literal l2 = b.m_v; + switch (propagate_ternary(l1, l2)) { + case l_undef: + update_binary_clause_reward(l1, l2); + break; + default: + break; + } + } + break; + case lookahead2: + // this could create a conflict from propagation, but we complete the loop. + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + propagate_ternary(b.m_u, b.m_v); + } + break; + } + } + + void lookahead::remove_ternary(literal l, literal u, literal v) { + unsigned idx = l.index(); + unsigned sz = m_ternary_count[idx]--; + auto& tv = m_ternary[idx]; + for (unsigned i = sz; i-- > 0; ) { + binary const& b = tv[i]; + if (b.m_u == u && b.m_v == v) { + std::swap(tv[i], tv[sz-1]); + return; + } + } + UNREACHABLE(); + } + + void lookahead::restore_ternary(literal l) { + unsigned sz = m_ternary_count[(~l).index()]; + for (binary const& b : m_ternary[(~l).index()]) { + if (sz-- == 0) break; + m_ternary_count[b.m_u.index()]++; + m_ternary_count[b.m_v.index()]++; + } + sz = m_ternary_count[l.index()]; + for (binary const& b : m_ternary[l.index()]) { + if (sz-- == 0) break; + m_ternary_count[b.m_u.index()]++; + m_ternary_count[b.m_v.index()]++; + } + } + + void lookahead::propagate_external(literal l) { + SASSERT(is_true(l)); + if (!m_s.m_ext || inconsistent()) return; + watch_list& wlist = m_watches[l.index()]; + watch_list::iterator it = wlist.begin(), it2 = it, end = wlist.end(); + for (; it != end && !inconsistent(); ++it) { + SASSERT(it->get_kind() == watched::EXT_CONSTRAINT); + bool keep = m_s.m_ext->propagate(l, it->get_ext_constraint_idx()); + if (m_search_mode == lookahead_mode::lookahead1 && !m_inconsistent) { + lookahead_literal_occs_fun literal_occs_fn(*this); + m_lookahead_reward += m_s.m_ext->get_reward(l, it->get_ext_constraint_idx(), literal_occs_fn); + } + if (inconsistent()) { + if (!keep) ++it; + } + else if (keep) { + *it2 = *it; + it2++; + } + } + for (; it != end; ++it, ++it2) { + *it2 = *it; + } + wlist.set_end(it2); + } + + + // new n-ary clause managment + + void lookahead::add_clause(clause const& c) { + SASSERT(c.size() > 3); + void * mem = m_allocator.allocate(nary::get_obj_size(c.size())); + nary * n = new (mem) nary(c.size(), c.begin()); + m_nary_clauses.push_back(n); + for (literal l : c) { + m_nary[l.index()].push_back(n); + m_nary_count[l.index()]++; + } + } + + + void lookahead::propagate_clauses_searching(literal l) { + // clauses where l is negative + unsigned sz = m_nary_count[(~l).index()]; + literal lit; + SASSERT(m_search_mode == lookahead_mode::searching); + for (nary* n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + unsigned len = n->dec_size(); + if (is_true(n->get_head())) continue; + if (inconsistent()) continue; + if (len <= 1) continue; // already processed + // find the two unassigned literals, if any + if (len == 2) { + literal l1 = null_literal; + literal l2 = null_literal; + bool found_true = false; + for (literal lit : *n) { + if (!is_fixed(lit)) { + if (l1 == null_literal) { + l1 = lit; + } + else { + SASSERT(l2 == null_literal); + l2 = lit; + break; + } + } + else if (is_true(lit)) { + n->set_head(lit); + found_true = true; + break; + } + } + if (found_true) { + // skip, the clause will be removed when propagating on 'lit' + } + else if (l1 == null_literal) { + set_conflict(); + } + else if (l2 == null_literal) { + // clause may get revisited during propagation, when l2 is true in this clause. + // m_removed_clauses.push_back(std::make_pair(~l, idx)); + // remove_clause_at(~l, idx); + propagated(l1); + } + else { + // extract binary clause. A unary or empty clause may get revisited, + // but we skip it then because it is already handled as a binary clause. + // m_removed_clauses.push_back(std::make_pair(~l, idx)); // need to restore this clause. + // remove_clause_at(~l, idx); + try_add_binary(l1, l2); + } + } + } + // clauses where l is positive: + sz = m_nary_count[l.index()]; + for (nary* n : m_nary[l.index()]) { + if (sz-- == 0) break; + remove_clause_at(l, *n); + } + } + + void lookahead::propagate_clauses_lookahead(literal l) { + // clauses where l is negative + unsigned sz = m_nary_count[(~l).index()]; + SASSERT(m_search_mode == lookahead_mode::lookahead1 || + m_search_mode == lookahead_mode::lookahead2); + + for (nary* n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + unsigned nonfixed = n->dec_size(); + // if (is_true(n->get_head())) continue; + if (inconsistent()) continue; + if (nonfixed <= 1 && !is_true(n->get_head())) { + bool found_conflict = true; + for (literal lit : *n) { + if (!is_fixed(lit)) { + propagated(lit); + found_conflict = false; + break; + } + else if (is_true(lit)) { + n->set_head(lit); + found_conflict = false; + break; + } + } + if (found_conflict) { + set_conflict(); + continue; + } + } + if (m_search_mode == lookahead_mode::lookahead1) { + //SASSERT(nonfixed >= 2); + switch (m_config.m_reward_type) { + case heule_schur_reward: { + double to_add = 0; + for (literal lit : *n) { + if (!is_fixed(lit)) { + to_add += literal_occs(lit); + } + } + m_lookahead_reward += pow(0.5, nonfixed) * to_add / nonfixed; + break; + } + case heule_unit_reward: + m_lookahead_reward += pow(0.5, nonfixed); + break; + case march_cu_reward: + m_lookahead_reward += nonfixed >= 2 ? 3.3 * pow(0.5, nonfixed - 2) : 0.0; + break; + case ternary_reward: + UNREACHABLE(); + break; + case unit_literal_reward: + break; + } + } + } + // clauses where l is positive: + sz = m_nary_count[l.index()]; + for (nary* n : m_nary[l.index()]) { + if (sz-- == 0) break; + if (get_level(l) > get_level(n->get_head())) { + n->set_head(l); + } + } + } + + void lookahead::remove_clause_at(literal l, nary& n) { + for (literal lit : n) { + if (lit != l) { + remove_clause(lit, n); + } + } + } + + void lookahead::remove_clause(literal l, nary& n) { + ptr_vector& pclauses = m_nary[l.index()]; + unsigned sz = m_nary_count[l.index()]--; + for (unsigned i = sz; i > 0; ) { + --i; + if (&n == pclauses[i]) { + std::swap(pclauses[i], pclauses[sz-1]); + return; + } + } + UNREACHABLE(); + } + + void lookahead::restore_clauses(literal l) { + SASSERT(m_search_mode == lookahead_mode::searching); + // increase the length of clauses where l is negative + unsigned sz = m_nary_count[(~l).index()]; + for (nary* n : m_nary[(~l).index()]) { + if (sz-- == 0) break; + n->inc_size(); + } + // add idx back to clause list where l is positive + // add them back in the same order as they were inserted + // in this way we can check that the clauses are the same. + sz = m_nary_count[l.index()]; + ptr_vector& pclauses = m_nary[l.index()]; + for (unsigned i = sz; i-- > 0; ) { + for (literal lit : *pclauses[i]) { + if (lit != l) { + SASSERT(m_nary[lit.index()][m_nary_count[lit.index()]] == pclauses[i]); + m_nary_count[lit.index()]++; + } + } + } + } + + void lookahead::propagate_clauses(literal l) { + SASSERT(is_true(l)); + propagate_ternary(l); + switch (m_search_mode) { + case lookahead_mode::searching: + propagate_clauses_searching(l); + break; + default: + propagate_clauses_lookahead(l); + break; + } + propagate_external(l); + } + + void lookahead::update_binary_clause_reward(literal l1, literal l2) { + SASSERT(!is_false(l1)); + SASSERT(!is_false(l2)); + switch (m_config.m_reward_type) { + case ternary_reward: + m_lookahead_reward += (*m_heur)[l1.index()] * (*m_heur)[l2.index()]; + break; + case heule_schur_reward: + m_lookahead_reward += (literal_occs(l1) + literal_occs(l2)) / 8.0; + break; + case heule_unit_reward: + m_lookahead_reward += 0.25; + break; + case march_cu_reward: + m_lookahead_reward += 3.3; + break; + case unit_literal_reward: + break; + } + } + + void lookahead::update_nary_clause_reward(clause const& c) { + if (m_config.m_reward_type == ternary_reward && m_lookahead_reward != 0) { + return; + } + literal const * l_it = c.begin() + 2, *l_end = c.end(); + unsigned sz = 0; + for (; l_it != l_end; ++l_it) { + if (is_true(*l_it)) return; + if (!is_false(*l_it)) ++sz; + } + switch (m_config.m_reward_type) { + case heule_schur_reward: { + SASSERT(sz > 0); + double to_add = 0; + for (literal l : c) { + if (!is_false(l)) { + to_add += literal_occs(l); + } + } + m_lookahead_reward += pow(0.5, sz) * to_add / sz; + break; + } + case heule_unit_reward: + m_lookahead_reward += pow(0.5, sz); + break; + case march_cu_reward: + m_lookahead_reward += 3.3 * pow(0.5, sz - 2); + break; + case ternary_reward: + m_lookahead_reward = (double)0.001; + break; + case unit_literal_reward: + break; + } + } + + // Sum_{ clause C that contains ~l } 1 + // FIXME: counts occurences of ~l; misleading + double lookahead::literal_occs(literal l) { + double result = m_binary[l.index()].size(); + result += literal_big_occs(l); + return result; + } + + // Sum_{ clause C that contains ~l such that |C| > 2} 1 + // FIXME: counts occurences of ~l; misleading + double lookahead::literal_big_occs(literal l) { + double result = m_nary_count[(~l).index()]; + result += m_ternary_count[(~l).index()]; + return result; + } + + void lookahead::propagate_binary(literal l) { + literal_vector const& lits = m_binary[l.index()]; + TRACE("sat", tout << l << " => " << lits << "\n";); + for (literal lit : lits) { + if (inconsistent()) break; + assign(lit); + } + } + + void lookahead::propagate() { + unsigned i = m_qhead; + for (; i < m_trail.size() && !inconsistent(); ++i) { + literal l = m_trail[i]; + TRACE("sat", tout << "propagate " << l << " @ " << m_level << "\n";); + propagate_binary(l); + } + while (m_qhead < m_trail.size() && !inconsistent()) { + propagate_clauses(m_trail[m_qhead++]); + } + SASSERT(m_qhead == m_trail.size() || (inconsistent() && m_qhead < m_trail.size())); + //SASSERT(!missed_conflict()); + //VERIFY(!missed_propagation()); + TRACE("sat_verbose", display(tout << scope_lvl() << " " << (inconsistent()?"unsat":"sat") << "\n");); + } + + + void lookahead::compute_lookahead_reward() { + TRACE("sat", display_lookahead(tout); ); + m_delta_decrease = pow(m_config.m_delta_rho, 1.0 / (double)m_lookahead.size()); + unsigned base = 2; + bool change = true; + literal last_changed = null_literal; + while (change && !inconsistent()) { + change = false; + for (unsigned i = 0; !inconsistent() && i < m_lookahead.size(); ++i) { + checkpoint(); + literal lit = m_lookahead[i].m_lit; + if (lit == last_changed) { + SASSERT(!change); + break; + } + if (scope_lvl() == 1) { + IF_VERBOSE(30, verbose_stream() << scope_lvl() << " " << lit << " binary: " << m_binary_trail.size() << " trail: " << m_trail_lim.back() << "\n";); + } + unsigned level = base + m_lookahead[i].m_offset; + unsigned dl_lvl = level; + if (is_fixed_at(lit, c_fixed_truth) || is_true_at(lit, level)) continue; + bool unsat = false; + if (is_false_at(lit, level)) { + unsat = true; + } + else { + TRACE("sat", tout << "lookahead: " << lit << " @ " << m_lookahead[i].m_offset << "\n";); + reset_lookahead_reward(lit); + unsigned num_units = push_lookahead1(lit, level); + update_lookahead_reward(lit, level); + num_units += do_double(lit, dl_lvl); + if (dl_lvl > level) { + base = dl_lvl; + //SASSERT(get_level(m_trail.back()) == base + m_lookahead[i].m_offset); + SASSERT(get_level(m_trail.back()) == base); + } + unsat = inconsistent(); + pop_lookahead1(lit, num_units); + } + // VERIFY(!missed_propagation()); + if (unsat) { + TRACE("sat", tout << "backtracking and settting " << ~lit << "\n";); + lookahead_backtrack(); + assign(~lit); + propagate(); + change = true; + last_changed = lit; + continue; + } + // if l was derived from lit and ~lit -> l, then l is a necessary assignment + literal_vector necessary_lits; + for (literal l : m_binary[(~lit).index()]) { + if (is_true_at(l, dl_lvl) && !is_fixed_at(l, c_fixed_truth)) { + necessary_lits.push_back(l); + } + } + if (!necessary_lits.empty()) { + change = true; + last_changed = lit; + lookahead_backtrack(); + for (literal l : necessary_lits) { + if (inconsistent()) break; + assign(l); + propagate(); + } + } + SASSERT(inconsistent() || !is_unsat()); + } + if (c_fixed_truth - 2 * m_lookahead.size() < base) { + break; + } + base += 2 * m_lookahead.size(); + } + lookahead_backtrack(); + TRACE("sat", display_lookahead(tout); ); + } + + literal lookahead::select_literal() { + literal l = null_literal; + double h = 0; + unsigned count = 1; + for (unsigned i = 0; i < m_lookahead.size(); ++i) { + literal lit = m_lookahead[i].m_lit; + if (lit.sign() || !is_undef(lit)) { + continue; + } + double diff1 = get_lookahead_reward(lit), diff2 = get_lookahead_reward(~lit); + double mixd = mix_diff(diff1, diff2); + + if (mixd == h) ++count; + if (mixd > h || (mixd == h && m_s.m_rand(count) == 0)) { + CTRACE("sat", l != null_literal, tout << lit << " mix diff: " << mixd << "\n";); + if (mixd > h) count = 1; + h = mixd; + l = diff1 < diff2 ? lit : ~lit; + } + } + TRACE("sat", tout << "selected: " << l << "\n";); + return l; + } + + + double lookahead::mix_diff(double l, double r) const { + switch (m_config.m_reward_type) { + case ternary_reward: return l + r + (1 << 10) * l * r; + case heule_schur_reward: return l * r; + case heule_unit_reward: return l * r; + case march_cu_reward: return 1024 * (1024 * l * r + l + r); + case unit_literal_reward: return l * r; + default: UNREACHABLE(); return l * r; + } + } + + void lookahead::reset_lookahead_reward(literal l) { + + m_lookahead_reward = 0; + + // inherit propagation effect from parent. + literal p = get_parent(l); + set_lookahead_reward(l, (p == null_literal || is_undef(p) || is_fixed_at(p, c_fixed_truth)) ? + 0 : get_lookahead_reward(p)); + } + + void lookahead::update_lookahead_reward(literal l, unsigned level) { + if (m_lookahead_reward != 0) { + inc_lookahead_reward(l, m_lookahead_reward); + } + } + + unsigned lookahead::do_double(literal l, unsigned& base) { + unsigned num_units = 0; + if (!inconsistent() && dl_enabled(l)) { + if (get_lookahead_reward(l) > m_delta_trigger) { + if (dl_no_overflow(base)) { + ++m_stats.m_double_lookahead_rounds; + num_units = double_look(l, base); + if (!inconsistent()) { + m_delta_trigger = get_lookahead_reward(l); + dl_disable(l); + } + } + } + else { + SASSERT(m_delta_decrease > 0.0); + m_delta_trigger *= m_delta_decrease; + } + } + return num_units; + } + + unsigned lookahead::double_look(literal l, unsigned& base) { + SASSERT(!inconsistent()); + SASSERT(dl_no_overflow(base)); + SASSERT(is_fixed_at(l, base)); + base += m_lookahead.size(); + unsigned dl_truth = base + m_lookahead.size() * m_config.m_dl_max_iterations; + scoped_level _sl(*this, dl_truth); + //SASSERT(get_level(m_trail.back()) == dl_truth); + IF_VERBOSE(3, verbose_stream() << "(sat-lookahead :double " << l << " :depth " << m_trail_lim.size() << ")\n";); + lookahead_backtrack(); + assign(l); + propagate(); + unsigned old_sz = m_trail.size(); + bool change = true; + literal last_changed = null_literal; + unsigned num_iterations = 0; + while (change && num_iterations < m_config.m_dl_max_iterations && !inconsistent()) { + num_iterations++; + for (unsigned i = 0; !inconsistent() && i < m_lookahead.size(); ++i) { + literal lit = m_lookahead[i].m_lit; + if (lit == last_changed) { + SASSERT(change == false); + break; + } + unsigned level = base + m_lookahead[i].m_offset; + if (level + m_lookahead.size() >= dl_truth) { + change = false; + break; + } + bool unsat = false; + if (is_false_at(lit, level) && !is_fixed_at(lit, dl_truth)) { + unsat = true; + } + else { + if (is_fixed_at(lit, level)) continue; + unsat = push_lookahead2(lit, level); + } + if (unsat) { + TRACE("sat", tout << "unit: " << ~lit << "\n";); + ++m_stats.m_double_lookahead_propagations; + SASSERT(m_level == dl_truth); + lookahead_backtrack(); + assign(~lit); + propagate(); + change = true; + last_changed = lit; + m_wstack.push_back(~lit); + } + } + base += 2 * m_lookahead.size(); + SASSERT(dl_truth >= base); + } + lookahead_backtrack(); + SASSERT(get_level(m_trail.back()) == dl_truth); + SASSERT(m_level == dl_truth); + base = dl_truth; + return m_trail.size() - old_sz; + } + + /** + \brief check if literal occurs in a non-tautological reduced clause. + */ + bool lookahead::in_reduced_clause(bool_var v) { + return + in_reduced_clause(literal(v, false)) || + in_reduced_clause(literal(v, true)); + } + + bool lookahead::in_reduced_clause(literal lit) { + if (lit == null_literal) return true; + if (m_trail_lim.empty()) return true; + unsigned sz = m_nary_count[lit.index()]; + for (nary* n : m_nary[lit.index()]) { + if (sz-- == 0) break; + if (!n->is_reduced()) continue; + bool has_true = false; + for (literal l : *n) { + if (is_true(l)) { + has_true = true; + break; + } + } + if (!has_true) return true; + } + + auto const& tv = m_ternary[lit.index()]; + sz = tv.size(); + unsigned i = m_ternary_count[lit.index()]; + for (; i < sz; ++i) { + binary const& b = tv[i]; + if (!is_true(b.m_u) && !is_true(b.m_v)) + return true; + } + return false; + } + + void lookahead::validate_assign(literal l) { + if (m_s.m_config.m_drat && m_search_mode == lookahead_mode::searching) { + m_assumptions.push_back(l); + m_drat.add(m_assumptions); + m_assumptions.pop_back(); + } + } + + void lookahead::assign(literal l) { + SASSERT(m_level > 0); + if (is_undef(l)) { + TRACE("sat", tout << "assign: " << l << " @ " << m_level << " " << m_trail_lim.size() << " " << m_search_mode << "\n";); + set_true(l); + SASSERT(m_trail.empty() || get_level(m_trail.back()) >= get_level(l)); + m_trail.push_back(l); + if (m_search_mode == lookahead_mode::searching) { + m_stats.m_propagations++; + TRACE("sat", tout << "removing free var v" << l.var() << "\n";); + if (l.var() > m_freevars.max_var()) IF_VERBOSE(0, verbose_stream() << "bigger than max-var: " << l << " " << " " << m_freevars.max_var() << "\n";); + if (!m_freevars.contains(l.var())) IF_VERBOSE(0, verbose_stream() << "does not contain: " << l << " eliminated: " << m_s.was_eliminated(l.var()) << "\n";); + if (m_freevars.contains(l.var())) { m_freevars.remove(l.var()); } + validate_assign(l); + } + } + else if (is_false(l)) { + TRACE("sat", tout << "conflict: " << l << " @ " << m_level << " " << m_search_mode << "\n";); + SASSERT(!is_true(l)); + validate_assign(l); + set_conflict(); + } + } + + void lookahead::propagated(literal l) { + assign(l); + for (unsigned i = m_trail.size()-1; i < m_trail.size() && !inconsistent(); ++i) { + literal l = m_trail[i]; + TRACE("sat", tout << "propagate " << l << " @ " << m_level << "\n";); + propagate_binary(l); + } + if (m_search_mode == lookahead_mode::lookahead1) { + m_wstack.push_back(l); + } + } + + bool lookahead::backtrack(literal_vector& trail) { + while (inconsistent()) { + if (trail.empty()) return false; + pop(); + flip_prefix(); + assign(~trail.back()); + trail.pop_back(); + propagate(); + } + return true; + } + + lbool lookahead::search() { + m_model.reset(); + scoped_level _sl(*this, c_fixed_truth); + literal_vector trail; + m_search_mode = lookahead_mode::searching; + while (true) { + TRACE("sat", display(tout);); + inc_istamp(); + checkpoint(); + literal l = choose(); + if (inconsistent()) { + if (!backtrack(trail)) return l_false; + continue; + } + if (l == null_literal) { + return l_true; + } + TRACE("sat", tout << "choose: " << l << " " << trail << "\n";); + ++m_stats.m_decisions; + IF_VERBOSE(1, display_search_string();); + push(l, c_fixed_truth); + trail.push_back(l); + SASSERT(inconsistent() || !is_unsat()); + } + } + + bool lookahead::backtrack(literal_vector& trail, svector & is_decision) { + while (inconsistent()) { + if (trail.empty()) return false; + if (is_decision.back()) { + pop(); + trail.back().neg(); + assign(trail.back()); + is_decision.back() = false; + propagate(); + } + else { + trail.pop_back(); + is_decision.pop_back(); + } + } + return true; + } + + void lookahead::update_cube_statistics(statistics& st) { + st.update("lh cube cutoffs", m_cube_state.m_cutoffs); + st.update("lh cube conflicts", m_cube_state.m_conflicts); + } + + double lookahead::psat_heur() { + double h = 0.0; + for (bool_var x : m_freevars) { + literal l(x, false); + for (literal lit : m_binary[l.index()]) { + h += l.index() > lit.index() ? 1.0 / m_config.m_cube_psat_clause_base : 0.0; + } + for (literal lit : m_binary[(~l).index()]) { + h += l.index() > lit.index() ? 1.0 / m_config.m_cube_psat_clause_base : 0.0; + } + for (binary b : m_ternary[l.index()]) { + h += l.index() > b.m_u.index() && l.index() > b.m_v.index() ? + 1.0 / pow(m_config.m_cube_psat_clause_base, 2) : + 0.0; + } + for (binary b : m_ternary[(~l).index()]) { + h += l.index() > b.m_u.index() && l.index() > b.m_v.index() ? + 1.0 / pow(m_config.m_cube_psat_clause_base, 2) : + 0.0; + } + } + for (nary * n : m_nary_clauses) { + h += 1.0 / pow(m_config.m_cube_psat_clause_base, n->size() - 1); + } + h /= pow(m_freevars.size(), m_config.m_cube_psat_var_exp); + IF_VERBOSE(10, verbose_stream() << "(sat-cube-psat :val " << h << ")\n";); + return h; + } + + lbool lookahead::cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level) { + scoped_ext _scoped_ext(*this); + lits.reset(); + bool is_first = m_cube_state.m_first; + if (is_first) { + m_select_lookahead_vars.reset(); + for (auto v : vars) { + m_select_lookahead_vars.insert(v); + } + init_search(); + m_model.reset(); + m_cube_state.m_first = false; + } + scoped_level _sl(*this, c_fixed_truth); + m_search_mode = lookahead_mode::searching; + unsigned depth = 0; + // unsigned init_trail = m_trail.size(); + + m_cube_state.reset_stats(); + if (!is_first) { + goto pick_up_work; + } + + while (true) { + TRACE("sat", display(tout);); + checkpoint(); + inc_istamp(); + if (inconsistent()) { + TRACE("sat", tout << "inconsistent: " << m_cube_state.m_cube << "\n";); + m_cube_state.m_freevars_threshold = m_freevars.size(); + m_cube_state.m_psat_threshold = m_config.m_cube_cutoff == adaptive_psat_cutoff ? psat_heur() : dbl_max; // MN. only compute PSAT if enabled + m_cube_state.inc_conflict(); + if (!backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision)) return l_false; + continue; + } + pick_up_work: + if (m_cube_state.m_cube.size() >= backtrack_level) { + IF_VERBOSE(10, verbose_stream() << "(sat-cube :cube: " << m_cube_state.m_cube.size() << " :backtrack_level " << backtrack_level << ")\n";); + while (m_cube_state.m_cube.size() >= backtrack_level) { + set_conflict(); + backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision); + } + } + 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)) { + 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); + return l_undef; + } + int prev_nfreevars = m_freevars.size(); + double prev_psat = m_config.m_cube_cutoff == adaptive_psat_cutoff ? psat_heur() : dbl_max; // MN. only compute PSAT if enabled + literal lit = choose(); + if (inconsistent()) { + TRACE("sat", tout << "inconsistent: " << m_cube_state.m_cube << "\n";); + m_cube_state.m_freevars_threshold = prev_nfreevars; + m_cube_state.m_psat_threshold = prev_psat; + m_cube_state.inc_conflict(); + if (!backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision)) return l_false; + continue; + } + if (lit == null_literal) { + vars.reset(); + for (auto v : m_freevars) if (in_reduced_clause(v)) vars.push_back(v); + return l_true; + } + TRACE("sat", tout << "choose: " << lit << " cube: " << m_cube_state.m_cube << "\n";); + ++m_stats.m_decisions; + push(lit, c_fixed_truth); + m_cube_state.m_cube.push_back(lit); + m_cube_state.m_is_decision.push_back(true); + SASSERT(inconsistent() || !is_unsat()); + } + m_cube_state.reset(); + return l_undef; + } + + void lookahead::init_model() { + m_model.reset(); + for (unsigned i = 0; i < m_num_vars; ++i) { + lbool val; + literal lit(i, false); + if (is_undef(lit)) { + val = l_undef; + } + if (is_true(lit)) { + val = l_true; + } + else { + val = l_false; + } + m_model.push_back(val); + } + } + + std::ostream& lookahead::display_cube(std::ostream& out, literal_vector const& cube) const { + out << "c"; + for (literal l : cube) { + out << " " << ~l; + } + return out << " 0\n"; + } + + std::ostream& lookahead::display_binary(std::ostream& out) const { + for (unsigned i = 0; i < m_binary.size(); ++i) { + literal_vector const& lits = m_binary[i]; + if (!lits.empty()) { + out << to_literal(i) << " -> " << lits << "\n"; + } + } + return out; + } + + std::ostream& lookahead::display_clauses(std::ostream& out) const { + for (unsigned idx = 0; idx < m_ternary.size(); ++idx) { + literal lit = to_literal(idx); + unsigned sz = m_ternary_count[idx]; + for (binary const& b : m_ternary[idx]) { + if (sz-- == 0) break; + if (idx < b.m_u.index() && idx << b.m_v.index()) { + out << lit << " " << b.m_u << " " << b.m_v << "\n"; + } + } + } + + for (nary * n : m_nary_clauses) { + for (literal l : *n) out << l << " "; + out << "\n"; + } + + return out; + } + + std::ostream& lookahead::display_values(std::ostream& out) const { + for (literal l : m_trail) { + out << l << "\n"; + } + return out; + } + + std::ostream& lookahead::display_lookahead(std::ostream& out) const { + for (unsigned i = 0; i < m_lookahead.size(); ++i) { + literal lit = m_lookahead[i].m_lit; + unsigned offset = m_lookahead[i].m_offset; + out << lit << "\toffset: " << offset; + out << (is_undef(lit)?" undef": (is_true(lit) ? " true": " false")); + out << " lookahead_reward: " << get_lookahead_reward(lit); + out << "\n"; + } + return out; + } + + void lookahead::init_search() { + m_search_mode = lookahead_mode::searching; + scoped_level _sl(*this, c_fixed_truth); + init(m_s.m_config.m_lookahead_use_learned); + } + + void lookahead::checkpoint() { + if (!m_rlimit.inc()) { + throw solver_exception(Z3_CANCELED_MSG); + } + if (memory::get_allocation_size() > m_s.m_config.m_max_memory) { + throw solver_exception(Z3_MAX_MEMORY_MSG); + } + } + + literal lookahead::choose() { + literal l = null_literal; + while (l == null_literal && !inconsistent()) { + pre_select(); + if (m_lookahead.empty()) { + break; + } + compute_lookahead_reward(); + if (inconsistent()) { + break; + } + l = select_literal(); + } + SASSERT(inconsistent() || !is_unsat()); + return l; + } + + /** + \brief simplify set of clauses by extracting units from a lookahead at base level. + */ + void lookahead::simplify(bool learned) { + scoped_ext _scoped_ext(*this); + SASSERT(m_prefix == 0); + SASSERT(m_watches.empty()); + m_search_mode = lookahead_mode::searching; + scoped_level _sl(*this, c_fixed_truth); + init(learned); + if (inconsistent()) return; + inc_istamp(); + choose(); + if (inconsistent()) return; + SASSERT(m_trail_lim.empty()); + unsigned num_units = 0; + + for (unsigned i = 0; i < m_trail.size() && !m_s.inconsistent(); ++i) { + literal lit = m_trail[i]; + if (m_s.value(lit) == l_undef && !m_s.was_eliminated(lit.var())) { + m_s.assign(lit, justification()); + ++num_units; + } + } + IF_VERBOSE(1, verbose_stream() << "(sat-lookahead :units " << num_units << " :propagations " << m_stats.m_propagations << ")\n";); + + if (m_s.inconsistent()) return; + + if (num_units > 0) { + m_s.propagate_core(false); + m_s.m_simplifier(false); + } + + if (select(0)) { + get_scc(); + if (!inconsistent()) { + normalize_parents(); + literal_vector roots; + bool_var_vector to_elim; + for (unsigned i = 0; i < m_num_vars; ++i) { + roots.push_back(literal(i, false)); + } + for (auto const& c : m_candidates) { + bool_var v = c.m_var; + literal q(v, false); + literal p = get_parent(q); + if (p != null_literal && p.var() != v && !m_s.is_external(v) && + !m_s.was_eliminated(v) && !m_s.was_eliminated(p.var())) { + to_elim.push_back(v); + roots[v] = p; + VERIFY(get_parent(p) == p); + VERIFY(get_parent(~p) == ~p); + } + } + IF_VERBOSE(1, verbose_stream() << "(sat-lookahead :equivalences " << to_elim.size() << ")\n";); + elim_eqs elim(m_s); + elim(roots, to_elim); + + if (learned && get_config().m_lookahead_simplify_bca) { + add_hyper_binary(); + } + } + } + m_lookahead.reset(); + } + + + /** + \brief reduction based on binary implication graph + */ + + void lookahead::add_hyper_binary() { + + unsigned num_lits = m_s.num_vars() * 2; + unsigned num_bins = 0; + + typedef std::pair u_pair; + hashtable, default_eq > imp; + for (unsigned idx = 0; idx < num_lits; ++idx) { + literal u = get_parent(to_literal(idx)); + if (null_literal != u) { + for (watched const& w : m_s.m_watches[idx]) { + if (!w.is_binary_clause()) continue; + literal v = get_parent(w.get_literal()); + if (null_literal != v) { + imp.insert(std::make_pair(u.index(), v.index())); + } + } + } + } + + big big(m_s.m_rand); + big.init(m_s, true); + svector> candidates; + + unsigned_vector bin_size(num_lits); + for (unsigned idx : m_binary_trail) { + bin_size[idx]++; + } + for (unsigned idx = 0; idx < num_lits; ++idx) { + literal u = to_literal(idx); + if (u != get_parent(u)) continue; + if (m_s.was_eliminated(u.var())) continue; + literal_vector const& lits = m_binary[idx]; + for (unsigned sz = bin_size[idx]; sz > 0; --sz) { + literal v = lits[lits.size() - sz]; + if (v == get_parent(v) && + !m_s.was_eliminated(v.var()) && + !imp.contains(std::make_pair(u.index(), v.index())) && + !big.connected(u, v)) { + candidates.push_back(std::make_pair(u, v)); + } + } + } + + for (unsigned count = 0; count < 5; ++count) { + big.init(m_s, true); + unsigned k = 0; + union_find_default_ctx ufctx; + union_find uf(ufctx); + for (unsigned i = 0; i < num_lits; ++i) uf.mk_var(); + for (unsigned j = 0; j < candidates.size(); ++j) { + literal u = candidates[j].first; + literal v = candidates[j].second; + if (!big.connected(u, v)) { + if (uf.find(u.index()) != uf.find(v.index())) { + ++num_bins; + uf.merge(u.index(), v.index()); + uf.merge((~u).index(), (~v).index()); + VERIFY(!m_s.was_eliminated(u.var())); + VERIFY(!m_s.was_eliminated(v.var())); + m_s.mk_clause(~u, v, true); + } + else { + candidates[k] = candidates[j]; + ++k; + } + } + } + // std::cout << candidates.size() << " -> " << k << "\n"; + if (k == candidates.size()) break; + candidates.shrink(k); + if (k == 0) break; + } + + IF_VERBOSE(10, verbose_stream() << "(sat-lookahead :bca " << num_bins << ")\n";); + m_stats.m_bca += num_bins; + } + + void lookahead::normalize_parents() { + literal_vector roots; + for (unsigned i = 0; i < m_num_vars; ++i) { + literal lit(i, false); + roots.push_back(lit); + roots.push_back(~lit); + SASSERT(roots[lit.index()] == lit); + } + for (auto const& c : m_candidates) { + bool_var v = c.m_var; + literal p(v, false); + literal q = get_parent(p); + literal r = ~get_parent(~p); + if (q != r) { + if (q.var() < r.var()) { + roots[q.index()] = r; + } + else { + roots[r.index()] = q; + } + } + } + for (auto const& c : m_candidates) { + literal p(c.m_var, false); + literal q = roots[get_parent(p).index()]; + set_parent(p, q); + set_parent(~p, ~q); + } + } + + std::ostream& lookahead::display_summary(std::ostream& out) const { + out << "Prefix: " << pp_prefix(m_prefix, m_trail_lim.size()) << "\n"; + out << "Level: " << m_level << "\n"; + out << "Free vars: " << m_freevars.size() << "\n"; + return out; + } + + std::ostream& lookahead::display(std::ostream& out) const { + display_summary(out); + display_values(out); + display_binary(out); + display_clauses(out); + out << "free vars: "; + for (bool_var b : m_freevars) out << b << " "; + out << "\n"; + clause_allocator dummy_allocator; + for (unsigned i = 0; i < m_watches.size(); ++i) { + watch_list const& wl = m_watches[i]; + if (!wl.empty()) { + sat::display_watch_list(out << to_literal(i) << " -> ", dummy_allocator, wl); + out << "\n"; + } + } + return out; + } + + model const& lookahead::get_model() { + if (m_model.empty()) { + init_model(); + } + return m_model; + } + + void lookahead::init_config() { + m_config.m_reward_type = m_s.m_config.m_lookahead_reward; + m_config.m_cube_cutoff = m_s.m_config.m_lookahead_cube_cutoff; + m_config.m_cube_fraction = m_s.m_config.m_lookahead_cube_fraction; + m_config.m_cube_depth = m_s.m_config.m_lookahead_cube_depth; + m_config.m_cube_freevars = m_s.m_config.m_lookahead_cube_freevars; + m_config.m_cube_psat_var_exp = m_s.m_config.m_lookahead_cube_psat_var_exp; + m_config.m_cube_psat_clause_base = m_s.m_config.m_lookahead_cube_psat_clause_base; + m_config.m_cube_psat_trigger = m_s.m_config.m_lookahead_cube_psat_trigger; + } + + void lookahead::collect_statistics(statistics& st) const { + st.update("lh bool var", m_vprefix.size()); + // TBD: keep count of ternary and >3-ary clauses. + st.update("lh bca", m_stats.m_bca); + st.update("lh add binary", m_stats.m_add_binary); + st.update("lh del binary", m_stats.m_del_binary); + st.update("lh propagations", m_stats.m_propagations); + st.update("lh decisions", m_stats.m_decisions); + st.update("lh windfalls", m_stats.m_windfall_binaries); + st.update("lh double lookahead propagations", m_stats.m_double_lookahead_propagations); + st.update("lh double lookahead rounds", m_stats.m_double_lookahead_rounds); + } + +} diff --git a/src/sat/sat_lookahead.h b/src/sat/sat_lookahead.h new file mode 100644 index 000000000..df954bdf1 --- /dev/null +++ b/src/sat/sat_lookahead.h @@ -0,0 +1,619 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_lookahead.h + +Abstract: + + Lookahead SAT solver in the style of March. + Thanks also to the presentation in sat11.w. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-2-11 + +Notes: + +--*/ +#ifndef _SAT_LOOKAHEAD_H_ +#define _SAT_LOOKAHEAD_H_ + +// #define OLD_NARY 0 + +#include "sat_elim_eqs.h" + +namespace sat { + + struct pp_prefix { + uint64_t m_prefix; + unsigned m_depth; + pp_prefix(uint64_t p, unsigned d) : m_prefix(p), m_depth(d) {} + }; + + inline std::ostream& operator<<(std::ostream& out, pp_prefix const& p) { + unsigned d = std::min(63u, p.m_depth); + for (unsigned i = 0; i <= d; ++i) { + if (0 != (p.m_prefix & (1ull << i))) out << "1"; else out << "0"; + } + if (d < p.m_depth) { + out << " d:" << p.m_depth; + } + return out; + } + + enum lookahead_mode { + searching, // normal search + lookahead1, // lookahead mode + lookahead2 // double lookahead + }; + + inline std::ostream& operator<<(std::ostream& out, lookahead_mode m) { + switch (m) { + case lookahead_mode::searching: return out << "searching"; + case lookahead_mode::lookahead1: return out << "lookahead1"; + case lookahead_mode::lookahead2: return out << "lookahead2"; + default: break; + } + return out; + } + + const double dbl_max = 100000000.0; + + class lookahead { + solver& m_s; + unsigned m_num_vars; + reslimit m_rlimit; + + friend class ccc; + friend class ba_solver; + + struct config { + double m_dl_success; + double m_alpha; + double m_max_score; + unsigned m_max_hlevel; + unsigned m_min_cutoff; + bool m_preselect; + unsigned m_level_cand; + double m_delta_rho; + unsigned m_dl_max_iterations; + unsigned m_tc1_limit; + reward_t m_reward_type; + cutoff_t m_cube_cutoff; + unsigned m_cube_depth; + double m_cube_fraction; + double m_cube_freevars; + double m_cube_psat_var_exp; + double m_cube_psat_clause_base; + double m_cube_psat_trigger; + + config() { + memset(this, 0, sizeof(*this)); + m_dl_success = 0.8; + m_alpha = 3.5; + m_max_score = 20.0; + m_max_hlevel = 50; + m_min_cutoff = 30; + m_preselect = false; + m_level_cand = 600; + m_delta_rho = (double)0.7; + m_dl_max_iterations = 2; + m_tc1_limit = 10000000; + m_reward_type = ternary_reward; + m_cube_cutoff = adaptive_freevars_cutoff; + m_cube_depth = 10; + m_cube_fraction = 0.4; + m_cube_freevars = 0.8; + m_cube_psat_var_exp = 1.0; + m_cube_psat_clause_base = 2.0; + m_cube_psat_trigger = 5.0; + } + }; + + struct prefix { + unsigned m_prefix; + unsigned m_length; + prefix(): m_prefix(0), m_length(0) {} + }; + + struct lit_info { + double m_lookahead_reward; + unsigned m_double_lookahead; + lit_info(): m_lookahead_reward(0), m_double_lookahead(0) {} + }; + + struct stats { + unsigned m_propagations; + unsigned m_bca; + unsigned m_add_binary; + unsigned m_del_binary; + unsigned m_decisions; + unsigned m_windfall_binaries; + unsigned m_double_lookahead_propagations; + unsigned m_double_lookahead_rounds; + stats() { reset(); } + void reset() { memset(this, 0, sizeof(*this)); } + }; + + struct binary { + binary(literal u, literal v): m_u(u), m_v(v) {} + literal m_u, m_v; + }; + + class nary { + unsigned m_size; // number of non-false literals + size_t m_obj_size; // object size (counting all literals) + literal m_head; // head literal + literal m_literals[0]; // list of literals, put any true literal in head. + size_t num_lits() const { + return (m_obj_size - sizeof(nary)) / sizeof(literal); + } + public: + static size_t get_obj_size(unsigned sz) { return sizeof(nary) + sz * sizeof(literal); } + size_t obj_size() const { return m_obj_size; } + nary(unsigned sz, literal const* lits): + m_size(sz), + m_obj_size(get_obj_size(sz)) { + for (unsigned i = 0; i < sz; ++i) m_literals[i] = lits[i]; + m_head = lits[0]; + } + unsigned size() const { return m_size; } + unsigned dec_size() { SASSERT(m_size > 0); return --m_size; } + void inc_size() { SASSERT(is_reduced()); ++m_size; } + literal get_head() const { return m_head; } + void set_head(literal l) { m_head = l; } + bool is_reduced() const { return m_size < num_lits(); } + + literal operator[](unsigned i) { SASSERT(i < num_lits()); return m_literals[i]; } + literal const* begin() const { return m_literals; } + literal const* end() const { return m_literals + num_lits(); } + // swap the true literal to the head. + // void swap(unsigned i, unsigned j) { SASSERT(i < num_lits() && j < num_lits()); std::swap(m_literals[i], m_literals[j]); } + }; + + struct cube_state { + bool m_first; + svector m_is_decision; + literal_vector m_cube; + double m_freevars_threshold; + double m_psat_threshold; + unsigned m_conflicts; + unsigned m_cutoffs; + cube_state() { reset(); } + void reset() { + m_first = true; + m_is_decision.reset(); + m_cube.reset(); + m_freevars_threshold = 0; + m_psat_threshold = dbl_max; + reset_stats(); + } + void reset_stats() { m_conflicts = 0; m_cutoffs = 0; } + void inc_conflict() { ++m_conflicts; } + void inc_cutoff() { ++m_cutoffs; } + }; + + config m_config; + double m_delta_trigger; + double m_delta_decrease; + + drat m_drat; + literal_vector m_assumptions; + + literal_vector m_trail; // trail of units + unsigned_vector m_trail_lim; + vector m_binary; // literal: binary clauses + unsigned_vector m_binary_trail; // trail of added binary clauses + unsigned_vector m_binary_trail_lim; + + // specialized clause managemet uses ternary clauses and dedicated clause data-structure. + // this replaces m_clauses below + vector> m_ternary; // lit |-> vector of ternary clauses + unsigned_vector m_ternary_count; // lit |-> current number of active ternary clauses for lit + + small_object_allocator m_allocator; + vector> m_nary; // lit |-> vector of nary clauses + ptr_vector m_nary_clauses; // vector of all nary clauses + unsigned_vector m_nary_count; // lit |-> number of valid clause_id in m_nary[lit] + + unsigned m_num_tc1; + unsigned_vector m_num_tc1_lim; + unsigned m_qhead; // propagation queue head + unsigned_vector m_qhead_lim; + bool m_inconsistent; + unsigned_vector m_bstamp; // literal: timestamp for binary implication + vector > m_H; // literal: fitness score + svector* m_heur; // current fitness + svector m_rating; // var: pre-selection rating + unsigned m_bstamp_id; // unique id for binary implication. + unsigned m_istamp_id; // unique id for managing double lookaheads + unsigned_vector m_stamp; // var: timestamp with truth value + unsigned m_level; // current level, = 2 * m_trail_lim.size() + const unsigned c_fixed_truth = UINT_MAX - 1; + vector m_watches; // literal: watch structure + svector m_lits; // literal: attributes. + double m_lookahead_reward; // metric associated with current lookahead1 literal. + literal_vector m_wstack; // windofall stack that is populated in lookahead1 mode + unsigned m_last_prefix_length; + uint64_t m_prefix; // where we are in search tree + svector m_vprefix; // var: prefix where variable participates in propagation + unsigned m_rating_throttle; // throttle to recompute rating + indexed_uint_set m_freevars; + unsigned m_init_freevars; + lookahead_mode m_search_mode; // mode of search + stats m_stats; + model m_model; + cube_state m_cube_state; + //scoped_ptr m_ext; + + // --------------------------------------- + // truth values + + + inline bool is_fixed_at(literal l, unsigned level) const { return m_stamp[l.var()] >= level; } + inline bool is_fixed(literal l) const { return is_fixed_at(l, m_level); } + inline bool is_undef(literal l) const { return !is_fixed(l); } + inline bool is_undef(bool_var v) const { return m_stamp[v] < m_level; } + inline bool is_false_at(literal l, unsigned level) const { + return is_fixed_at(l, level) && (bool)((m_stamp[l.var()] & 0x1) ^ l.sign()); + } // even iff l.sign() + inline bool is_false(literal l) const { return is_false_at(l, m_level); } + inline bool is_true_at(literal l, unsigned level) const { + return is_fixed_at(l, level) && !(bool)((m_stamp[l.var()] & 0x1) ^ l.sign()); + } + inline bool is_true(literal l) const { return is_true_at(l, m_level); } + inline void set_true(literal l) { m_stamp[l.var()] = m_level + l.sign(); } + inline void set_undef(literal l) { m_stamp[l.var()] = 0; } + inline unsigned get_level(literal l) const { return m_stamp[l.var()] & ~0x1; } + void set_level(literal d, literal s) { m_stamp[d.var()] = get_level(s) + d.sign(); } + lbool value(literal l) const { return is_undef(l) ? l_undef : is_true(l) ? l_true : l_false; } + + // set the level within a scope of the search. + class scoped_level { + lookahead& m_parent; + unsigned m_save; + public: + scoped_level(lookahead& p, unsigned l): + m_parent(p), m_save(p.m_level) { + p.m_level = l; + } + ~scoped_level() { + m_parent.m_level = m_save; + } + }; + + class scoped_ext { + lookahead& p; + public: + scoped_ext(lookahead& p); + ~scoped_ext(); + }; + + class scoped_assumptions { + lookahead& p; + literal_vector lits; + public: + scoped_assumptions(lookahead& p, literal_vector const& lits); + ~scoped_assumptions(); + }; + + // ------------------------------------- + // prefix updates. I use low order bits. + + void flip_prefix(); + void prune_prefix(); + + /** + length < trail_lim.size: + - mask m_prefix and p wrt length + - update if different. + */ + void update_prefix(literal l); + + bool active_prefix(bool_var x); + + // ---------------------------------------- + + void add_binary(literal l1, literal l2); + void del_binary(unsigned idx); + void validate_binary(literal l1, literal l2); + + // ------------------------------------- + // track consequences of binary clauses + // see also 72 - 79 in sat11.w + + void inc_bstamp(); + void inc_istamp(); + void set_bstamp(literal l) { m_bstamp[l.index()] = m_bstamp_id; } + void set_bstamps(literal l); + bool is_stamped(literal l) const { return m_bstamp[l.index()] == m_bstamp_id; } + bool add_tc1(literal u, literal v); + + /** + \brief main routine for adding a new binary clause dynamically. + */ + void try_add_binary(literal u, literal v); + + // ------------------------------------- + // pre-selection + // see also 91 - 102 sat11.w + + void pre_select(); + + struct candidate { + bool_var m_var; + double m_rating; + candidate(bool_var v, double r): m_var(v), m_rating(r) {} + }; + svector m_candidates; + uint_set m_select_lookahead_vars; + + double get_rating(bool_var v) const { return m_rating[v]; } + double get_rating(literal l) const { return get_rating(l.var()); } + bool select(unsigned level); + void heap_sort(); + void heapify(); + void sift_down(unsigned j, unsigned sz); + bool validate_heap_sort(); + double init_candidates(unsigned level, bool newbies); + std::ostream& display_candidates(std::ostream& out) const; + bool is_unsat() const; + bool is_sat() const; + bool missed_propagation() const; + bool missed_conflict() const; + void init_pre_selection(unsigned level); + void ensure_H(unsigned level); + void h_scores(svector& h, svector& hp); + void heule_schur_scores(); + double heule_schur_score(literal l); + void heule_unit_scores(); + double heule_unit_score(literal l); + void march_cu_scores(); + double march_cu_score(literal l); + double l_score(literal l, svector const& h, double factor, double sqfactor, double afactor); + + // ------------------------------------ + // Implication graph + // Compute implication ordering and strongly connected components. + // sat11.w 103 - 114. + + struct arcs : public literal_vector {}; + // Knuth uses a shared pool of fixed size for arcs. + // Should it be useful we could use this approach too + // by changing the arcs abstraction and associated functions. + + struct dfs_info { + unsigned m_rank; + unsigned m_height; + literal m_parent; + arcs m_next; + unsigned m_nextp; + literal m_link; + literal m_min; + literal m_vcomp; + dfs_info() { reset(); } + void reset() { + m_rank = 0; + m_height = 0; + m_parent = null_literal; + m_next.reset(); + m_link = null_literal; + m_min = null_literal; + m_vcomp = null_literal; + m_nextp = 0; + } + }; + + literal m_active; + unsigned m_rank; + unsigned m_rank_max; + literal m_settled; + vector m_dfs; + + void get_scc(); + void init_scc(); + void init_dfs_info(literal l); + void init_arcs(literal l); + void add_arc(literal u, literal v); + bool has_arc(literal v) const { return m_dfs[v.index()].m_next.size() > m_dfs[v.index()].m_nextp; } + arcs get_arcs(literal v) const { return m_dfs[v.index()].m_next; } + literal pop_arc(literal u) { return m_dfs[u.index()].m_next[m_dfs[u.index()].m_nextp++]; } + unsigned num_next(literal u) const { return m_dfs[u.index()].m_next.size(); } + literal get_next(literal u, unsigned i) const { return m_dfs[u.index()].m_next[i]; } + literal get_min(literal v) const { return m_dfs[v.index()].m_min; } + unsigned get_rank(literal v) const { return m_dfs[v.index()].m_rank; } + bool maxed_rank(literal v) const { return get_rank(v) >= m_rank_max; } + unsigned get_height(literal v) const { return m_dfs[v.index()].m_height; } + literal get_parent(literal u) const { return m_dfs[u.index()].m_parent; } + literal get_link(literal u) const { return m_dfs[u.index()].m_link; } + literal get_vcomp(literal u) const { return m_dfs[u.index()].m_vcomp; } + void set_link(literal v, literal u) { m_dfs[v.index()].m_link = u; } + void set_min(literal v, literal u) { m_dfs[v.index()].m_min = u; } + void set_rank(literal v, unsigned r) { m_dfs[v.index()].m_rank = r; } + void set_height(literal v, unsigned h) { m_dfs[v.index()].m_height = h; } + void set_parent(literal v, literal p) { TRACE("scc", tout << v << " <- " << p << "\n";); m_dfs[v.index()].m_parent = p; } + void set_vcomp(literal v, literal u) { m_dfs[v.index()].m_vcomp = u; } + void get_scc(literal v); + void activate_scc(literal l); + void found_scc(literal v); + std::ostream& display_dfs(std::ostream& out) const; + std::ostream& display_dfs(std::ostream& out, literal l) const; + std::ostream& display_scc(std::ostream& out) const; + std::ostream& display_scc(std::ostream& out, literal l) const; + + + // ------------------------------------ + // lookahead forest + // sat11.w 115-121 + + literal m_root_child; + + literal get_child(literal u) const; + void set_child(literal v, literal u); + void find_heights(); + std::ostream& display_forest(std::ostream& out, literal l); + + struct literal_offset { + literal m_lit; + unsigned m_offset; + literal_offset(literal l): m_lit(l), m_offset(0) {} + }; + svector m_lookahead; + void set_offset(unsigned idx, unsigned offset) { m_lookahead[idx].m_offset = offset; } + void set_lookahead(literal l) { m_lookahead.push_back(literal_offset(l)); } + void construct_lookahead_table(); + + // ------------------------------------ + // clause management + + watch_list& get_wlist(literal l) { return m_watches[l.index()]; } + watch_list const& get_wlist(literal l) const { return m_watches[l.index()]; } + + // new clause managment: + void add_ternary(literal u, literal v, literal w); + void propagate_ternary(literal l); + lbool propagate_ternary(literal l1, literal l2); + void remove_ternary(literal l, literal u, literal v); + void restore_ternary(literal l); + + void propagate_external(literal l); + void add_clause(clause const& c); + void propagate_clauses_searching(literal l); + void propagate_clauses_lookahead(literal l); + void restore_clauses(literal l); + void remove_clause(literal l, nary& n); + void remove_clause_at(literal l, nary& n); + // ------------------------------------ + // initialization + + void init_var(bool_var v); + void init(bool learned); + void copy_clauses(clause_vector const& clauses, bool learned); + nary * copy_clause(clause const& c); + + // ------------------------------------ + // search + + void push(literal lit, unsigned level); + void pop(); + bool push_lookahead2(literal lit, unsigned level); + unsigned push_lookahead1(literal lit, unsigned level); + void pop_lookahead1(literal lit, unsigned num_units); + void lookahead_backtrack(); + double mix_diff(double l, double r) const; + clause const& get_clause(watch_list::iterator it) const; + bool is_nary_propagation(clause const& c, literal l) const; + void propagate_clauses(literal l); + void propagate_binary(literal l); + void propagate(); + literal choose(); + void compute_lookahead_reward(); + literal select_literal(); + void update_binary_clause_reward(literal l1, literal l2); + void update_nary_clause_reward(clause const& c); + + void set_lookahead_reward(literal l, double f) { m_lits[l.index()].m_lookahead_reward = f; } + void inc_lookahead_reward(literal l, double f) { m_lits[l.index()].m_lookahead_reward += f; } + double get_lookahead_reward(literal l) const { return m_lits[l.index()].m_lookahead_reward; } + + void reset_lookahead_reward(literal l); + void update_lookahead_reward(literal l, unsigned level); + bool dl_enabled(literal l) const { return m_lits[l.index()].m_double_lookahead != m_istamp_id; } + void dl_disable(literal l) { m_lits[l.index()].m_double_lookahead = m_istamp_id; } + bool dl_no_overflow(unsigned base) const { return base + 2 * m_lookahead.size() * static_cast(m_config.m_dl_max_iterations + 1) < c_fixed_truth; } + + unsigned do_double(literal l, unsigned& base); + unsigned double_look(literal l, unsigned& base); + void set_conflict() { TRACE("sat", tout << "conflict\n";); m_inconsistent = true; } + bool inconsistent() const { return m_inconsistent; } + + unsigned scope_lvl() const { return m_trail_lim.size(); } + + bool in_reduced_clause(literal l); + bool in_reduced_clause(bool_var v); + void validate_assign(literal l); + void assign(literal l); + void propagated(literal l); + bool backtrack(literal_vector& trail); + bool backtrack(literal_vector& trail, svector & is_decision); + lbool search(); + void init_model(); + std::ostream& display_binary(std::ostream& out) const; + std::ostream& display_clauses(std::ostream& out) const; + std::ostream& display_values(std::ostream& out) const; + std::ostream& display_lookahead(std::ostream& out) const; + std::ostream& display_cube(std::ostream& out, literal_vector const& cube) const; + void display_search_string(); + + void init_search(); + void checkpoint(); + + void init_config(); + + void normalize_parents(); + + void add_hyper_binary(); + + double psat_heur(); + + public: + lookahead(solver& s) : + m_s(s), + m_num_vars(s.num_vars()), + m_drat(s), + m_num_tc1(0), + m_level(2), + m_last_prefix_length(0), + m_prefix(0), + m_rating_throttle(0) { + m_s.rlimit().push_child(&m_rlimit); + init_config(); + } + + ~lookahead() { + m_s.rlimit().pop_child(); + for (nary* n : m_nary_clauses) { + m_allocator.deallocate(n->obj_size(), n); + } + } + + + lbool check() { + init_search(); + return search(); + } + + /** + \brief create cubes to std-out in DIMACS form. + The cubes are controlled using cut-depth and cut-fraction parameters. + If cut-depth != 0, then it is used to control thedepth of cuts. + Otherwise, cut-fraction gives an adaptive threshold for creating cuts. + */ + + lbool cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level); + + void update_cube_statistics(statistics& st); + + /** + \brief simplify set of clauses by extracting units from a lookahead at base level. + */ + void simplify(bool learned); + + std::ostream& display(std::ostream& out) const; + std::ostream& display_summary(std::ostream& out) const; + model const& get_model(); + + void collect_statistics(statistics& st) const; + + double literal_occs(literal l); + double literal_big_occs(literal l); + + sat::config const& get_config() const { return m_s.get_config(); } + + }; +} + +#endif + diff --git a/src/sat/sat_model_converter.cpp b/src/sat/sat_model_converter.cpp index 1a881618a..d132f1cd4 100644 --- a/src/sat/sat_model_converter.cpp +++ b/src/sat/sat_model_converter.cpp @@ -18,11 +18,12 @@ Revision History: --*/ #include "sat/sat_model_converter.h" #include "sat/sat_clause.h" +#include "sat/sat_solver.h" #include "util/trace.h" namespace sat { - model_converter::model_converter() { + model_converter::model_converter(): m_solver(nullptr) { } model_converter::~model_converter() { @@ -33,50 +34,117 @@ namespace sat { m_entries.finalize(); } + model_converter& model_converter::operator=(model_converter const& other) { + copy(other); + return *this; + } + + bool model_converter::legal_to_flip(bool_var v) const { + // std::cout << "check " << v << " " << m_solver << "\n"; + if (m_solver && m_solver->is_assumption(v)) { + std::cout << "flipping assumption v" << v << "\n"; + UNREACHABLE(); + throw solver_exception("flipping assumption"); + } + if (m_solver && m_solver->is_external(v) && m_solver->is_incremental()) { + std::cout << "flipping external v" << v << "\n"; + UNREACHABLE(); + throw solver_exception("flipping external"); + } + return !m_solver || !m_solver->is_assumption(v); + } + + + void model_converter::process_stack(model & m, literal_vector const& c, elim_stackv const& stack) const { + SASSERT(!stack.empty()); + unsigned sz = stack.size(); + for (unsigned i = sz; i-- > 0; ) { + unsigned csz = stack[i].first; + literal lit = stack[i].second; + bool sat = false; + for (unsigned j = 0; !sat && j < csz; ++j) { + sat = value_at(c[j], m) == l_true; + } + if (!sat) { + VERIFY(legal_to_flip(lit.var())); + m[lit.var()] = lit.sign() ? l_false : l_true; + } + } + } + void model_converter::operator()(model & m) const { vector::const_iterator begin = m_entries.begin(); vector::const_iterator it = m_entries.end(); + bool first = false; // true; // false; // // true; + //SASSERT(!m_solver || m_solver->check_clauses(m)); while (it != begin) { --it; - SASSERT(it->get_kind() != ELIM_VAR || m[it->var()] == l_undef); - // if it->get_kind() == BLOCK_LIT, then it might be the case that m[it->var()] != l_undef, + bool_var v0 = it->var(); + SASSERT(it->get_kind() != ELIM_VAR || v0 == null_bool_var || m[v0] == l_undef); + // if it->get_kind() == BCE, then it might be the case that m[v] != l_undef, // and the following procedure flips its value. bool sat = false; bool var_sign = false; - literal_vector::const_iterator it2 = it->m_clauses.begin(); - literal_vector::const_iterator end2 = it->m_clauses.end(); - for (; it2 != end2; ++it2) { - literal l = *it2; + unsigned index = 0; + literal_vector clause; + VERIFY(v0 == null_bool_var || legal_to_flip(v0)); + for (literal l : it->m_clauses) { if (l == null_literal) { // end of clause - if (!sat) { - m[it->var()] = var_sign ? l_false : l_true; - break; + if (!sat && it->get_kind() == ATE) { + IF_VERBOSE(0, display(verbose_stream() << "violated ate\n", *it) << "\n"); + IF_VERBOSE(0, for (unsigned v = 0; v < m.size(); ++v) verbose_stream() << v << " := " << m[v] << "\n";); + IF_VERBOSE(0, display(verbose_stream())); + exit(0); + first = false; + } + if (!sat && it->get_kind() != ATE && v0 != null_bool_var) { + VERIFY(legal_to_flip(v0)); + m[v0] = var_sign ? l_false : l_true; + //IF_VERBOSE(0, verbose_stream() << "assign " << v0 << " "<< m[v0] << "\n"); + } + elim_stack* st = it->m_elim_stack[index]; + if (st) { + process_stack(m, clause, st->stack()); } sat = false; - continue; + if (first && m_solver && !m_solver->check_clauses(m)) { + IF_VERBOSE(0, display(verbose_stream() << "after processing stack\n", *it) << "\n"); + IF_VERBOSE(0, display(verbose_stream())); + first = false; + } + ++index; + clause.reset(); + continue; } + clause.push_back(l); if (sat) continue; bool sign = l.sign(); bool_var v = l.var(); - if (v == it->var()) + if (v >= m.size()) std::cout << v << " model size: " << m.size() << "\n"; + VERIFY(v < m.size()); + if (v == v0) var_sign = sign; if (value_at(l, m) == l_true) sat = true; - else if (!sat && v != it->var() && m[v] == l_undef) { + else if (!sat && v != v0 && m[v] == l_undef) { + VERIFY(legal_to_flip(v)); // clause can be satisfied by assigning v. m[v] = sign ? l_false : l_true; + // if (first) std::cout << "set: " << l << "\n"; sat = true; + if (first && m_solver && !m_solver->check_clauses(m)) { + IF_VERBOSE(0, display(verbose_stream() << "after flipping undef\n", *it) << "\n"); + first = false; + } } } DEBUG_CODE({ // all clauses must be satisfied bool sat = false; - it2 = it->m_clauses.begin(); - for (; it2 != end2; ++it2) { - literal l = *it2; + for (literal l : it->m_clauses) { if (l == null_literal) { SASSERT(sat); sat = false; @@ -132,18 +200,43 @@ namespace sat { entry & e = m_entries.back(); SASSERT(e.var() == v); SASSERT(e.get_kind() == k); + VERIFY(v == null_bool_var || legal_to_flip(v)); return e; } + void model_converter::add_ate(clause const& c) { + if (stackv().empty()) return; + insert(mk(ATE, null_bool_var), c); + } + + void model_converter::add_ate(literal_vector const& lits) { + if (stackv().empty()) return; + insert(mk(ATE, null_bool_var), lits); + } + + void model_converter::add_ate(literal l1, literal l2) { + if (stackv().empty()) return; + insert(mk(ATE, null_bool_var), l1, l2); + } + + void model_converter::add_elim_stack(entry & e) { + e.m_elim_stack.push_back(stackv().empty() ? nullptr : alloc(elim_stack, stackv())); +#if 0 + if (!stackv().empty() && e.get_kind() == ATE) { + IF_VERBOSE(0, display(verbose_stream(), e) << "\n"); + } +#endif + for (auto const& s : stackv()) VERIFY(legal_to_flip(s.second.var())); + stackv().reset(); + } + void model_converter::insert(entry & e, clause const & c) { SASSERT(c.contains(e.var())); SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - e.m_clauses.push_back(c[i]); - } + for (literal l : c) e.m_clauses.push_back(l); e.m_clauses.push_back(null_literal); + add_elim_stack(e); TRACE("sat_mc_bug", tout << "adding: " << c << "\n";); } @@ -154,6 +247,7 @@ namespace sat { e.m_clauses.push_back(l1); e.m_clauses.push_back(l2); e.m_clauses.push_back(null_literal); + add_elim_stack(e); TRACE("sat_mc_bug", tout << "adding (binary): " << l1 << " " << l2 << "\n";); } @@ -162,30 +256,42 @@ namespace sat { SASSERT(m_entries.begin() <= &e); SASSERT(&e < m_entries.end()); unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) + for (unsigned i = 0; i < sz; ++i) e.m_clauses.push_back(c[i]); e.m_clauses.push_back(null_literal); - TRACE("sat_mc_bug", tout << "adding (wrapper): "; for (unsigned i = 0; i < c.size(); i++) tout << c[i] << " "; tout << "\n";); + add_elim_stack(e); + // TRACE("sat_mc_bug", tout << "adding (wrapper): "; for (literal l : c) tout << l << " "; tout << "\n";); } + void model_converter::insert(entry & e, literal_vector const& c) { + SASSERT(c.contains(literal(e.var(), false)) || c.contains(literal(e.var(), true))); + SASSERT(m_entries.begin() <= &e); + SASSERT(&e < m_entries.end()); + for (literal l : c) e.m_clauses.push_back(l); + e.m_clauses.push_back(null_literal); + add_elim_stack(e); + TRACE("sat_mc_bug", tout << "adding: " << c << "\n";); + } + + bool model_converter::check_invariant(unsigned num_vars) const { // After a variable v occurs in an entry n and the entry has kind ELIM_VAR, // then the variable must not occur in any other entry occurring after it. vector::const_iterator it = m_entries.begin(); vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { - SASSERT(it->var() < num_vars); + SASSERT(it->var() == null_bool_var || it->var() < num_vars); if (it->get_kind() == ELIM_VAR) { svector::const_iterator it2 = it; it2++; for (; it2 != end; ++it2) { SASSERT(it2->var() != it->var()); - literal_vector::const_iterator it3 = it2->m_clauses.begin(); - literal_vector::const_iterator end3 = it2->m_clauses.end(); - for (; it3 != end3; ++it3) { - CTRACE("sat_model_converter", it3->var() == it->var(), tout << "var: " << it->var() << "\n"; display(tout);); - SASSERT(it3->var() != it->var()); - SASSERT(*it3 == null_literal || it3->var() < num_vars); + if (it2->var() == it->var()) return false; + for (literal l : it2->m_clauses) { + CTRACE("sat_model_converter", l.var() == it->var(), tout << "var: " << it->var() << "\n"; display(tout);); + SASSERT(l.var() != it->var()); + VERIFY(l == null_literal || l.var() < num_vars); + if (it2->var() == it->var()) return false; } } } @@ -194,61 +300,75 @@ namespace sat { } void model_converter::display(std::ostream & out) const { - out << "(sat::model-converter"; - vector::const_iterator it = m_entries.begin(); - vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - out << "\n (" << (it->get_kind() == ELIM_VAR ? "elim" : "blocked") << " " << it->var(); - bool start = true; - literal_vector::const_iterator it2 = it->m_clauses.begin(); - literal_vector::const_iterator end2 = it->m_clauses.end(); - for (; it2 != end2; ++it2) { - if (start) { - out << "\n ("; - start = false; - } - else { - if (*it2 != null_literal) - out << " "; - } - if (*it2 == null_literal) { - out << ")"; - start = true; - continue; - } - out << *it2; - } - out << ")"; + out << "(sat::model-converter\n"; + bool first = true; + for (auto & entry : m_entries) { + if (first) first = false; else out << "\n"; + display(out, entry); } out << ")\n"; } + std::ostream& model_converter::display(std::ostream& out, entry const& entry) const { + out << " (" << entry.get_kind() << " "; + if (entry.var() != null_bool_var) out << entry.var(); + bool start = true; + unsigned index = 0; + for (literal l : entry.m_clauses) { + if (start) { + out << "\n ("; + start = false; + } + else { + if (l != null_literal) + out << " "; + } + if (l == null_literal) { + out << ")"; + start = true; + elim_stack* st = entry.m_elim_stack[index]; + if (st) { + elim_stackv const& stack = st->stack(); + unsigned sz = stack.size(); + for (unsigned i = sz; i-- > 0; ) { + out << "\n " << stack[i].first << " " << stack[i].second; + } + } + ++index; + continue; + } + out << l; + } + out << ")"; + for (literal l : entry.m_clauses) { + if (l != null_literal && l.var() != null_bool_var) { + if (false && m_solver && m_solver->was_eliminated(l.var())) out << "\neliminated: " << l; + } + } + return out; + } + void model_converter::copy(model_converter const & src) { - vector::const_iterator it = src.m_entries.begin(); - vector::const_iterator end = src.m_entries.end(); - for (; it != end; ++it) - m_entries.push_back(*it); + reset(); + m_entries.append(src.m_entries); + } + + void model_converter::flush(model_converter & src) { + VERIFY(this != &src); + m_entries.append(src.m_entries); + src.m_entries.reset(); } void model_converter::collect_vars(bool_var_set & s) const { - vector::const_iterator it = m_entries.begin(); - vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - s.insert(it->m_var); - } + for (entry const & e : m_entries) s.insert(e.m_var); } unsigned model_converter::max_var(unsigned min) const { unsigned result = min; - vector::const_iterator it = m_entries.begin(); - vector::const_iterator end = m_entries.end(); - for (; it != end; ++it) { - literal_vector::const_iterator lvit = it->m_clauses.begin(); - literal_vector::const_iterator lvend = it->m_clauses.end(); - for (; lvit != lvend; ++lvit) { - literal l = *lvit; + for (entry const& e : m_entries) { + for (literal l : e.m_clauses) { if (l != null_literal) { - if (l.var() > result) + if (l.var() != null_bool_var && l.var() > result) result = l.var(); } } @@ -256,4 +376,48 @@ namespace sat { return result; } + void model_converter::swap(bool_var v, unsigned sz, literal_vector& clause) { + for (unsigned j = 0; j < sz; ++j) { + if (v == clause[j].var()) { + std::swap(clause[0], clause[j]); + return; + } + } + IF_VERBOSE(0, verbose_stream() << "not found: v" << v << " " << clause << "\n";); + UNREACHABLE(); + } + + void model_converter::expand(literal_vector& update_stack) { + sat::literal_vector clause; + for (entry const& e : m_entries) { + unsigned index = 0; + clause.reset(); + for (literal l : e.m_clauses) { + if (l == null_literal) { + elim_stack* st = e.m_elim_stack[index]; + if (st) { + // clause sizes increase, so we can always swap + // the blocked literal to the front from the prefix. + for (auto const& p : st->stack()) { + unsigned csz = p.first; + literal lit = p.second; + swap(lit.var(), csz, clause); + update_stack.append(csz, clause.c_ptr()); + update_stack.push_back(null_literal); + } + } + if (e.var() != null_bool_var) { + swap(e.var(), clause.size(), clause); + update_stack.append(clause); + update_stack.push_back(null_literal); + } + clause.reset(); + } + else { + clause.push_back(l); + } + } + } + } + }; diff --git a/src/sat/sat_model_converter.h b/src/sat/sat_model_converter.h index 9a5ebc0ff..65e132729 100644 --- a/src/sat/sat_model_converter.h +++ b/src/sat/sat_model_converter.h @@ -20,6 +20,7 @@ Revision History: #define SAT_MODEL_CONVERTER_H_ #include "sat/sat_types.h" +#include "util/ref_vector.h" namespace sat { /** @@ -35,35 +36,87 @@ namespace sat { This is a low-level model_converter. Given a mapping from bool_var to expr, it can be converted into a general Z3 model_converter */ + + class solver; + + static unsigned counter = 0; + class model_converter { + public: - enum kind { ELIM_VAR = 0, BLOCK_LIT }; + typedef svector> elim_stackv; + + + class elim_stack { + unsigned m_counter; + unsigned m_refcount; + elim_stackv m_stack; + elim_stack(elim_stack const& ); + public: + elim_stack(elim_stackv const& stack): + m_counter(0), + m_refcount(0), + m_stack(stack) { + m_counter = ++counter; + } + ~elim_stack() { } + void inc_ref() { ++m_refcount; } + void dec_ref() { if (0 == --m_refcount) { dealloc(this); } } + elim_stackv const& stack() const { return m_stack; } + unsigned ref_count() const { return m_refcount; } + }; + + enum kind { ELIM_VAR = 0, BCE, CCE, ACCE, ABCE, ATE }; class entry { friend class model_converter; - unsigned m_var:31; - unsigned m_kind:1; - literal_vector m_clauses; // the different clauses are separated by null_literal - entry(kind k, bool_var v):m_var(v), m_kind(k) {} + bool_var m_var; + kind m_kind; + literal_vector m_clauses; // the different clauses are separated by null_literal + sref_vector m_elim_stack; + entry(kind k, bool_var v): m_var(v), m_kind(k) {} public: entry(entry const & src): m_var(src.m_var), m_kind(src.m_kind), m_clauses(src.m_clauses) { + m_elim_stack.append(src.m_elim_stack); } bool_var var() const { return m_var; } - kind get_kind() const { return static_cast(m_kind); } + kind get_kind() const { return m_kind; } }; private: vector m_entries; + solver const* m_solver; + elim_stackv m_elim_stack; + + void process_stack(model & m, literal_vector const& clause, elim_stackv const& stack) const; + + std::ostream& display(std::ostream & out, entry const& entry) const; + + bool legal_to_flip(bool_var v) const; + + void swap(bool_var v, unsigned sz, literal_vector& clause); + + void add_elim_stack(entry & e); + public: model_converter(); ~model_converter(); + void set_solver(solver const* s) { m_solver = s; } void operator()(model & m) const; + model_converter& operator=(model_converter const& other); + + elim_stackv& stackv() { return m_elim_stack; } entry & mk(kind k, bool_var v); void insert(entry & e, clause const & c); void insert(entry & e, literal l1, literal l2); void insert(entry & e, clause_wrapper const & c); + void insert(entry & c, literal_vector const& covered_clause); + + void add_ate(literal_vector const& lits); + void add_ate(literal l1, literal l2); + void add_ate(clause const& c); bool empty() const { return m_entries.empty(); } @@ -71,12 +124,37 @@ namespace sat { bool check_invariant(unsigned num_vars) const; void display(std::ostream & out) const; bool check_model(model const & m) const; - void copy(model_converter const & src); + + /* + \brief Append entries from src, then remove entries in src. + */ + void flush(model_converter& src); void collect_vars(bool_var_set & s) const; unsigned max_var(unsigned min) const; + /* + * \brief expand entries to a list of clauses, such that + * the first literal in each clause is the literal whose + * truth value is updated as follows: + * + * lit0 := lit0 or (~lit1 & ~lit2 & ... & ~lit_k) + * + */ + void expand(literal_vector& update_stack); }; + inline std::ostream& operator<<(std::ostream& out, model_converter::kind k) { + switch (k) { + case model_converter::ELIM_VAR: out << "elim"; break; + case model_converter::BCE: out << "bce"; break; + case model_converter::CCE: out << "cce"; break; + case model_converter::ACCE: out << "acce"; break; + case model_converter::ABCE: out << "abce"; break; + case model_converter::ATE: out << "ate"; break; + } + return out; + } + }; #endif diff --git a/src/sat/sat_parallel.cpp b/src/sat/sat_parallel.cpp new file mode 100644 index 000000000..c6e29f64c --- /dev/null +++ b/src/sat/sat_parallel.cpp @@ -0,0 +1,315 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_parallel.cpp + + Abstract: + + Utilities for parallel SAT solving. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-1-29. + +Revision History: + +--*/ +#include "sat_parallel.h" +#include "sat_clause.h" +#include "sat_solver.h" + + +namespace sat { + + void parallel::vector_pool::next(unsigned& index) { + SASSERT(index < m_size); + unsigned n = index + 2 + get_length(index); + if (n >= m_size) { + index = 0; + } + else { + index = n; + } + } + + void parallel::vector_pool::reserve(unsigned num_threads, unsigned sz) { + m_vectors.reset(); + m_vectors.resize(sz, 0); + m_heads.reset(); + m_heads.resize(num_threads, 0); + m_at_end.reset(); + m_at_end.resize(num_threads, true); + m_tail = 0; + m_size = sz; + } + + void parallel::vector_pool::begin_add_vector(unsigned owner, unsigned n) { + SASSERT(m_tail < m_size); + unsigned capacity = n + 2; + m_vectors.reserve(m_size + capacity, 0); + IF_VERBOSE(3, verbose_stream() << owner << ": begin-add " << n << " tail: " << m_tail << " size: " << m_size << "\n";); + for (unsigned i = 0; i < m_heads.size(); ++i) { + while (m_tail < m_heads[i] && m_heads[i] < m_tail + capacity) { + next(m_heads[i]); + } + m_at_end[i] = false; + } + m_vectors[m_tail++] = owner; + m_vectors[m_tail++] = n; + } + + void parallel::vector_pool::add_vector_elem(unsigned e) { + m_vectors[m_tail++] = e; + } + + void parallel::vector_pool::end_add_vector() { + if (m_tail >= m_size) { + m_tail = 0; + } + } + + + bool parallel::vector_pool::get_vector(unsigned owner, unsigned& n, unsigned const*& ptr) { + unsigned head = m_heads[owner]; + unsigned iterations = 0; + while (head != m_tail || !m_at_end[owner]) { + ++iterations; + SASSERT(head < m_size && m_tail < m_size); + bool is_self = owner == get_owner(head); + next(m_heads[owner]); + IF_VERBOSE(static_cast(iterations > m_size ? 0 : 3), verbose_stream() << owner << ": [" << head << ":" << m_heads[owner] << "] tail: " << m_tail << "\n";); + m_at_end[owner] = (m_heads[owner] == m_tail); + if (!is_self) { + n = get_length(head); + ptr = get_ptr(head); + return true; + } + head = m_heads[owner]; + } + return false; + } + + parallel::parallel(solver& s): m_num_clauses(0), m_consumer_ready(false), m_scoped_rlimit(s.rlimit()) {} + + parallel::~parallel() { + for (unsigned i = 0; i < m_solvers.size(); ++i) { + dealloc(m_solvers[i]); + } + } + + void parallel::init_solvers(solver& s, unsigned num_extra_solvers) { + unsigned num_threads = num_extra_solvers + 1; + m_solvers.resize(num_extra_solvers); + symbol saved_phase = s.m_params.get_sym("phase", symbol("caching")); + for (unsigned i = 0; i < num_extra_solvers; ++i) { + m_limits.push_back(reslimit()); + } + + for (unsigned i = 0; i < num_extra_solvers; ++i) { + s.m_params.set_uint("random_seed", s.m_rand()); + if (i == 1 + num_threads/2) { + s.m_params.set_sym("phase", symbol("random")); + } + m_solvers[i] = alloc(sat::solver, s.m_params, m_limits[i]); + m_solvers[i]->copy(s); + m_solvers[i]->set_par(this, i); + push_child(m_solvers[i]->rlimit()); + } + s.set_par(this, num_extra_solvers); + s.m_params.set_sym("phase", saved_phase); + } + + void parallel::push_child(reslimit& rl) { + m_scoped_rlimit.push_child(&rl); + } + + + void parallel::exchange(solver& s, literal_vector const& in, unsigned& limit, literal_vector& out) { + if (s.get_config().m_num_threads == 1 || s.m_par_syncing_clauses) return; + flet _disable_sync_clause(s.m_par_syncing_clauses, true); + #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(); + } + } + + void parallel::share_clause(solver& s, literal l1, literal l2) { + if (s.get_config().m_num_threads == 1 || s.m_par_syncing_clauses) return; + flet _disable_sync_clause(s.m_par_syncing_clauses, true); + IF_VERBOSE(3, verbose_stream() << s.m_par_id << ": share " << l1 << " " << l2 << "\n";); + #pragma omp critical (par_solver) + { + m_pool.begin_add_vector(s.m_par_id, 2); + m_pool.add_vector_elem(l1.index()); + m_pool.add_vector_elem(l2.index()); + m_pool.end_add_vector(); + } + } + + void parallel::share_clause(solver& s, clause const& c) { + if (s.get_config().m_num_threads == 1 || !enable_add(c) || s.m_par_syncing_clauses) return; + flet _disable_sync_clause(s.m_par_syncing_clauses, true); + unsigned n = c.size(); + unsigned owner = s.m_par_id; + IF_VERBOSE(3, verbose_stream() << owner << ": share " << c << "\n";); + #pragma omp critical (par_solver) + { + m_pool.begin_add_vector(owner, n); + for (unsigned i = 0; i < n; ++i) { + m_pool.add_vector_elem(c[i].index()); + } + m_pool.end_add_vector(); + } + } + + void parallel::get_clauses(solver& s) { + if (s.m_par_syncing_clauses) return; + flet _disable_sync_clause(s.m_par_syncing_clauses, true); + #pragma omp critical (par_solver) + { + _get_clauses(s); + } + } + + void parallel::_get_clauses(solver& s) { + unsigned n; + unsigned const* ptr; + unsigned owner = s.m_par_id; + while (m_pool.get_vector(owner, n, ptr)) { + m_lits.reset(); + bool usable_clause = true; + for (unsigned i = 0; usable_clause && i < n; ++i) { + literal lit(to_literal(ptr[i])); + m_lits.push_back(lit); + usable_clause = lit.var() <= s.m_par_num_vars && !s.was_eliminated(lit.var()); + } + IF_VERBOSE(3, verbose_stream() << s.m_par_id << ": retrieve " << m_lits << "\n";); + SASSERT(n >= 2); + if (usable_clause) { + s.mk_clause_core(m_lits.size(), m_lits.c_ptr(), true); + } + } + } + + bool parallel::enable_add(clause const& c) const { + // plingeling, glucose heuristic: + return (c.size() <= 40 && c.glue() <= 8) || c.glue() <= 2; + } + + void parallel::_set_phase(solver& s) { + if (!m_phase.empty()) { + m_phase.reserve(s.num_vars(), l_undef); + for (unsigned i = 0; i < s.num_vars(); ++i) { + if (s.value(i) != l_undef) { + m_phase[i] = s.value(i); + continue; + } + switch (s.m_phase[i]) { + case POS_PHASE: + m_phase[i] = l_true; + break; + case NEG_PHASE: + m_phase[i] = l_false; + break; + default: + m_phase[i] = l_undef; + break; + } + } + } + if (m_consumer_ready && (m_num_clauses == 0 || (m_num_clauses > s.m_clauses.size()))) { + // time to update local search with new clauses. + // there could be multiple local search engines runing at the same time. + IF_VERBOSE(1, verbose_stream() << "(sat-parallel refresh :from " << m_num_clauses << " :to " << s.m_clauses.size() << ")\n";); + m_solver_copy = alloc(solver, s.m_params, s.rlimit()); + m_solver_copy->copy(s); + m_num_clauses = s.m_clauses.size(); + } + } + + void parallel::set_phase(solver& s) { + #pragma omp critical (par_solver) + { + _set_phase(s); + } + } + + void parallel::get_phase(solver& s) { + #pragma omp critical (par_solver) + { + _get_phase(s); + } + } + + void parallel::_get_phase(solver& s) { + if (!m_phase.empty()) { + m_phase.reserve(s.num_vars(), l_undef); + for (unsigned i = 0; i < s.num_vars(); ++i) { + switch (m_phase[i]) { + case l_false: s.m_phase[i] = NEG_PHASE; break; + case l_true: s.m_phase[i] = POS_PHASE; break; + default: break; + } + } + } + } + + bool parallel::get_phase(local_search& s) { + bool copied = false; + #pragma omp critical (par_solver) + { + m_consumer_ready = true; + if (m_solver_copy && s.num_non_binary_clauses() > m_solver_copy->m_clauses.size()) { + copied = true; + s.import(*m_solver_copy.get(), true); + } + for (unsigned i = 0; i < m_phase.size(); ++i) { + s.set_phase(i, m_phase[i]); + m_phase[i] = l_undef; + } + m_phase.reserve(s.num_vars(), l_undef); + } + return copied; + } + + void parallel::set_phase(local_search& s) { + #pragma omp critical (par_solver) + { + m_consumer_ready = true; + m_phase.reserve(s.num_vars(), l_undef); + for (unsigned i = 0; i < s.num_vars(); ++i) { + m_phase[i] = s.get_phase(i) ? l_true : l_false; + } + m_num_clauses = s.num_non_binary_clauses(); + } + } + + bool parallel::copy_solver(solver& s) { + bool copied = false; + #pragma omp critical (par_solver) + { + m_consumer_ready = true; + if (m_solver_copy && s.m_clauses.size() > m_solver_copy->m_clauses.size()) { + s.copy(*m_solver_copy); + copied = true; + m_num_clauses = s.m_clauses.size(); + } + } + return copied; + } + +}; + diff --git a/src/sat/sat_parallel.h b/src/sat/sat_parallel.h new file mode 100644 index 000000000..256623380 --- /dev/null +++ b/src/sat/sat_parallel.h @@ -0,0 +1,116 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_parallel.h + +Abstract: + + Utilities for parallel SAT solving. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-1-29. + +Revision History: + +--*/ +#ifndef SAT_PARALLEL_H_ +#define SAT_PARALLEL_H_ + +#include "sat/sat_types.h" +#include "util/hashtable.h" +#include "util/map.h" +#include "util/rlimit.h" + +namespace sat { + + class local_search; + + class parallel { + + // shared pool of learned clauses. + class vector_pool { + unsigned_vector m_vectors; + unsigned m_size; + unsigned m_tail; + unsigned_vector m_heads; + svector m_at_end; + void next(unsigned& index); + unsigned get_owner(unsigned index) const { return m_vectors[index]; } + unsigned get_length(unsigned index) const { return m_vectors[index+1]; } + unsigned const* get_ptr(unsigned index) const { return m_vectors.c_ptr() + index + 2; } + public: + vector_pool() {} + void reserve(unsigned num_owners, unsigned sz); + void begin_add_vector(unsigned owner, unsigned n); + void end_add_vector(); + void add_vector_elem(unsigned e); + bool get_vector(unsigned owner, unsigned& n, unsigned const*& ptr); + }; + + bool enable_add(clause const& c) const; + void _get_clauses(solver& s); + void _get_phase(solver& s); + void _set_phase(solver& s); + + typedef hashtable index_set; + literal_vector m_units; + index_set m_unit_set; + literal_vector m_lits; + vector_pool m_pool; + + // for exchange with local search: + svector m_phase; + unsigned m_num_clauses; + scoped_ptr m_solver_copy; + bool m_consumer_ready; + + scoped_limits m_scoped_rlimit; + vector m_limits; + ptr_vector m_solvers; + + public: + + parallel(solver& s); + + ~parallel(); + + void init_solvers(solver& s, unsigned num_extra_solvers); + + void push_child(reslimit& rl); + + // reserve space + void reserve(unsigned num_owners, unsigned sz) { m_pool.reserve(num_owners, sz); } + + solver& get_solver(unsigned i) { return *m_solvers[i]; } + + void cancel_solver(unsigned i) { m_limits[i].cancel(); } + + // exchange unit literals + void exchange(solver& s, literal_vector const& in, unsigned& limit, literal_vector& out); + + // add clause to shared clause pool + void share_clause(solver& s, clause const& c); + + void share_clause(solver& s, literal l1, literal l2); + + // receive clauses from shared clause pool + void get_clauses(solver& s); + + // exchange phase of variables. + void set_phase(solver& s); + + void get_phase(solver& s); + + void set_phase(local_search& s); + + bool get_phase(local_search& s); + + bool copy_solver(solver& s); + }; + +}; + +#endif diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 2b1dc6646..dd840468e 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -5,23 +5,62 @@ def_module_params('sat', ('phase', SYMBOL, 'caching', 'phase selection strategy: always_false, always_true, caching, random'), ('phase.caching.on', UINT, 400, 'phase caching on period (in number of conflicts)'), ('phase.caching.off', UINT, 100, 'phase caching off period (in number of conflicts)'), - ('restart', SYMBOL, 'luby', 'restart strategy: luby or geometric'), - ('restart.initial', UINT, 100, 'initial restart (number of conflicts)'), - ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), + ('phase.sticky', BOOL, False, 'use sticky phase caching for local search'), + ('propagate.prefetch', BOOL, True, 'prefetch watch lists for assigned literals'), + ('restart', SYMBOL, 'ema', 'restart strategy: static, luby, ema or geometric'), + ('restart.initial', UINT, 2, 'initial restart (number of conflicts)'), + ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), + ('restart.fast', BOOL, True, 'use fast restart approach only removing less active literals.'), ('restart.factor', DOUBLE, 1.5, 'restart increment factor for geometric strategy'), + ('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'), + ('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'), ('random_freq', DOUBLE, 0.01, 'frequency of random case splits'), ('random_seed', UINT, 0, 'random seed'), ('burst_search', UINT, 100, 'number of conflicts before first global simplification'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts'), ('gc', SYMBOL, 'glue_psm', 'garbage collection strategy: psm, glue, glue_psm, dyn_psm'), - ('gc.initial', UINT, 20000, 'learned clauses garbage collection frequence'), + ('gc.initial', UINT, 20000, 'learned clauses garbage collection frequency'), ('gc.increment', UINT, 500, 'increment to the garbage collection threshold'), ('gc.small_lbd', UINT, 3, 'learned clauses with small LBD are never deleted (only used in dyn_psm)'), ('gc.k', UINT, 7, 'learned clauses that are inactive for k gc rounds are permanently deleted (only used in dyn_psm)'), + ('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'), ('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'), ('core.minimize_partial', BOOL, False, 'apply partial (cheap) core minimization'), - ('parallel_threads', UINT, 1, 'number of parallel threads to use'), + ('threads', UINT, 1, 'number of parallel threads to use'), ('dimacs.core', BOOL, False, 'extract core from DIMACS benchmarks'), - ('dimacs.display', BOOL, False, 'display SAT instance in DIMACS format and return unknown instead of solving'))) + ('drat.file', SYMBOL, '', 'file to dump DRAT proofs'), + ('drat.check_unsat', BOOL, False, 'build up internal proof and check'), + ('drat.check_sat', BOOL, False, 'build up internal trace, check satisfying model'), + ('cardinality.solver', BOOL, True, 'use cardinality solver'), + ('pb.solver', SYMBOL, 'solver', 'method for handling Pseudo-Boolean constraints: circuit (arithmetical circuit), sorting (sorting circuit), totalizer (use totalizer encoding), solver (use native solver)'), + ('xor.solver', BOOL, False, 'use xor solver'), + ('atmost1_encoding', SYMBOL, 'grouped', 'encoding used for at-most-1 constraints grouped, bimander, ordered'), + ('local_search', BOOL, False, 'use local search instead of CDCL'), + ('local_search_threads', UINT, 0, 'number of local search threads to find satisfiable solution'), + ('local_search_mode', SYMBOL, 'wsat', 'local search algorithm, either default wsat or qsat'), + ('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'), + ('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.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'))) + diff --git a/src/sat/sat_probing.cpp b/src/sat/sat_probing.cpp index b23d57164..52ab9f9f7 100644 --- a/src/sat/sat_probing.cpp +++ b/src/sat/sat_probing.cpp @@ -19,6 +19,7 @@ Revision History: --*/ #include "sat/sat_probing.h" #include "sat/sat_solver.h" +#include "sat/sat_simplifier_params.hpp" namespace sat { probing::probing(solver & _s, params_ref const & p): @@ -60,13 +61,11 @@ namespace sat { bool probing::try_lit(literal l, bool updt_cache) { SASSERT(s.m_qhead == s.m_trail.size()); SASSERT(s.value(l.var()) == l_undef); - literal_vector * implied_lits = updt_cache ? 0 : cached_implied_lits(l); + literal_vector * implied_lits = updt_cache ? nullptr : cached_implied_lits(l); if (implied_lits) { - literal_vector::iterator it = implied_lits->begin(); - literal_vector::iterator end = implied_lits->end(); - for (; it != end; ++it) { - if (m_assigned.contains(*it)) { - s.assign(*it, justification()); + for (literal lit : *implied_lits) { + if (m_assigned.contains(lit)) { + s.assign(lit, justification()); m_num_assigned++; } } @@ -96,10 +95,8 @@ namespace sat { cache_bins(l, old_tr_sz); s.pop(1); - literal_vector::iterator it = m_to_assert.begin(); - literal_vector::iterator end = m_to_assert.end(); - for (; it != end; ++it) { - s.assign(*it, justification()); + for (literal l : m_to_assert) { + s.assign(l, justification()); m_num_assigned++; } } @@ -139,10 +136,9 @@ namespace sat { if (m_probing_binary) { watch_list & wlist = s.get_wlist(~l); - for (unsigned i = 0; i < wlist.size(); i++) { - watched & w = wlist[i]; + for (watched & w : wlist) { if (!w.is_binary_clause()) - break; + continue; literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; @@ -243,12 +239,13 @@ namespace sat { return r; } - void probing::updt_params(params_ref const & p) { - m_probing = p.get_bool("probing", true); - m_probing_limit = p.get_uint("probing_limit", 5000000); - m_probing_cache = p.get_bool("probing_cache", true); - m_probing_binary = p.get_bool("probing_binary", true); - m_probing_cache_limit = megabytes_to_bytes(p.get_uint("probing_chache_limit", 1024)); + void probing::updt_params(params_ref const & _p) { + sat_simplifier_params p(_p); + m_probing = p.probing(); + m_probing_limit = p.probing_limit(); + m_probing_cache = p.probing_cache(); + m_probing_binary = p.probing_binary(); + m_probing_cache_limit = p.probing_cache_limit(); } void probing::collect_param_descrs(param_descrs & d) { diff --git a/src/sat/sat_probing.h b/src/sat/sat_probing.h index 391098ef7..2e1f97909 100644 --- a/src/sat/sat_probing.h +++ b/src/sat/sat_probing.h @@ -55,7 +55,6 @@ namespace sat { struct report; - void reset_cache(literal l); void cache_bins(literal l, unsigned old_tr_sz); bool try_lit(literal l, bool updt_cache); void process(bool_var v); @@ -66,6 +65,7 @@ namespace sat { bool operator()(bool force = false); + void reset_cache(literal l); void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); @@ -77,10 +77,10 @@ namespace sat { // return the literals implied by l. // return 0, if the cache is not available literal_vector * cached_implied_lits(literal l) { - if (!m_probing_cache) return 0; - if (l.index() >= m_cached_bins.size()) return 0; + if (!m_probing_cache) return nullptr; + if (l.index() >= m_cached_bins.size()) return nullptr; cache_entry & e = m_cached_bins[l.index()]; - if (!e.m_available) return 0; + if (!e.m_available) return nullptr; return &(e.m_lits); } diff --git a/src/sat/sat_scc.cpp b/src/sat/sat_scc.cpp index 9682da4e8..e430bcb47 100644 --- a/src/sat/sat_scc.cpp +++ b/src/sat/sat_scc.cpp @@ -26,17 +26,19 @@ Revision History: namespace sat { scc::scc(solver & s, params_ref const & p): - m_solver(s) { + m_solver(s), + m_big(s.m_rand) { reset_statistics(); updt_params(p); } struct frame { unsigned m_lidx; + unsigned m_succ_idx; bool m_first; watched * m_it; watched * m_end; - frame(unsigned lidx, watched * it, watched * end):m_lidx(lidx), m_first(true), m_it(it), m_end(end) {} + frame(unsigned lidx, watched * it, watched * end, unsigned sidx = 0):m_lidx(lidx), m_succ_idx(sidx), m_first(true), m_it(it), m_end(end) {} }; typedef svector frames; @@ -44,16 +46,20 @@ namespace sat { scc & m_scc; stopwatch m_watch; unsigned m_num_elim; + unsigned m_num_elim_bin; report(scc & c): m_scc(c), - m_num_elim(c.m_num_elim) { + m_num_elim(c.m_num_elim), + m_num_elim_bin(c.m_num_elim_bin) { m_watch.start(); } ~report() { m_watch.stop(); + unsigned elim_bin = m_scc.m_num_elim_bin - m_num_elim_bin; IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-scc :elim-vars " << (m_scc.m_num_elim - m_num_elim) - << mk_stat(m_scc.m_solver) + verbose_stream() << " (sat-scc :elim-vars " << (m_scc.m_num_elim - m_num_elim); + if (elim_bin > 0) verbose_stream() << " :elim-bin " << elim_bin; + verbose_stream() << mk_stat(m_scc.m_solver) << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } }; @@ -75,7 +81,7 @@ namespace sat { index.resize(num_lits, UINT_MAX); lowlink.resize(num_lits, UINT_MAX); in_s.resize(num_lits, false); - literal_vector roots; + literal_vector roots, lits; roots.resize(m_solver.num_vars(), null_literal); unsigned next_index = 0; svector frames; @@ -98,14 +104,15 @@ namespace sat { watch_list & wlist = m_solver.get_wlist(LIDX); \ frames.push_back(frame(LIDX, wlist.begin(), wlist.end())); \ } - + NEW_NODE(l_idx); - + while (!frames.empty()) { loop: - frame & fr = frames.back(); - unsigned l_idx = fr.m_lidx; + frame & fr = frames.back(); + unsigned l_idx = fr.m_lidx; if (!fr.m_first) { + SASSERT(fr.m_it->is_binary_clause()); // after visiting child literal l2 = fr.m_it->get_literal(); unsigned l2_idx = l2.index(); @@ -136,20 +143,19 @@ namespace sat { if (lowlink[l_idx] == index[l_idx]) { // found new SCC CTRACE("scc_cycle", s.back() != l_idx, { - tout << "cycle: "; - unsigned j = s.size() - 1; - unsigned l2_idx; - do { - l2_idx = s[j]; - j--; - tout << to_literal(l2_idx) << " "; - } - while (l2_idx != l_idx); - tout << "\n"; - }); + tout << "cycle: "; + unsigned j = s.size() - 1; + unsigned l2_idx; + do { + l2_idx = s[j]; + j--; + tout << to_literal(l2_idx) << " "; + } while (l2_idx != l_idx); + tout << "\n"; + }); SASSERT(!s.empty()); - literal l = to_literal(l_idx); + literal l = to_literal(l_idx); bool_var v = l.var(); if (roots[v] != null_literal) { // variable was already assigned... just consume stack @@ -158,10 +164,9 @@ namespace sat { do { l2_idx = s.back(); s.pop_back(); - in_s[l2_idx] = false; + in_s[l2_idx] = false; SASSERT(roots[to_literal(l2_idx).var()].var() == roots[v].var()); - } - while (l2_idx != l_idx); + } while (l2_idx != l_idx); } else { // check if the SCC has an external variable, and check for conflicts @@ -180,20 +185,19 @@ namespace sat { r = to_literal(l2_idx); break; } - } - while (l2_idx != l_idx); - + } while (l2_idx != l_idx); + if (r == null_literal) { // SCC does not contain external variable r = to_literal(l_idx); } - + TRACE("scc_detail", tout << "r: " << r << "\n";); do { l2_idx = s.back(); s.pop_back(); - in_s[l2_idx] = false; + in_s[l2_idx] = false; literal l2 = to_literal(l2_idx); bool_var v2 = l2.var(); if (roots[v2] == null_literal) { @@ -203,37 +207,63 @@ namespace sat { else { roots[v2] = r; } - if (v2 != r.var()) + if (v2 != r.var()) to_elim.push_back(v2); } - } - while (l2_idx != l_idx); + } while (l2_idx != l_idx); } } frames.pop_back(); } } + for (unsigned i = 0; i < m_solver.num_vars(); ++i) { + if (roots[i] == null_literal) { + roots[i] = literal(i, false); + } + } TRACE("scc", for (unsigned i = 0; i < roots.size(); i++) { tout << i << " -> " << roots[i] << "\n"; } - tout << "to_elim: "; for (unsigned i = 0; i < to_elim.size(); i++) tout << to_elim[i] << " "; tout << "\n";); + tout << "to_elim: "; for (unsigned v : to_elim) tout << v << " "; tout << "\n";); m_num_elim += to_elim.size(); elim_eqs eliminator(m_solver); eliminator(roots, to_elim); TRACE("scc_detail", m_solver.display(tout);); CASSERT("scc_bug", m_solver.check_invariant()); + + if (m_scc_tr) { + reduce_tr(); + } + TRACE("scc_detail", m_solver.display(tout);); return to_elim.size(); } + unsigned scc::reduce_tr(bool learned) { + init_big(learned); + unsigned num_elim = m_big.reduce_tr(m_solver); + m_num_elim_bin += num_elim; + return num_elim; + } + + void scc::reduce_tr() { + unsigned quota = 0, num_reduced = 0; + while ((num_reduced = reduce_tr(false)) > quota) { quota = std::max(100u, num_reduced / 2); } + quota = 0; + while ((num_reduced = reduce_tr(true)) > quota) { quota = std::max(100u, num_reduced / 2); } + } + void scc::collect_statistics(statistics & st) const { - st.update("elim bool vars", m_num_elim); + st.update("elim bool vars scc", m_num_elim); + st.update("elim binary", m_num_elim_bin); } void scc::reset_statistics() { m_num_elim = 0; + m_num_elim_bin = 0; } void scc::updt_params(params_ref const & _p) { sat_scc_params p(_p); m_scc = p.scc(); + m_scc_tr = p.scc_tr(); } void scc::collect_param_descrs(param_descrs & d) { diff --git a/src/sat/sat_scc.h b/src/sat/sat_scc.h index c8392685e..146bd2366 100644 --- a/src/sat/sat_scc.h +++ b/src/sat/sat_scc.h @@ -19,9 +19,10 @@ Revision History: #ifndef SAT_SCC_H_ #define SAT_SCC_H_ -#include "sat/sat_types.h" #include "util/statistics.h" #include "util/params.h" +#include "sat/sat_types.h" +#include "sat/sat_big.h" namespace sat { class solver; @@ -31,9 +32,18 @@ namespace sat { solver & m_solver; // config bool m_scc; + bool m_scc_tr; // stats unsigned m_num_elim; + unsigned m_num_elim_bin; + + big m_big; + + void reduce_tr(); + unsigned reduce_tr(bool learned); + public: + scc(solver & s, params_ref const & p); unsigned operator()(); @@ -42,6 +52,16 @@ namespace sat { void collect_statistics(statistics & st) const; void reset_statistics(); + + /* + \brief create binary implication graph and associated data-structures to check transitivity. + */ + void init_big(bool learned) { m_big.init(m_solver, learned); } + void ensure_big(bool learned) { m_big.ensure_big(m_solver, learned); } + int get_left(literal l) const { return m_big.get_left(l); } + int get_right(literal l) const { return m_big.get_right(l); } + literal get_root(literal l) const { return m_big.get_root(l); } + bool connected(literal u, literal v) const { return m_big.connected(u, v); } }; }; diff --git a/src/sat/sat_scc_params.pyg b/src/sat/sat_scc_params.pyg index b88de4de8..ead4eeb96 100644 --- a/src/sat/sat_scc_params.pyg +++ b/src/sat/sat_scc_params.pyg @@ -1,5 +1,6 @@ def_module_params(module_name='sat', class_name='sat_scc_params', export=True, - params=(('scc', BOOL, True, 'eliminate Boolean variables by computing strongly connected components'),)) + params=(('scc', BOOL, True, 'eliminate Boolean variables by computing strongly connected components'), + ('scc.tr', BOOL, True, 'apply transitive reduction, eliminate redundant binary clauses'), )) diff --git a/src/sat/sat_simplifier.cpp b/src/sat/sat_simplifier.cpp index 84534bf3f..d19cd14d4 100644 --- a/src/sat/sat_simplifier.cpp +++ b/src/sat/sat_simplifier.cpp @@ -21,6 +21,8 @@ Revision History: #include "sat/sat_simplifier.h" #include "sat/sat_simplifier_params.hpp" #include "sat/sat_solver.h" +#include "sat/sat_elim_vars.h" +#include "sat/sat_integrity_checker.h" #include "util/stopwatch.h" #include "util/trace.h" @@ -33,26 +35,29 @@ namespace sat { } void use_list::insert(clause & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - m_use_list[c[i].index()].insert(c); - } + for (literal l : c) + m_use_list[l.index()].insert(c); } void use_list::erase(clause & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - m_use_list[c[i].index()].erase(c); - } + for (literal l : c) + m_use_list[l.index()].erase(c); } void use_list::erase(clause & c, literal l) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - literal l2 = c[i]; + for (literal l2 : c) if (l2 != l) m_use_list[l2.index()].erase(c); - } + } + + void use_list::block(clause& c) { + for (literal l : c) + m_use_list[l.index()].block(c); + } + + void use_list::unblock(clause& c) { + for (literal l : c) + m_use_list[l.index()].unblock(c); } simplifier::simplifier(solver & _s, params_ref const & p): @@ -66,11 +71,18 @@ namespace sat { finalize(); } - inline watch_list & simplifier::get_wlist(literal l) { return s.get_wlist(l); } + watch_list & simplifier::get_wlist(literal l) { return s.get_wlist(l); } - inline watch_list const & simplifier::get_wlist(literal l) const { return s.get_wlist(l); } + watch_list const & simplifier::get_wlist(literal l) const { return s.get_wlist(l); } - inline bool simplifier::is_external(bool_var v) const { return s.is_external(v); } + bool simplifier::is_external(bool_var v) const { + return + s.is_assumption(v) || + (s.is_external(v) && s.is_incremental()) || + (s.is_external(v) && s.m_ext && + (!m_ext_use_list.get(literal(v, false)).empty() || + !m_ext_use_list.get(literal(v, true)).empty())); + } inline bool simplifier::was_eliminated(bool_var v) const { return s.was_eliminated(v); } @@ -80,24 +92,41 @@ namespace sat { inline void simplifier::checkpoint() { s.checkpoint(); } + bool simplifier::single_threaded() const { return s.m_config.m_num_threads == 1; } + + bool simplifier::bce_enabled_base() const { + return + !m_incremental_mode && !s.tracking_assumptions() && + !m_learned_in_use_lists && m_num_calls >= m_bce_delay && single_threaded(); + } + + bool simplifier::ate_enabled() const { return m_num_calls >= m_bce_delay && m_ate; } + bool simplifier::bce_enabled() const { return bce_enabled_base() && (m_bce || m_bce_at == m_num_calls || m_acce || m_abce || m_cce); } + bool simplifier::acce_enabled() const { return bce_enabled_base() && m_acce; } + bool simplifier::cce_enabled() const { return bce_enabled_base() && (m_cce || m_acce); } + bool simplifier::abce_enabled() const { return bce_enabled_base() && m_abce; } + bool simplifier::bca_enabled() const { return bce_enabled_base() && m_bca; } + bool simplifier::elim_vars_bdd_enabled() const { + return !m_incremental_mode && !s.tracking_assumptions() && m_elim_vars_bdd && m_num_calls >= m_elim_vars_bdd_delay && single_threaded(); + } + bool simplifier::elim_vars_enabled() const { + return !m_incremental_mode && !s.tracking_assumptions() && m_elim_vars && single_threaded(); + } + void simplifier::register_clauses(clause_vector & cs) { std::stable_sort(cs.begin(), cs.end(), size_lt()); - clause_vector::iterator it = cs.begin(); - clause_vector::iterator end = cs.end(); - for (; it != end; ++it) { - clause & c = *(*it); - if (!c.frozen()) { - m_use_list.insert(c); - if (c.strengthened()) - m_sub_todo.insert(c); + for (clause* c : cs) { + if (!c->frozen()) { + m_use_list.insert(*c); + if (c->strengthened()) + m_sub_todo.insert(*c); } } } inline void simplifier::remove_clause_core(clause & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) - insert_elim_todo(c[i].var()); + for (literal l : c) + insert_elim_todo(l.var()); m_sub_todo.erase(c); c.set_removed(true); TRACE("resolution_bug", tout << "del_clause: " << c << "\n";); @@ -114,10 +143,17 @@ namespace sat { m_use_list.erase(c, l); } - inline void simplifier::remove_bin_clause_half(literal l1, literal l2, bool learned) { - SASSERT(s.get_wlist(~l1).contains(watched(l2, learned))); - s.get_wlist(~l1).erase(watched(l2, learned)); - m_sub_bin_todo.erase(bin_clause(l1, l2, learned)); + inline void simplifier::set_learned(clause & c) { + m_need_cleanup = true; + s.set_learned(c, true); + m_use_list.block(c); + } + + inline void simplifier::set_learned(literal l1, literal l2) { + m_sub_bin_todo.erase(bin_clause(l1, l2, false)); + m_sub_bin_todo.erase(bin_clause(l2, l1, false)); + m_sub_bin_todo.push_back(bin_clause(l1, l2, true)); + m_sub_bin_todo.push_back(bin_clause(l2, l1, true)); } void simplifier::init_visited() { @@ -133,6 +169,7 @@ namespace sat { m_visited.finalize(); m_bs_cs.finalize(); m_bs_ls.finalize(); + m_ext_use_list.finalize(); } void simplifier::initialize() { @@ -140,6 +177,7 @@ namespace sat { s.m_cleaner(true); m_last_sub_trail_sz = s.m_trail.size(); m_use_list.init(s.num_vars()); + if (s.m_ext) s.m_ext->init_use_list(m_ext_use_list); m_sub_todo.reset(); m_sub_bin_todo.reset(); m_elim_todo.reset(); @@ -149,78 +187,73 @@ namespace sat { } void simplifier::operator()(bool learned) { + if (s.inconsistent()) return; - if (!m_subsumption && !m_elim_blocked_clauses && !m_resolution) + if (!m_subsumption && !bce_enabled() && !bca_enabled() && !elim_vars_enabled()) return; - - // solver::scoped_disable_checkpoint _scoped_disable_checkpoint(s); initialize(); CASSERT("sat_solver", s.check_invariant()); - TRACE("before_simplifier", s.display(tout);); + TRACE("sat_simplifier", s.display(tout);); - m_sub_todo.reset(); - m_sub_bin_todo.reset(); s.m_cleaner(true); - m_last_sub_trail_sz = s.m_trail.size(); TRACE("after_cleanup", s.display(tout);); CASSERT("sat_solver", s.check_invariant()); m_need_cleanup = false; m_use_list.init(s.num_vars()); - m_learned_in_use_lists = false; + m_learned_in_use_lists = learned; if (learned) { register_clauses(s.m_learned); - m_learned_in_use_lists = true; } register_clauses(s.m_clauses); - if (!learned && (m_elim_blocked_clauses || m_elim_blocked_clauses_at == m_num_calls)) + if (!learned && (bce_enabled() || bca_enabled() || ate_enabled())) { elim_blocked_clauses(); + } - if (!learned) + if (!learned) { m_num_calls++; + } m_sub_counter = m_subsumption_limit; m_elim_counter = m_res_limit; m_old_num_elim_vars = m_num_elim_vars; - scoped_finalize _scoped_finalize(*this); + for (bool_var v = 0; v < s.num_vars(); ++v) { + if (!s.m_eliminated[v] && !is_external(v)) { + insert_elim_todo(v); + } + } do { if (m_subsumption) subsume(); if (s.inconsistent()) return; - if (!learned && m_resolution) - elim_vars(); + if (!learned && elim_vars_enabled()) + elim_vars(); if (s.inconsistent()) return; if (!m_subsumption || m_sub_counter < 0) break; } while (!m_sub_todo.empty()); - } - - void simplifier::scoped_finalize_fn() { bool vars_eliminated = m_num_elim_vars > m_old_num_elim_vars; - if (m_need_cleanup) { + if (m_need_cleanup || vars_eliminated) { TRACE("after_simplifier", tout << "cleanning watches...\n";); cleanup_watches(); + move_clauses(s.m_learned, true); + move_clauses(s.m_clauses, false); cleanup_clauses(s.m_learned, true, vars_eliminated, m_learned_in_use_lists); cleanup_clauses(s.m_clauses, false, vars_eliminated, true); } - else { - TRACE("after_simplifier", tout << "skipping cleanup...\n";); - if (vars_eliminated) { - // must remove learned clauses with eliminated variables - cleanup_clauses(s.m_learned, true, true, m_learned_in_use_lists); - } - } + CASSERT("sat_solver", s.check_invariant()); - TRACE("after_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); + TRACE("sat_simplifier", s.display(tout); tout << "model_converter:\n"; s.m_mc.display(tout);); + finalize(); } @@ -228,10 +261,7 @@ namespace sat { \brief Eliminate all ternary and clause watches. */ void simplifier::cleanup_watches() { - vector::iterator it = s.m_watches.begin(); - vector::iterator end = s.m_watches.end(); - for (; it != end; ++it) { - watch_list & wlist = *it; + for (watch_list& wlist : s.m_watches) { watch_list::iterator it2 = wlist.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist.end(); @@ -247,10 +277,33 @@ namespace sat { break; } } - wlist.set_end(itprev); + wlist.set_end(itprev); } } + void simplifier::move_clauses(clause_vector& cs, bool learned) { + clause_vector::iterator it = cs.begin(); + clause_vector::iterator it2 = it; + clause_vector::iterator end = cs.end(); + unsigned nm = 0; + for (; it != end; ++it) { + clause & c = *(*it); + if (learned && !c.is_learned()) { + s.m_clauses.push_back(&c); + ++nm; + } + else if (!learned && c.is_learned()) { + s.m_learned.push_back(&c); + ++nm; + } + else { + *it2 = *it; + ++it2; + } + } + cs.set_end(it2); + } + void simplifier::cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists) { TRACE("sat", tout << "cleanup_clauses\n";); clause_vector::iterator it = cs.begin(); @@ -258,6 +311,7 @@ namespace sat { clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); + VERIFY(learned == c.is_learned()); if (c.was_removed()) { s.del_clause(c); continue; @@ -284,7 +338,7 @@ namespace sat { if (sz == 0) { s.set_conflict(justification()); for (; it != end; ++it, ++it2) { - *it2 = *it; + *it2 = *it; } break; } @@ -298,48 +352,36 @@ namespace sat { s.del_clause(c); continue; } - // clause became a problem clause - if (learned && !c.is_learned()) { - SASSERT(!c.frozen()); - s.m_clauses.push_back(&c); - continue; - } *it2 = *it; it2++; if (!c.frozen()) { - if (sz == 3) - s.attach_ter_clause(c); - else - s.attach_nary_clause(c); + s.attach_clause(c); + if (s.m_config.m_drat) { + s.m_drat.add(c, true); + } } } cs.set_end(it2); } - void simplifier::mark_all_but(clause const & c, literal l) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - if (c[i] == l) - continue; - mark_visited(c[i]); - } + void simplifier::mark_all_but(clause const & c, literal l1) { + for (literal l2 : c) + if (l2 != l1) + mark_visited(l2); } void simplifier::unmark_all(clause const & c) { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) - unmark_visited(c[i]); + for (literal l : c) + unmark_visited(l); } /** \brief Return the variable in c with the minimal number positive+negative occurrences. */ bool_var simplifier::get_min_occ_var(clause const & c) const { - literal l_best = c[0]; - unsigned best = m_use_list.get(l_best).size() + m_use_list.get(~l_best).size(); - unsigned sz = c.size(); - for (unsigned i = 1; i < sz; i++) { - literal l = c[i]; + literal l_best = null_literal; + unsigned best = UINT_MAX; + for (literal l : c) { unsigned num = m_use_list.get(l).size() + m_use_list.get(~l).size(); if (num < best) { l_best = l; @@ -355,17 +397,15 @@ namespace sat { Otherwise return false */ bool simplifier::subsumes1(clause const & c1, clause const & c2, literal & l) { - unsigned sz2 = c2.size(); - for (unsigned i = 0; i < sz2; i++) - mark_visited(c2[i]); + for (literal lit : c2) + mark_visited(lit); bool r = true; l = null_literal; - unsigned sz1 = c1.size(); - for (unsigned i = 0; i < sz1; i++) { - if (!is_marked(c1[i])) { - if (l == null_literal && is_marked(~c1[i])) { - l = ~c1[i]; + for (literal lit : c1) { + if (!is_marked(lit)) { + if (l == null_literal && is_marked(~lit)) { + l = ~lit; } else { l = null_literal; @@ -375,8 +415,8 @@ namespace sat { } } - for (unsigned i = 0; i < sz2; i++) - unmark_visited(c2[i]); + for (literal lit : c2) + unmark_visited(lit); return r; } @@ -387,8 +427,7 @@ namespace sat { void simplifier::collect_subsumed1_core(clause const & c1, clause_vector & out, literal_vector & out_lits, literal target) { clause_use_list const & cs = m_use_list.get(target); - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { + for (auto it = cs.mk_iterator(); !it.at_end(); it.next()) { clause & c2 = it.curr(); CTRACE("resolution_bug", c2.was_removed(), tout << "clause has been removed:\n" << c2 << "\n";); SASSERT(!c2.was_removed()); @@ -402,7 +441,6 @@ namespace sat { out_lits.push_back(l); } } - it.next(); } } @@ -416,7 +454,7 @@ namespace sat { } /** - \brief Perform backward subsumption and self-subsumption resolution using c. + \brief Perform backward subsumption and self-subsumption resolution using c1. */ void simplifier::back_subsumption1(clause & c1) { m_bs_cs.reset(); @@ -431,7 +469,7 @@ namespace sat { if (!c2.was_removed() && *l_it == null_literal) { // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) - c1.unset_learned(); + s.set_learned(c1, false); TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2); m_num_subsumed++; @@ -458,11 +496,9 @@ namespace sat { \brief Return the literal in c with the minimal number of occurrences. */ literal simplifier::get_min_occ_var0(clause const & c) const { - literal l_best = c[0]; - unsigned best = m_use_list.get(l_best).size(); - unsigned sz = c.size(); - for (unsigned i = 1; i < sz; i++) { - literal l = c[i]; + literal l_best = null_literal; + unsigned best = UINT_MAX; + for (literal l : c) { unsigned num = m_use_list.get(l).size(); if (num < best) { l_best = l; @@ -477,21 +513,19 @@ namespace sat { Otherwise return false */ bool simplifier::subsumes0(clause const & c1, clause const & c2) { - unsigned sz2 = c2.size(); - for (unsigned i = 0; i < sz2; i++) - mark_visited(c2[i]); + for (literal l : c2) + mark_visited(l); bool r = true; - unsigned sz1 = c1.size(); - for (unsigned i = 0; i < sz1; i++) { - if (!is_marked(c1[i])) { + for (literal l : c1) { + if (!is_marked(l)) { r = false; break; } } - for (unsigned i = 0; i < sz2; i++) - unmark_visited(c2[i]); + for (literal l : c2) + unmark_visited(l); return r; } @@ -502,7 +536,7 @@ namespace sat { void simplifier::collect_subsumed0_core(clause const & c1, clause_vector & out, literal target) { clause_use_list const & cs = m_use_list.get(target); clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { + for (; !it.at_end(); it.next()) { clause & c2 = it.curr(); SASSERT(!c2.was_removed()); if (&c2 != &c1 && @@ -513,7 +547,6 @@ namespace sat { out.push_back(&c2); } } - it.next(); } } @@ -532,13 +565,11 @@ namespace sat { void simplifier::back_subsumption0(clause & c1) { m_bs_cs.reset(); collect_subsumed0(c1, m_bs_cs); - clause_vector::iterator it = m_bs_cs.begin(); - clause_vector::iterator end = m_bs_cs.end(); - for (; it != end; ++it) { - clause & c2 = *(*it); + for (clause* cp : m_bs_cs) { + clause & c2 = *cp; // c2 was subsumed if (c1.is_learned() && !c2.is_learned()) - c1.unset_learned(); + s.set_learned(c1, false); TRACE("subsumption", tout << c1 << " subsumed " << c2 << "\n";); remove_clause(c2); m_num_subsumed++; @@ -558,7 +589,9 @@ namespace sat { literal l = c[i]; switch (value(l)) { case l_undef: - c[j] = l; + if (i != j) { + std::swap(c[j], c[i]); + } j++; break; case l_false: @@ -571,12 +604,18 @@ namespace sat { break; case l_true: r = true; - c[j] = l; + if (i != j) { + std::swap(c[j], c[i]); + } j++; break; } } - c.shrink(j); + if (j < sz) { + if (s.m_config.m_drat) s.m_drat.del(c); + c.shrink(j); + if (s.m_config.m_drat) s.m_drat.add(c, true); + } return r; } @@ -592,7 +631,9 @@ namespace sat { literal l = c[i]; switch (value(l)) { case l_undef: - c[j] = l; + if (i != j) { + std::swap(c[j], c[i]); + } j++; break; case l_false: @@ -601,7 +642,7 @@ namespace sat { return true; } } - c.shrink(j); + c.shrink(j); return false; } @@ -614,38 +655,28 @@ namespace sat { unsigned new_trail_sz = s.m_trail.size(); for (unsigned i = old_trail_sz; i < new_trail_sz; i++) { literal l = s.m_trail[i]; - { - // put clauses with literals assigned to false back into todo-list - clause_use_list & cs = m_use_list.get(~l); - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { - clause & c = it.curr(); - it.next(); - m_sub_todo.insert(c); - } + // put clauses with literals assigned to false back into todo-list + for (auto it = m_use_list.get(~l).mk_iterator(); !it.at_end(); it.next()) { + m_sub_todo.insert(it.curr()); } - { - // erase satisfied clauses - clause_use_list & cs = m_use_list.get(l); - { - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { - clause & c = it.curr(); - it.next(); - remove_clause(c, l); - } - } - cs.reset(); + clause_use_list& cs = m_use_list.get(l); + for (auto it = cs.mk_iterator(); !it.at_end(); ) { + clause & c = it.curr(); + it.next(); + remove_clause(c, l); } + cs.reset(); } } void simplifier::elim_lit(clause & c, literal l) { - TRACE("elim_lit", tout << "processing: " << c << "\n";); + TRACE("elim_lit", tout << "processing: " << l << " @ " << c << "\n";); m_need_cleanup = true; m_num_elim_lits++; insert_elim_todo(l.var()); c.elim(l); + if (s.m_config.m_drat) s.m_drat.add(c, true); + // if (s.m_config.m_drat) s.m_drat.del(c0); // can delete previous version clause_use_list & occurs = m_use_list.get(l); occurs.erase_not_removed(c); m_sub_counter -= occurs.size()/2; @@ -689,7 +720,7 @@ namespace sat { // should not traverse wlist using iterators, since back_subsumption1 may add new binary clauses there for (unsigned j = 0; j < wlist.size(); j++) { watched w = wlist[j]; - if (w.is_binary_clause()) { + if (w.is_binary_non_learned_clause()) { literal l2 = w.get_literal(); if (l.index() < l2.index()) { m_dummy.set(l, l2, w.is_learned()); @@ -697,9 +728,9 @@ namespace sat { back_subsumption1(c); if (w.is_learned() && !c.is_learned()) { SASSERT(wlist[j] == w); - TRACE("mark_not_learned_bug", + TRACE("set_not_learned_bug", tout << "marking as not learned: " << l2 << " " << wlist[j].is_learned() << "\n";); - wlist[j].mark_not_learned(); + wlist[j].set_learned(false); mark_as_not_learned_core(get_wlist(~l2), l); } if (s.inconsistent()) @@ -714,11 +745,9 @@ namespace sat { } void simplifier::mark_as_not_learned_core(watch_list & wlist, literal l2) { - watch_list::iterator it = wlist.begin(); - watch_list::iterator end = wlist.end(); - for (; it != end; ++it) { - if (it->is_binary_clause() && it->get_literal() == l2 && it->is_learned()) { - it->mark_not_learned(); + for (watched & w : wlist) { + if (w.is_binary_clause() && w.get_literal() == l2 && w.is_learned()) { + w.set_learned(false); return; } } @@ -745,34 +774,31 @@ namespace sat { \brief Eliminate duplicated binary clauses. */ void simplifier::elim_dup_bins() { - vector::iterator it = s.m_watches.begin(); - vector::iterator end = s.m_watches.end(); #ifdef _TRACE unsigned l_idx = 0; #endif unsigned elim = 0; - for (; it != end; ++it) { + for (watch_list & wlist : s.m_watches) { checkpoint(); - watch_list & wlist = *it; std::stable_sort(wlist.begin(), wlist.end(), bin_lt()); literal last_lit = null_literal; - watch_list::iterator it2 = wlist.begin(); - watch_list::iterator itprev = it2; - watch_list::iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (!it2->is_binary_clause()) { - *itprev = *it2; + watch_list::iterator it = wlist.begin(); + watch_list::iterator itprev = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + if (!it->is_binary_clause()) { + *itprev = *it; itprev++; continue; } - if (it2->get_literal() == last_lit) { + if (it->get_literal() == last_lit) { TRACE("subsumption", tout << "eliminating: " << ~to_literal(l_idx) - << " " << it2->get_literal() << "\n";); + << " " << it->get_literal() << "\n";); elim++; } else { - last_lit = it2->get_literal(); - *itprev = *it2; + last_lit = it->get_literal(); + *itprev = *it; itprev++; } } @@ -840,6 +866,7 @@ namespace sat { break; clause & c = m_sub_todo.erase(); + c.unmark_strengthened(); m_sub_counter--; TRACE("subsumption", tout << "next: " << c << "\n";); @@ -888,6 +915,47 @@ namespace sat { } }; + class clause_ante { + bool m_from_ri; + literal m_lit1; + literal m_lit2; + clause* m_clause; + public: + clause_ante(): + m_from_ri(false), m_lit1(null_literal), m_lit2(null_literal), m_clause(nullptr) {} + clause_ante(literal l1, bool from_ri): + m_from_ri(from_ri), m_lit1(l1), m_lit2(null_literal), m_clause(nullptr) {} + clause_ante(literal l1, literal l2): + m_from_ri(false), m_lit1(l1), m_lit2(l2), m_clause(nullptr) {} + clause_ante(clause& c): + m_from_ri(false), m_lit1(null_literal), m_lit2(null_literal), m_clause(&c) {} + literal lit1() const { return m_lit1; } + literal lit2() const { return m_lit2; } + clause* cls() const { return m_clause; } + bool from_ri() const { return m_from_ri; } + bool operator==(clause_ante const& a) const { + return a.m_lit1 == m_lit1 && a.m_lit2 == m_lit2 && a.m_clause == m_clause; + } + std::ostream& display(std::ostream& out, literal lit) const { + if (cls()) { + out << *cls() << " "; + } + else { + out << "(" << ~lit; + } + if (lit1() != null_literal) { + out << " " << lit1(); + } + if (lit2() != null_literal) { + out << " " << lit2(); + } + if (!cls()) out << ")"; + if (from_ri()) out << "ri"; + out << "\n"; + return out; + } + }; + class queue { heap m_queue; public: @@ -907,148 +975,698 @@ namespace sat { } literal next() { SASSERT(!empty()); return to_literal(m_queue.erase_min()); } bool empty() const { return m_queue.empty(); } + void reset() { m_queue.reset(); } }; simplifier & s; int m_counter; - model_converter & mc; + model_converter & m_mc; queue m_queue; - clause_vector m_to_remove; + + literal_vector m_covered_clause; // covered clause + svector m_covered_antecedent; // explainations for literals in covered clause + literal_vector m_intersection; // current resolution intersection + literal_vector m_tautology; // literals that are used in blocking tautology + literal_vector m_new_intersection; + svector m_in_intersection; + unsigned m_ala_qhead; + clause_wrapper m_clause; blocked_clause_elim(simplifier & _s, unsigned limit, model_converter & _mc, use_list & l, vector & wlist): s(_s), m_counter(limit), - mc(_mc), - m_queue(l, wlist) { + m_mc(_mc), + m_queue(l, wlist), + m_clause(null_literal, null_literal) { + m_in_intersection.resize(s.s.num_vars() * 2, false); } void insert(literal l) { + VERIFY(process_var(l.var())); m_queue.insert(l); } bool process_var(bool_var v) { - return !s.is_external(v) && !s.was_eliminated(v); + return !s.s.is_assumption(v) && !s.was_eliminated(v) && !s.is_external(v) && s.value(v) == l_undef; } - void operator()(unsigned num_vars) { + enum elim_type { + bce_t, + cce_t, + acce_t, + abce_t, + ate_t, + no_t + }; + + void operator()() { + if (s.acce_enabled()) { + cce(); + } + if (s.ate_enabled() && !s.abce_enabled() && !s.acce_enabled()) { + cce(); + } + if (s.cce_enabled() && !s.acce_enabled()) { + cce(); + } + if (s.abce_enabled() && !s.acce_enabled()) { + cce(); + } + if (s.bce_enabled() && !s.cce_enabled() && !s.abce_enabled()) { + cce(); + } + if (s.bca_enabled()) { + bca(); + } + } + + void insert_queue() { + unsigned num_vars = s.s.num_vars(); for (bool_var v = 0; v < num_vars; v++) { if (process_var(v)) { insert(literal(v, false)); insert(literal(v, true)); } } - while (!m_queue.empty()) { + } + + void reset_intersection() { + for (literal l : m_intersection) m_in_intersection[l.index()] = false; + m_intersection.reset(); + } + + void add_intersection(literal lit) { + m_intersection.push_back(lit); + m_in_intersection[lit.index()] = true; + } + + // + // Resolve intersection + // Find literals that are in the intersection of all resolvents with l. + // + bool resolution_intersection(literal l, bool adding) { + unsigned tsz = m_tautology.size(); + reset_intersection(); + if (!process_var(l.var())) return false; + bool first = true; + VERIFY(s.value(l) == l_undef); + for (watched & w : s.get_wlist(l)) { + // when adding a blocked clause, then all non-learned clauses have to be considered for the + // resolution intersection. + bool process_bin = adding ? w.is_binary_clause() : w.is_binary_non_learned_clause(); + if (process_bin) { + literal lit = w.get_literal(); + if (s.is_marked(~lit) && lit != ~l) { + m_tautology.push_back(~lit); + continue; // tautology + } + if (!first || s.is_marked(lit)) { + reset_intersection(); + m_tautology.shrink(tsz); + return false; // intersection is empty or does not add anything new. + } + first = false; + SASSERT(m_intersection.empty()); + add_intersection(lit); + } + } + clause_use_list & neg_occs = s.m_use_list.get(~l); + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { + bool tautology = false; + clause & c = it.curr(); + if (c.is_learned() && !adding) continue; + if (c.was_removed()) continue; + for (literal lit : c) { + if (s.is_marked(~lit) && lit != ~l) { + m_tautology.push_back(~lit); + tautology = true; + break; + } + } + if (!tautology) { + if (first) { + for (literal lit : c) + if (lit != ~l && !s.is_marked(lit)) + add_intersection(lit); + first = false; + } + else { + m_new_intersection.reset(); + for (literal lit : c) + if (m_in_intersection[lit.index()]) + m_new_intersection.push_back(lit); + reset_intersection(); + for (literal lit : m_new_intersection) + add_intersection(lit); + } + if (m_intersection.empty()) { + m_tautology.shrink(tsz); + return false; + } + } + } + // remove tautology literals if literal has no resolution intersection + if (m_intersection.empty() && !first) { + m_tautology.shrink(tsz); + } + // if (first) IF_VERBOSE(0, verbose_stream() << "taut: " << m_tautology << "\n";); + return first; + } + + bool check_abce_tautology(literal l) { + unsigned tsz = m_tautology.size(); + if (!process_var(l.var())) return false; + for (watched & w : s.get_wlist(l)) { + if (w.is_binary_non_learned_clause()) { + literal lit = w.get_literal(); + VERIFY(lit != ~l); + if (!s.is_marked(~lit)) { + m_tautology.shrink(tsz); + return false; + } + m_tautology.push_back(~lit); + } + } + clause_use_list & neg_occs = s.m_use_list.get(~l); + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { + clause & c = it.curr(); + if (c.is_learned() || c.was_removed()) continue; + bool tautology = false; + for (literal lit : c) { + if (s.is_marked(~lit) && lit != ~l) { + tautology = true; + m_tautology.push_back(~lit); + break; + } + } + if (!tautology) { + m_tautology.shrink(tsz); + return false; + } + } + return true; + } + + bool revisit_binary(literal l1, literal l2) const { + return m_clause.is_binary() && + ((m_clause[0] == l1 && m_clause[1] == l2) || + (m_clause[0] == l2 && m_clause[1] == l1)); + } + + bool revisit_clause(clause const& c) const { + return !m_clause.is_binary() && (m_clause.get_clause() == &c); + } + + /** + \brief idx is the index of the blocked literal. + m_tautology contains literals that were used to establish that the current members of m_covered_clause is blocked. + This routine removes literals that were not relevant to establishing it was blocked. + + It has a bug: literals that are used to prune tautologies during resolution intersection should be included + in the dependencies. They may get used in some RI prunings and then they have to be included to avoid flipping + RI literals. + */ + void minimize_covered_clause(unsigned idx) { + // IF_VERBOSE(0, verbose_stream() << "minimize: " << m_covered_clause + // << " @ " << idx << "\n" << "tautology: " << m_tautology << "\n";); + literal _blocked = m_covered_clause[idx]; + for (literal l : m_tautology) VERIFY(s.is_marked(l)); + for (literal l : m_covered_clause) s.unmark_visited(l); + for (literal l : m_tautology) s.mark_visited(l); + s.mark_visited(m_covered_clause[idx]); + for (unsigned i = 0; i < m_covered_clause.size(); ++i) { + literal lit = m_covered_clause[i]; + if (m_covered_antecedent[i] == clause_ante()) s.mark_visited(lit); + if (s.is_marked(lit)) idx = i; + } + if (false && _blocked.var() == 8074) { + IF_VERBOSE(0, verbose_stream() << "covered: " << m_covered_clause << "\n"; + verbose_stream() << "tautology: " << m_tautology << "\n"; + verbose_stream() << "index: " << idx << "\n"; + for (unsigned i = idx; i > 0; --i) { + m_covered_antecedent[i].display(verbose_stream(), m_covered_clause[i]); + }); + } + for (unsigned i = idx; i > 0; --i) { + literal lit = m_covered_clause[i]; + //s.mark_visited(lit); + //continue; + if (!s.is_marked(lit)) continue; + clause_ante const& ante = m_covered_antecedent[i]; + if (ante.cls()) { + for (literal l : *ante.cls()) { + if (l != ~lit) s.mark_visited(l); + } + } + if (ante.lit1() != null_literal) { + s.mark_visited(ante.lit1()); + } + if (ante.lit2() != null_literal) { + s.mark_visited(ante.lit2()); + } + } + unsigned j = 0; + literal blocked = null_literal; + for (unsigned i = 0; i <= idx; ++i) { + literal lit = m_covered_clause[i]; + if (s.is_marked(lit)) { + // + // Record that the resolving literal for + // resolution intersection can be flipped. + // + clause_ante const& ante = m_covered_antecedent[i]; + if (ante.from_ri() && blocked != ante.lit1()) { + blocked = ante.lit1(); + VERIFY(s.value(blocked) == l_undef); + m_mc.stackv().push_back(std::make_pair(j, blocked)); + } + m_covered_clause[j++] = lit; + s.unmark_visited(lit); + } + } + for (literal l : m_covered_clause) VERIFY(!s.is_marked(l)); + for (bool_var v = 0; v < s.s.num_vars(); ++v) VERIFY(!s.is_marked(literal(v, true)) && !s.is_marked(literal(v, false))); + + // unsigned sz0 = m_covered_clause.size(); + m_covered_clause.resize(j); + VERIFY(j >= m_clause.size()); + if (false && _blocked.var() == 16774) { + IF_VERBOSE(0, verbose_stream() << "covered: " << m_covered_clause << "\n"); + } + // IF_VERBOSE(0, verbose_stream() << "reduced from size " << sz0 << " to " << m_covered_clause << "\n" << m_clause << "\n";); + } + + /* + * C \/ l l \/ lit + * ------------------- + * C \/ l \/ ~lit + * + * C \/ lit \/ l l \/ lit + * ------------------------ + * l \/ lit C \/ lit \/ l can be removed + * + * C \/ l1 \/ ... \/ lk l1 \/ ... \/ lk \/ lit + * ----------------------------------------------- + * C \/ l1 \/ ... \/ lk \/ ~lit + * unless C contains lit, and it is a tautology. + */ + bool add_ala() { + for (; m_ala_qhead < m_covered_clause.size(); ++m_ala_qhead) { + literal l = m_covered_clause[m_ala_qhead]; + for (watched & w : s.get_wlist(~l)) { + if (w.is_binary_non_learned_clause()) { + literal lit = w.get_literal(); + if (revisit_binary(l, lit)) continue; + if (s.is_marked(lit)) { + return true; + } + if (!s.is_marked(~lit)) { + // if (m_covered_clause[0].var() == 10219) IF_VERBOSE(0, verbose_stream() << "ala: " << l << " " << lit << "\n"); + m_covered_clause.push_back(~lit); + m_covered_antecedent.push_back(clause_ante(l, false)); + s.mark_visited(~lit); + } + } + } + clause_use_list & pos_occs = s.m_use_list.get(l); + clause_use_list::iterator it = pos_occs.mk_iterator(); + for (; !it.at_end(); it.next()) { + clause & c = it.curr(); + if (c.is_learned() || c.was_removed()) continue; + if (revisit_clause(c)) continue; + literal lit1 = null_literal; + bool ok = true; + for (literal lit : c) { + if (lit == l) continue; + if (s.is_marked(lit)) continue; + if (!s.is_marked(~lit) && lit1 == null_literal) { + lit1 = lit; + } + else { + ok = false; + break; + } + } + if (!ok) continue; + if (lit1 == null_literal) { + return true; + } + // if (m_covered_clause[0].var() == 10219) IF_VERBOSE(0, verbose_stream() << "ala: " << c << " " << lit1 << "\n"); + m_covered_clause.push_back(~lit1); + m_covered_antecedent.push_back(clause_ante(c)); + s.mark_visited(~lit1); + } + } + return false; + } + + + /* + * C \/ l ~l \/ lit \/ D_i for i = 1...N all the clauses that have ~l + * ------------------------- + * C \/ l \/ lit + * + * + */ + bool add_cla(literal& blocked) { + for (unsigned i = 0; i < m_covered_clause.size(); ++i) { + literal lit = m_covered_clause[i]; + if (resolution_intersection(lit, false)) { + blocked = m_covered_clause[i]; + minimize_covered_clause(i); + return true; + } + for (literal l : m_intersection) { + if (!s.is_marked(l)) { + s.mark_visited(l); + m_covered_clause.push_back(l); + m_covered_antecedent.push_back(clause_ante(lit, true)); + } + } + } + return false; + } + + bool above_threshold(unsigned sz0) const { + // if (sz0 * 400 < m_covered_clause.size()) IF_VERBOSE(0, verbose_stream() << "above threshold " << sz0 << " " << m_covered_clause.size() << "\n";); + return sz0 * 400 < m_covered_clause.size(); + } + + void reset_mark() { + for (literal l : m_covered_clause) s.unmark_visited(l); + } + + template + elim_type cce(literal& blocked, model_converter::kind& k) { + bool first = true; + unsigned sz = 0, sz0 = m_covered_clause.size(); + for (literal l : m_covered_clause) s.mark_visited(l); + shuffle(m_covered_clause.size(), m_covered_clause.c_ptr(), s.s.m_rand); + m_tautology.reset(); + m_mc.stackv().reset(); + m_ala_qhead = 0; + + switch (et) { + case cce_t: + k = model_converter::CCE; + break; + case acce_t: + k = model_converter::ACCE; + break; + default: + k = model_converter::BCE; + break; + } + + /* + * For blocked clause elimination with asymmetric literal addition (ABCE) + * it suffices to check if one of the original + * literals in the clause is blocked modulo the additional literals added to the clause. + * So we record sz0, the original set of literals in the clause, mark additional literals, + * and then check if any of the first sz0 literals are blocked. + */ + + if (et == ate_t) { + bool ala = add_ala(); + reset_mark(); + m_covered_clause.shrink(sz0); + return ala ? ate_t : no_t; + } + + while (m_covered_clause.size() > sz && !above_threshold(sz0)) { + + if ((et == abce_t || et == acce_t) && add_ala()) { + reset_mark(); + if (first) { + m_covered_clause.shrink(sz0); + } + else { + /* + * tautology depends on resolution intersection. + * literals used for resolution intersection may have to be flipped. + */ + for (literal l : m_covered_clause) { + m_tautology.push_back(l); + s.mark_visited(l); + } + minimize_covered_clause(m_covered_clause.size()-1); + } + return ate_t; + } + + if (first) { + for (unsigned i = 0; i < sz0; ++i) { + if (check_abce_tautology(m_covered_clause[i])) { + blocked = m_covered_clause[i]; + reset_mark(); +#if 0 + if (sz0 == 3 && blocked.var() == 10219) { + IF_VERBOSE(0, verbose_stream() << "abce: " << m_covered_clause << "\n"; + for (literal l : m_covered_clause) verbose_stream() << s.value(l) << "\n"; + ); + literal l = blocked; + clause_use_list & neg_occs = s.m_use_list.get(~l); + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { + clause & c = it.curr(); + IF_VERBOSE(0, verbose_stream() << c << "\n"); + } + } +#endif + m_covered_clause.shrink(sz0); + if (et == bce_t) return bce_t; + k = model_converter::ABCE; + return abce_t; + } + } + } + first = false; + + if (et == abce_t || et == bce_t) { + break; + } + + /* + * Add resolution intersection while checking if the clause becomes a tautology. + */ + sz = m_covered_clause.size(); + if ((et == cce_t || et == acce_t) && add_cla(blocked)) { + reset_mark(); + return et; + } + } + reset_mark(); + return no_t; + } + + // perform covered clause elimination. + // first extract the covered literal addition (CLA). + // then check whether the CLA is blocked. + template + elim_type cce(clause& c, literal& blocked, model_converter::kind& k) { + m_clause = clause_wrapper(c); + m_covered_clause.reset(); + m_covered_antecedent.reset(); + for (literal l : c) { + m_covered_clause.push_back(l); + m_covered_antecedent.push_back(clause_ante()); + } + return cce(blocked, k); + } + + template + elim_type cce(literal l1, literal l2, literal& blocked, model_converter::kind& k) { + m_clause = clause_wrapper(l1, l2); + m_covered_clause.reset(); + m_covered_antecedent.reset(); + m_covered_clause.push_back(l1); + m_covered_clause.push_back(l2); + m_covered_antecedent.push_back(clause_ante()); + m_covered_antecedent.push_back(clause_ante()); + return cce(blocked, k); + } + + template + void cce() { + insert_queue(); + cce_clauses(); + cce_binary(); + } + + template + void cce_binary() { + while (!m_queue.empty() && m_counter >= 0) { s.checkpoint(); - if (m_counter < 0) - return; - literal l = m_queue.next(); - process(l); + process_cce_binary(m_queue.next()); } } - void process(literal l) { - TRACE("blocked_clause", tout << "processing: " << l << "\n";); - model_converter::entry * new_entry = 0; - if (!process_var(l.var())) { + template + void process_cce_binary(literal l) { + literal blocked = null_literal; + watch_list & wlist = s.get_wlist(~l); + m_counter -= wlist.size(); + model_converter::kind k; + for (watched & w : wlist) { + if (!w.is_binary_non_learned_clause()) continue; + if (!select_clause(2)) continue; + literal l2 = w.get_literal(); + elim_type r = cce(l, l2, blocked, k); + inc_bc(r); + switch (r) { + case ate_t: + w.set_learned(true); + s.s.set_learned1(l2, l, true); + m_mc.add_ate(m_covered_clause); + break; + case no_t: + break; + default: + w.set_learned(true); + s.s.set_learned1(l2, l, true); + block_covered_binary(w, l, blocked, k); + break; + } + } + } + + template + void cce_clauses() { + literal blocked; + model_converter::kind k; + for (clause* cp : s.s.m_clauses) { + clause& c = *cp; + if (c.was_removed() || c.is_learned()) continue; + if (!select_clause(c.size())) continue; + elim_type r = cce(c, blocked, k); + inc_bc(r); + switch (r) { + case ate_t: + m_mc.add_ate(m_covered_clause); + s.set_learned(c); + break; + case no_t: + break; + default: + block_covered_clause(c, blocked, k); + s.set_learned(c); + break; + } + } + } + + void inc_bc(elim_type et) { + switch (et) { + case cce_t: s.m_num_cce++; break; + case acce_t: s.m_num_acce++; break; + case abce_t: s.m_num_abce++; break; + case ate_t: s.m_num_ate++; break; + case bce_t: s.m_num_bce++; break; + default: break; + } + } + + // select 25% of clauses size 2, 3 + // always try to remove larger clauses. + template + bool select_clause(unsigned sz) { + return (sz > 3) || s.s.m_rand(4) == 0; + } + + void block_covered_clause(clause& c, literal l, model_converter::kind k) { + if (false) { + IF_VERBOSE(0, verbose_stream() << "blocked: " << l << " @ " << c << " :covered " << m_covered_clause << "\n"; + s.m_use_list.display(verbose_stream() << "use " << l << ":", l); + s.m_use_list.display(verbose_stream() << "use " << ~l << ":", ~l); + s.s.display_watch_list(verbose_stream() << ~l << ": ", s.get_wlist(l)) << "\n"; + s.s.display_watch_list(verbose_stream() << l << ": ", s.get_wlist(~l)) << "\n"; + ); + + } + TRACE("blocked_clause", tout << "new blocked clause: " << c << "\n";); + SASSERT(!s.is_external(l)); + model_converter::entry& new_entry = m_mc.mk(k, l.var()); + for (literal lit : c) { + if (lit != l && process_var(lit.var())) { + m_queue.decreased(~lit); + } + } + m_mc.insert(new_entry, m_covered_clause); + } + + void block_covered_binary(watched const& w, literal l1, literal blocked, model_converter::kind k) { + SASSERT(!s.is_external(blocked)); + model_converter::entry& new_entry = m_mc.mk(k, blocked.var()); + literal l2 = w.get_literal(); + TRACE("blocked_clause", tout << "new blocked clause: " << l2 << " " << l1 << "\n";); + s.set_learned(l1, l2); + m_mc.insert(new_entry, m_covered_clause); + m_queue.decreased(~l2); + } + + void bca() { + m_queue.reset(); + insert_queue(); + while (!m_queue.empty() && m_counter >= 0) { + s.checkpoint(); + bca(m_queue.next()); + } + } + + /* + \brief blocked binary clause addition for literal l + Let RI be the resolution intersection with l, e.g, RI are the literals + that are in all clauses of the form ~l \/ C. + If RI is non-empty, then let l' be a literal in RI. + Then the following binary clause is blocked: l \/ ~l' + */ + void bca(literal l) { + m_tautology.reset(); + if (resolution_intersection(l, true)) { + // this literal is pure. return; } - { - m_to_remove.reset(); - { - clause_use_list & occs = s.m_use_list.get(l); - clause_use_list::iterator it = occs.mk_iterator(); - while (!it.at_end()) { - clause & c = it.curr(); - m_counter -= c.size(); - SASSERT(c.contains(l)); - s.mark_all_but(c, l); - if (all_tautology(l)) { - TRACE("blocked_clause", tout << "new blocked clause: " << c << "\n";); - if (new_entry == 0) - new_entry = &(mc.mk(model_converter::BLOCK_LIT, l.var())); - m_to_remove.push_back(&c); - s.m_num_blocked_clauses++; - mc.insert(*new_entry, c); - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - literal lit = c[i]; - if (lit != l && process_var(lit.var())) { - m_queue.decreased(~lit); - } - } - } - s.unmark_all(c); - it.next(); - } + for (literal l2 : m_intersection) { + watched* w = find_binary_watch(s.get_wlist(~l), ~l2); + if (!w) { + s.s.mk_bin_clause(l, ~l2, true); + ++s.m_num_bca; } - clause_vector::iterator it = m_to_remove.begin(); - clause_vector::iterator end = m_to_remove.end(); - for (; it != end; ++it) { - s.remove_clause(*(*it)); - } - } - { - watch_list & wlist = s.get_wlist(~l); - m_counter -= wlist.size(); - watch_list::iterator it = wlist.begin(); - watch_list::iterator it2 = it; - watch_list::iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_clause()) { - *it2 = *it; - it2++; - continue; - } - literal l2 = it->get_literal(); - s.mark_visited(l2); - if (all_tautology(l)) { - if (new_entry == 0) - new_entry = &(mc.mk(model_converter::BLOCK_LIT, l.var())); - TRACE("blocked_clause", tout << "new blocked clause: " << l2 << " " << l << "\n";); - s.remove_bin_clause_half(l2, l, it->is_learned()); - s.m_num_blocked_clauses++; - m_queue.decreased(~l2); - mc.insert(*new_entry, l, l2); - } - else { - *it2 = *it; - it2++; - } - s.unmark_visited(l2); - } - wlist.set_end(it2); } } bool all_tautology(literal l) { - { - watch_list & wlist = s.get_wlist(l); - m_counter -= wlist.size(); - watch_list::iterator it = wlist.begin(); - watch_list::iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_non_learned_clause()) - continue; - if (!s.is_marked(~it->get_literal())) - return false; - } + watch_list & wlist = s.get_wlist(l); + m_counter -= wlist.size(); + for (auto const& w : wlist) { + if (w.is_binary_non_learned_clause() && + !s.is_marked(~w.get_literal())) + return false; } - { - clause_use_list & neg_occs = s.m_use_list.get(~l); - clause_use_list::iterator it = neg_occs.mk_iterator(); - while (!it.at_end()) { - clause & c = it.curr(); - m_counter -= c.size(); - unsigned sz = c.size(); - unsigned i; - for (i = 0; i < sz; i++) { - if (s.is_marked(~c[i])) - break; - } - if (i == sz) + + clause_use_list & neg_occs = s.m_use_list.get(~l); + clause_use_list::iterator it = neg_occs.mk_iterator(); + for (; !it.at_end(); it.next()) { + clause & c = it.curr(); + if (c.is_learned()) continue; + if (c.was_removed()) continue; + m_counter -= c.size(); + unsigned sz = c.size(); + unsigned i; + for (i = 0; i < sz; i++) { + if (s.is_marked(~c[i])) + break; + } + if (i == sz) + return false; + } + + if (s.s.m_ext) { + ext_constraint_list const& ext_list = s.m_ext_use_list.get(~l); + for (ext_constraint_idx idx : ext_list) { + if (!s.s.m_ext->is_blocked(l, idx)) { return false; - it.next(); + } } } return true; @@ -1058,37 +1676,54 @@ namespace sat { struct simplifier::blocked_cls_report { simplifier & m_simplifier; stopwatch m_watch; - unsigned m_num_blocked_clauses; + unsigned m_num_bce; + unsigned m_num_cce; + unsigned m_num_acce; + unsigned m_num_abce; + unsigned m_num_ate; + unsigned m_num_bca; blocked_cls_report(simplifier & s): m_simplifier(s), - m_num_blocked_clauses(s.m_num_blocked_clauses) { + m_num_bce(s.m_num_bce), + m_num_cce(s.m_num_cce), + m_num_acce(s.m_num_acce), + m_num_abce(s.m_num_abce), + m_num_ate(s.m_num_ate), + m_num_bca(s.m_num_bca) { m_watch.start(); } ~blocked_cls_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-blocked-clauses :elim-blocked-clauses " - << (m_simplifier.m_num_blocked_clauses - m_num_blocked_clauses) - << mem_stat() + verbose_stream() << " (sat-blocked-clauses"; + report(m_simplifier.m_num_ate, m_num_ate, " :ate "); + report(m_simplifier.m_num_bce, m_num_bce, " :bce "); + report(m_simplifier.m_num_abce, m_num_abce, " :abce "); + report(m_simplifier.m_num_cce, m_num_cce, " :cce "); + report(m_simplifier.m_num_bca, m_num_bca, " :bca "); + report(m_simplifier.m_num_acce, m_num_acce, " :acce "); + verbose_stream() << mem_stat() << " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds() << ")\n";); } + + void report(unsigned n, unsigned m, char const* s) { + if (n > m) verbose_stream() << s << (n - m); + } }; void simplifier::elim_blocked_clauses() { TRACE("blocked_clause_bug", tout << "trail: " << s.m_trail.size() << "\n"; s.display_watches(tout); s.display(tout);); blocked_cls_report rpt(*this); blocked_clause_elim elim(*this, m_blocked_clause_limit, s.m_mc, m_use_list, s.m_watches); - elim(s.num_vars()); + elim(); } - unsigned simplifier::get_num_non_learned_bin(literal l) const { + unsigned simplifier::num_nonlearned_bin(literal l) const { unsigned r = 0; - watch_list const & wlist = get_wlist(~l); - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (it->is_binary_non_learned_clause()) + watch_list const & wlist = get_wlist(~l); + for (auto & w : wlist) { + if (w.is_binary_non_learned_clause()) r++; } return r; @@ -1099,8 +1734,8 @@ namespace sat { literal neg_l(v, true); unsigned num_pos = m_use_list.get(pos_l).size(); unsigned num_neg = m_use_list.get(neg_l).size(); - unsigned num_bin_pos = get_num_non_learned_bin(pos_l); - unsigned num_bin_neg = get_num_non_learned_bin(neg_l); + unsigned num_bin_pos = num_nonlearned_bin(pos_l); + unsigned num_bin_neg = num_nonlearned_bin(neg_l); unsigned cost = 2 * num_pos * num_neg + num_pos * num_bin_neg + num_neg * num_bin_pos; CTRACE("elim_vars_detail", cost == 0, tout << v << " num_pos: " << num_pos << " num_neg: " << num_neg << " num_bin_pos: " << num_bin_pos << " num_bin_neg: " << num_bin_neg << " cost: " << cost << "\n";); @@ -1115,10 +1750,7 @@ namespace sat { void simplifier::order_vars_for_elim(bool_var_vector & r) { svector tmp; - bool_var_set::iterator it = m_elim_todo.begin(); - bool_var_set::iterator end = m_elim_todo.end(); - for (; it != end; ++it) { - bool_var v = *it; + for (bool_var v : m_elim_todo) { if (is_external(v)) continue; if (was_eliminated(v)) @@ -1131,17 +1763,10 @@ namespace sat { m_elim_todo.reset(); std::stable_sort(tmp.begin(), tmp.end(), bool_var_and_cost_lt()); TRACE("elim_vars", - svector::iterator it = tmp.begin(); - svector::iterator end = tmp.end(); - for (; it != end; ++it) { - tout << "(" << it->first << ", " << it->second << ") "; - } + for (auto& p : tmp) tout << "(" << p.first << ", " << p.second << ") "; tout << "\n";); - svector::iterator it2 = tmp.begin(); - svector::iterator end2 = tmp.end(); - for (; it2 != end2; ++it2) { - r.push_back(it2->first); - } + for (auto& p : tmp) + r.push_back(p.first); } /** @@ -1149,19 +1774,18 @@ namespace sat { */ void simplifier::collect_clauses(literal l, clause_wrapper_vector & r) { clause_use_list const & cs = m_use_list.get(l); - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { - r.push_back(clause_wrapper(it.curr())); - SASSERT(r.back().size() == it.curr().size()); - it.next(); + for (auto it = cs.mk_iterator(); !it.at_end(); it.next()) { + clause& c = it.curr(); + if (!c.is_learned() && !c.was_removed()) { + r.push_back(clause_wrapper(c)); + SASSERT(r.back().size() == c.size()); + } } watch_list & wlist = get_wlist(~l); - watch_list::iterator it2 = wlist.begin(); - watch_list::iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_non_learned_clause()) { - r.push_back(clause_wrapper(l, it2->get_literal())); + for (auto & w : wlist) { + if (w.is_binary_non_learned_clause()) { + r.push_back(clause_wrapper(l, w.get_literal())); SASSERT(r.back().size() == 2); } } @@ -1179,20 +1803,19 @@ namespace sat { SASSERT(c1.contains(l)); SASSERT(c2.contains(~l)); bool res = true; - unsigned sz = c1.size(); - m_elim_counter -= sz; - for (unsigned i = 0; i < sz; i++) { - literal l2 = c1[i]; - if (l == l2) + m_elim_counter -= c1.size() + c2.size(); + unsigned sz1 = c1.size(); + for (unsigned i = 0; i < sz1; ++i) { + literal l1 = c1[i]; + if (l == l1) continue; - m_visited[l2.index()] = true; - r.push_back(l2); + m_visited[l1.index()] = true; + r.push_back(l1); } literal not_l = ~l; - sz = c2.size(); - m_elim_counter -= sz; - for (unsigned i = 0; i < sz; i++) { + unsigned sz2 = c2.size(); + for (unsigned i = 0; i < sz2; ++i) { literal l2 = c2[i]; if (not_l == l2) continue; @@ -1204,50 +1827,50 @@ namespace sat { r.push_back(l2); } - sz = c1.size(); - for (unsigned i = 0; i < sz; i++) { - literal l2 = c1[i]; - if (l == l2) - continue; - m_visited[l2.index()] = false; + for (unsigned i = 0; i < sz1; ++i) { + literal l1 = c1[i]; + m_visited[l1.index()] = false; } return res; } void simplifier::save_clauses(model_converter::entry & mc_entry, clause_wrapper_vector const & cs) { - model_converter & mc = s.m_mc; - clause_wrapper_vector::const_iterator it = cs.begin(); - clause_wrapper_vector::const_iterator end = cs.end(); - for (; it != end; ++it) - mc.insert(mc_entry, *it); + for (auto & e : cs) { + s.m_mc.insert(mc_entry, e); + } } void simplifier::add_non_learned_binary_clause(literal l1, literal l2) { - watch_list & wlist1 = s.m_watches[(~l1).index()]; - watch_list & wlist2 = s.m_watches[(~l2).index()]; - watch_list::iterator it1 = wlist1.begin(); - watch_list::iterator end1 = wlist1.end(); - for (; it1 != end1; ++it1) { - if (it1->is_binary_clause() && it1->get_literal() == l2) { - *it1 = watched(l2, false); - watch_list::iterator it2 = wlist2.begin(); - watch_list::iterator end2 = wlist2.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause() && it2->get_literal() == l1) { - *it2 = watched(l1, false); - break; - } - } - CTRACE("resolve_bug", it2 == end2, - tout << ~l1 << " -> "; - display(tout, s.m_cls_allocator, wlist1); tout << "\n" << ~l2 << " -> "; - display(tout, s.m_cls_allocator, wlist2); tout << "\n";); - SASSERT(it2 != end2); - return; - } +#if 0 + if ((l1.var() == 2039 || l2.var() == 2039) && + (l1.var() == 27042 || l2.var() == 27042)) { + IF_VERBOSE(1, verbose_stream() << "add_bin: " << l1 << " " << l2 << "\n"); } - wlist1.push_back(watched(l2, false)); - wlist2.push_back(watched(l1, false)); +#endif +#if 0 + watched* w; + watch_list & wlist1 = get_wlist(~l1); + watch_list & wlist2 = get_wlist(~l2); + w = find_binary_watch(wlist1, l2); + if (w) { + if (w->is_learned()) + w->set_learned(false); + } + else { + wlist1.push_back(watched(l2, false)); + } + + w = find_binary_watch(wlist2, l1); + if (w) { + if (w->is_learned()) + w->set_learned(false); + } + else { + wlist2.push_back(watched(l1, false)); + } +#else + s.mk_bin_clause(l1, l2, false); +#endif } /** @@ -1255,17 +1878,20 @@ namespace sat { */ void simplifier::remove_bin_clauses(literal l) { watch_list & wlist = get_wlist(~l); - watch_list::iterator it = wlist.begin(); - watch_list::iterator end = wlist.end(); - for (; it != end; ++it) { - if (it->is_binary_clause()) { - literal l2 = it->get_literal(); + for (auto & w : wlist) { + if (w.is_binary_clause()) { + literal l2 = w.get_literal(); watch_list & wlist2 = get_wlist(~l2); watch_list::iterator it2 = wlist2.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist2.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause() && it2->get_literal() == l) { + if ((l.var() == 2039 || l2.var() == 2039) && + (l.var() == 27042 || l2.var() == 27042)) { + IF_VERBOSE(1, verbose_stream() << "remove_bin: " << l << " " << l2 << "\n"); + } + TRACE("bin_clause_bug", tout << "removing: " << l << " " << it2->get_literal() << "\n";); m_sub_bin_todo.erase(bin_clause(l2, l, it2->is_learned())); continue; @@ -1274,7 +1900,7 @@ namespace sat { itprev++; } wlist2.set_end(itprev); - m_sub_bin_todo.erase(bin_clause(l, l2, it->is_learned())); + m_sub_bin_todo.erase(bin_clause(l, l2, w.is_learned())); } } TRACE("bin_clause_bug", tout << "collapsing watch_list of: " << l << "\n";); @@ -1285,8 +1911,7 @@ namespace sat { \brief Eliminate the clauses where the variable being eliminated occur. */ void simplifier::remove_clauses(clause_use_list const & cs, literal l) { - clause_use_list::iterator it = cs.mk_iterator(); - while (!it.at_end()) { + for (auto it = cs.mk_iterator(); !it.at_end(); ) { clause & c = it.curr(); it.next(); SASSERT(c.contains(l)); @@ -1305,12 +1930,12 @@ namespace sat { literal pos_l(v, false); literal neg_l(v, true); - unsigned num_bin_pos = get_num_non_learned_bin(pos_l); - unsigned num_bin_neg = get_num_non_learned_bin(neg_l); + unsigned num_bin_pos = num_nonlearned_bin(pos_l); + unsigned num_bin_neg = num_nonlearned_bin(neg_l); clause_use_list & pos_occs = m_use_list.get(pos_l); clause_use_list & neg_occs = m_use_list.get(neg_l); - unsigned num_pos = pos_occs.size() + num_bin_pos; - unsigned num_neg = neg_occs.size() + num_bin_neg; + unsigned num_pos = pos_occs.num_irredundant() + num_bin_pos; + unsigned num_neg = neg_occs.num_irredundant() + num_bin_neg; TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << "\n";); @@ -1319,20 +1944,14 @@ namespace sat { unsigned before_lits = num_bin_pos*2 + num_bin_neg*2; - { - clause_use_list::iterator it = pos_occs.mk_iterator(); - while (!it.at_end()) { + for (auto it = pos_occs.mk_iterator(); !it.at_end(); it.next()) { + if (!it.curr().is_learned()) before_lits += it.curr().size(); - it.next(); - } } - { - clause_use_list::iterator it2 = neg_occs.mk_iterator(); - while (!it2.at_end()) { - before_lits += it2.curr().size(); - it2.next(); - } + for (auto it = neg_occs.mk_iterator(); !it.at_end(); it.next()) { + if (!it.curr().is_learned()) + before_lits += it.curr().size(); } TRACE("resolution", tout << v << " num_pos: " << num_pos << " neg_pos: " << num_neg << " before_lits: " << before_lits << "\n";); @@ -1354,16 +1973,12 @@ namespace sat { TRACE("resolution_detail", tout << "collecting number of after_clauses\n";); unsigned before_clauses = num_pos + num_neg; unsigned after_clauses = 0; - clause_wrapper_vector::iterator it1 = m_pos_cls.begin(); - clause_wrapper_vector::iterator end1 = m_pos_cls.end(); - for (; it1 != end1; ++it1) { - clause_wrapper_vector::iterator it2 = m_neg_cls.begin(); - clause_wrapper_vector::iterator end2 = m_neg_cls.end(); - for (; it2 != end2; ++it2) { + for (clause_wrapper& c1 : m_pos_cls) { + for (clause_wrapper& c2 : m_neg_cls) { m_new_cls.reset(); - if (resolve(*it1, *it2, pos_l, m_new_cls)) { - TRACE("resolution_detail", tout << *it1 << "\n" << *it2 << "\n-->\n"; - for (unsigned i = 0; i < m_new_cls.size(); i++) tout << m_new_cls[i] << " "; tout << "\n";); + if (resolve(c1, c2, pos_l, m_new_cls)) { + TRACE("resolution_detail", tout << c1 << "\n" << c2 << "\n-->\n"; + for (literal l : m_new_cls) tout << l << " "; tout << "\n";); after_clauses++; if (after_clauses > before_clauses) { TRACE("resolution", tout << "too many after clauses: " << after_clauses << "\n";); @@ -1375,7 +1990,18 @@ namespace sat { TRACE("resolution", tout << "found var to eliminate, before: " << before_clauses << " after: " << after_clauses << "\n";); m_elim_counter -= num_pos * num_neg + before_lits; + m_elim_counter -= num_pos * num_neg + before_lits; + + if (false) { + literal l(v, false); + IF_VERBOSE(0, + verbose_stream() << "elim: " << l << "\n"; + s.display_watch_list(verbose_stream() << ~l << ": ", get_wlist(l)) << "\n"; + s.display_watch_list(verbose_stream() << l << ": ", get_wlist(~l)) << "\n";); + } // eliminate variable + ++s.m_stats.m_elim_var_res; + VERIFY(!is_external(v)); model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); save_clauses(mc_entry, m_pos_cls); save_clauses(mc_entry, m_neg_cls); @@ -1389,16 +2015,13 @@ namespace sat { m_elim_counter -= num_pos * num_neg + before_lits; - it1 = m_pos_cls.begin(); - end1 = m_pos_cls.end(); - for (; it1 != end1; ++it1) { - clause_wrapper_vector::iterator it2 = m_neg_cls.begin(); - clause_wrapper_vector::iterator end2 = m_neg_cls.end(); - for (; it2 != end2; ++it2) { + for (auto & c1 : m_pos_cls) { + for (auto & c2 : m_neg_cls) { m_new_cls.reset(); - if (!resolve(*it1, *it2, pos_l, m_new_cls)) + if (!resolve(c1, c2, pos_l, m_new_cls)) continue; - TRACE("resolution_new_cls", tout << *it1 << "\n" << *it2 << "\n-->\n" << m_new_cls << "\n";); + if (false && v == 767) IF_VERBOSE(0, verbose_stream() << "elim: " << c1 << " + " << c2 << " -> " << m_new_cls << "\n"); + TRACE("resolution_new_cls", tout << c1 << "\n" << c2 << "\n-->\n" << m_new_cls << "\n";); if (cleanup_clause(m_new_cls)) continue; // clause is already satisfied. switch (m_new_cls.size()) { @@ -1418,8 +2041,11 @@ namespace sat { s.m_stats.m_mk_ter_clause++; else s.m_stats.m_mk_clause++; - clause * new_c = s.m_cls_allocator.mk_clause(m_new_cls.size(), m_new_cls.c_ptr(), false); + clause * new_c = s.alloc_clause(m_new_cls.size(), m_new_cls.c_ptr(), false); + + if (s.m_config.m_drat) s.m_drat.add(*new_c, true); s.m_clauses.push_back(new_c); + m_use_list.insert(*new_c); if (m_sub_counter > 0) back_subsumption1(*new_c); @@ -1431,7 +2057,6 @@ namespace sat { return true; } } - return true; } @@ -1448,7 +2073,7 @@ namespace sat { ~elim_var_report() { m_watch.stop(); IF_VERBOSE(SAT_VB_LVL, - verbose_stream() << " (sat-resolution :elim-bool-vars " + verbose_stream() << " (sat-resolution :elim-vars " << (m_simplifier.m_num_elim_vars - m_num_elim_vars) << " :threshold " << m_simplifier.m_elim_counter << mem_stat() @@ -1457,19 +2082,22 @@ namespace sat { }; void simplifier::elim_vars() { - if (!m_elim_vars) return; + if (!elim_vars_enabled()) return; elim_var_report rpt(*this); bool_var_vector vars; order_vars_for_elim(vars); - - bool_var_vector::iterator it = vars.begin(); - bool_var_vector::iterator end = vars.end(); - for (; it != end; ++it) { + sat::elim_vars elim_bdd(*this); + for (bool_var v : vars) { checkpoint(); - if (m_elim_counter < 0) - return; - bool_var v = *it; - if (try_eliminate(v)) { + if (m_elim_counter < 0) + break; + if (is_external(v)) { + // skip + } + else if (try_eliminate(v)) { + m_num_elim_vars++; + } + else if (elim_vars_bdd_enabled() && elim_bdd(v)) { m_num_elim_vars++; } } @@ -1481,10 +2109,16 @@ namespace sat { void simplifier::updt_params(params_ref const & _p) { sat_simplifier_params p(_p); - m_elim_blocked_clauses = p.elim_blocked_clauses(); - m_elim_blocked_clauses_at = p.elim_blocked_clauses_at(); + m_cce = p.cce(); + m_acce = p.acce(); + m_bca = false && p.bca(); // disabled + m_abce = p.abce(); + m_ate = p.ate(); + m_bce_delay = p.bce_delay(); + m_bce = p.bce(); + m_bce_at = p.bce_at(); + m_retain_blocked_clauses = p.retain_blocked_clauses(); m_blocked_clause_limit = p.blocked_clause_limit(); - m_resolution = p.resolution(); m_res_limit = p.resolution_limit(); m_res_occ_cutoff = p.resolution_occ_cutoff(); m_res_occ_cutoff1 = p.resolution_occ_cutoff_range1(); @@ -1498,6 +2132,9 @@ namespace sat { m_subsumption = p.subsumption(); m_subsumption_limit = p.subsumption_limit(); m_elim_vars = p.elim_vars(); + m_elim_vars_bdd = false && p.elim_vars_bdd(); // buggy? + m_elim_vars_bdd_delay = p.elim_vars_bdd_delay(); + m_incremental_mode = s.get_config().m_incremental && !p.override_incremental(); } void simplifier::collect_param_descrs(param_descrs & r) { @@ -1508,15 +2145,24 @@ namespace sat { st.update("subsumed", m_num_subsumed); st.update("subsumption resolution", m_num_sub_res); st.update("elim literals", m_num_elim_lits); - st.update("elim bool vars", m_num_elim_vars); - st.update("elim blocked clauses", m_num_blocked_clauses); + st.update("bce", m_num_bce); + st.update("cce", m_num_cce); + st.update("acce", m_num_acce); + st.update("abce", m_num_abce); + st.update("bca", m_num_bca); + st.update("ate", m_num_ate); } void simplifier::reset_statistics() { - m_num_blocked_clauses = 0; + m_num_bce = 0; + m_num_cce = 0; + m_num_acce = 0; + m_num_abce = 0; m_num_subsumed = 0; m_num_sub_res = 0; m_num_elim_lits = 0; m_num_elim_vars = 0; + m_num_bca = 0; + m_num_ate = 0; } }; diff --git a/src/sat/sat_simplifier.h b/src/sat/sat_simplifier.h index 44e4276e0..3787b5894 100644 --- a/src/sat/sat_simplifier.h +++ b/src/sat/sat_simplifier.h @@ -25,6 +25,7 @@ Revision History: #include "sat/sat_clause.h" #include "sat/sat_clause_set.h" #include "sat/sat_clause_use_list.h" +#include "sat/sat_extension.h" #include "sat/sat_watched.h" #include "sat/sat_model_converter.h" #include "util/heap.h" @@ -39,17 +40,23 @@ namespace sat { public: void init(unsigned num_vars); void insert(clause & c); + void block(clause & c); + void unblock(clause & c); void erase(clause & c); void erase(clause & c, literal l); clause_use_list & get(literal l) { return m_use_list[l.index()]; } clause_use_list const & get(literal l) const { return m_use_list[l.index()]; } void finalize() { m_use_list.finalize(); } + std::ostream& display(std::ostream& out, literal l) const { return m_use_list[l.index()].display(out); } }; class simplifier { + friend class ba_solver; + friend class elim_vars; solver & s; unsigned m_num_calls; use_list m_use_list; + ext_use_list m_ext_use_list; clause_set m_sub_todo; svector m_sub_bin_todo; unsigned m_last_sub_trail_sz; // size of the trail since last cleanup @@ -65,9 +72,17 @@ namespace sat { int m_elim_counter; // config - bool m_elim_blocked_clauses; - unsigned m_elim_blocked_clauses_at; + bool m_abce; // block clauses using asymmetric added literals + bool m_cce; // covered clause elimination + bool m_acce; // cce with asymetric literal addition + bool m_bca; // blocked (binary) clause addition. + unsigned m_bce_delay; + bool m_bce; // blocked clause elimination + bool m_ate; // asymmetric tautology elimination + unsigned m_bce_at; + bool m_retain_blocked_clauses; unsigned m_blocked_clause_limit; + bool m_incremental_mode; bool m_resolution; unsigned m_res_limit; unsigned m_res_occ_cutoff; @@ -83,9 +98,16 @@ namespace sat { bool m_subsumption; unsigned m_subsumption_limit; bool m_elim_vars; + bool m_elim_vars_bdd; + unsigned m_elim_vars_bdd_delay; // stats - unsigned m_num_blocked_clauses; + unsigned m_num_bce; + unsigned m_num_cce; + unsigned m_num_acce; + unsigned m_num_abce; + unsigned m_num_bca; + unsigned m_num_ate; unsigned m_num_subsumed; unsigned m_num_elim_vars; unsigned m_num_sub_res; @@ -114,7 +136,8 @@ namespace sat { void remove_clause_core(clause & c); void remove_clause(clause & c); void remove_clause(clause & c, literal l); - void remove_bin_clause_half(literal l1, literal l2, bool learned); + void set_learned(clause & c); + void set_learned(literal l1, literal l2); bool_var get_min_occ_var(clause const & c) const; bool subsumes1(clause const & c1, clause const & c2, literal & l); @@ -133,18 +156,18 @@ namespace sat { bool cleanup_clause(clause & c, bool in_use_list); bool cleanup_clause(literal_vector & c); - void propagate_unit(literal l); void elim_lit(clause & c, literal l); void elim_dup_bins(); bool subsume_with_binaries(); void mark_as_not_learned_core(watch_list & wlist, literal l2); void mark_as_not_learned(literal l1, literal l2); - void subsume(); void cleanup_watches(); + void move_clauses(clause_vector & cs, bool learned); void cleanup_clauses(clause_vector & cs, bool learned, bool vars_eliminated, bool in_use_lists); bool is_external(bool_var v) const; + bool is_external(literal l) const { return is_external(l.var()); } bool was_eliminated(bool_var v) const; lbool value(bool_var v) const; lbool value(literal l) const; @@ -154,7 +177,18 @@ namespace sat { struct blocked_clause_elim; void elim_blocked_clauses(); - unsigned get_num_non_learned_bin(literal l) const; + bool single_threaded() const; // { return s.m_config.m_num_threads == 1; } + bool bce_enabled_base() const; + bool ate_enabled() const; + bool bce_enabled() const; + bool acce_enabled() const; + bool cce_enabled() const; + bool abce_enabled() const; + bool bca_enabled() const; + bool elim_vars_bdd_enabled() const; + bool elim_vars_enabled() const; + + unsigned num_nonlearned_bin(literal l) const; unsigned get_to_elim_cost(bool_var v) const; void order_vars_for_elim(bool_var_vector & r); void collect_clauses(literal l, clause_wrapper_vector & r); @@ -185,6 +219,8 @@ namespace sat { simplifier(solver & s, params_ref const & p); ~simplifier(); + void init_search() { m_num_calls = 0; } + void insert_elim_todo(bool_var v) { m_elim_todo.insert(v); } void reset_todos() { @@ -202,6 +238,10 @@ namespace sat { void collect_statistics(statistics & st) const; void reset_statistics(); + + void propagate_unit(literal l); + void subsume(); + }; }; diff --git a/src/sat/sat_simplifier_params.pyg b/src/sat/sat_simplifier_params.pyg index ff2944987..3757aad2d 100644 --- a/src/sat/sat_simplifier_params.pyg +++ b/src/sat/sat_simplifier_params.pyg @@ -1,10 +1,17 @@ def_module_params(module_name='sat', class_name='sat_simplifier_params', export=True, - params=(('elim_blocked_clauses', BOOL, False, 'eliminate blocked clauses'), - ('elim_blocked_clauses_at', UINT, 2, 'eliminate blocked clauses only once at the given simplification round'), + params=(('bce', BOOL, False, 'eliminate blocked clauses'), + ('abce', BOOL, False, 'eliminate blocked clauses using asymmmetric literals'), + ('cce', BOOL, False, 'eliminate covered clauses'), + ('ate', BOOL, True, 'asymmetric tautology elimination'), + ('acce', BOOL, False, 'eliminate covered clauses using asymmetric added literals'), + ('bce_at', UINT, 2, 'eliminate blocked clauses only once at the given simplification round'), + ('bca', BOOL, False, 'blocked clause addition - add blocked binary clauses'), + ('bce_delay', UINT, 2, 'delay eliminate blocked clauses until simplification round'), + ('retain_blocked_clauses', BOOL, True, 'retain blocked clauses as lemmas'), ('blocked_clause_limit', UINT, 100000000, 'maximum number of literals visited during blocked clause elimination'), - ('resolution', BOOL, True, 'eliminate boolean variables using resolution'), + ('override_incremental', BOOL, False, 'override incemental safety gaps. Enable elimination of blocked clauses and variables even if solver is reused'), ('resolution.limit', UINT, 500000000, 'approx. maximum number of literals visited during variable elimination'), ('resolution.occ_cutoff', UINT, 10, 'first cutoff (on number of positive/negative occurrences) for Boolean variable elimination'), ('resolution.occ_cutoff_range1', UINT, 8, 'second cutoff (number of positive/negative occurrences) for Boolean variable elimination, for problems containing less than res_cls_cutoff1 clauses'), @@ -15,6 +22,13 @@ def_module_params(module_name='sat', ('resolution.lit_cutoff_range3', UINT, 300, 'second cutoff (total number of literals) for Boolean variable elimination, for problems containing more than res_cls_cutoff2'), ('resolution.cls_cutoff1', UINT, 100000000, 'limit1 - total number of problems clauses for the second cutoff of Boolean variable elimination'), ('resolution.cls_cutoff2', UINT, 700000000, 'limit2 - total number of problems clauses for the second cutoff of Boolean variable elimination'), - ('elim_vars', BOOL, True, 'enable variable elimination during simplification'), + ('elim_vars', BOOL, True, 'enable variable elimination using resolution during simplification'), + ('elim_vars_bdd', BOOL, True, 'enable variable elimination using BDD recompilation during simplification'), + ('elim_vars_bdd_delay', UINT, 3, 'delay elimination of variables using BDDs until after simplification round'), + ('probing', BOOL, True, 'apply failed literal detection during simplification'), + ('probing_limit', UINT, 5000000, 'limit to the number of probe calls'), + ('probing_cache', BOOL, True, 'add binary literals as lemmas'), + ('probing_cache_limit', UINT, 1024, 'cache binaries unless overall memory usage exceeds cache limit'), + ('probing_binary', BOOL, True, 'probe binary clauses'), ('subsumption', BOOL, True, 'eliminate subsumed clauses'), ('subsumption.limit', UINT, 100000000, 'approx. maximum number of literals visited during subsumption (and subsumption resolution)'))) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 03c17aaf0..a59dd2b46 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -16,114 +16,192 @@ Author: Revision History: --*/ + +#include #include "sat/sat_solver.h" #include "sat/sat_integrity_checker.h" +#include "sat/sat_lookahead.h" +#include "sat/sat_unit_walk.h" #include "util/luby.h" #include "util/trace.h" #include "util/max_cliques.h" +#include "util/gparams.h" // define to update glue during propagation #define UPDATE_GLUE -// define to create a copy of the solver before starting the search -// useful for checking models -// #define CLONE_BEFORE_SOLVING - namespace sat { - solver::solver(params_ref const & p, reslimit& l, extension * ext): + solver::solver(params_ref const & p, reslimit& l): m_rlimit(l), m_checkpoint_enabled(true), m_config(p), - m_ext(ext), - m_par(0), + m_par(nullptr), + m_cls_allocator_idx(false), m_cleaner(*this), m_simplifier(*this, p), m_scc(*this, p), m_asymm_branch(*this, p), m_probing(*this, p), m_mus(*this), + m_drat(*this), m_inconsistent(false), + m_searching(false), m_num_frozen(0), m_activity_inc(128), m_case_split_queue(m_activity), m_qhead(0), m_scope_lvl(0), - m_params(p) { + m_search_lvl(0), + m_fast_glue_avg(), + m_slow_glue_avg(), + m_params(p), + m_par_id(0), + m_par_syncing_clauses(false) { + init_reason_unknown(); updt_params(p); m_conflicts_since_gc = 0; - m_conflicts = 0; + m_conflicts_since_init = 0; m_next_simplify = 0; m_num_checkpoints = 0; + m_simplifications = 0; + m_ext = 0; + m_cuber = nullptr; + m_mc.set_solver(this); } solver::~solver() { + m_ext = 0; SASSERT(check_invariant()); TRACE("sat", tout << "Delete clauses\n";); - del_clauses(m_clauses.begin(), m_clauses.end()); + del_clauses(m_clauses); TRACE("sat", tout << "Delete learned\n";); - del_clauses(m_learned.begin(), m_learned.end()); + del_clauses(m_learned); } - void solver::del_clauses(clause * const * begin, clause * const * end) { - for (clause * const * it = begin; it != end; ++it) { - m_cls_allocator.del_clause(*it); - } + void solver::del_clauses(clause_vector& clauses) { + for (clause * cp : clauses) + dealloc_clause(cp); + clauses.reset(); ++m_stats.m_non_learned_generation; } + void solver::set_extension(extension* ext) { + m_ext = ext; + if (ext) ext->set_solver(this); + } + void solver::copy(solver const & src) { pop_to_base_level(); - SASSERT(m_mc.empty() && src.m_mc.empty()); - SASSERT(scope_lvl() == 0); + del_clauses(m_clauses); + del_clauses(m_learned); + m_watches.reset(); + m_assignment.reset(); + m_justification.reset(); + m_decision.reset(); + m_eliminated.reset(); + m_activity.reset(); + m_level.reset(); + m_mark.reset(); + m_lit_mark.reset(); + m_phase.reset(); + m_prev_phase.reset(); + m_assigned_since_gc.reset(); + m_last_conflict.reset(); + m_last_propagation.reset(); + m_participated.reset(); + m_canceled.reset(); + m_reasoned.reset(); + m_simplifier.reset_todos(); + m_qhead = 0; + m_trail.reset(); + m_scopes.reset(); + // create new vars - if (num_vars() < src.num_vars()) { - for (bool_var v = num_vars(); v < src.num_vars(); v++) { - SASSERT(!src.was_eliminated(v)); - bool ext = src.m_external[v] != 0; - bool dvar = src.m_decision[v] != 0; - VERIFY(v == mk_var(ext, dvar)); + for (bool_var v = num_vars(); v < src.num_vars(); v++) { + bool ext = src.m_external[v] != 0; + bool dvar = src.m_decision[v] != 0; + VERIFY(v == mk_var(ext, dvar)); + if (src.was_eliminated(v)) { + m_eliminated[v] = true; } + m_phase[v] = src.m_phase[v]; + m_prev_phase[v] = src.m_prev_phase[v]; + +#if 1 + // inherit activity: + m_activity[v] = src.m_activity[v]; + m_case_split_queue.activity_changed_eh(v, false); +#endif } - unsigned sz = src.init_trail_size(); - for (unsigned i = 0; i < sz; ++i) { + + // + // register the extension before performing assignments. + // the assignments may call back into the extension. + // + if (src.get_extension()) { + m_ext = src.get_extension()->copy(this); + } + + unsigned trail_sz = src.init_trail_size(); + for (unsigned i = 0; i < trail_sz; ++i) { assign(src.m_trail[i], justification()); } + // copy binary clauses { - // copy binary clauses unsigned sz = src.m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; ++l_idx) { literal l = ~to_literal(l_idx); + if (src.was_eliminated(l.var())) continue; watch_list const & wlist = src.m_watches[l_idx]; - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_non_learned_clause()) + for (auto & wi : wlist) { + if (!wi.is_binary_clause()) continue; - literal l2 = it->get_literal(); - if (l.index() > l2.index()) + literal l2 = wi.get_literal(); + if (l.index() > l2.index() || + src.was_eliminated(l2.var())) continue; - mk_clause_core(l, l2); + + watched w1(l2, wi.is_learned()); + watched w2(l, wi.is_learned()); + m_watches[(~l).index()].push_back(w1); + m_watches[(~l2).index()].push_back(w2); } } } + { literal_vector buffer; - // copy clause - clause_vector::const_iterator it = src.m_clauses.begin(); - clause_vector::const_iterator end = src.m_clauses.end(); - for (; it != end; ++it) { - clause const & c = *(*it); + // copy clauses + for (clause* c : src.m_clauses) { buffer.reset(); - for (unsigned i = 0; i < c.size(); i++) - buffer.push_back(c[i]); + for (literal l : *c) buffer.push_back(l); mk_clause_core(buffer); } + + // copy high quality lemmas + unsigned num_learned = 0; + for (clause* c : src.m_learned) { + if (c->glue() <= 2 || (c->size() <= 40 && c->glue() <= 8)) { + buffer.reset(); + for (literal l : *c) buffer.push_back(l); + clause* c1 = mk_clause_core(buffer.size(), buffer.c_ptr(), true); + if (c1) { + ++num_learned; + c1->set_glue(c->glue()); + c1->set_psm(c->psm()); + } + } + } + IF_VERBOSE(1, verbose_stream() << "(sat.copy :learned " << num_learned << ")\n";); } m_user_scope_literals.reset(); m_user_scope_literals.append(src.m_user_scope_literals); + + m_mc = src.m_mc; + m_stats.m_units = init_trail_size(); } // ----------------------- @@ -152,13 +230,43 @@ namespace sat { m_phase.push_back(PHASE_NOT_AVAILABLE); m_prev_phase.push_back(PHASE_NOT_AVAILABLE); m_assigned_since_gc.push_back(false); + m_last_conflict.push_back(0); + m_last_propagation.push_back(0); + m_participated.push_back(0); + m_canceled.push_back(0); + m_reasoned.push_back(0); m_case_split_queue.mk_var_eh(v); m_simplifier.insert_elim_todo(v); SASSERT(!was_eliminated(v)); return v; } - void solver::mk_clause(unsigned num_lits, literal * lits) { + void solver::set_non_external(bool_var v) { + m_external[v] = 0; + } + + void solver::set_external(bool_var v) { + if (m_external[v] != 0) return; + m_external[v] = 1; + if (!m_ext) return; + + lbool val = value(v); + + switch (val) { + case l_true: { + m_ext->asserted(literal(v, false)); + break; + } + case l_false: { + m_ext->asserted(literal(v, true)); + break; + } + default: + break; + } + } + + void solver::mk_clause(unsigned num_lits, literal * lits, bool learned) { m_model_is_current = false; DEBUG_CODE({ for (unsigned i = 0; i < num_lits; i++) @@ -166,29 +274,34 @@ namespace sat { }); if (m_user_scope_literals.empty()) { - mk_clause_core(num_lits, lits, false); + mk_clause_core(num_lits, lits, learned); } else { m_aux_literals.reset(); m_aux_literals.append(num_lits, lits); m_aux_literals.append(m_user_scope_literals); - mk_clause_core(m_aux_literals.size(), m_aux_literals.c_ptr(), false); + mk_clause_core(m_aux_literals.size(), m_aux_literals.c_ptr(), learned); } } - void solver::mk_clause(literal l1, literal l2) { + void solver::mk_clause(literal l1, literal l2, bool learned) { literal ls[2] = { l1, l2 }; - mk_clause(2, ls); + mk_clause(2, ls, learned); } - void solver::mk_clause(literal l1, literal l2, literal l3) { + void solver::mk_clause(literal l1, literal l2, literal l3, bool learned) { literal ls[3] = { l1, l2, l3 }; - mk_clause(3, ls); + mk_clause(3, ls, learned); } void solver::del_clause(clause& c) { - if (!c.is_learned()) m_stats.m_non_learned_generation++; - m_cls_allocator.del_clause(&c); + if (!c.is_learned()) { + m_stats.m_non_learned_generation++; + } + if (m_config.m_drat && !m_drat.is_cleaned(c)) { + m_drat.del(c); + } + dealloc_clause(&c); m_stats.m_del_clause++; } @@ -198,21 +311,23 @@ namespace sat { bool keep = simplify_clause(num_lits, lits); TRACE("sat_mk_clause", tout << "mk_clause (after simp), keep: " << keep << "\n" << mk_lits_pp(num_lits, lits) << "\n";); if (!keep) { - return 0; // clause is equivalent to true. + return nullptr; // clause is equivalent to true. } ++m_stats.m_non_learned_generation; } - + switch (num_lits) { case 0: + if (m_config.m_drat) m_drat.add(); set_conflict(justification()); - return 0; + return nullptr; case 1: assign(lits[0], justification()); - return 0; + return nullptr; case 2: mk_bin_clause(lits[0], lits[1], learned); - return 0; + if (learned && m_par) m_par->share_clause(*this, lits[0], lits[1]); + return nullptr; case 3: return mk_ter_clause(lits, learned); default: @@ -221,15 +336,44 @@ namespace sat { } void solver::mk_bin_clause(literal l1, literal l2, bool learned) { - if (propagate_bin_clause(l1, l2)) { - if (scope_lvl() == 0) +#if 0 + if ((l1.var() == 2039 || l2.var() == 2039) && + (l1.var() == 27042 || l2.var() == 27042)) { + IF_VERBOSE(1, verbose_stream() << "mk_bin: " << l1 << " " << l2 << " " << learned << "\n"); + } +#endif + if (find_binary_watch(get_wlist(~l1), ~l2)) { + assign(l1, justification()); + return; + } + if (find_binary_watch(get_wlist(~l2), ~l1)) { + assign(l2, justification()); + return; + } + watched* w0 = find_binary_watch(get_wlist(~l1), l2); + if (w0) { + if (w0->is_learned() && !learned) { + w0->set_learned(false); + } + else { return; - if (!learned) + } + w0 = find_binary_watch(get_wlist(~l2), l1); + VERIFY(w0); + w0->set_learned(false); + return; + } + if (m_config.m_drat) + m_drat.add(l1, l2, learned); + if (propagate_bin_clause(l1, l2)) { + if (at_base_lvl()) + return; + if (!learned && !at_search_lvl()) m_clauses_to_reinit.push_back(clause_wrapper(l1, l2)); } m_stats.m_mk_bin_clause++; - m_watches[(~l1).index()].push_back(watched(l2, learned)); - m_watches[(~l2).index()].push_back(watched(l1, learned)); + get_wlist(~l1).push_back(watched(l2, learned)); + get_wlist(~l2).push_back(watched(l1, learned)); } bool solver::propagate_bin_clause(literal l1, literal l2) { @@ -255,9 +399,10 @@ namespace sat { clause * solver::mk_ter_clause(literal * lits, bool learned) { m_stats.m_mk_ter_clause++; - clause * r = m_cls_allocator.mk_clause(3, lits, learned); + clause * r = alloc_clause(3, lits, learned); bool reinit = attach_ter_clause(*r); if (reinit && !learned) push_reinit_stack(*r); + if (m_config.m_drat) m_drat.add(*r, learned); if (learned) m_learned.push_back(r); @@ -271,7 +416,7 @@ namespace sat { m_watches[(~c[0]).index()].push_back(watched(c[1], c[2])); m_watches[(~c[1]).index()].push_back(watched(c[0], c[2])); m_watches[(~c[2]).index()].push_back(watched(c[0], c[1])); - if (scope_lvl() > 0) { + if (!at_base_lvl()) { if (value(c[1]) == l_false && value(c[2]) == l_false) { m_stats.m_ter_propagate++; assign(c[0], justification(c[1], c[2])); @@ -293,21 +438,25 @@ namespace sat { clause * solver::mk_nary_clause(unsigned num_lits, literal * lits, bool learned) { m_stats.m_mk_clause++; - clause * r = m_cls_allocator.mk_clause(num_lits, lits, learned); + clause * r = alloc_clause(num_lits, lits, learned); SASSERT(!learned || r->is_learned()); bool reinit = attach_nary_clause(*r); if (reinit && !learned) push_reinit_stack(*r); - if (learned) + if (learned) { m_learned.push_back(r); - else + } + else { m_clauses.push_back(r); + } + if (m_config.m_drat) + m_drat.add(*r, learned); return r; } bool solver::attach_nary_clause(clause & c) { bool reinit = false; - clause_offset cls_off = m_cls_allocator.get_offset(&c); - if (scope_lvl() > 0) { + clause_offset cls_off = cls_allocator().get_offset(&c); + if (!at_base_lvl()) { if (c.is_learned()) { unsigned w2_idx = select_learned_watch_lit(c); std::swap(c[1], c[w2_idx]); @@ -332,6 +481,9 @@ namespace sat { } unsigned some_idx = c.size() >> 1; literal block_lit = c[some_idx]; + DEBUG_CODE(for (auto const& w : m_watches[(~c[0]).index()]) VERIFY(!w.is_clause() || w.get_clause_offset() != cls_off);); + DEBUG_CODE(for (auto const& w : m_watches[(~c[1]).index()]) VERIFY(!w.is_clause() || w.get_clause_offset() != cls_off);); + VERIFY(c[0] != c[1]); m_watches[(~c[0]).index()].push_back(watched(block_lit, cls_off)); m_watches[(~c[1]).index()].push_back(watched(block_lit, cls_off)); return reinit; @@ -346,6 +498,109 @@ namespace sat { reinit = attach_nary_clause(c); } + void solver::set_learned(clause& c, bool learned) { + if (c.is_learned() != learned) + c.set_learned(learned); + } + + void solver::set_learned1(literal l1, literal l2, bool learned) { + for (watched& w : get_wlist(~l1)) { + if (w.is_binary_clause() && l2 == w.get_literal() && !w.is_learned()) { + w.set_learned(learned); + break; + } + } + } + + bool solver::memory_pressure() { + return 3*cls_allocator().get_allocation_size()/2 + memory::get_allocation_size() > memory::get_max_memory_size(); + } + + struct solver::cmp_activity { + solver& s; + cmp_activity(solver& s):s(s) {} + bool operator()(bool_var v1, bool_var v2) const { + return s.m_activity[v1] > s.m_activity[v2]; + } + }; + + bool solver::should_defrag() { + if (m_defrag_threshold > 0) --m_defrag_threshold; + return m_defrag_threshold == 0 && m_config.m_gc_defrag; + } + + void solver::defrag_clauses() { + if (memory_pressure()) return; + pop(scope_lvl()); + IF_VERBOSE(1, verbose_stream() << "(sat-defrag)\n"); + clause_allocator& alloc = m_cls_allocator[!m_cls_allocator_idx]; + ptr_vector new_clauses, new_learned; + for (clause* c : m_clauses) c->unmark_used(); + for (clause* c : m_learned) c->unmark_used(); + + svector vars; + for (unsigned i = 0; i < num_vars(); ++i) vars.push_back(i); + std::stable_sort(vars.begin(), vars.end(), cmp_activity(*this)); + literal_vector lits; + for (bool_var v : vars) lits.push_back(literal(v, false)), lits.push_back(literal(v, true)); + // walk clauses, reallocate them in an order that defragments memory and creates locality. + for (literal lit : lits) { + watch_list& wlist = m_watches[lit.index()]; + for (watched& w : wlist) { + if (w.is_clause()) { + clause& c1 = get_clause(w); + clause_offset offset; + if (c1.was_used()) { + offset = c1.get_new_offset(); + } + else { + clause* c2 = alloc.copy_clause(c1); + c1.mark_used(); + if (c1.is_learned()) { + new_learned.push_back(c2); + } + else { + new_clauses.push_back(c2); + } + offset = get_offset(*c2); + c1.set_new_offset(offset); + } + w = watched(w.get_blocked_literal(), offset); + } + } + } + + // reallocate ternary clauses. + for (clause* c : m_clauses) { + if (!c->was_used()) { + SASSERT(c->size() == 3); + new_clauses.push_back(alloc.copy_clause(*c)); + } + dealloc_clause(c); + } + + for (clause* c : m_learned) { + if (!c->was_used()) { + SASSERT(c->size() == 3); + new_learned.push_back(alloc.copy_clause(*c)); + } + dealloc_clause(c); + } + m_clauses.swap(new_clauses); + m_learned.swap(new_learned); + + cls_allocator().finalize(); + m_cls_allocator_idx = !m_cls_allocator_idx; + + reinit_assumptions(); + } + + + void solver::set_learned(literal l1, literal l2, bool learned) { + set_learned1(l1, l2, learned); + set_learned1(l2, l1, learned); + } + /** \brief Select a watch literal starting the search at the given position. This method is only used for clauses created during the search. @@ -457,7 +712,7 @@ namespace sat { } bool solver::simplify_clause(unsigned & num_lits, literal * lits) const { - if (scope_lvl() == 0) + if (at_base_lvl()) return simplify_clause_core(num_lits, lits); else return simplify_clause_core(num_lits, lits); @@ -466,6 +721,7 @@ namespace sat { void solver::detach_bin_clause(literal l1, literal l2, bool learned) { get_wlist(~l1).erase(watched(l2, learned)); get_wlist(~l2).erase(watched(l1, learned)); + if (m_config.m_drat) m_drat.del(l1, l2); } void solver::detach_clause(clause & c) { @@ -504,8 +760,10 @@ namespace sat { void solver::assign_core(literal l, justification j) { SASSERT(value(l) == l_undef); TRACE("sat_assign_core", tout << l << " " << j << " level: " << scope_lvl() << "\n";); - if (scope_lvl() == 0) + if (at_base_lvl()) { + if (m_config.m_drat) m_drat.add(l, !j.is_none()); j = justification(); // erase justification for level 0 + } m_assignment[l.index()] = l_true; m_assignment[(~l).index()] = l_false; bool_var v = l.var(); @@ -518,9 +776,39 @@ namespace sat { if (m_ext && m_external[v]) m_ext->asserted(l); + switch (m_config.m_branching_heuristic) { + case BH_VSIDS: + break; + case BH_CHB: + m_last_propagation[v] = m_stats.m_conflict; + break; + case BH_LRB: + m_participated[v] = 0; + m_reasoned[v] = 0; + break; + } + + if (m_config.m_anti_exploration) { + uint64_t age = m_stats.m_conflict - m_canceled[v]; + if (age > 0) { + double decay = pow(0.95, age); + m_activity[v] = static_cast(m_activity[v] * decay); + // NB. MapleSAT does not update canceled. + m_canceled[v] = m_stats.m_conflict; + m_case_split_queue.activity_changed_eh(v, false); + } + } + + if (m_config.m_propagate_prefetch) { +#if defined(__GNUC__) || defined(__clang__) + __builtin_prefetch((const char*)((m_watches[l.index()].c_ptr()))); +#else + _mm_prefetch((const char*)((m_watches[l.index()].c_ptr())), _MM_HINT_T1); +#endif + } + SASSERT(!l.sign() || m_phase[v] == NEG_PHASE); SASSERT(l.sign() || m_phase[v] == POS_PHASE); - SASSERT(!l.sign() || value(v) == l_false); SASSERT(l.sign() || value(v) == l_true); SASSERT(value(l) == l_true); @@ -529,9 +817,8 @@ namespace sat { lbool solver::status(clause const & c) const { bool found_undef = false; - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - switch (value(c[i])) { + for (literal lit : c) { + switch (value(lit)) { case l_true: return l_true; case l_undef: @@ -559,7 +846,7 @@ namespace sat { while (m_qhead < m_trail.size()) { checkpoint(); m_cleaner.dec(); - SASSERT(!m_inconsistent); + if (m_inconsistent) return false; l = m_trail[m_qhead]; TRACE("sat_propagate", tout << "propagating: " << l << " " << m_justification[l.var()] << "\n";); m_qhead++; @@ -620,15 +907,13 @@ namespace sat { case watched::CLAUSE: { if (value(it->get_blocked_literal()) == l_true) { TRACE("propagate_clause_bug", tout << "blocked literal " << it->get_blocked_literal() << "\n"; - clause_offset cls_off = it->get_clause_offset(); - clause & c = *(m_cls_allocator.get_clause(cls_off)); - tout << c << "\n";); + tout << get_clause(it) << "\n";); *it2 = *it; it2++; break; } clause_offset cls_off = it->get_clause_offset(); - clause & c = *(m_cls_allocator.get_clause(cls_off)); + clause & c = get_clause(cls_off); TRACE("propagate_clause_bug", tout << "processing... " << c << "\nwas_removed: " << c.was_removed() << "\n";); if (c[0] == not_l) std::swap(c[0], c[1]); @@ -643,18 +928,19 @@ namespace sat { it2++; break; } - SASSERT(c[1] == not_l); if (value(c[0]) == l_true) { it2->set_clause(c[0], cls_off); it2++; break; } + VERIFY(c[1] == not_l); literal * l_it = c.begin() + 2; literal * l_end = c.end(); for (; l_it != l_end; ++l_it) { if (value(*l_it) != l_false) { c[1] = *l_it; *l_it = not_l; + DEBUG_CODE(for (auto const& w : m_watches[(~c[1]).index()]) VERIFY(!w.is_clause() || w.get_clause_offset() != cls_off);); m_watches[(~c[1]).index()].push_back(watched(c[0], cls_off)); goto end_clause_case; } @@ -674,8 +960,8 @@ namespace sat { assign_core(c[0], justification(cls_off)); #ifdef UPDATE_GLUE if (update && c.is_learned() && c.glue() > 2) { - unsigned glue = num_diff_levels(c.size(), c.begin()); - if (glue+1 < c.glue()) { + unsigned glue; + if (num_diff_levels_below(c.size(), c.begin(), c.glue()-1, glue)) { c.set_glue(glue); } } @@ -686,15 +972,18 @@ namespace sat { } case watched::EXT_CONSTRAINT: SASSERT(m_ext); - m_ext->propagate(l, it->get_ext_constraint_idx(), keep); + keep = m_ext->propagate(l, it->get_ext_constraint_idx()); + if (m_inconsistent) { + if (!keep) { + ++it; + } + CONFLICT_CLEANUP(); + return false; + } if (keep) { *it2 = *it; it2++; } - if (m_inconsistent) { - CONFLICT_CLEANUP(); - return false; - } break; default: UNREACHABLE(); @@ -709,46 +998,72 @@ namespace sat { } bool solver::propagate(bool update) { + unsigned qhead = m_qhead; bool r = propagate_core(update); + if (m_config.m_branching_heuristic == BH_CHB) { + update_chb_activity(r, qhead); + } CASSERT("sat_propagate", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); return r; } + lbool solver::cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level) { + if (!m_cuber) { + 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) { + dealloc(m_cuber); + m_cuber = nullptr; + } + return result; + } + + // ----------------------- // // Search // // ----------------------- lbool solver::check(unsigned num_lits, literal const* lits) { + init_reason_unknown(); pop_to_base_level(); + m_stats.m_units = init_trail_size(); IF_VERBOSE(2, verbose_stream() << "(sat.sat-solver)\n";); - SASSERT(scope_lvl() == 0); - if (m_config.m_dimacs_display) { - display_dimacs(std::cout); - for (unsigned i = 0; i < num_lits; ++i) { - std::cout << dimacs_lit(lits[i]) << " 0\n"; - } - return l_undef; + SASSERT(at_base_lvl()); + + if (m_config.m_local_search) { + return do_local_search(num_lits, lits); } - if (m_config.m_num_parallel > 1 && !m_par) { + if ((m_config.m_num_threads > 1 || m_config.m_local_search_threads > 0 || m_config.m_unit_walk_threads > 0) && !m_par) { + SASSERT(scope_lvl() == 0); return check_par(num_lits, lits); } -#ifdef CLONE_BEFORE_SOLVING - if (m_mc.empty()) { - m_clone = alloc(solver, m_params, 0 /* do not clone extension */); - SASSERT(m_clone); + flet _searching(m_searching, true); + if (m_mc.empty() && gparams::get_ref().get_bool("model_validate", false)) { + m_clone = alloc(solver, m_params, m_rlimit); + m_clone->copy(*this); } -#endif try { - if (inconsistent()) return l_false; init_search(); + if (inconsistent()) return l_false; propagate(false); if (inconsistent()) return l_false; init_assumptions(num_lits, lits); propagate(false); if (check_inconsistent()) return l_false; cleanup(); + + if (m_config.m_unit_walk) { + return do_unit_walk(); + } + if (m_config.m_gc_burst) { + // force gc + m_conflicts_since_gc = m_gc_threshold + 1; + gc(); + } if (m_config.m_max_conflicts > 0 && m_config.m_burst_search > 0) { m_restart_threshold = m_config.m_burst_search; lbool r = bounded_search(); @@ -756,15 +1071,14 @@ namespace sat { return r; pop_reinit(scope_lvl()); m_conflicts_since_restart = 0; - m_restart_threshold = m_config.m_restart_initial; + m_restart_threshold = m_config.m_restart_initial; } // iff3_finder(*this)(); simplify_problem(); if (check_inconsistent()) return l_false; - if (m_config.m_max_conflicts == 0) { - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = 0\")\n";); + if (reached_max_conflicts()) { return l_undef; } @@ -775,24 +1089,30 @@ namespace sat { if (r != l_undef) return r; - if (m_conflicts > m_config.m_max_conflicts) { - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts << "\")\n";); + if (reached_max_conflicts()) { return l_undef; } - restart(); + restart(!m_config.m_restart_fast); simplify_problem(); if (check_inconsistent()) return l_false; gc(); if (m_config.m_restart_max <= m_restarts) { + m_reason_unknown = "sat.max.restarts"; IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-restarts\")\n";); return l_undef; } + if (m_config.m_inprocess_max <= m_simplifications) { + m_reason_unknown = "sat.max.inprocess"; + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-inprocess\")\n";); + return l_undef; + } } } catch (abort_solver) { + m_reason_unknown = "sat.giveup"; return l_undef; } } @@ -802,37 +1122,87 @@ namespace sat { ERROR_EX }; + lbool solver::do_local_search(unsigned num_lits, literal const* lits) { + scoped_limits scoped_rl(rlimit()); + local_search srch; + srch.config().set_config(m_config); + srch.import(*this, false); + scoped_rl.push_child(&srch.rlimit()); + lbool r = srch.check(num_lits, lits, 0); + m_model = srch.get_model(); + // srch.collect_statistics(m_aux_stats); + return r; + } + + lbool solver::do_unit_walk() { + unit_walk srch(*this); + lbool r = srch(); + return r; + } + lbool solver::check_par(unsigned num_lits, literal const* lits) { - int num_threads = static_cast(m_config.m_num_parallel); - int num_extra_solvers = num_threads - 1; - scoped_limits scoped_rlimit(rlimit()); - vector rlims(num_extra_solvers); - ptr_vector solvers(num_extra_solvers); - sat::par par; - symbol saved_phase = m_params.get_sym("phase", symbol("caching")); - for (int i = 0; i < num_extra_solvers; ++i) { - m_params.set_uint("random_seed", m_rand()); - if (i == 1 + num_threads/2) { - m_params.set_sym("phase", symbol("random")); - } - solvers[i] = alloc(sat::solver, m_params, rlims[i], 0); - solvers[i]->copy(*this); - solvers[i]->set_par(&par); - scoped_rlimit.push_child(&solvers[i]->rlimit()); + scoped_ptr_vector ls; + scoped_ptr_vector uw; + int num_extra_solvers = m_config.m_num_threads - 1; + int num_local_search = static_cast(m_config.m_local_search_threads); + int num_unit_walk = static_cast(m_config.m_unit_walk_threads); + int num_threads = num_extra_solvers + 1 + num_local_search + num_unit_walk; + for (int i = 0; i < num_local_search; ++i) { + local_search* l = alloc(local_search); + l->config().set_config(m_config); + l->config().set_random_seed(m_config.m_random_seed + i); + l->import(*this, false); + ls.push_back(l); + } + + // set up unit walk + vector lims(num_unit_walk); + for (int i = 0; i < num_unit_walk; ++i) { + solver* s = alloc(solver, m_params, lims[i]); + s->copy(*this); + s->m_config.m_unit_walk = true; + uw.push_back(s); + } + + int local_search_offset = num_extra_solvers; + int unit_walk_offset = num_extra_solvers + num_local_search; + int main_solver_offset = unit_walk_offset + num_unit_walk; + +#define IS_AUX_SOLVER(i) (0 <= i && i < num_extra_solvers) +#define IS_LOCAL_SEARCH(i) (local_search_offset <= i && i < unit_walk_offset) +#define IS_UNIT_WALK(i) (unit_walk_offset <= i && i < main_solver_offset) +#define IS_MAIN_SOLVER(i) (i == main_solver_offset) + + sat::parallel par(*this); + par.reserve(num_threads, 1 << 12); + par.init_solvers(*this, num_extra_solvers); + for (unsigned i = 0; i < ls.size(); ++i) { + par.push_child(ls[i]->rlimit()); + } + for (reslimit& rl : lims) { + par.push_child(rl); + } + for (unsigned i = 0; i < uw.size(); ++i) { + uw[i]->set_par(&par, 0); } - set_par(&par); - m_params.set_sym("phase", saved_phase); int finished_id = -1; std::string ex_msg; par_exception_kind ex_kind = DEFAULT_EX; unsigned error_code = 0; lbool result = l_undef; + bool canceled = false; #pragma omp parallel for for (int i = 0; i < num_threads; ++i) { try { lbool r = l_undef; - if (i < num_extra_solvers) { - r = solvers[i]->check(num_lits, lits); + if (IS_AUX_SOLVER(i)) { + r = par.get_solver(i).check(num_lits, lits); + } + else if (IS_LOCAL_SEARCH(i)) { + r = ls[i-local_search_offset]->check(num_lits, lits, &par); + } + else if (IS_UNIT_WALK(i)) { + r = uw[i-unit_walk_offset]->check(num_lits, lits); } else { r = check(num_lits, lits); @@ -847,41 +1217,57 @@ namespace sat { } } if (first) { - if (r == l_true && i < num_extra_solvers) { - set_model(solvers[i]->get_model()); + for (unsigned j = 0; j < ls.size(); ++j) { + ls[j]->rlimit().cancel(); } - else if (r == l_false && i < num_extra_solvers) { - m_core.reset(); - m_core.append(solvers[i]->get_core()); + for (auto& rl : lims) { + rl.cancel(); } for (int j = 0; j < num_extra_solvers; ++j) { if (i != j) { - rlims[j].cancel(); + par.cancel_solver(j); + } + } + if (!IS_MAIN_SOLVER(i)) { + canceled = !rlimit().inc(); + if (!canceled) { + rlimit().cancel(); } } } } catch (z3_error & err) { - if (i == 0) { - error_code = err.error_code(); - ex_kind = ERROR_EX; - } + error_code = err.error_code(); + ex_kind = ERROR_EX; } catch (z3_exception & ex) { - if (i == 0) { - ex_msg = ex.msg(); - ex_kind = DEFAULT_EX; - } + ex_msg = ex.msg(); + ex_kind = DEFAULT_EX; } } - set_par(0); - if (finished_id != -1 && finished_id < num_extra_solvers) { - m_stats = solvers[finished_id]->m_stats; + + if (IS_AUX_SOLVER(finished_id)) { + m_stats = par.get_solver(finished_id).m_stats; } - - for (int i = 0; i < num_extra_solvers; ++i) { - dealloc(solvers[i]); + if (result == l_true && IS_AUX_SOLVER(finished_id)) { + set_model(par.get_solver(finished_id).get_model()); } + else if (result == l_false && IS_AUX_SOLVER(finished_id)) { + m_core.reset(); + m_core.append(par.get_solver(finished_id).get_core()); + } + if (result == l_true && IS_LOCAL_SEARCH(finished_id)) { + set_model(ls[finished_id - local_search_offset]->get_model()); + } + if (result == l_true && IS_UNIT_WALK(finished_id)) { + set_model(uw[finished_id - unit_walk_offset]->get_model()); + } + if (!canceled) { + rlimit().reset_cancel(); + } + set_par(0, 0); + ls.reset(); + uw.reset(); if (finished_id == -1) { switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); @@ -896,7 +1282,10 @@ namespace sat { \brief import lemmas/units from parallel sat solvers. */ void solver::exchange_par() { - if (m_par && scope_lvl() == 0) { + if (m_par && at_base_lvl() && m_config.m_num_threads > 1) m_par->get_clauses(*this); + if (m_par && at_base_lvl() && m_config.m_num_threads > 1) { + // SASSERT(scope_lvl() == search_lvl()); + // TBD: import also dependencies of assumptions. unsigned sz = init_trail_size(); unsigned num_in = 0, num_out = 0; literal_vector in, out; @@ -908,7 +1297,7 @@ namespace sat { } } m_par_limit_out = sz; - m_par->exchange(out, m_par_limit_in, in); + m_par->exchange(*this, out, m_par_limit_in, in); for (unsigned i = 0; !inconsistent() && i < in.size(); ++i) { literal lit = in[i]; SASSERT(lit.var() < m_par_num_vars); @@ -918,16 +1307,18 @@ namespace sat { } } if (num_in > 0 || num_out > 0) { - IF_VERBOSE(1, verbose_stream() << "(sat-sync out: " << num_out << " in: " << num_in << ")\n";); + IF_VERBOSE(2, verbose_stream() << "(sat-sync out: " << num_out << " in: " << num_in << ")\n";); } } } - void solver::set_par(par* p) { + void solver::set_par(parallel* p, unsigned id) { m_par = p; m_par_num_vars = num_vars(); m_par_limit_in = 0; m_par_limit_out = 0; + m_par_id = id; + m_par_syncing_clauses = false; } bool_var solver::next_var() { @@ -943,6 +1334,17 @@ namespace sat { } while (!m_case_split_queue.empty()) { + if (m_config.m_anti_exploration) { + next = m_case_split_queue.min_var(); + auto age = m_stats.m_conflict - m_canceled[next]; + while (age > 0) { + m_activity[next] = static_cast(m_activity[next] * pow(0.95, age)); + m_case_split_queue.activity_changed_eh(next, false); + m_canceled[next] = m_stats.m_conflict; + next = m_case_split_queue.min_var(); + age = m_stats.m_conflict - m_canceled[next]; + } + } next = m_case_split_queue.next_var(); if (value(next) == l_undef && !was_eliminated(next)) return next; @@ -968,10 +1370,12 @@ namespace sat { phase = l_false; break; case PS_CACHING: - if (m_phase_cache_on && m_phase[next] != PHASE_NOT_AVAILABLE) + if ((m_phase_cache_on || m_config.m_phase_sticky) && m_phase[next] != PHASE_NOT_AVAILABLE) { phase = m_phase[next] == POS_PHASE ? l_true : l_false; - else + } + else { phase = l_false; + } break; case PS_RANDOM: phase = to_lbool((m_rand() % 2) == 0); @@ -1017,11 +1421,11 @@ namespace sat { return l_true; if (!resolve_conflict()) return l_false; - if (m_conflicts > m_config.m_max_conflicts) + if (reached_max_conflicts()) return l_undef; - if (m_conflicts_since_restart > m_restart_threshold) + if (should_restart()) return l_undef; - if (scope_lvl() == 0) { + if (at_base_lvl()) { cleanup(); // cleaner may propagate frozen clauses if (inconsistent()) { TRACE("sat", tout << "conflict at level 0\n";); @@ -1069,6 +1473,7 @@ namespace sat { return; } + SASSERT(at_base_lvl()); reset_assumptions(); push(); @@ -1096,9 +1501,10 @@ namespace sat { add_assumption(lit); assign(lit, justification()); } + m_search_lvl = scope_lvl(); + SASSERT(m_search_lvl == 1); } - void solver::update_min_core() { if (!m_min_core_valid || m_core.size() < m_min_core.size()) { m_min_core.reset(); @@ -1115,6 +1521,7 @@ namespace sat { void solver::add_assumption(literal lit) { m_assumption_set.insert(lit); m_assumptions.push_back(lit); + set_external(lit.var()); } void solver::pop_assumption() { @@ -1128,8 +1535,7 @@ namespace sat { push(); reset_assumptions(); TRACE("sat", tout << "reassert: " << m_min_core << "\n";); - for (unsigned i = 0; i < m_min_core.size(); ++i) { - literal lit = m_min_core[i]; + for (literal lit : m_min_core) { SASSERT(is_external(lit.var())); add_assumption(lit); assign(lit, justification()); @@ -1139,7 +1545,7 @@ namespace sat { } void solver::reinit_assumptions() { - if (tracking_assumptions() && scope_lvl() == 0) { + if (tracking_assumptions() && at_base_lvl() && !inconsistent()) { TRACE("sat", tout << m_assumptions << "\n";); push(); for (unsigned i = 0; !inconsistent() && i < m_user_scope_literals.size(); ++i) { @@ -1149,12 +1555,12 @@ namespace sat { assign(m_assumptions[i], justification()); } TRACE("sat", - for (unsigned i = 0; i < m_assumptions.size(); ++i) { - index_set s; - if (m_antecedents.find(m_assumptions[i].var(), s)) { - tout << m_assumptions[i] << ": "; display_index_set(tout, s) << "\n"; - } - }); + for (literal a : m_assumptions) { + index_set s; + if (m_antecedents.find(a.var(), s)) { + tout << a << ": "; display_index_set(tout, s) << "\n"; + } + }); } } @@ -1166,86 +1572,134 @@ namespace sat { return tracking_assumptions() && m_assumption_set.contains(l); } + bool solver::is_assumption(bool_var v) const { + return is_assumption(literal(v, false)) || is_assumption(literal(v, true)); + } + void solver::init_search() { m_model_is_current = false; m_phase_counter = 0; - m_phase_cache_on = false; + m_phase_cache_on = m_config.m_phase_sticky; m_conflicts_since_restart = 0; m_restart_threshold = m_config.m_restart_initial; m_luby_idx = 1; m_gc_threshold = m_config.m_gc_initial; + m_defrag_threshold = 2; m_restarts = 0; + m_simplifications = 0; + m_conflicts_since_init = 0; + m_next_simplify = m_config.m_simplify_delay; m_min_d_tk = 1.0; + m_search_lvl = 0; + m_conflicts_since_gc = 0; + m_restart_next_out = 0; + m_asymm_branch.init_search(); m_stopwatch.reset(); m_stopwatch.start(); m_core.reset(); m_min_core_valid = false; m_min_core.reset(); + m_simplifier.init_search(); TRACE("sat", display(tout);); } /** \brief Apply all simplifications. - */ void solver::simplify_problem() { - if (m_conflicts < m_next_simplify) { + if (m_conflicts_since_init < m_next_simplify) { return; } - IF_VERBOSE(2, verbose_stream() << "(sat.simplify)\n";); + m_simplifications++; + IF_VERBOSE(2, verbose_stream() << "(sat.simplify :simplifications " << m_simplifications << ")\n";); - // Disable simplification during MUS computation. - // if (m_mus.is_active()) return; TRACE("sat", tout << "simplify\n";); pop(scope_lvl()); - SASSERT(scope_lvl() == 0); + SASSERT(at_base_lvl()); m_cleaner(); CASSERT("sat_simplify_bug", check_invariant()); m_scc(); CASSERT("sat_simplify_bug", check_invariant()); - + m_simplifier(false); + CASSERT("sat_simplify_bug", check_invariant()); CASSERT("sat_missed_prop", check_missed_propagation()); - if (!m_learned.empty()) { m_simplifier(true); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); } - sort_watch_lits(); CASSERT("sat_simplify_bug", check_invariant()); m_probing(); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); + m_asymm_branch(false); - m_asymm_branch(); CASSERT("sat_missed_prop", check_missed_propagation()); CASSERT("sat_simplify_bug", check_invariant()); - if (m_ext) { m_ext->clauses_modifed(); m_ext->simplify(); } + if (m_config.m_lookahead_simplify && !m_ext) { + lookahead lh(*this); + lh.simplify(true); + lh.collect_statistics(m_aux_stats); + } TRACE("sat", display(tout << "consistent: " << (!inconsistent()) << "\n");); reinit_assumptions(); if (m_next_simplify == 0) { - m_next_simplify = m_config.m_restart_initial * m_config.m_simplify_mult1; + m_next_simplify = m_config.m_next_simplify1; } else { - m_next_simplify = static_cast(m_conflicts * m_config.m_simplify_mult2); - if (m_next_simplify > m_conflicts + m_config.m_simplify_max) - m_next_simplify = m_conflicts + m_config.m_simplify_max; + m_next_simplify = static_cast(m_conflicts_since_init * m_config.m_simplify_mult2); + if (m_next_simplify > m_conflicts_since_init + m_config.m_simplify_max) + m_next_simplify = m_conflicts_since_init + m_config.m_simplify_max; } + + + if (m_par) m_par->set_phase(*this); + +#if 0 + static unsigned file_no = 0; + #pragma omp critical (print_sat) + { + ++file_no; + std::ostringstream ostrm; + ostrm << "s" << file_no << ".txt"; + std::ofstream ous(ostrm.str()); + display(ous); + } +#endif + } + + unsigned solver::get_hash() const { + unsigned result = 0; + for (clause* cp : m_clauses) { + result = combine_hash(cp->size(), combine_hash(result, cp->id())); + } + for (clause* cp : m_learned) { + result = combine_hash(cp->size(), combine_hash(result, cp->id())); + } + return result; + } + + bool solver::set_root(literal l, literal r) { + return !m_ext || m_ext->set_root(l, r); + } + + void solver::flush_roots() { + if (m_ext) m_ext->flush_roots(); } void solver::sort_watch_lits() { @@ -1266,70 +1720,101 @@ namespace sat { unsigned num = num_vars(); m_model.resize(num, l_undef); for (bool_var v = 0; v < num; v++) { - if (!was_eliminated(v)) + if (!was_eliminated(v)) { m_model[v] = value(v); + m_phase[v] = (value(v) == l_true) ? POS_PHASE : NEG_PHASE; + } } TRACE("sat_mc_bug", m_mc.display(tout);); - m_mc(m_model); - TRACE("sat", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); -#ifndef _EXTERNAL_RELEASE - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"checking model\"\n";); - if (!check_model(m_model)) +#if 0 + IF_VERBOSE(0, for (bool_var v = 0; v < num; v++) verbose_stream() << v << ": " << m_model[v] << "\n";); + for (auto p : big::s_del_bin) { + if (value(p.first) != l_true && value(p.second) != l_true) { + IF_VERBOSE(0, verbose_stream() << "binary violation: " << p.first << " " << p.second << "\n"); + } + } +#endif + + IF_VERBOSE(10, verbose_stream() << "\"checking model\"\n";); + if (!check_clauses(m_model)) { throw solver_exception("check model failed"); + } + + if (m_config.m_drat) m_drat.check_model(m_model); + + // m_mc.set_solver(nullptr); + m_mc(m_model); + + if (!check_clauses(m_model)) { + IF_VERBOSE(0, verbose_stream() << "failure checking clauses on transformed model\n";); + IF_VERBOSE(10, m_mc.display(verbose_stream())); + //IF_VERBOSE(0, display_units(verbose_stream())); + //IF_VERBOSE(0, display(verbose_stream())); + IF_VERBOSE(0, for (bool_var v = 0; v < num; v++) verbose_stream() << v << ": " << m_model[v] << "\n";); + + throw solver_exception("check model failed"); + } + + TRACE("sat", for (bool_var v = 0; v < num; v++) tout << v << ": " << m_model[v] << "\n";); if (m_clone) { IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "\"checking model (on original set of clauses)\"\n";); - if (!m_clone->check_model(m_model)) + if (!m_clone->check_model(m_model)) { + //IF_VERBOSE(0, display(verbose_stream())); + //IF_VERBOSE(0, display_watches(verbose_stream())); + IF_VERBOSE(0, m_mc.display(verbose_stream())); + IF_VERBOSE(0, display_units(verbose_stream())); + //IF_VERBOSE(0, m_clone->display(verbose_stream() << "clone\n")); throw solver_exception("check model failed (for cloned solver)"); - } -#endif - } - - bool solver::check_model(model const & m) const { - bool ok = true; - clause_vector const * vs[2] = { &m_clauses, &m_learned }; - for (unsigned i = 0; i < 2; i++) { - clause_vector const & cs = *(vs[i]); - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause const & c = *(*it); - if (!c.satisfied_by(m)) { - TRACE("sat", tout << "failed: " << c << "\n"; - tout << "assumptions: " << m_assumptions << "\n"; - tout << "trail: " << m_trail << "\n"; - tout << "model: " << m << "\n"; - m_mc.display(tout); - ); - ok = false; - } } } - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { + } + + bool solver::check_clauses(model const& m) const { + bool ok = true; + for (clause const* cp : m_clauses) { + clause const & c = *cp; + if (!c.satisfied_by(m)) { + IF_VERBOSE(0, verbose_stream() << "failed clause " << c.id() << ": " << c << "\n";); + TRACE("sat", tout << "failed: " << c << "\n"; + tout << "assumptions: " << m_assumptions << "\n"; + tout << "trail: " << m_trail << "\n"; + tout << "model: " << m << "\n"; + m_mc.display(tout); + ); + for (literal l : c) { + if (was_eliminated(l.var())) IF_VERBOSE(0, verbose_stream() << "eliminated: " << l << "\n";); + } + ok = false; + } + } + unsigned l_idx = 0; + for (watch_list const& wlist : m_watches) { literal l = ~to_literal(l_idx); if (value_at(l, m) != l_true) { - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (!it2->is_binary_clause()) + for (watched const& w : wlist) { + if (!w.is_binary_non_learned_clause()) + continue; + literal l2 = w.get_literal(); + if (l.index() > l2.index()) continue; - literal l2 = it2->get_literal(); if (value_at(l2, m) != l_true) { - TRACE("sat", tout << "failed binary: " << l << " " << l2 << " learned: " << it2->is_learned() << "\n"; - m_mc.display(tout);); + IF_VERBOSE(0, verbose_stream() << "failed binary: " << l << " := " << value_at(l, m) << " " << l2 << " := " << value_at(l2, m) << "\n"); + IF_VERBOSE(0, verbose_stream() << "elim l1: " << was_eliminated(l.var()) << " elim l2: " << was_eliminated(l2) << "\n"); + TRACE("sat", m_mc.display(tout << "failed binary: " << l << " " << l2 << "\n");); ok = false; } } } + ++l_idx; } - for (unsigned i = 0; i < m_assumptions.size(); ++i) { - if (value_at(m_assumptions[i], m) != l_true) { + for (literal l : m_assumptions) { + if (value_at(l, m) != l_true) { + VERIFY(is_external(l.var())); + IF_VERBOSE(0, verbose_stream() << "assumption: " << l << " does not model check " << value_at(l, m) << "\n";); TRACE("sat", - tout << m_assumptions[i] << " does not model check\n"; + tout << l << " does not model check\n"; tout << "trail: " << m_trail << "\n"; tout << "model: " << m << "\n"; m_mc.display(tout); @@ -1337,22 +1822,75 @@ namespace sat { ok = false; } } - if (ok && !m_mc.check_model(m)) { + if (m_ext && !m_ext->check_model(m)) { ok = false; - TRACE("sat", tout << "model: " << m << "\n"; m_mc.display(tout);); } return ok; } - void solver::restart() { + bool solver::check_model(model const & m) const { + bool ok = check_clauses(m); + if (ok && !m_mc.check_model(m)) { + ok = false; + TRACE("sat", tout << "model: " << m << "\n"; m_mc.display(tout);); + } + IF_VERBOSE(1, verbose_stream() << "model check " << (ok?"OK":"failed") << "\n";); + return ok; + } + + bool solver::should_restart() const { + if (m_conflicts_since_restart <= m_restart_threshold) return false; + if (scope_lvl() < 2 + search_lvl()) return false; + if (m_config.m_restart != RS_EMA) return true; + return + m_fast_glue_avg + search_lvl() <= scope_lvl() && + m_config.m_restart_margin * m_slow_glue_avg <= m_fast_glue_avg; + } + + void solver::restart(bool to_base) { m_stats.m_restart++; m_restarts++; - IF_VERBOSE(1, - verbose_stream() << "(sat-restart :conflicts " << m_stats.m_conflict << " :decisions " << m_stats.m_decision - << " :restarts " << m_stats.m_restart << mk_stat(*this) - << " :time " << std::fixed << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n";); + if (m_conflicts_since_init > m_restart_next_out + 500) { + m_restart_next_out = m_conflicts_since_init; + IF_VERBOSE(1, + verbose_stream() << "(sat-restart :conflicts " << m_stats.m_conflict << " :decisions " << m_stats.m_decision + << " :restarts " << m_stats.m_restart << mk_stat(*this) + << " :time " << std::fixed << std::setprecision(2) << m_stopwatch.get_current_seconds() << ")\n";); + } IF_VERBOSE(30, display_status(verbose_stream());); - pop_reinit(scope_lvl()); + pop_reinit(restart_level(to_base)); + set_next_restart(); + } + + unsigned solver::restart_level(bool to_base) { + if (to_base || scope_lvl() == search_lvl()) { + return scope_lvl() - search_lvl(); + } + else { + bool_var next = m_case_split_queue.min_var(); + + // Implementations of Marijn's idea of reusing the + // trail when the next decision literal has lower precedence. + // pop trail from top +#if 0 + unsigned n = 0; + do { + bool_var prev = scope_literal(scope_lvl() - n - 1).var(); + if (m_case_split_queue.more_active(prev, next)) break; + ++n; + } + while (n < scope_lvl() - search_lvl()); + return n; +#endif + // pop trail from bottom + unsigned n = search_lvl(); + for (; n < scope_lvl() && m_case_split_queue.more_active(scope_literal(n).var(), next); ++n) { + } + return n - search_lvl(); + } + } + + void solver::set_next_restart() { m_conflicts_since_restart = 0; switch (m_config.m_restart) { case RS_GEOMETRIC: @@ -1362,6 +1900,11 @@ namespace sat { m_luby_idx++; m_restart_threshold = m_config.m_restart_initial * get_luby(m_luby_idx); break; + case RS_EMA: + m_restart_threshold = m_config.m_restart_initial; + break; + case RS_STATIC: + break; default: UNREACHABLE(); break; @@ -1378,6 +1921,10 @@ namespace sat { void solver::gc() { if (m_conflicts_since_gc <= m_gc_threshold) return; + if (m_config.m_gc_strategy == GC_DYN_PSM && !at_base_lvl()) + return; + unsigned gc = m_stats.m_gc_clause; + IF_VERBOSE(10, verbose_stream() << "(sat.gc)\n";); CASSERT("sat_gc_bug", check_invariant()); switch (m_config.m_gc_strategy) { case GC_GLUE: @@ -1393,7 +1940,7 @@ namespace sat { gc_psm_glue(); break; case GC_DYN_PSM: - if (m_scope_lvl != 0) + if (!at_base_lvl()) return; gc_dyn_psm(); break; @@ -1401,6 +1948,10 @@ namespace sat { UNREACHABLE(); break; } + if (m_ext) m_ext->gc(); + if (gc > 0 && should_defrag()) { + defrag_clauses(); + } m_conflicts_since_gc = 0; m_gc_threshold += m_config.m_gc_increment; CASSERT("sat_gc_bug", check_invariant()); @@ -1479,11 +2030,8 @@ namespace sat { \brief Compute the psm of all learned clauses. */ void solver::save_psm() { - clause_vector::iterator it = m_learned.begin(); - clause_vector::iterator end = m_learned.end(); - for (; it != end; ++it) { - clause & c = *(*it); - c.set_psm(psm(c)); + for (clause* cp : m_learned) { + cp->set_psm(psm(*cp)); } } @@ -1493,7 +2041,7 @@ namespace sat { void solver::gc_half(char const * st_name) { TRACE("sat", tout << "gc\n";); unsigned sz = m_learned.size(); - unsigned new_sz = sz/2; + unsigned new_sz = sz/2; // std::min(sz/2, m_clauses.size()*2); unsigned j = new_sz; for (unsigned i = new_sz; i < sz; i++) { clause & c = *(m_learned[i]); @@ -1519,7 +2067,7 @@ namespace sat { TRACE("sat", tout << "gc\n";); // To do gc at scope_lvl() > 0, I will need to use the reinitialization stack, or live with the fact // that I may miss some propagations for reactivated clauses. - SASSERT(scope_lvl() == 0); + SASSERT(at_base_lvl()); // compute // d_tk unsigned h = 0; @@ -1610,7 +2158,7 @@ namespace sat { // return true if should keep the clause, and false if we should delete it. bool solver::activate_frozen_clause(clause & c) { TRACE("sat_gc", tout << "reactivating:\n" << c << "\n";); - SASSERT(scope_lvl() == 0); + SASSERT(at_base_lvl()); // do some cleanup unsigned sz = c.size(); unsigned j = 0; @@ -1622,7 +2170,9 @@ namespace sat { case l_false: break; case l_undef: - c[j] = c[i]; + if (i != j) { + std::swap(c[i], c[j]); + } j++; break; } @@ -1640,7 +2190,11 @@ namespace sat { mk_bin_clause(c[0], c[1], true); return false; default: - c.shrink(new_sz); + if (new_sz != sz) { + if (m_config.m_drat) m_drat.del(c); + c.shrink(new_sz); + if (m_config.m_drat) m_drat.add(c, true); + } attach_clause(c); return true; } @@ -1651,9 +2205,7 @@ namespace sat { */ unsigned solver::psm(clause const & c) const { unsigned r = 0; - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - literal l = c[i]; + for (literal l : c) { if (l.sign()) { if (m_phase[l.var()] == NEG_PHASE) r++; @@ -1686,10 +2238,13 @@ namespace sat { bool solver::resolve_conflict_core() { - m_stats.m_conflict++; - m_conflicts++; + m_conflicts_since_init++; m_conflicts_since_restart++; m_conflicts_since_gc++; + m_stats.m_conflict++; + if (m_step_size > m_config.m_step_size_min) { + m_step_size -= m_config.m_step_size_dec; + } m_conflict_lvl = get_max_lvl(m_not_l, m_conflict); TRACE("sat", tout << "conflict detected at level " << m_conflict_lvl << " for "; @@ -1705,21 +2260,36 @@ namespace sat { return false; } - m_lemma.reset(); - forget_phase_of_vars(m_conflict_lvl); + if (m_ext) { + switch (m_ext->resolve_conflict()) { + case l_true: + learn_lemma_and_backjump(); + return true; + case l_undef: + break; + case l_false: + // backjumping was taken care of internally. + return true; + } + } + + m_lemma.reset(); + unsigned idx = skip_literals_above_conflict_level(); + // save space for first uip m_lemma.push_back(null_literal); unsigned num_marks = 0; + literal consequent = null_literal; if (m_not_l != null_literal) { TRACE("sat_conflict", tout << "not_l: " << m_not_l << "\n";); process_antecedent(m_not_l, num_marks); + consequent = ~m_not_l; } - literal consequent = m_not_l; justification js = m_conflict; do { @@ -1736,7 +2306,7 @@ namespace sat { process_antecedent(~(js.get_literal2()), num_marks); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = get_clause(js); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); @@ -1755,10 +2325,8 @@ namespace sat { } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) - process_antecedent(*it, num_marks); + for (literal l : m_ext_antecedents) + process_antecedent(l, num_marks); break; } default: @@ -1785,38 +2353,48 @@ namespace sat { while (num_marks > 0); m_lemma[0] = ~consequent; + learn_lemma_and_backjump(); + return true; + } + + void solver::learn_lemma_and_backjump() { TRACE("sat_lemma", tout << "new lemma size: " << m_lemma.size() << "\n" << m_lemma << "\n";); - if (m_config.m_minimize_lemmas) { - minimize_lemma(); - reset_lemma_var_marks(); - if (m_config.m_dyn_sub_res) - dyn_sub_res(); - TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";); - } - else - reset_lemma_var_marks(); - - literal_vector::iterator it = m_lemma.begin(); - literal_vector::iterator end = m_lemma.end(); unsigned new_scope_lvl = 0; - ++it; - for(; it != end; ++it) { - bool_var var = (*it).var(); - new_scope_lvl = std::max(new_scope_lvl, lvl(var)); + if (!m_lemma.empty()) { + if (m_config.m_minimize_lemmas) { + minimize_lemma(); + reset_lemma_var_marks(); + if (m_config.m_dyn_sub_res) + dyn_sub_res(); + TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";); + } + else + reset_lemma_var_marks(); + + literal_vector::iterator it = m_lemma.begin(); + literal_vector::iterator end = m_lemma.end(); + ++it; + for(; it != end; ++it) { + bool_var var = (*it).var(); + new_scope_lvl = std::max(new_scope_lvl, lvl(var)); + } } unsigned glue = num_diff_levels(m_lemma.size(), m_lemma.c_ptr()); + m_fast_glue_avg.update(glue); + m_slow_glue_avg.update(glue); pop_reinit(m_scope_lvl - new_scope_lvl); - TRACE("sat_conflict_detail", display(tout); tout << "assignment:\n"; display_assignment(tout);); + TRACE("sat_conflict_detail", tout << new_scope_lvl << "\n"; display(tout);); + // unsound: m_asymm_branch.minimize(m_scc, m_lemma); clause * lemma = mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); if (lemma) { lemma->set_glue(glue); + if (m_par) m_par->share_clause(*this, *lemma); } decay_activity(); updt_phase_counters(); - return true; } void solver::process_antecedent_for_unsat_core(literal antecedent) { @@ -1836,8 +2414,7 @@ namespace sat { TRACE("sat", tout << "processing consequent: "; if (consequent == null_literal) tout << "null\n"; else tout << consequent << "\n"; - display_justification(tout << "js kind: ", js); - tout << "\n";); + display_justification(tout << "js kind: ", js) << "\n";); switch (js.get_kind()) { case justification::NONE: break; @@ -1851,7 +2428,7 @@ namespace sat { process_antecedent_for_unsat_core(~(js.get_literal2())); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = get_clause(js); unsigned i = 0; if (consequent != null_literal) { SASSERT(c[0] == consequent || c[1] == consequent); @@ -1870,10 +2447,9 @@ namespace sat { } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) - process_antecedent_for_unsat_core(*it); + for (literal l : m_ext_antecedents) { + process_antecedent_for_unsat_core(l); + } break; } default: @@ -1882,125 +2458,10 @@ namespace sat { } } - bool solver::resolve_conflict_for_init() { - if (m_conflict_lvl == 0) { - return false; - } - m_lemma.reset(); - m_lemma.push_back(null_literal); // asserted literal - if (m_not_l != null_literal) { - TRACE("sat", tout << "not_l: " << m_not_l << "\n";); - process_antecedent_for_init(m_not_l); - } - literal consequent = m_not_l; - justification js = m_conflict; - - SASSERT(m_trail.size() > 0); - unsigned idx = m_trail.size(); - while (process_consequent_for_init(consequent, js)) { - while (true) { - --idx; - literal l = m_trail[idx]; - if (is_marked(l.var())) - break; - SASSERT(idx > 0); - } - consequent = m_trail[idx]; - bool_var c_var = consequent.var(); - if (lvl(consequent) == 0) { - return false; - } - SASSERT(m_conflict_lvl == 1); - js = m_justification[c_var]; - reset_mark(c_var); - } - - unsigned new_scope_lvl = 0; - m_lemma[0] = ~consequent; - for (unsigned i = 1; i < m_lemma.size(); ++i) { - bool_var var = m_lemma[i].var(); - if (is_marked(var)) { - reset_mark(var); - new_scope_lvl = std::max(new_scope_lvl, lvl(var)); - } - else { - m_lemma[i] = m_lemma.back(); - m_lemma.pop_back(); - --i; - } - } - TRACE("sat", tout << "lemma: " << m_lemma << "\n"; display(tout); tout << "assignment:\n"; display_assignment(tout);); - if (new_scope_lvl == 0) { - pop_reinit(m_scope_lvl); - } - else { - unassign_vars(idx); - } - mk_clause_core(m_lemma.size(), m_lemma.c_ptr(), true); - TRACE("sat", tout << "Trail: " << m_trail << "\n";); - m_inconsistent = false; - return true; - } - - bool solver::process_consequent_for_init(literal consequent, justification const& js) { - switch (js.get_kind()) { - case justification::NONE: - return false; - case justification::BINARY: - process_antecedent_for_init(~(js.get_literal())); - break; - case justification::TERNARY: - process_antecedent_for_init(~(js.get_literal1())); - process_antecedent_for_init(~(js.get_literal2())); - break; - case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); - unsigned i = 0; - if (consequent != null_literal) { - SASSERT(c[0] == consequent || c[1] == consequent); - if (c[0] == consequent) { - i = 1; - } - else { - process_antecedent_for_init(~c[0]); - i = 2; - } - } - unsigned sz = c.size(); - for (; i < sz; i++) - process_antecedent_for_init(~c[i]); - break; - } - case justification::EXT_JUSTIFICATION: { - fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) - process_antecedent_for_init(*it); - break; - } - default: - UNREACHABLE(); - break; - } - return true; - } - - void solver::process_antecedent_for_init(literal antecedent) { - bool_var var = antecedent.var(); - SASSERT(var < num_vars()); - if (!is_marked(var) && lvl(var) > 0) { - mark(var); - m_lemma.push_back(~antecedent); - } - } - - void solver::resolve_conflict_for_unsat_core() { TRACE("sat", display(tout); unsigned level = 0; - for (unsigned i = 0; i < m_trail.size(); ++i) { - literal l = m_trail[i]; + for (literal l : m_trail) { if (level != m_level[l.var()]) { level = m_level[l.var()]; tout << level << ": "; @@ -2020,18 +2481,18 @@ namespace sat { } SASSERT(m_unmark.empty()); DEBUG_CODE({ - for (unsigned i = 0; i < m_trail.size(); ++i) { - SASSERT(!is_marked(m_trail[i].var())); + for (literal lit : m_trail) { + SASSERT(!is_marked(lit.var())); }}); unsigned old_size = m_unmark.size(); int idx = skip_literals_above_conflict_level(); + literal consequent = m_not_l; if (m_not_l != null_literal) { justification js = m_justification[m_not_l.var()]; TRACE("sat", tout << "not_l: " << m_not_l << "\n"; - display_justification(tout, js); - tout << "\n";); + display_justification(tout, js) << "\n";); process_antecedent_for_unsat_core(m_not_l); if (is_assumption(~m_not_l)) { @@ -2040,11 +2501,10 @@ namespace sat { else { process_consequent_for_unsat_core(m_not_l, js); } + consequent = ~m_not_l; } - literal consequent = m_not_l; - justification js = m_conflict; - + justification js = m_conflict; while (true) { process_consequent_for_unsat_core(consequent, js); @@ -2069,6 +2529,14 @@ namespace sat { idx--; } reset_unmark(old_size); + if (m_core.size() > 1) { + unsigned j = 0; + for (unsigned i = 0; i < m_core.size(); ++i) { + if (lvl(m_core[i]) > 0) m_core[j++] = m_core[i]; + } + m_core.shrink(j); + } + if (m_config.m_core_minimize) { if (m_min_core_valid && m_min_core.size() < m_core.size()) { IF_VERBOSE(1, verbose_stream() << "(sat.updating core " << m_min_core.size() << " " << m_core.size() << ")\n";); @@ -2082,62 +2550,35 @@ namespace sat { set_model(m_mus.get_model()); IF_VERBOSE(2, verbose_stream() << "(sat.core: " << m_core << ")\n";); } + } - unsigned solver::get_max_lvl(literal consequent, justification js) { - if (!m_ext) + unsigned solver::get_max_lvl(literal not_l, justification js) { + if (!m_ext || at_base_lvl()) return scope_lvl(); - if (scope_lvl() == 0) - return 0; - - unsigned r = 0; - - if (consequent != null_literal) - r = lvl(consequent); - switch (js.get_kind()) { case justification::NONE: - break; case justification::BINARY: - r = std::max(r, lvl(js.get_literal())); - break; case justification::TERNARY: - r = std::max(r, lvl(js.get_literal1())); - r = std::max(r, lvl(js.get_literal2())); - break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); - unsigned i = 0; - if (consequent != null_literal) { - SASSERT(c[0] == consequent || c[1] == consequent); - if (c[0] == consequent) { - i = 1; - } - else { - r = std::max(r, lvl(c[0])); - i = 2; - } - } - unsigned sz = c.size(); - for (; i < sz; i++) - r = std::max(r, lvl(c[i])); - break; + return scope_lvl(); } case justification::EXT_JUSTIFICATION: { - fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) - r = std::max(r, lvl(*it)); - break; + unsigned r = 0; + SASSERT(not_l != null_literal); + r = lvl(not_l); + fill_ext_antecedents(~not_l, js); + for (literal l : m_ext_antecedents) { + r = std::max(r, lvl(l)); + } + return r; } default: UNREACHABLE(); - break; + return 0; } - return r; } /** @@ -2165,7 +2606,16 @@ namespace sat { SASSERT(var < num_vars()); if (!is_marked(var) && var_lvl > 0) { mark(var); - inc_activity(var); + switch (m_config.m_branching_heuristic) { + case BH_VSIDS: + inc_activity(var); + break; + case BH_CHB: + m_last_conflict[var] = m_stats.m_conflict; + break; + default: + break; + } if (var_lvl == m_conflict_lvl) num_marks++; else @@ -2232,6 +2682,51 @@ namespace sat { return r; } + bool solver::num_diff_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue) { + m_diff_levels.reserve(scope_lvl() + 1, false); + glue = 0; + unsigned i = 0; + for (; i < num && glue < max_glue; i++) { + SASSERT(value(lits[i]) != l_undef); + unsigned lit_lvl = lvl(lits[i]); + if (m_diff_levels[lit_lvl] == false) { + m_diff_levels[lit_lvl] = true; + glue++; + } + } + num = i; + // reset m_diff_levels. + for (i = 0; i < num; i++) + m_diff_levels[lvl(lits[i])] = false; + return glue < max_glue; + } + + bool solver::num_diff_false_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue) { + m_diff_levels.reserve(scope_lvl() + 1, false); + glue = 0; + unsigned i = 0; + for (; i < num && glue < max_glue; i++) { + if (value(lits[i]) == l_false) { + unsigned lit_lvl = lvl(lits[i]); + if (m_diff_levels[lit_lvl] == false) { + m_diff_levels[lit_lvl] = true; + glue++; + } + } + } + num = i; + // reset m_diff_levels. + for (i = 0; i < num; i++) { + literal lit = lits[i]; + if (value(lit) == l_false) { + VERIFY(lvl(lit) < m_diff_levels.size()); + m_diff_levels[lvl(lit)] = false; + } + } + return glue < max_glue; + } + + /** \brief Process an antecedent for lemma minimization. */ @@ -2266,7 +2761,7 @@ namespace sat { while (!m_lemma_min_stack.empty()) { bool_var var = m_lemma_min_stack.back(); m_lemma_min_stack.pop_back(); - justification js = m_justification[var]; + justification const& js = m_justification[var]; switch(js.get_kind()) { case justification::NONE: // it is a decision variable from a previous scope level @@ -2289,7 +2784,7 @@ namespace sat { } break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); + clause & c = get_clause(js); unsigned i = 0; if (c[0].var() == var) { i = 1; @@ -2314,10 +2809,8 @@ namespace sat { case justification::EXT_JUSTIFICATION: { literal consequent(var, value(var) == l_false); fill_ext_antecedents(consequent, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) { - if (!process_antecedent_for_minimization(*it)) { + for (literal l : m_ext_antecedents) { + if (!process_antecedent_for_minimization(l)) { reset_unmark(old_size); return false; } @@ -2349,10 +2842,8 @@ namespace sat { */ void solver::updt_lemma_lvl_set() { m_lvl_set.reset(); - literal_vector::const_iterator it = m_lemma.begin(); - literal_vector::const_iterator end = m_lemma.end(); - for(; it != end; ++it) - m_lvl_set.insert(lvl(*it)); + for (literal l : m_lemma) + m_lvl_set.insert(lvl(l)); } /** @@ -2361,6 +2852,7 @@ namespace sat { assigned at level 0. */ void solver::minimize_lemma() { + SASSERT(!m_lemma.empty()); SASSERT(m_unmark.empty()); //m_unmark.reset(); updt_lemma_lvl_set(); @@ -2368,11 +2860,14 @@ namespace sat { unsigned sz = m_lemma.size(); unsigned i = 1; // the first literal is the FUIP unsigned j = 1; + //bool drop = false; + //unsigned bound = sz/5+10; for (; i < sz; i++) { literal l = m_lemma[i]; if (implied_by_marked(l)) { TRACE("sat", tout << "drop: " << l << "\n";); m_unmark.push_back(l.var()); + //drop = true; } else { if (j != i) { @@ -2380,6 +2875,12 @@ namespace sat { } j++; } +#if 0 + if (!drop && i >= bound) { + j = sz; + break; + } +#endif } reset_unmark(0); @@ -2391,6 +2892,10 @@ namespace sat { \brief Reset the mark of the variables in the current lemma. */ void solver::reset_lemma_var_marks() { + if (m_config.m_branching_heuristic == BH_LRB || + m_config.m_branching_heuristic == BH_VSIDS) { + update_lrb_reasoned(); + } literal_vector::iterator it = m_lemma.begin(); literal_vector::iterator end = m_lemma.end(); SASSERT(!is_marked((*it).var())); @@ -2401,6 +2906,55 @@ namespace sat { } } + void solver::update_lrb_reasoned() { + unsigned sz = m_lemma.size(); + SASSERT(!is_marked(m_lemma[0].var())); + mark(m_lemma[0].var()); + for (unsigned i = m_lemma.size(); i-- > 0; ) { + justification js = m_justification[m_lemma[i].var()]; + switch (js.get_kind()) { + case justification::NONE: + break; + case justification::BINARY: + update_lrb_reasoned(js.get_literal()); + break; + case justification::TERNARY: + update_lrb_reasoned(js.get_literal1()); + update_lrb_reasoned(js.get_literal2()); + break; + case justification::CLAUSE: { + clause & c = get_clause(js); + for (literal l : c) { + update_lrb_reasoned(l); + } + break; + } + case justification::EXT_JUSTIFICATION: { + fill_ext_antecedents(m_lemma[i], js); + for (literal l : m_ext_antecedents) { + update_lrb_reasoned(l); + } + break; + } + } + } + reset_mark(m_lemma[0].var()); + for (unsigned i = m_lemma.size(); i-- > sz; ) { + reset_mark(m_lemma[i].var()); + } + m_lemma.shrink(sz); + } + + void solver::update_lrb_reasoned(literal lit) { + bool_var v = lit.var(); + if (!is_marked(v)) { + mark(v); + m_reasoned[v]++; + inc_activity(v); + m_lemma.push_back(lit); + } + } + /** \brief Apply dynamic subsumption resolution to new lemma. Only binary and ternary clauses are used. @@ -2423,25 +2977,23 @@ namespace sat { continue; // literal was eliminated // first use watch lists watch_list const & wlist = get_wlist(~l); - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { + for (watched const& w : wlist) { // In this for-loop, the conditions l0 != ~l2 and l0 != ~l3 // are not really needed if the solver does not miss unit propagations. // However, we add them anyway because we don't want to rely on this // property of the propagator. // For example, if this property is relaxed in the future, then the code // without the conditions l0 != ~l2 and l0 != ~l3 may remove the FUIP - if (it->is_binary_clause()) { - literal l2 = it->get_literal(); + if (w.is_binary_clause()) { + literal l2 = w.get_literal(); if (is_marked_lit(~l2) && l0 != ~l2) { // eliminate ~l2 from lemma because we have the clause l \/ l2 unmark_lit(~l2); } } - else if (it->is_ternary_clause()) { - literal l2 = it->get_literal1(); - literal l3 = it->get_literal2(); + else if (w.is_ternary_clause()) { + literal l2 = w.get_literal1(); + literal l3 = w.get_literal2(); if (is_marked_lit(l2) && is_marked_lit(~l3) && l0 != ~l3) { // eliminate ~l3 from lemma because we have the clause l \/ l2 \/ l3 unmark_lit(~l3); @@ -2460,10 +3012,7 @@ namespace sat { // try to use cached implication if available literal_vector * implied_lits = m_probing.cached_implied_lits(~l); if (implied_lits) { - literal_vector::iterator it = implied_lits->begin(); - literal_vector::iterator end = implied_lits->end(); - for (; it != end; ++it) { - literal l2 = *it; + for (literal l2 : *implied_lits) { // Here, we must check l0 != ~l2. // l \/ l2 is an implied binary clause. // However, it may have been deduced using a lemma that has been deleted. @@ -2549,6 +3098,7 @@ namespace sat { pop(num_scopes); exchange_par(); reinit_assumptions(); + m_stats.m_units = init_trail_size(); } void solver::pop(unsigned num_scopes) { @@ -2564,6 +3114,8 @@ namespace sat { m_scope_lvl -= num_scopes; m_scopes.shrink(new_lvl); reinit_clauses(s.m_clauses_to_reinit_lim); + if (m_ext) + m_ext->pop_reinit(); } void solver::unassign_vars(unsigned old_sz) { @@ -2577,6 +3129,18 @@ namespace sat { bool_var v = l.var(); SASSERT(value(v) == l_undef); m_case_split_queue.unassign_var_eh(v); + if (m_config.m_branching_heuristic == BH_LRB) { + uint64_t interval = m_stats.m_conflict - m_last_propagation[v]; + if (interval > 0) { + auto activity = m_activity[v]; + auto reward = (m_config.m_reward_offset * (m_participated[v] + m_reasoned[v])) / interval; + m_activity[v] = static_cast(m_step_size * reward + ((1 - m_step_size) * activity)); + m_case_split_queue.activity_changed_eh(v, m_activity[v] > activity); + } + } + if (m_config.m_anti_exploration) { + m_canceled[v] = m_stats.m_conflict; + } } m_trail.shrink(old_sz); m_qhead = old_sz; @@ -2592,7 +3156,7 @@ namespace sat { bool reinit = false; if (cw.is_binary()) { if (propagate_bin_clause(cw[0], cw[1])) { - if (scope_lvl() > 0) { + if (!at_base_lvl()) { m_clauses_to_reinit[j] = cw; j++; } @@ -2602,7 +3166,7 @@ namespace sat { clause & c = *(cw.get_clause()); detach_clause(c); attach_clause(c, reinit); - if (scope_lvl() > 0 && reinit) { + if (!at_base_lvl() && reinit) { // clause propagated literal, must keep it in the reinit stack. m_clauses_to_reinit[j] = cw; j++; @@ -2644,15 +3208,22 @@ namespace sat { clauses.shrink(j); } - void solver::gc_bin(bool learned, literal nlit) { - m_user_bin_clauses.reset(); - collect_bin_clauses(m_user_bin_clauses, learned); - for (unsigned i = 0; i < m_user_bin_clauses.size(); ++i) { - literal l1 = m_user_bin_clauses[i].first; - literal l2 = m_user_bin_clauses[i].second; - if (nlit == l1 || nlit == l2) { - detach_bin_clause(l1, l2, learned); + void solver::gc_bin(literal lit) { + bool_var v = lit.var(); + for (watch_list& wlist : m_watches) { + watch_list::iterator it = wlist.begin(); + watch_list::iterator it2 = wlist.begin(); + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + if (it->is_binary_clause() && it->get_literal().var() == v) { + // skip + } + else { + *it2 = *it; + ++it2; + } } + wlist.set_end(it2); } } @@ -2669,16 +3240,11 @@ namespace sat { } bool_var solver::max_var(clause_vector& clauses, bool_var v) { - for (unsigned i = 0; i < clauses.size(); ++i) { - clause & c = *(clauses[i]); - literal* it = c.begin(); - literal * end = c.end(); - for (; it != end; ++it) { - if (it->var() > v) { + for (clause* cp : clauses) + for (auto it = cp->begin(), end = cp->end(); it != end; ++it) { + if (it->var() > v) v = it->var(); - } } - } return v; } @@ -2689,8 +3255,8 @@ namespace sat { w = max_var(true, w); w = max_var(false, w); v = m_mc.max_var(w); - for (unsigned i = 0; i < m_trail.size(); ++i) { - if (m_trail[i].var() > w) w = m_trail[i].var(); + for (literal lit : m_trail) { + if (lit.var() > w) w = lit.var(); } v = std::max(v, w + 1); } @@ -2698,6 +3264,8 @@ namespace sat { if (v < m_level.size()) { for (bool_var i = v; i < m_level.size(); ++i) { m_case_split_queue.del_var_eh(i); + m_probing.reset_cache(literal(i, true)); + m_probing.reset_cache(literal(i, false)); } m_watches.shrink(2*v); m_assignment.shrink(2*v); @@ -2718,6 +3286,7 @@ namespace sat { void solver::user_pop(unsigned num_scopes) { pop_to_base_level(); + TRACE("sat", display(tout);); while (num_scopes > 0) { literal lit = m_user_scope_literals.back(); m_user_scope_literals.pop_back(); @@ -2726,8 +3295,7 @@ namespace sat { gc_lit(m_learned, lit); gc_lit(m_clauses, lit); - gc_bin(true, lit); - gc_bin(false, lit); + gc_bin(lit); TRACE("sat", tout << "gc: " << lit << "\n"; display(tout);); --num_scopes; for (unsigned i = 0; i < m_trail.size(); ++i) { @@ -2737,8 +3305,10 @@ namespace sat { break; } } - gc_var(lit.var()); + gc_var(lit.var()); } + m_qhead = 0; + propagate(false); } void solver::pop_to_base_level() { @@ -2760,6 +3330,10 @@ namespace sat { m_probing.updt_params(p); m_scc.updt_params(p); m_rand.set_seed(m_config.m_random_seed); + m_step_size = m_config.m_step_size_init; + m_drat.updt_config(); + m_fast_glue_avg.set_alpha(m_config.m_fast_glue_avg); + m_slow_glue_avg.set_alpha(m_config.m_slow_glue_avg); } void solver::collect_param_descrs(param_descrs & d) { @@ -2777,6 +3351,8 @@ namespace sat { m_scc.collect_statistics(st); m_asymm_branch.collect_statistics(st); m_probing.collect_statistics(st); + if (m_ext) m_ext->collect_statistics(st); + st.copy(m_aux_stats); } void solver::reset_statistics() { @@ -2785,6 +3361,7 @@ namespace sat { m_simplifier.reset_statistics(); m_asymm_branch.reset_statistics(); m_probing.reset_statistics(); + m_aux_stats.reset(); } // ----------------------- @@ -2794,33 +3371,44 @@ namespace sat { // ----------------------- void solver::rescale_activity() { - svector::iterator it = m_activity.begin(); - svector::iterator end = m_activity.end(); - for (; it != end; ++it) { - *it >>= 14; + SASSERT(m_config.m_branching_heuristic == BH_VSIDS); + for (unsigned& act : m_activity) { + act >>= 14; } m_activity_inc >>= 14; } + void solver::update_chb_activity(bool is_sat, unsigned qhead) { + SASSERT(m_config.m_branching_heuristic == BH_CHB); + double multiplier = m_config.m_reward_offset * (is_sat ? m_config.m_reward_multiplier : 1.0); + for (unsigned i = qhead; i < m_trail.size(); ++i) { + auto v = m_trail[i].var(); + auto reward = multiplier / (m_stats.m_conflict - m_last_conflict[v] + 1); + auto activity = m_activity[v]; + m_activity[v] = static_cast(m_step_size * reward + ((1.0 - m_step_size) * activity)); + m_case_split_queue.activity_changed_eh(v, m_activity[v] > activity); + } + } + // ----------------------- // // Iterators // // ----------------------- - void solver::collect_bin_clauses(svector & r, bool learned) const { + void solver::collect_bin_clauses(svector & r, bool learned, bool learned_only) const { + SASSERT(learned || !learned_only); unsigned sz = m_watches.size(); for (unsigned l_idx = 0; l_idx < sz; l_idx++) { literal l = to_literal(l_idx); l.neg(); - watch_list const & wlist = m_watches[l_idx]; - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_clause()) + for (watched const& w : m_watches[l_idx]) { + if (!w.is_binary_clause()) continue; - if (!learned && it->is_learned()) + if (!learned && w.is_learned()) continue; - literal l2 = it->get_literal(); + else if (learned && learned_only && !w.is_learned()) + continue; + literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; TRACE("cleanup_bug", tout << "collected: " << l << " " << l2 << "\n";); @@ -2837,7 +3425,8 @@ namespace sat { bool solver::check_invariant() const { if (!m_rlimit.inc()) return true; integrity_checker checker(*this); - SASSERT(checker()); + VERIFY(checker()); + VERIFY(!m_ext || m_ext->validate()); return true; } @@ -2853,27 +3442,32 @@ namespace sat { for (unsigned l_idx = 0; l_idx < sz; l_idx++) { literal l = to_literal(l_idx); l.neg(); - watch_list const & wlist = m_watches[l_idx]; - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (; it != end; ++it) { - if (!it->is_binary_clause()) + for (watched const& w : m_watches[l_idx]) { + if (!w.is_binary_clause()) continue; - literal l2 = it->get_literal(); + literal l2 = w.get_literal(); if (l.index() > l2.index()) continue; - out << "(" << l << " " << l2 << ")\n"; + out << "(" << l << " " << l2 << ")"; + if (w.is_learned()) out << "*"; + out << "\n"; } } } void solver::display_units(std::ostream & out) const { - unsigned end = init_trail_size(); - for (unsigned i = 0; i < end; i++) { - out << m_trail[i] << " "; + unsigned level = 0; + for (literal lit : m_trail) { + if (lvl(lit) > level) { + level = lvl(lit); + out << level << ": "; + } + else { + out << " "; + } + out << lit << " "; + display_justification(out, m_justification[lit.var()]) << "\n"; } - if (end != 0) - out << "\n"; } void solver::display(std::ostream & out) const { @@ -2881,66 +3475,69 @@ namespace sat { display_units(out); display_binary(out); out << m_clauses << m_learned; + if (m_ext) { + m_ext->display(out); + } out << ")\n"; } - void solver::display_justification(std::ostream & out, justification const& js) const { + std::ostream& solver::display_justification(std::ostream & out, justification const& js) const { out << js; if (js.is_clause()) { - out << *(m_cls_allocator.get_clause(js.get_clause_offset())); + out << get_clause(js); } + else if (js.is_ext_justification() && m_ext) { + m_ext->display_justification(out << " ", js.get_ext_justification_idx()); + } + return out; } unsigned solver::num_clauses() const { - unsigned num_cls = 0; - num_cls += m_trail.size(); // units; - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) + unsigned num_cls = m_trail.size(); // units; + unsigned l_idx = 0; + for (auto const& wl : m_watches) { + literal l = ~to_literal(l_idx++); + for (auto const& w : wl) { + if (w.is_binary_clause() && l.index() < w.get_literal().index()) num_cls++; } } - clause_vector const * vs[2] = { &m_clauses, &m_learned }; - for (unsigned i = 0; i < 2; i++) { - clause_vector const & cs = *(vs[i]); - num_cls += cs.size(); + return num_cls + m_clauses.size() + m_learned.size(); + } + + void solver::num_binary(unsigned& given, unsigned& learned) const { + given = learned = 0; + unsigned l_idx = 0; + for (auto const& wl : m_watches) { + literal l = ~to_literal(l_idx++); + for (auto const& w : wl) { + if (w.is_binary_clause() && l.index() < w.get_literal().index()) { + if (w.is_learned()) ++learned; else ++given; + } + } } - return num_cls; } void solver::display_dimacs(std::ostream & out) const { out << "p cnf " << num_vars() << " " << num_clauses() << "\n"; - for (unsigned i = 0; i < m_trail.size(); i++) { - out << dimacs_lit(m_trail[i]) << " 0\n"; + for (literal lit : m_trail) { + out << dimacs_lit(lit) << " 0\n"; } - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) - out << dimacs_lit(l) << " " << dimacs_lit(it2->get_literal()) << " 0\n"; + unsigned l_idx = 0; + for (auto const& wlist : m_watches) { + literal l = ~to_literal(l_idx++); + for (auto const& w : wlist) { + if (w.is_binary_clause() && l.index() < w.get_literal().index()) + out << dimacs_lit(l) << " " << dimacs_lit(w.get_literal()) << " 0\n"; } } clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause const & c = *(*it); - unsigned sz = c.size(); - for (unsigned j = 0; j < sz; j++) - out << dimacs_lit(c[j]) << " "; + for (auto cp : cs) { + for (literal l : *cp) { + out << dimacs_lit(l) << " "; + } out << "0\n"; } } @@ -2956,32 +3553,26 @@ namespace sat { out << "p wcnf " << num_vars() << " " << num_clauses() + sz << " " << max_weight << "\n"; out << "c soft " << sz << "\n"; - for (unsigned i = 0; i < m_trail.size(); i++) { - out << max_weight << " " << dimacs_lit(m_trail[i]) << " 0\n"; + for (literal lit : m_trail) { + out << max_weight << " " << dimacs_lit(lit) << " 0\n"; } - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { + unsigned l_idx = 0; + for (watch_list const& wlist : m_watches) { literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - if (it2->is_binary_clause() && l.index() < it2->get_literal().index()) - out << max_weight << " " << dimacs_lit(l) << " " << dimacs_lit(it2->get_literal()) << " 0\n"; + for (watched const& w : wlist) { + if (w.is_binary_clause() && l.index() < w.get_literal().index()) + out << max_weight << " " << dimacs_lit(l) << " " << dimacs_lit(w.get_literal()) << " 0\n"; } + ++l_idx; } clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause const & c = *(*it); - unsigned clsz = c.size(); + for (clause const* cp : cs) { + clause const & c = *cp; out << max_weight << " "; - for (unsigned j = 0; j < clsz; j++) - out << dimacs_lit(c[j]) << " "; + for (literal l : c) + out << dimacs_lit(l) << " "; out << "0\n"; } } @@ -2991,19 +3582,23 @@ namespace sat { out.flush(); } + void solver::display_watches(std::ostream & out, literal lit) const { + display_watch_list(out << lit << ": ", get_wlist(lit)) << "\n"; + } void solver::display_watches(std::ostream & out) const { - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - watch_list const & wlist = *it; - literal l = to_literal(l_idx); - out << l << ": "; - sat::display(out, m_cls_allocator, wlist); - out << "\n"; + unsigned l_idx = 0; + for (watch_list const& wlist : m_watches) { + literal l = to_literal(l_idx++); + if (!wlist.empty()) + display_watch_list(out << l << ": ", wlist) << "\n"; } } + std::ostream& solver::display_watch_list(std::ostream& out, watch_list const& wl) const { + return sat::display_watch_list(out, cls_allocator(), wl); + } + void solver::display_assignment(std::ostream & out) const { out << m_trail << "\n"; } @@ -3013,9 +3608,8 @@ namespace sat { */ bool solver::is_unit(clause const & c) const { bool found_undef = false; - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - switch (value(c[i])) { + for (literal l : c) { + switch (value(l)) { case l_undef: if (found_undef) return false; @@ -3034,24 +3628,20 @@ namespace sat { \brief Return true, if all literals in c are assigned to false. */ bool solver::is_empty(clause const & c) const { - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - if (value(c[i]) != l_false) + for (literal lit : c) + if (value(lit) != l_false) return false; - } return true; } bool solver::check_missed_propagation(clause_vector const & cs) const { - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause const & c = *(*it); + for (clause* cp : cs) { + clause const & c = *cp; if (c.frozen()) continue; if (is_empty(c) || is_unit(c)) { TRACE("sat_missed_prop", tout << "missed_propagation: " << c << "\n"; - for (unsigned i = 0; i < c.size(); i++) tout << c[i] << ": " << value(c[i]) << "\n";); + for (literal l : c) tout << l << ": " << value(l) << "\n";); UNREACHABLE(); } SASSERT(!is_empty(c)); @@ -3072,14 +3662,14 @@ namespace sat { // // ----------------------- void solver::cleanup() { - if (scope_lvl() > 0 || inconsistent()) + if (!at_base_lvl() || inconsistent()) return; if (m_cleaner() && m_ext) m_ext->clauses_modifed(); } void solver::simplify(bool learned) { - if (scope_lvl() > 0 || inconsistent()) + if (!at_base_lvl() || inconsistent()) return; m_simplifier(learned); m_simplifier.finalize(); @@ -3088,7 +3678,7 @@ namespace sat { } unsigned solver::scc_bin() { - if (scope_lvl() > 0 || inconsistent()) + if (!at_base_lvl() || inconsistent()) return 0; unsigned r = m_scc(); if (r > 0 && m_ext) @@ -3113,11 +3703,10 @@ namespace sat { m_user_bin_clauses.reset(); m_binary_clause_graph.reset(); collect_bin_clauses(m_user_bin_clauses, true); - collect_bin_clauses(m_user_bin_clauses, false); hashtable, default_eq > seen_bc; - for (unsigned i = 0; i < m_user_bin_clauses.size(); ++i) { - literal l1 = m_user_bin_clauses[i].first; - literal l2 = m_user_bin_clauses[i].second; + for (auto const& b : m_user_bin_clauses) { + literal l1 = b.first; + literal l2 = b.second; literal_pair p(l1, l2); if (!seen_bc.contains(p)) { seen_bc.insert(p); @@ -3125,17 +3714,21 @@ namespace sat { } } vector _mutexes; + literal_vector _lits(lits); + if (m_ext) { + // m_ext->find_mutexes(_lits, mutexes); + } unsigned_vector ps; - for (unsigned i = 0; i < lits.size(); ++i) { - ps.push_back(lits[i].index()); + for (literal lit : _lits) { + ps.push_back(lit.index()); } mc.cliques(ps, _mutexes); - for (unsigned i = 0; i < _mutexes.size(); ++i) { - literal_vector lits; - for (unsigned j = 0; j < _mutexes[i].size(); ++j) { - lits.push_back(to_literal(_mutexes[i][j])); + for (auto const& mux : _mutexes) { + literal_vector clique; + for (auto const& idx : mux) { + clique.push_back(to_literal(idx)); } - mutexes.push_back(lits); + mutexes.push_back(clique); } return l_true; } @@ -3171,10 +3764,9 @@ namespace sat { } static void brute_force_consequences(sat::solver& s, sat::literal_vector const& asms, sat::literal_vector const& gamma, vector& conseq) { - for (unsigned i = 0; i < gamma.size(); ++i) { - sat::literal nlit = ~gamma[i]; + for (literal lit : gamma) { sat::literal_vector asms1(asms); - asms1.push_back(nlit); + asms1.push_back(~lit); lbool r = s.check(asms1.size(), asms1.c_ptr()); if (r == l_false) { conseq.push_back(s.get_core()); @@ -3184,8 +3776,8 @@ namespace sat { static lbool core_chunking(sat::solver& s, model const& m, sat::bool_var_vector const& vars, sat::literal_vector const& asms, vector& conseq, unsigned K) { sat::literal_vector lambda; - for (unsigned i = 0; i < vars.size(); i++) { - lambda.push_back(sat::literal(vars[i], m[vars[i]] == l_false)); + for (bool_var v : vars) { + lambda.push_back(sat::literal(v, m[v] == l_false)); } while (!lambda.empty()) { IF_VERBOSE(1, verbose_stream() << "(sat-backbone-core " << lambda.size() << " " << conseq.size() << ")\n";); @@ -3282,13 +3874,23 @@ namespace sat { s |= m_antecedents.find(m_core[i].var()); } m_core.reset(); - index_set::iterator it = s.begin(), end = s.end(); - for (; it != end; ++it) { - m_core.push_back(to_literal(*it)); + for (unsigned idx : s) { + m_core.push_back(to_literal(idx)); } TRACE("sat", tout << m_core << "\n";); } + bool solver::reached_max_conflicts() { + if (m_config.m_max_conflicts == 0 || m_conflicts_since_init > m_config.m_max_conflicts) { + if (m_reason_unknown != "sat.max.conflicts") { + m_reason_unknown = "sat.max.conflicts"; + IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts_since_init << "\")\n";); + } + return true; + } + return false; + } + lbool solver::get_bounded_consequences(literal_vector const& asms, bool_var_vector const& vars, vector& conseq) { bool_var_set unfixed_vars; @@ -3334,12 +3936,11 @@ namespace sat { extract_fixed_consequences(num_units, asms, unfixed_vars, conseq); - if (m_conflicts > m_config.m_max_conflicts) { - IF_VERBOSE(SAT_VB_LVL, verbose_stream() << "(sat \"abort: max-conflicts = " << m_conflicts << "\")\n";); + if (reached_max_conflicts()) { return l_undef; } - restart(); + restart(true); simplify_problem(); if (check_inconsistent()) { fixup_consequence_core(); @@ -3378,24 +3979,23 @@ namespace sat { } propagate(false); if (check_inconsistent()) return l_false; + SASSERT(search_lvl() == 1); unsigned num_iterations = 0; extract_fixed_consequences(unfixed_lits, assumptions, unfixed_vars, conseq); update_unfixed_literals(unfixed_lits, unfixed_vars); while (!unfixed_lits.empty()) { - if (scope_lvl() > 1) { - pop(scope_lvl() - 1); + if (scope_lvl() > search_lvl()) { + pop(scope_lvl() - search_lvl()); } propagate(false); ++num_iterations; checkpoint(); - literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); unsigned num_resolves = 0; unsigned num_fixed = 0; unsigned num_assigned = 0; lbool is_sat = l_true; - for (; it != end; ++it) { - literal lit = *it; + for (literal lit : unfixed_lits) { if (value(lit) != l_undef) { ++num_fixed; if (lvl(lit) <= 1 && value(lit) == l_true) { @@ -3417,7 +4017,7 @@ namespace sat { propagate(false); ++num_resolves; } - if (false && scope_lvl() == 1) { + if (false && scope_lvl() == search_lvl()) { is_sat = l_undef; break; } @@ -3426,14 +4026,14 @@ namespace sat { extract_fixed_consequences(unfixed_lits, assumptions, unfixed_vars, conseq); if (is_sat == l_true) { - if (scope_lvl() == 1 && num_resolves > 0) { + if (scope_lvl() == search_lvl() && num_resolves > 0) { IF_VERBOSE(1, verbose_stream() << "(sat.get-consequences backjump)\n";); is_sat = l_undef; } else { is_sat = bounded_search(); if (is_sat == l_undef) { - restart(); + restart(true); } extract_fixed_consequences(unfixed_lits, assumptions, unfixed_vars, conseq); } @@ -3464,9 +4064,7 @@ namespace sat { void solver::delete_unfixed(literal_set& unfixed_lits, bool_var_set& unfixed_vars) { literal_set to_keep; - literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); - for (; it != end; ++it) { - literal lit = *it; + for (literal lit : unfixed_lits) { if (value(lit) == l_true) { to_keep.insert(lit); } @@ -3479,9 +4077,7 @@ namespace sat { void solver::update_unfixed_literals(literal_set& unfixed_lits, bool_var_set& unfixed_vars) { literal_vector to_delete; - literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); - for (; it != end; ++it) { - literal lit = *it; + for (literal lit : unfixed_lits) { if (!unfixed_vars.contains(lit.var())) { to_delete.push_back(lit); } @@ -3502,9 +4098,7 @@ namespace sat { } void solver::extract_fixed_consequences(literal_set const& unfixed_lits, literal_set const& assumptions, bool_var_set& unfixed_vars, vector& conseq) { - literal_set::iterator it = unfixed_lits.begin(), end = unfixed_lits.end(); - for (; it != end; ++it) { - literal lit = *it; + for (literal lit: unfixed_lits) { TRACE("sat", tout << "extract: " << lit << " " << value(lit) << " " << lvl(lit) << "\n";); if (lvl(lit) <= 1 && value(lit) == l_true) { @@ -3543,11 +4137,11 @@ namespace sat { s |= m_antecedents.find(js.get_literal2().var()); break; case justification::CLAUSE: { - clause & c = *(m_cls_allocator.get_clause(js.get_clause_offset())); - for (unsigned i = 0; i < c.size(); ++i) { - if (c[i] != lit) { - if (check_domain(lit, ~c[i]) && all_found) { - s |= m_antecedents.find(c[i].var()); + clause & c = get_clause(js); + for (literal l : c) { + if (l != lit) { + if (check_domain(lit, ~l) && all_found) { + s |= m_antecedents.find(l.var()); } else { all_found = false; @@ -3558,11 +4152,9 @@ namespace sat { } case justification::EXT_JUSTIFICATION: { fill_ext_antecedents(lit, js); - literal_vector::iterator it = m_ext_antecedents.begin(); - literal_vector::iterator end = m_ext_antecedents.end(); - for (; it != end; ++it) { - if (check_domain(lit, *it) && all_found) { - s |= m_antecedents.find(it->var()); + for (literal l : m_ext_antecedents) { + if (check_domain(lit, l) && all_found) { + s |= m_antecedents.find(l.var()); } else { all_found = false; @@ -3579,10 +4171,8 @@ namespace sat { } std::ostream& solver::display_index_set(std::ostream& out, index_set const& s) const { - index_set::iterator it = s.begin(); - index_set::iterator end = s.end(); - for (; it != end; ++it) { - out << to_literal(*it) << " "; + for (unsigned idx : s) { + out << to_literal(idx) << " "; } return out; } @@ -3607,9 +4197,8 @@ namespace sat { if (unfixed.contains(lit.var())) { literal_vector cons; cons.push_back(lit); - index_set::iterator it = s.begin(), end = s.end(); - for (; it != end; ++it) { - cons.push_back(to_literal(*it)); + for (unsigned idx : s) { + cons.push_back(to_literal(idx)); } unfixed.remove(lit.var()); conseq.push_back(cons); @@ -3627,14 +4216,6 @@ namespace sat { } } - void solver::asymmetric_branching() { - if (scope_lvl() > 0 || inconsistent()) - return; - m_asymm_branch(); - if (m_ext) - m_ext->clauses_modifed(); - } - // ----------------------- // // Statistics @@ -3645,17 +4226,13 @@ namespace sat { unsigned num_bin = 0; unsigned num_ext = 0; unsigned num_lits = 0; - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - literal l = ~to_literal(l_idx); - watch_list const & wlist = *it; - watch_list::const_iterator it2 = wlist.begin(); - watch_list::const_iterator end2 = wlist.end(); - for (; it2 != end2; ++it2) { - switch (it2->get_kind()) { + unsigned l_idx = 0; + for (watch_list const& wlist : m_watches) { + literal l = ~to_literal(l_idx++); + for (watched const& w : wlist) { + switch (w.get_kind()) { case watched::BINARY: - if (l.index() < it2->get_literal().index()) { + if (l.index() < w.get_literal().index()) { num_lits += 2; num_bin++; } @@ -3678,10 +4255,8 @@ namespace sat { clause_vector const * vs[2] = { &m_clauses, &m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector const & cs = *(vs[i]); - clause_vector::const_iterator it = cs.begin(); - clause_vector::const_iterator end = cs.end(); - for (; it != end; ++it) { - clause & c = *(*it); + for (clause* cp : cs) { + clause & c = *cp; if (c.size() == 3) num_ter++; else @@ -3721,35 +4296,26 @@ namespace sat { st.update("minimized lits", m_minimized_lits); st.update("dyn subsumption resolution", m_dyn_sub_res); st.update("blocked correction sets", m_blocked_corr_sets); + st.update("units", m_units); + st.update("elim bool vars res", m_elim_var_res); + st.update("elim bool vars bdd", m_elim_var_bdd); } void stats::reset() { - m_mk_var = 0; - m_mk_bin_clause = 0; - m_mk_ter_clause = 0; - m_mk_clause = 0; - m_conflict = 0; - m_propagate = 0; - m_bin_propagate = 0; - m_ter_propagate = 0; - m_decision = 0; - m_restart = 0; - m_gc_clause = 0; - m_del_clause = 0; - m_minimized_lits = 0; - m_dyn_sub_res = 0; - m_non_learned_generation = 0; - m_blocked_corr_sets = 0; + memset(this, 0, sizeof(*this)); } void mk_stat::display(std::ostream & out) const { + unsigned given, learned; + m_solver.num_binary(given, learned); if (!m_solver.m_clauses.empty()) - out << " :clauses " << m_solver.m_clauses.size(); + out << " :clauses " << m_solver.m_clauses.size() + given << "/" << given; if (!m_solver.m_learned.empty()) { - out << " :learned " << (m_solver.m_learned.size() - m_solver.m_num_frozen); + out << " :learned " << (m_solver.m_learned.size() + learned - m_solver.m_num_frozen) << "/" << learned; if (m_solver.m_num_frozen > 0) out << " :frozen " << m_solver.m_num_frozen; } + out << " :units " << m_solver.init_trail_size(); out << " :gc-clause " << m_solver.m_stats.m_gc_clause; out << mem_stat(); } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index c011eb46d..b44c04604 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -19,6 +19,7 @@ Revision History: #ifndef SAT_SOLVER_H_ #define SAT_SOLVER_H_ + #include "sat/sat_types.h" #include "sat/sat_clause.h" #include "sat/sat_watched.h" @@ -33,12 +34,17 @@ Revision History: #include "sat/sat_iff3_finder.h" #include "sat/sat_probing.h" #include "sat/sat_mus.h" +#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" +#include "util/ema.h" #include "util/trace.h" #include "util/rlimit.h" +#include "util/scoped_ptr_vector.h" namespace sat { @@ -62,6 +68,9 @@ namespace sat { unsigned m_dyn_sub_res; unsigned m_non_learned_generation; unsigned m_blocked_corr_sets; + unsigned m_elim_var_res; + unsigned m_elim_var_bdd; + unsigned m_units; stats() { reset(); } void reset(); void collect_statistics(statistics & st) const; @@ -75,10 +84,11 @@ namespace sat { bool m_checkpoint_enabled; config m_config; stats m_stats; - extension * m_ext; - par* m_par; + scoped_ptr m_ext; + parallel* m_par; random_gen m_rand; - clause_allocator m_cls_allocator; + clause_allocator m_cls_allocator[2]; + bool m_cls_allocator_idx; cleaner m_cleaner; model m_model; model_converter m_mc; @@ -88,7 +98,9 @@ namespace sat { asymm_branch m_asymm_branch; probing m_probing; mus m_mus; // MUS for minimal core extraction + drat m_drat; // DRAT for generating proofs bool m_inconsistent; + bool m_searching; // A conflict is usually a single justification. That is, a justification // for false. If m_not_l is not null_literal, then m_conflict is a // justification for l, and the conflict is union of m_no_l and m_conflict; @@ -106,8 +118,17 @@ namespace sat { svector m_eliminated; svector m_external; svector m_level; + // branch variable selection: svector m_activity; unsigned m_activity_inc; + svector m_last_conflict; + svector m_last_propagation; + svector m_participated; + svector m_canceled; + svector m_reasoned; + int m_action; + double m_step_size; + // phase svector m_phase; svector m_prev_phase; svector m_assigned_since_gc; @@ -116,8 +137,13 @@ namespace sat { var_queue m_case_split_queue; unsigned m_qhead; unsigned m_scope_lvl; + unsigned m_search_lvl; + ema m_fast_glue_avg; + ema m_slow_glue_avg; literal_vector m_trail; clause_wrapper_vector m_clauses_to_reinit; + std::string m_reason_unknown; + struct scope { unsigned m_trail_lim; unsigned m_clauses_to_reinit_lim; @@ -131,24 +157,39 @@ namespace sat { literal_set m_assumption_set; // set of enabled assumptions literal_vector m_core; // unsat core + unsigned m_par_id; unsigned m_par_limit_in; unsigned m_par_limit_out; unsigned m_par_num_vars; + bool m_par_syncing_clauses; - void del_clauses(clause * const * begin, clause * const * end); + class lookahead* m_cuber; + + statistics m_aux_stats; + + void del_clauses(clause_vector& clauses); friend class integrity_checker; friend class cleaner; friend class simplifier; friend class scc; + friend class big; friend class elim_eqs; friend class asymm_branch; friend class probing; friend class iff3_finder; friend class mus; + friend class drat; + friend class ba_solver; + friend class parallel; + friend class lookahead; + friend class local_search; + friend class unit_walk; friend struct mk_stat; + friend class elim_vars; + friend class scoped_detach; public: - solver(params_ref const & p, reslimit& l, extension * ext); + solver(params_ref const & p, reslimit& l); ~solver(); // ----------------------- @@ -156,7 +197,7 @@ namespace sat { // Misc // // ----------------------- - void updt_params(params_ref const & p); + void updt_params(params_ref const & p); static void collect_param_descrs(param_descrs & d); void collect_statistics(statistics & st) const; @@ -177,16 +218,24 @@ namespace sat { // // ----------------------- bool_var mk_var(bool ext = false, bool dvar = true); - void mk_clause(literal_vector const& lits) { mk_clause(lits.size(), lits.c_ptr()); } - void mk_clause(unsigned num_lits, literal * lits); - void mk_clause(literal l1, literal l2); - void mk_clause(literal l1, literal l2, literal l3); + void mk_clause(literal_vector const& lits, bool learned = false) { mk_clause(lits.size(), lits.c_ptr(), learned); } + void mk_clause(unsigned num_lits, literal * lits, bool learned = false); + void mk_clause(literal l1, literal l2, bool learned = false); + void mk_clause(literal l1, literal l2, literal l3, bool learned = false); protected: + inline clause_allocator& cls_allocator() { return m_cls_allocator[m_cls_allocator_idx]; } + inline clause_allocator const& cls_allocator() const { return m_cls_allocator[m_cls_allocator_idx]; } + inline clause * alloc_clause(unsigned num_lits, literal const * lits, bool learned) { return cls_allocator().mk_clause(num_lits, lits, learned); } + inline void dealloc_clause(clause* c) { cls_allocator().del_clause(c); } + struct cmp_activity; + void defrag_clauses(); + bool should_defrag(); + bool memory_pressure(); void del_clause(clause & c); clause * mk_clause_core(unsigned num_lits, literal * lits, bool learned); - void mk_clause_core(literal_vector const& lits) { mk_clause_core(lits.size(), lits.c_ptr()); } - void mk_clause_core(unsigned num_lits, literal * lits) { mk_clause_core(num_lits, lits, false); } + clause * mk_clause_core(literal_vector const& lits) { return mk_clause_core(lits.size(), lits.c_ptr()); } + clause * mk_clause_core(unsigned num_lits, literal * lits) { return mk_clause_core(num_lits, lits, false); } void mk_clause_core(literal l1, literal l2) { literal lits[2] = { l1, l2 }; mk_clause_core(2, lits); } void mk_bin_clause(literal l1, literal l2, bool learned); bool propagate_bin_clause(literal l1, literal l2); @@ -196,25 +245,13 @@ namespace sat { bool attach_nary_clause(clause & c); void attach_clause(clause & c, bool & reinit); void attach_clause(clause & c) { bool reinit; attach_clause(c, reinit); } - class scoped_detach { - solver& s; - clause& c; - bool m_deleted; - public: - scoped_detach(solver& s, clause& c): s(s), c(c), m_deleted(false) { - s.detach_clause(c); - } - ~scoped_detach() { - if (!m_deleted) s.attach_clause(c); - } + void set_learned(clause& c, bool learned); + void set_learned(literal l1, literal l2, bool learned); + void set_learned1(literal l1, literal l2, bool learned); + void add_ate(clause& c) { m_mc.add_ate(c); } + void add_ate(literal l1, literal l2) { m_mc.add_ate(l1, l2); } + void add_ate(literal_vector const& lits) { m_mc.add_ate(lits); } - void del_clause() { - if (!m_deleted) { - s.del_clause(c); - m_deleted = true; - } - } - }; class scoped_disable_checkpoint { solver& s; public: @@ -245,16 +282,26 @@ namespace sat { bool inconsistent() const { return m_inconsistent; } unsigned num_vars() const { return m_level.size(); } unsigned num_clauses() const; + void num_binary(unsigned& given, unsigned& learned) const; unsigned num_restarts() const { return m_restarts; } bool is_external(bool_var v) const { return m_external[v] != 0; } - void set_external(bool_var v) { m_external[v] = true; } + bool is_external(literal l) const { return is_external(l.var()); } + void set_external(bool_var v); + void set_non_external(bool_var v); bool was_eliminated(bool_var v) const { return m_eliminated[v] != 0; } + void set_eliminated(bool_var v, bool f) { m_eliminated[v] = f; } + bool was_eliminated(literal l) const { return was_eliminated(l.var()); } unsigned scope_lvl() const { return m_scope_lvl; } + unsigned search_lvl() const { return m_search_lvl; } + bool at_search_lvl() const { return m_scope_lvl == m_search_lvl; } + bool at_base_lvl() const { return m_scope_lvl == 0; } lbool value(literal l) const { return static_cast(m_assignment[l.index()]); } lbool value(bool_var v) const { return static_cast(m_assignment[literal(v, false).index()]); } unsigned lvl(bool_var v) const { return m_level[v]; } unsigned lvl(literal l) const { return m_level[l.var()]; } - unsigned init_trail_size() const { return scope_lvl() == 0 ? m_trail.size() : m_scopes[0].m_trail_lim; } + unsigned init_trail_size() const { return at_base_lvl() ? m_trail.size() : m_scopes[0].m_trail_lim; } + literal trail_literal(unsigned i) const { return m_trail[i]; } + literal scope_literal(unsigned n) const { return m_trail[m_scopes[n].m_trail_lim]; } void assign(literal l, justification j) { TRACE("sat_assign", tout << l << " previous value: " << value(l) << "\n";); switch (value(l)) { @@ -267,7 +314,7 @@ namespace sat { void set_conflict(justification c, literal not_l); void set_conflict(justification c) { set_conflict(c, null_literal); } lbool status(clause const & c) const; - clause_offset get_offset(clause const & c) const { return m_cls_allocator.get_offset(&c); } + clause_offset get_offset(clause const & c) const { return cls_allocator().get_offset(&c); } void checkpoint() { if (!m_checkpoint_enabled) return; if (!m_rlimit.inc()) { @@ -280,9 +327,15 @@ namespace sat { m_num_checkpoints = 0; if (memory::get_allocation_size() > m_config.m_max_memory) throw solver_exception(Z3_MAX_MEMORY_MSG); } - void set_par(par* p); + void set_par(parallel* p, unsigned id); bool canceled() { return !m_rlimit.inc(); } - config const& get_config() { return m_config; } + config const& get_config() const { return m_config; } + void set_incremental(bool b) { m_config.m_incremental = b; } + bool is_incremental() const { return m_config.m_incremental; } + extension* get_extension() const { return m_ext.get(); } + void set_extension(extension* e); + bool set_root(literal l, literal r); + void flush_roots(); typedef std::pair bin_clause; protected: watch_list & get_wlist(literal l) { return m_watches[l.index()]; } @@ -315,22 +368,32 @@ namespace sat { // // ----------------------- public: - lbool check(unsigned num_lits = 0, literal const* lits = 0); + lbool check(unsigned num_lits = 0, literal const* lits = nullptr); model const & get_model() const { return m_model; } bool model_is_current() const { return m_model_is_current; } literal_vector const& get_core() const { return m_core; } model_converter const & get_model_converter() const { return m_mc; } + void flush(model_converter& mc) { mc.flush(m_mc); } void set_model(model const& mdl); + char const* get_reason_unknown() const { return m_reason_unknown.c_str(); } + bool check_clauses(model const& m) const; + bool is_assumption(bool_var v) const; + + lbool cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level); protected: - unsigned m_conflicts; + + unsigned m_conflicts_since_init; unsigned m_restarts; + unsigned m_restart_next_out; unsigned m_conflicts_since_restart; + unsigned m_simplifications; unsigned m_restart_threshold; unsigned m_luby_idx; unsigned m_conflicts_since_gc; unsigned m_gc_threshold; + unsigned m_defrag_threshold; unsigned m_num_checkpoints; double m_min_d_tk; unsigned m_next_simplify; @@ -343,6 +406,7 @@ namespace sat { literal_vector m_min_core; bool m_min_core_valid; + void init_reason_unknown() { m_reason_unknown = "no reason given"; } void init_assumptions(unsigned num_lits, literal const* lits); void reassert_min_core(); void update_min_core(); @@ -356,10 +420,16 @@ namespace sat { void simplify_problem(); void mk_model(); bool check_model(model const & m) const; - void restart(); + void restart(bool to_base); + unsigned restart_level(bool to_base); + bool should_restart() const; + void set_next_restart(); + bool reached_max_conflicts(); void sort_watch_lits(); void exchange_par(); lbool check_par(unsigned num_lits, literal const* lits); + lbool do_local_search(unsigned num_lits, literal const* lits); + lbool do_unit_walk(); // ----------------------- // @@ -386,7 +456,26 @@ namespace sat { if (value(l0) != l_true) return true; justification const & jst = m_justification[l0.var()]; - return !jst.is_clause() || m_cls_allocator.get_clause(jst.get_clause_offset()) != &c; + return !jst.is_clause() || cls_allocator().get_clause(jst.get_clause_offset()) != &c; + } + + clause& get_clause(watch_list::iterator it) const { + SASSERT(it->get_kind() == watched::CLAUSE); + return get_clause(it->get_clause_offset()); + } + + clause& get_clause(watched const& w) const { + SASSERT(w.get_kind() == watched::CLAUSE); + return get_clause(w.get_clause_offset()); + } + + clause& get_clause(justification const& j) const { + SASSERT(j.is_clause()); + return get_clause(j.get_clause_offset()); + } + + clause& get_clause(clause_offset cls_off) const { + return *(cls_allocator().get_clause(cls_off)); } // ----------------------- @@ -400,20 +489,20 @@ namespace sat { literal_vector m_ext_antecedents; bool resolve_conflict(); bool resolve_conflict_core(); + void learn_lemma_and_backjump(); unsigned get_max_lvl(literal consequent, justification js); void process_antecedent(literal antecedent, unsigned & num_marks); void resolve_conflict_for_unsat_core(); void process_antecedent_for_unsat_core(literal antecedent); void process_consequent_for_unsat_core(literal consequent, justification const& js); - bool resolve_conflict_for_init(); - void process_antecedent_for_init(literal antecedent); - bool process_consequent_for_init(literal consequent, justification const& js); void fill_ext_antecedents(literal consequent, justification js); unsigned skip_literals_above_conflict_level(); void forget_phase_of_vars(unsigned from_lvl); void updt_phase_counters(); svector m_diff_levels; unsigned num_diff_levels(unsigned num, literal const * lits); + bool num_diff_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue); + bool num_diff_false_levels_below(unsigned num, literal const* lits, unsigned max_glue, unsigned& glue); // lemma minimization typedef approx_set_tpl level_approx_set; @@ -444,7 +533,7 @@ namespace sat { literal_vector m_aux_literals; svector m_user_bin_clauses; void gc_lit(clause_vector& clauses, literal lit); - void gc_bin(bool learned, literal nlit); + void gc_bin(literal lit); void gc_var(bool_var v); bool_var max_var(clause_vector& clauses, bool_var v); @@ -476,8 +565,13 @@ namespace sat { lbool get_consequences(literal_vector const& assms, bool_var_vector const& vars, vector& conseq); + // initialize and retrieve local search. + // local_search& init_local_search(); + private: + unsigned get_hash() const; + typedef hashtable index_set; u_map m_antecedents; @@ -523,13 +617,19 @@ namespace sat { } void decay_activity() { - m_activity_inc *= 11; - m_activity_inc /= 10; + m_activity_inc *= m_config.m_variable_decay; + m_activity_inc /= 100; } private: void rescale_activity(); + void update_chb_activity(bool is_sat, unsigned qhead); + + void update_lrb_reasoned(); + + void update_lrb_reasoned(literal lit); + // ----------------------- // // Iterators @@ -540,7 +640,9 @@ namespace sat { clause * const * end_clauses() const { return m_clauses.end(); } clause * const * begin_learned() const { return m_learned.begin(); } clause * const * end_learned() const { return m_learned.end(); } - void collect_bin_clauses(svector & r, bool learned) const; + clause_vector const& learned() const { return m_learned; } + clause_vector const& clauses() const { return m_clauses; } + void collect_bin_clauses(svector & r, bool learned, bool learned_only = false) const; // ----------------------- // @@ -551,10 +653,12 @@ namespace sat { bool check_invariant() const; void display(std::ostream & out) const; void display_watches(std::ostream & out) const; + void display_watches(std::ostream & out, literal lit) const; void display_dimacs(std::ostream & out) const; void display_wcnf(std::ostream & out, unsigned sz, literal const* lits, unsigned const* weights) const; void display_assignment(std::ostream & out) const; - void display_justification(std::ostream & out, justification const& j) const; + std::ostream& display_justification(std::ostream & out, justification const& j) const; + std::ostream& display_watch_list(std::ostream& out, watch_list const& wl) const; protected: void display_binary(std::ostream & out) const; @@ -572,6 +676,27 @@ namespace sat { void display(std::ostream & out) const; }; + class scoped_detach { + solver& s; + clause& c; + bool m_deleted; + public: + scoped_detach(solver& s, clause& c): s(s), c(c), m_deleted(false) { + s.detach_clause(c); + } + ~scoped_detach() { + if (!m_deleted) s.attach_clause(c); + } + + void del_clause() { + if (!m_deleted) { + s.del_clause(c); + m_deleted = true; + } + } + }; + + std::ostream & operator<<(std::ostream & out, mk_stat const & stat); }; diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 191c49294..8b7e2e63c 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -17,32 +17,39 @@ Notes: --*/ + +#include "util/gparams.h" +#include "ast/ast_pp.h" +#include "ast/ast_translation.h" +#include "ast/ast_util.h" #include "solver/solver.h" -#include "tactic/tactical.h" -#include "sat/sat_solver.h" #include "solver/tactic2solver.h" +#include "solver/parallel_params.hpp" +#include "solver/parallel_tactic.h" +#include "tactic/tactical.h" #include "tactic/aig/aig_tactic.h" #include "tactic/core/propagate_values_tactic.h" #include "tactic/bv/max_bv_sharing_tactic.h" #include "tactic/arith/card2bv_tactic.h" #include "tactic/bv/bit_blaster_tactic.h" #include "tactic/core/simplify_tactic.h" -#include "sat/tactic/goal2sat.h" -#include "ast/ast_pp.h" -#include "model/model_smt2_pp.h" -#include "tactic/filter_model_converter.h" +#include "tactic/core/solve_eqs_tactic.h" #include "tactic/bv/bit_blaster_model_converter.h" -#include "ast/ast_translation.h" -#include "ast/ast_util.h" -#include "tactic/core/propagate_values_tactic.h" +#include "model/model_smt2_pp.h" +#include "model/model_v2_pp.h" +#include "model/model_evaluator.h" +#include "sat/sat_solver.h" +#include "sat/sat_params.hpp" +#include "sat/tactic/goal2sat.h" +#include "sat/tactic/sat_tactic.h" +#include "sat/sat_simplifier_params.hpp" // incremental SAT solver. class inc_sat_solver : public solver { ast_manager& m; - sat::solver m_solver; + mutable sat::solver m_solver; goal2sat m_goal2sat; params_ref m_params; - bool m_optimize_model; // parameter expr_ref_vector m_fmls; expr_ref_vector m_asmsf; unsigned_vector m_fmls_lim; @@ -51,62 +58,84 @@ class inc_sat_solver : public solver { unsigned m_fmls_head; expr_ref_vector m_core; atom2bool_var m_map; - model_ref m_model; scoped_ptr m_bb_rewriter; tactic_ref m_preprocess; unsigned m_num_scopes; sat::literal_vector m_asms; goal_ref_buffer m_subgoals; proof_converter_ref m_pc; - model_converter_ref m_mc; - model_converter_ref m_mc0; - expr_dependency_ref m_dep_core; + sref_vector m_mcs; + mutable model_converter_ref m_mc0; // TBD: should be saved/retained under push/pop + mutable obj_hashtable m_inserted_const2bits; + mutable ref m_sat_mc; + mutable model_converter_ref m_cached_mc; svector m_weights; std::string m_unknown; - + // access formulas after they have been pre-processed and handled by the sat solver. + // this allows to access the internal state of the SAT solver and carry on partial results. + bool m_internalized_converted; // have internalized formulas been converted back + expr_ref_vector m_internalized_fmls; // formulas in internalized format typedef obj_map dep2asm_t; + + bool is_internalized() const { return m_fmls_head == m_fmls.size(); } public: - inc_sat_solver(ast_manager& m, params_ref const& p): - m(m), m_solver(p, m.limit(), 0), - m_optimize_model(false), + inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode): + m(m), + m_solver(p, m.limit()), m_fmls(m), m_asmsf(m), m_fmls_head(0), m_core(m), m_map(m), m_num_scopes(0), - m_dep_core(m), - m_unknown("no reason given") { - m_params.set_bool("elim_vars", false); + m_unknown("no reason given"), + m_internalized_converted(false), + m_internalized_fmls(m) { updt_params(p); + m_mcs.push_back(nullptr); init_preprocess(); + m_solver.set_incremental(incremental_mode && !override_incremental()); } - virtual ~inc_sat_solver() {} + bool override_incremental() const { + sat_simplifier_params p(m_params); + return p.override_incremental(); + } - virtual solver* translate(ast_manager& dst_m, params_ref const& p) { - ast_translation tr(m, dst_m); + bool is_incremental() const { + return m_solver.get_config().m_incremental; + } + + ~inc_sat_solver() override {} + + solver* translate(ast_manager& dst_m, params_ref const& p) override { if (m_num_scopes > 0) { throw default_exception("Cannot translate sat solver at non-base level"); } - inc_sat_solver* result = alloc(inc_sat_solver, dst_m, p); - expr_ref fml(dst_m); - for (unsigned i = 0; i < m_fmls.size(); ++i) { - fml = tr(m_fmls[i].get()); - result->m_fmls.push_back(fml); - } - for (unsigned i = 0; i < m_asmsf.size(); ++i) { - fml = tr(m_asmsf[i].get()); - result->m_asmsf.push_back(fml); - } + 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()); + result->m_solver.copy(m_solver); + result->m_fmls_head = m_fmls_head; + for (expr* f : m_fmls) result->m_fmls.push_back(tr(f)); + for (expr* f : m_asmsf) result->m_asmsf.push_back(tr(f)); + for (auto & kv : m_map) result->m_map.insert(tr(kv.m_key), kv.m_value); + for (unsigned l : m_fmls_lim) result->m_fmls_lim.push_back(l); + for (unsigned a : m_asms_lim) result->m_asms_lim.push_back(a); + for (unsigned h : m_fmls_head_lim) result->m_fmls_head_lim.push_back(h); + for (expr* f : m_internalized_fmls) result->m_internalized_fmls.push_back(tr(f)); + if (m_mcs.back()) result->m_mcs.push_back(m_mcs.back()->translate(tr)); + if (m_sat_mc) result->m_sat_mc = dynamic_cast(m_sat_mc->translate(tr)); + // copy m_bb_rewriter? + result->m_internalized_converted = m_internalized_converted; return result; } - virtual void set_progress_callback(progress_callback * callback) {} + void set_progress_callback(progress_callback * callback) override {} void display_weighted(std::ostream& out, unsigned sz, expr * const * assumptions, unsigned const* weights) { - if (weights != 0) { + if (weights != nullptr) { for (unsigned i = 0; i < sz; ++i) m_weights.push_back(weights[i]); } init_preprocess(); @@ -135,8 +164,10 @@ public: (m.is_not(e, e) && is_uninterp_const(e)); } - virtual lbool check_sat(unsigned sz, expr * const * assumptions) { + lbool check_sat(unsigned sz, expr * const * assumptions) override { m_solver.pop_to_base_level(); + m_core.reset(); + if (m_solver.inconsistent()) return l_false; expr_ref_vector _assumptions(m); obj_map asm2fml; for (unsigned i = 0; i < sz; ++i) { @@ -155,19 +186,21 @@ public: TRACE("sat", tout << _assumptions << "\n";); dep2asm_t dep2asm; - m_model = 0; lbool r = internalize_formulas(); if (r != l_true) return r; r = internalize_assumptions(sz, _assumptions.c_ptr(), dep2asm); if (r != l_true) return r; - r = m_solver.check(m_asms.size(), m_asms.c_ptr()); - if (r == l_undef && m_solver.get_config().m_dimacs_display) { - for (auto const& kv : m_map) { - std::cout << "c " << kv.m_value << " " << mk_pp(kv.m_key, m) << "\n"; - } + init_reason_unknown(); + m_internalized_converted = false; + try { + // IF_VERBOSE(0, m_solver.display(verbose_stream())); + r = m_solver.check(m_asms.size(), m_asms.c_ptr()); + } + catch (z3_exception& ex) { + IF_VERBOSE(10, verbose_stream() << "exception: " << ex.msg() << "\n";); + r = l_undef; } - switch (r) { case l_true: if (sz > 0) { @@ -181,30 +214,37 @@ public: } break; default: + set_reason_unknown(m_solver.get_reason_unknown()); break; } return r; } - virtual void push() { + + void push() override { internalize_formulas(); m_solver.user_push(); ++m_num_scopes; + m_mcs.push_back(m_mcs.back()); m_fmls_lim.push_back(m_fmls.size()); m_asms_lim.push_back(m_asmsf.size()); m_fmls_head_lim.push_back(m_fmls_head); if (m_bb_rewriter) m_bb_rewriter->push(); m_map.push(); } - virtual void pop(unsigned n) { + + void pop(unsigned n) override { if (n > m_num_scopes) { // allow inc_sat_solver to n = m_num_scopes; // take over for another solver. } if (m_bb_rewriter) m_bb_rewriter->pop(n); + m_inserted_const2bits.reset(); m_map.pop(n); SASSERT(n <= m_num_scopes); m_solver.user_pop(n); m_num_scopes -= n; + // ? m_internalized_converted = false; while (n > 0) { + m_mcs.pop_back(); m_fmls_head = m_fmls_head_lim.back(); m_fmls.resize(m_fmls_lim.back()); m_fmls_lim.pop_back(); @@ -214,54 +254,101 @@ public: --n; } } - virtual unsigned get_scope_level() const { + + unsigned get_scope_level() const override { return m_num_scopes; } - virtual void assert_expr(expr * t, expr * a) { + + void assert_expr_core2(expr * t, expr * a) override { if (a) { m_asmsf.push_back(a); - assert_expr(m.mk_implies(a, t)); + assert_expr_core(m.mk_implies(a, t)); } else { - assert_expr(t); + assert_expr_core(t); } } - virtual ast_manager& get_manager() const { return m; } - virtual void assert_expr(expr * t) { - TRACE("sat", tout << mk_pp(t, m) << "\n";); + + ast_manager& get_manager() const override { return m; } + void assert_expr_core(expr * t) override { + TRACE("goal2sat", tout << mk_pp(t, m) << "\n";); m_fmls.push_back(t); } - virtual void set_produce_models(bool f) {} - virtual void collect_param_descrs(param_descrs & r) { + void set_produce_models(bool f) override {} + void collect_param_descrs(param_descrs & r) override { + solver::collect_param_descrs(r); goal2sat::collect_param_descrs(r); sat::solver::collect_param_descrs(r); } - virtual void updt_params(params_ref const & p) { - solver::updt_params(p); - m_params.set_bool("elim_vars", false); + void updt_params(params_ref const & p) override { + m_params.append(p); + sat_params p1(p); + m_params.set_bool("keep_cardinality_constraints", p1.cardinality_solver()); + m_params.set_sym("pb.solver", p1.pb_solver()); + + m_params.set_bool("keep_pb_constraints", m_solver.get_config().m_pb_solver == sat::PB_SOLVER); + m_params.set_bool("pb_num_system", m_solver.get_config().m_pb_solver == sat::PB_SORTING); + m_params.set_bool("pb_totalizer", m_solver.get_config().m_pb_solver == sat::PB_TOTALIZER); + + m_params.set_bool("xor_solver", p1.xor_solver()); m_solver.updt_params(m_params); - m_optimize_model = m_params.get_bool("optimize_model", false); + m_solver.set_incremental(is_incremental() && !override_incremental()); + } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { if (m_preprocess) m_preprocess->collect_statistics(st); m_solver.collect_statistics(st); } - virtual void get_unsat_core(ptr_vector & r) { + void get_unsat_core(expr_ref_vector & r) override { r.reset(); r.append(m_core.size(), m_core.c_ptr()); } - virtual void get_model(model_ref & mdl) { - if (!m_model.get()) { - extract_model(); - } - mdl = m_model; - } - virtual proof * get_proof() { + proof * get_proof() override { UNREACHABLE(); - return 0; + return nullptr; } - virtual lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) { + 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); + } + convert_internalized(); + obj_hashtable _vs; + for (expr* v : vs) _vs.insert(v); + sat::bool_var_vector vars; + for (auto& kv : m_map) { + if (_vs.empty() || _vs.contains(kv.m_key)) + vars.push_back(kv.m_value); + } + 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) { + return expr_ref_vector(m); + } + expr_ref_vector fmls(m); + expr_ref_vector lit2expr(m); + lit2expr.resize(m_solver.num_vars() * 2); + m_map.mk_inv(lit2expr); + for (sat::literal l : lits) { + fmls.push_back(lit2expr[l.index()].get()); + } + vs.reset(); + for (sat::bool_var v : vars) { + expr* x = lit2expr[sat::literal(v, false).index()].get(); + if (x) { + vs.push_back(x); + } + } + return fmls; + } + + lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) override { init_preprocess(); TRACE("sat", tout << assumptions << "\n" << vars << "\n";); sat::literal_vector asms; @@ -295,16 +382,16 @@ public: // extract original fixed variables u_map asm2dep; extract_asm2dep(dep2asm, asm2dep); - for (unsigned i = 0; i < vars.size(); ++i) { + for (auto v : vars) { expr_ref cons(m); - if (extract_fixed_variable(dep2asm, asm2dep, vars[i], bool_var2conseq, lconseq, cons)) { + if (extract_fixed_variable(dep2asm, asm2dep, v, bool_var2conseq, lconseq, cons)) { conseq.push_back(cons); } } return r; } - virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { sat::literal_vector ls; u_map lit2var; for (unsigned i = 0; i < vars.size(); ++i) { @@ -319,38 +406,86 @@ public: } vector ls_mutexes; m_solver.find_mutexes(ls, ls_mutexes); - for (unsigned i = 0; i < ls_mutexes.size(); ++i) { - sat::literal_vector const ls_mutex = ls_mutexes[i]; + for (sat::literal_vector const& ls_mutex : ls_mutexes) { expr_ref_vector mutex(m); - for (unsigned j = 0; j < ls_mutex.size(); ++j) { - mutex.push_back(lit2var.find(ls_mutex[j].index())); + for (sat::literal l : ls_mutex) { + mutex.push_back(lit2var.find(l.index())); } mutexes.push_back(mutex); } return l_true; } - virtual std::string reason_unknown() const { + void init_reason_unknown() { + m_unknown = "no reason given"; + } + + std::string reason_unknown() const override { return m_unknown; } - virtual void set_reason_unknown(char const* msg) { + + void set_reason_unknown(char const* msg) override { m_unknown = msg; } - virtual void get_labels(svector & r) { + + void get_labels(svector & r) override { } - virtual unsigned get_num_assertions() const { - return m_fmls.size(); + + unsigned get_num_assertions() const override { + const_cast(this)->convert_internalized(); + if (is_internalized() && m_internalized_converted) { + return m_internalized_fmls.size(); + } + else { + return m_fmls.size(); + } } - virtual expr * get_assertion(unsigned idx) const { + + expr * get_assertion(unsigned idx) const override { + if (is_internalized() && m_internalized_converted) { + return m_internalized_fmls[idx]; + } return m_fmls[idx]; } - virtual unsigned get_num_assumptions() const { + + unsigned get_num_assumptions() const override { return m_asmsf.size(); } - virtual expr * get_assumption(unsigned idx) const { + + expr * get_assumption(unsigned idx) const override { return m_asmsf[idx]; } + model_converter_ref get_model_converter() const override { + const_cast(this)->convert_internalized(); + if (m_cached_mc) + return m_cached_mc; + if (is_internalized() && m_internalized_converted) { + m_sat_mc->flush_smc(m_solver, m_map); + m_cached_mc = m_mcs.back(); + m_cached_mc = concat(solver::get_model_converter().get(), m_cached_mc.get()); + m_cached_mc = concat(m_cached_mc.get(), m_sat_mc.get()); + return m_cached_mc; + } + else { + return solver::get_model_converter(); + } + } + + void convert_internalized() { + if (!is_internalized() && m_fmls_head > 0) { + internalize_formulas(); + } + if (!is_internalized() || m_internalized_converted) return; + sat2goal s2g; + m_cached_mc = nullptr; + goal g(m, false, true, false); + s2g(m_solver, m_map, m_params, g, m_sat_mc); + m_internalized_fmls.reset(); + g.get_formulas(m_internalized_fmls); + m_internalized_converted = true; + } + void init_preprocess() { if (m_preprocess) { m_preprocess->reset(); @@ -369,12 +504,13 @@ public: simp2_p.set_bool("elim_and", true); simp2_p.set_bool("blast_distinct", true); m_preprocess = - and_then(mk_card2bv_tactic(m, m_params), + and_then(mk_simplify_tactic(m), + mk_propagate_values_tactic(m), + //time consuming if done in inner loop: mk_solve_eqs_tactic(m, simp2_p), + mk_card2bv_tactic(m, m_params), // updates model converter using_params(mk_simplify_tactic(m), simp2_p), mk_max_bv_sharing_tactic(m), - mk_bit_blaster_tactic(m, m_bb_rewriter.get()), - //mk_aig_tactic(), - //mk_propagate_values_tactic(m, simp2_p), + mk_bit_blaster_tactic(m, m_bb_rewriter.get()), // updates model converter using_params(mk_simplify_tactic(m), simp2_p)); while (m_bb_rewriter->get_num_scopes() < m_num_scopes) { m_bb_rewriter->push(); @@ -384,11 +520,8 @@ public: private: - - lbool internalize_goal(goal_ref& g, dep2asm_t& dep2asm) { - m_mc.reset(); + lbool internalize_goal(goal_ref& g, dep2asm_t& dep2asm, bool is_lemma) { m_pc.reset(); - m_dep_core.reset(); m_subgoals.reset(); init_preprocess(); SASSERT(g->models_enabled()); @@ -398,24 +531,31 @@ private: SASSERT(!g->proofs_enabled()); TRACE("sat", g->display(tout);); try { - (*m_preprocess)(g, m_subgoals, m_mc, m_pc, m_dep_core); + (*m_preprocess)(g, m_subgoals); } catch (tactic_exception & ex) { IF_VERBOSE(0, verbose_stream() << "exception in tactic " << ex.msg() << "\n";); TRACE("sat", tout << "exception: " << ex.msg() << "\n";); - m_preprocess = 0; - m_bb_rewriter = 0; + m_preprocess = nullptr; + m_bb_rewriter = nullptr; return l_undef; - } + } if (m_subgoals.size() != 1) { - IF_VERBOSE(0, verbose_stream() << "size of subgoals is not 1, it is: " << m_subgoals.size() << "\n";); + IF_VERBOSE(0, verbose_stream() << "size of subgoals is not 1, it is: " << m_subgoals.size() << "\n"); return l_undef; } g = m_subgoals[0]; expr_ref_vector atoms(m); + m_pc = g->pc(); + m_mcs.set(m_mcs.size()-1, concat(m_mcs.back(), g->mc())); TRACE("sat", g->display_with_dependencies(tout);); - m_goal2sat(*g, m_params, m_solver, m_map, dep2asm, true); + + // ensure that if goal is already internalized, then import mc from m_solver. + + m_goal2sat(*g, m_params, m_solver, m_map, dep2asm, is_incremental(), is_lemma); m_goal2sat.get_interpreted_atoms(atoms); + if (!m_sat_mc) m_sat_mc = alloc(sat2goal::mc, m); + m_sat_mc->flush_smc(m_solver, m_map); if (!atoms.empty()) { std::stringstream strm; strm << "interpreted atoms sent to SAT solver " << atoms; @@ -439,7 +579,7 @@ private: for (unsigned i = 0; i < get_num_assumptions(); ++i) { g->assert_expr(get_assumption(i), m.mk_leaf(get_assumption(i))); } - lbool res = internalize_goal(g, dep2asm); + lbool res = internalize_goal(g, dep2asm, false); if (res == l_true) { extract_assumptions(sz, asms, dep2asm); } @@ -447,20 +587,21 @@ private: } lbool internalize_vars(expr_ref_vector const& vars, sat::bool_var_vector& bvars) { - for (unsigned i = 0; i < vars.size(); ++i) { - internalize_var(vars[i], bvars); + for (expr* v : vars) { + internalize_var(v, bvars); } return l_true; } bool internalize_var(expr* v, sat::bool_var_vector& bvars) { - obj_map const& const2bits = m_bb_rewriter->const2bits(); + obj_map const2bits; + ptr_vector newbits; + m_bb_rewriter->end_rewrite(const2bits, newbits); expr* bv; bv_util bvutil(m); bool internalized = false; if (is_uninterp_const(v) && m.is_bool(v)) { sat::bool_var b = m_map.to_bool_var(v); - if (b != sat::null_bool_var) { bvars.push_back(b); internalized = true; @@ -470,10 +611,9 @@ private: SASSERT(bvutil.is_bv(bv)); app* abv = to_app(bv); internalized = true; - unsigned sz = abv->get_num_args(); - for (unsigned j = 0; j < sz; ++j) { - SASSERT(is_uninterp_const(abv->get_arg(j))); - sat::bool_var b = m_map.to_bool_var(abv->get_arg(j)); + for (expr* arg : *abv) { + SASSERT(is_uninterp_const(arg)); + sat::bool_var b = m_map.to_bool_var(arg); if (b == sat::null_bool_var) { internalized = false; } @@ -481,7 +621,7 @@ private: bvars.push_back(b); } } - CTRACE("sat", internalized, tout << "var: "; for (unsigned j = 0; j < sz; ++j) tout << bvars[bvars.size()-sz+j] << " "; tout << "\n";); + CTRACE("sat", internalized, tout << "var: " << bvars << "\n";); } else if (is_uninterp_const(v) && bvutil.is_bv(v)) { // variable does not occur in assertions, so is unconstrained. @@ -498,9 +638,9 @@ private: } sat::literal_vector value; sat::literal_set premises; - for (unsigned i = 0; i < bvars.size(); ++i) { + for (sat::bool_var bv : bvars) { unsigned index; - if (bool_var2conseq.find(bvars[i], index)) { + if (bool_var2conseq.find(bv, index)) { value.push_back(lconseq[index][0]); for (unsigned j = 1; j < lconseq[index].size(); ++j) { premises.insert(lconseq[index][j]); @@ -515,7 +655,7 @@ private: expr_ref_vector conj(m); internalize_value(value, v, val); while (!premises.empty()) { - expr* e = 0; + expr* e = nullptr; VERIFY(asm2dep.find(premises.pop().index(), e)); conj.push_back(e); } @@ -560,10 +700,11 @@ private: for (unsigned i = m_fmls_head ; i < m_fmls.size(); ++i) { g->assert_expr(m_fmls[i].get()); } - lbool res = internalize_goal(g, dep2asm); + lbool res = internalize_goal(g, dep2asm, false); if (res != l_undef) { m_fmls_head = m_fmls.size(); } + m_internalized_converted = false; return res; } @@ -592,10 +733,8 @@ private: } void extract_asm2dep(dep2asm_t const& dep2asm, u_map& asm2dep) { - dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); - for (; it != end; ++it) { - expr* e = it->m_key; - asm2dep.insert(it->m_value.index(), e); + for (auto const& kv : dep2asm) { + asm2dep.insert(kv.m_value.index(), kv.m_key); } } @@ -615,23 +754,22 @@ private: ); m_core.reset(); - for (unsigned i = 0; i < core.size(); ++i) { - expr* e = 0; - VERIFY(asm2dep.find(core[i].index(), e)); + for (sat::literal c : core) { + expr* e = nullptr; + VERIFY(asm2dep.find(c.index(), e)); if (asm2fml.contains(e)) { e = asm2fml.find(e); } - m_core.push_back(e); + m_core.push_back(e); } } void check_assumptions(dep2asm_t& dep2asm) { sat::model const & ll_m = m_solver.get_model(); - dep2asm_t::iterator it = dep2asm.begin(), end = dep2asm.end(); - for (; it != end; ++it) { - sat::literal lit = it->m_value; + for (auto const& kv : dep2asm) { + sat::literal lit = kv.m_value; if (sat::value_at(lit, ll_m) != l_true) { - IF_VERBOSE(0, verbose_stream() << mk_pp(it->m_key, m) << " does not evaluate to true\n"; + IF_VERBOSE(0, verbose_stream() << mk_pp(kv.m_key, m) << " does not evaluate to true\n"; verbose_stream() << m_asms << "\n"; m_solver.display_assignment(verbose_stream()); m_solver.display(verbose_stream());); @@ -640,14 +778,14 @@ private: } } - void extract_model() { + void get_model_core(model_ref & mdl) override { TRACE("sat", tout << "retrieve model " << (m_solver.model_is_current()?"present":"absent") << "\n";); if (!m_solver.model_is_current()) { - m_model = 0; + mdl = nullptr; return; } sat::model const & ll_m = m_solver.get_model(); - model_ref md = alloc(model, m); + mdl = alloc(model, m); for (auto const& kv : m_map) { expr * n = kv.m_key; if (is_app(n) && to_app(n)->get_num_args() > 0) { @@ -656,42 +794,65 @@ private: sat::bool_var v = kv.m_value; switch (sat::value_at(v, ll_m)) { case l_true: - md->register_decl(to_app(n)->get_decl(), m.mk_true()); + mdl->register_decl(to_app(n)->get_decl(), m.mk_true()); break; case l_false: - md->register_decl(to_app(n)->get_decl(), m.mk_false()); + mdl->register_decl(to_app(n)->get_decl(), m.mk_false()); break; default: break; } } - m_model = md; + //IF_VERBOSE(0, model_v2_pp(verbose_stream(), *mdl, true);); - if (m_bb_rewriter.get() && !m_bb_rewriter->const2bits().empty()) { - m_mc0 = concat(m_mc0.get(), mk_bit_blaster_model_converter(m, m_bb_rewriter->const2bits())); + if (m_sat_mc) { + //IF_VERBOSE(0, m_sat_mc->display(verbose_stream() << "satmc\n");); + (*m_sat_mc)(mdl); } - if (m_mc0) { - (*m_mc0)(m_model); + if (m_mcs.back()) { + //IF_VERBOSE(0, m_mc0->display(verbose_stream() << "mc0\n");); + (*m_mcs.back())(mdl); } - SASSERT(m_model); + TRACE("sat", model_smt2_pp(tout, m, *mdl, 0);); + - DEBUG_CODE( - for (unsigned i = 0; i < m_fmls.size(); ++i) { - expr_ref tmp(m); - if (m_model->eval(m_fmls[i].get(), tmp, true)) { - CTRACE("sat", !m.is_true(tmp), - tout << "Evaluation failed: " << mk_pp(m_fmls[i].get(), m) - << " to " << tmp << "\n"; - model_smt2_pp(tout, m, *(m_model.get()), 0);); - SASSERT(m.is_true(tmp)); - } - }); + if (!gparams::get_ref().get_bool("model_validate", false)) return; + IF_VERBOSE(1, verbose_stream() << "Verifying solution\n";); + model_evaluator eval(*mdl); + eval.set_model_completion(false); + bool all_true = true; + //unsigned i = 0; + for (expr * f : m_fmls) { + expr_ref tmp(m); + eval(f, tmp); + CTRACE("sat", !m.is_true(tmp), + tout << "Evaluation failed: " << mk_pp(f, m) << " to " << mk_pp(f, m) << "\n"; + model_smt2_pp(tout, m, *(mdl.get()), 0);); + if (!m.is_true(tmp)) { + IF_VERBOSE(0, verbose_stream() << "failed to verify: " << mk_pp(f, m) << "\n";); + all_true = false; + } + //IF_VERBOSE(0, verbose_stream() << (i++) << ": " << mk_pp(f, m) << "\n"); + } + if (!all_true) { + IF_VERBOSE(0, verbose_stream() << m_params << "\n"); + IF_VERBOSE(0, m_sat_mc->display(verbose_stream() << "sat mc\n")); + IF_VERBOSE(0, if (m_mcs.back()) m_mcs.back()->display(verbose_stream() << "mc0\n")); + //IF_VERBOSE(0, m_solver.display(verbose_stream())); + IF_VERBOSE(0, for (auto const& kv : m_map) verbose_stream() << mk_pp(kv.m_key, m) << " |-> " << kv.m_value << "\n"); + } + else { + IF_VERBOSE(1, verbose_stream() << "solution verified\n"); +// IF_VERBOSE(0, if (m_mcs.back()) m_mcs.back()->display(verbose_stream() << "mcs\n")); +// IF_VERBOSE(0, if (m_sat_mc) m_sat_mc->display(verbose_stream() << "sat_mc\n")); +// IF_VERBOSE(0, model_smt2_pp(verbose_stream() << "after\n", m, *mdl, 0);); + } } }; -solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p) { - return alloc(inc_sat_solver, m, p); +solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode) { + return alloc(inc_sat_solver, m, p, incremental_mode); } @@ -707,3 +868,8 @@ void inc_sat_display(std::ostream& out, solver& _s, unsigned sz, expr*const* sof s.display_weighted(out, sz, soft, weights.c_ptr()); } + +tactic * mk_psat_tactic(ast_manager& m, params_ref const& p) { + parallel_params pp(p); + return pp.enable() ? mk_parallel_tactic(mk_inc_sat_solver(m, p, false), p) : mk_sat_tactic(m); +} diff --git a/src/sat/sat_solver/inc_sat_solver.h b/src/sat/sat_solver/inc_sat_solver.h index 658c0583d..71ec48b99 100644 --- a/src/sat/sat_solver/inc_sat_solver.h +++ b/src/sat/sat_solver/inc_sat_solver.h @@ -22,7 +22,11 @@ Notes: #include "solver/solver.h" -solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p); +class tactic; + +solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_mode = true); + +tactic* mk_psat_tactic(ast_manager& m, params_ref const& p); void inc_sat_display(std::ostream& out, solver& s, unsigned sz, expr*const* soft, rational const* _weights); diff --git a/src/sat/sat_types.h b/src/sat/sat_types.h index 6652fb3b0..002e49006 100644 --- a/src/sat/sat_types.h +++ b/src/sat/sat_types.h @@ -25,6 +25,7 @@ Revision History: #include "util/z3_exception.h" #include "util/common_msgs.h" #include "util/vector.h" +#include "util/uint_set.h" #include namespace sat { @@ -103,14 +104,14 @@ namespace sat { inline bool operator==(literal const & l1, literal const & l2) { return l1.m_val == l2.m_val; } inline bool operator!=(literal const & l1, literal const & l2) { return l1.m_val != l2.m_val; } - inline std::ostream & operator<<(std::ostream & out, literal l) { out << (l.sign() ? "-" : "") << l.var(); return out; } + inline std::ostream & operator<<(std::ostream & out, literal l) { if (l == null_literal) out << "null"; else out << (l.sign() ? "-" : "") << l.var(); return out; } typedef svector literal_vector; typedef std::pair literal_pair; - typedef unsigned clause_offset; - typedef unsigned ext_constraint_idx; - typedef unsigned ext_justification_idx; + typedef size_t clause_offset; + typedef size_t ext_constraint_idx; + typedef size_t ext_justification_idx; struct literal2unsigned { unsigned operator()(literal l) const { return l.to_uint(); } }; @@ -123,6 +124,8 @@ namespace sat { }; class solver; + class lookahead; + class unit_walk; class clause; class clause_wrapper; class integrity_checker; @@ -137,6 +140,7 @@ namespace sat { typedef svector model; + inline void negate(literal_vector& ls) { for (unsigned i = 0; i < ls.size(); ++i) ls[i].neg(); } inline lbool value_at(bool_var v, model const & m) { return m[v]; } inline lbool value_at(literal l, model const & m) { lbool r = value_at(l.var(), m); return l.sign() ? ~r : r; } @@ -150,79 +154,7 @@ namespace sat { return out; } - class uint_set { - svector m_in_set; - svector m_set; - public: - typedef svector::const_iterator iterator; - void insert(unsigned v) { - m_in_set.reserve(v+1, false); - if (m_in_set[v]) - return; - m_in_set[v] = true; - m_set.push_back(v); - } - - void remove(unsigned v) { - if (contains(v)) { - m_in_set[v] = false; - unsigned i = 0; - for (i = 0; i < m_set.size() && m_set[i] != v; ++i) - ; - SASSERT(i < m_set.size()); - m_set[i] = m_set.back(); - m_set.pop_back(); - } - } - - uint_set& operator=(uint_set const& other) { - m_in_set = other.m_in_set; - m_set = other.m_set; - return *this; - } - - bool contains(unsigned v) const { - return v < m_in_set.size() && m_in_set[v] != 0; - } - - bool empty() const { - return m_set.empty(); - } - - // erase some variable from the set - unsigned erase() { - SASSERT(!empty()); - unsigned v = m_set.back(); - m_set.pop_back(); - m_in_set[v] = false; - return v; - } - unsigned size() const { return m_set.size(); } - iterator begin() const { return m_set.begin(); } - iterator end() const { return m_set.end(); } - void reset() { m_set.reset(); m_in_set.reset(); } - void finalize() { m_set.finalize(); m_in_set.finalize(); } - uint_set& operator&=(uint_set const& other) { - unsigned j = 0; - for (unsigned i = 0; i < m_set.size(); ++i) { - if (other.contains(m_set[i])) { - m_set[j] = m_set[i]; - ++j; - } - else { - m_in_set[m_set[i]] = false; - } - } - m_set.resize(j); - return *this; - } - uint_set& operator|=(uint_set const& other) { - for (unsigned i = 0; i < other.m_set.size(); ++i) { - insert(other.m_set[i]); - } - return *this; - } - }; + typedef tracked_uint_set uint_set; typedef uint_set bool_var_set; diff --git a/src/sat/sat_unit_walk.cpp b/src/sat/sat_unit_walk.cpp new file mode 100644 index 000000000..4a3cda47c --- /dev/null +++ b/src/sat/sat_unit_walk.cpp @@ -0,0 +1,370 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_unit_walk.cpp + +Abstract: + + unit walk local search procedure. + A variant of UnitWalk. Hirsch and Kojevinkov, SAT 2001. + This version uses a trail to reset assignments and integrates directly with the + watch list structure. Thus, assignments are not delayed and we avoid treating + pending units as a multi-set. + + It uses standard DPLL approach for backracking, flipping the last decision literal that + lead to a conflict. It restarts after evern 100 conflicts. + + It does not attempt to add conflict clauses or alternate with + walksat. + + It can receive conflict clauses from a concurrent CDCL solver and does not + create its own conflict clauses. + + The phase of variables is optionally sticky between rounds. We use a decay rate + to compute stickiness of a variable. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-12-15. + +Revision History: + +--*/ + +#include "sat_unit_walk.h" + +namespace sat { + + unit_walk::unit_walk(solver& s): + s(s) + { + m_runs = 0; + m_periods = 0; + m_max_runs = UINT_MAX; + m_max_periods = 5000; // UINT_MAX; // TBD configure + m_max_conflicts = 100; + m_sticky_phase = s.get_config().m_phase_sticky; + m_flips = 0; + } + + class scoped_set_unit_walk { + solver& s; + public: + scoped_set_unit_walk(unit_walk* u, solver& s): s(s) { + if (s.get_extension()) s.get_extension()->set_unit_walk(u); + } + ~scoped_set_unit_walk() { + if (s.get_extension()) s.get_extension()->set_unit_walk(nullptr); + } + }; + + lbool unit_walk::operator()() { + scoped_set_unit_walk _scoped_set(this, s); + init_runs(); + for (m_runs = 0; m_runs < m_max_runs || m_max_runs == UINT_MAX; ++m_runs) { + init_propagation(); + init_phase(); + for (m_periods = 0; m_periods < m_max_periods || m_max_periods == UINT_MAX; ++m_periods) { + if (!s.rlimit().inc()) return l_undef; + lbool r = unit_propagation(); + if (r != l_undef) return r; + } + } + return l_undef; + } + + lbool unit_walk::unit_propagation() { + init_propagation(); + while (!m_freevars.empty() && !inconsistent()) { + bool_var v = m_freevars.begin()[m_rand(m_freevars.size())]; + literal lit(v, !m_phase[v]); + ++s.m_stats.m_decision; + m_decisions.push_back(lit); + assign(lit); + propagate(); + while (inconsistent() && !m_decisions.empty()) { + ++m_conflicts; + backtrack(); + propagate(); + } + if (m_conflicts >= m_max_conflicts && !m_freevars.empty()) { + set_conflict(); + break; + } + } + if (!inconsistent()) { + log_status(); + IF_VERBOSE(1, verbose_stream() << "(sat-unit-walk sat)\n";); + s.mk_model(); + return l_true; + } + return l_undef; + } + + void unit_walk::init_runs() { + m_freevars.reset(); + m_trail.reset(); + m_decisions.reset(); + m_phase.resize(s.num_vars()); + double2 d2; + d2.t = 1.0; + d2.f = 1.0; + m_phase_tf.resize(s.num_vars(), d2); + for (unsigned i = 0; i < s.num_vars(); ++i) { + literal l(i, false); + if (!s.was_eliminated(l.var()) && s.m_assignment[l.index()] == l_undef) + m_freevars.insert(l.var()); + } + IF_VERBOSE(1, verbose_stream() << "num vars: " << s.num_vars() << " free vars: " << m_freevars.size() << "\n";); + } + + void unit_walk::init_phase() { + m_max_trail = 0; + if (m_sticky_phase) { + for (bool_var v : m_freevars) { + if (s.m_phase[v] == POS_PHASE) { + m_phase[v] = true; + } + else if (s.m_phase[v] == NEG_PHASE) { + m_phase[v] = false; + } + else { + m_phase[v] = m_rand(100 * static_cast(m_phase_tf[v].t + m_phase_tf[v].f)) <= 100 * m_phase_tf[v].t; + } + } + } + else { + for (bool_var v : m_freevars) + m_phase[v] = (m_rand(2) == 0); + } + } + + void unit_walk::init_propagation() { + if (s.m_par && s.m_par->copy_solver(s)) { + IF_VERBOSE(1, verbose_stream() << "(sat-unit-walk fresh copy)\n";); + if (s.get_extension()) s.get_extension()->set_unit_walk(this); + init_runs(); + init_phase(); + } + if (m_max_trail == 0 || m_trail.size() > m_max_trail) { + m_max_trail = m_trail.size(); + log_status(); + } + for (literal lit : m_trail) { + s.m_assignment[lit.index()] = l_undef; + s.m_assignment[(~lit).index()] = l_undef; + m_freevars.insert(lit.var()); + } + m_flips = 0; + m_trail.reset(); + m_conflicts = 0; + m_decisions.reset(); + m_qhead = 0; + m_inconsistent = false; + } + + void unit_walk::propagate() { + while (m_qhead < m_trail.size() && !inconsistent()) + propagate(choose_literal()); + // IF_VERBOSE(1, verbose_stream() << m_trail.size() << " " << inconsistent() << "\n";); + } + + void unit_walk::propagate(literal l) { + ++s.m_stats.m_propagate; + literal not_l = ~l; + literal l1, l2; + lbool val1, val2; + bool keep; + watch_list & wlist = s.get_wlist(l); + watch_list::iterator it = wlist.begin(); + watch_list::iterator it2 = it; + watch_list::iterator end = wlist.end(); + for (; it != end; ++it) { + switch (it->get_kind()) { + case watched::BINARY: + l1 = it->get_literal(); + switch (value(l1)) { + case l_false: + conflict_cleanup(it, it2, wlist); + set_conflict(l,l1); + return; + case l_undef: + assign(l1); + break; + case l_true: + break; // skip + } + *it2 = *it; + it2++; + break; + case watched::TERNARY: + l1 = it->get_literal1(); + l2 = it->get_literal2(); + val1 = value(l1); + val2 = value(l2); + if (val1 == l_false && val2 == l_undef) { + assign(l2); + } + else if (val1 == l_undef && val2 == l_false) { + assign(l1); + } + else if (val1 == l_false && val2 == l_false) { + conflict_cleanup(it, it2, wlist); + set_conflict(l,l1,l2); + return; + } + *it2 = *it; + it2++; + break; + case watched::CLAUSE: { + if (value(it->get_blocked_literal()) == l_true) { + *it2 = *it; + it2++; + break; + } + clause_offset cls_off = it->get_clause_offset(); + clause & c = s.get_clause(cls_off); + if (c[0] == not_l) + std::swap(c[0], c[1]); + if (c[1] != not_l) { + *it2 = *it; + it2++; + break; + } + if (value(c[0]) == l_true) { + it2->set_clause(c[0], cls_off); + it2++; + break; + } + SASSERT(c[1] == not_l); + literal * l_it = c.begin() + 2; + literal * l_end = c.end(); + for (; l_it != l_end; ++l_it) { + if (value(*l_it) != l_false) { + c[1] = *l_it; + *l_it = not_l; + s.get_wlist((~c[1]).index()).push_back(watched(c[0], cls_off)); + goto end_clause_case; + } + } + SASSERT(value(c[0]) == l_false || value(c[0]) == l_undef); + if (value(c[0]) == l_false) { + c.mark_used(); + conflict_cleanup(it, it2, wlist); + set_conflict(c); + return; + } + else { + *it2 = *it; + it2++; + assign(c[0]); + } + end_clause_case: + break; + } + case watched::EXT_CONSTRAINT: + SASSERT(s.get_extension()); + keep = s.get_extension()->propagate(l, it->get_ext_constraint_idx()); + if (inconsistent()) { + if (!keep) { + ++it; + } + set_conflict(l, l); + conflict_cleanup(it, it2, wlist); + return; + } + if (keep) { + *it2 = *it; + it2++; + } + break; + default: + UNREACHABLE(); + break; + } + } + wlist.set_end(it2); + } + + void unit_walk::assign(literal lit) { + SASSERT(value(lit) == l_undef); + s.m_assignment[lit.index()] = l_true; + s.m_assignment[(~lit).index()] = l_false; + m_trail.push_back(lit); + m_freevars.remove(lit.var()); + if (s.get_extension() && s.is_external(lit.var())) { + s.get_extension()->asserted(lit); + } + if (m_phase[lit.var()] == lit.sign()) { + ++m_flips; + flip_phase(lit); + } + } + + void unit_walk::flip_phase(literal l) { + bool_var v = l.var(); + m_phase[v] = !m_phase[v]; + if (m_sticky_phase) { + m_phase_tf[v].f *= 0.98; + m_phase_tf[v].t *= 0.98; + if (m_phase[v]) m_phase_tf[v].t += 1; else m_phase_tf[v].f += 1; + } + } + + void unit_walk::log_status() { + IF_VERBOSE(1, verbose_stream() << "(sat-unit-walk :trail " << m_max_trail + << " :branches " << m_decisions.size() + << " :free " << m_freevars.size() + << " :periods " << m_periods + << " :decisions " << s.m_stats.m_decision + << " :propagations " << s.m_stats.m_propagate + << ")\n";); + } + + literal unit_walk::choose_literal() { + SASSERT(m_qhead < m_trail.size()); + unsigned idx = m_rand(m_trail.size() - m_qhead); + std::swap(m_trail[m_qhead], m_trail[m_qhead + idx]); + literal lit = m_trail[m_qhead++]; + return lit; + } + + void unit_walk::set_conflict(literal l1, literal l2) { + set_conflict(); + } + + void unit_walk::set_conflict(literal l1, literal l2, literal l3) { + set_conflict(); + } + + void unit_walk::set_conflict(clause const& c) { + set_conflict(); + } + + void unit_walk::set_conflict() { + m_inconsistent = true; + } + + void unit_walk::backtrack() { + if (m_decisions.empty()) return; + literal dlit = m_decisions.back(); + literal lit; + do { + SASSERT(!m_trail.empty()); + lit = m_trail.back(); + s.m_assignment[lit.index()] = l_undef; + s.m_assignment[(~lit).index()] = l_undef; + m_freevars.insert(lit.var()); + m_trail.pop_back(); + } + while (lit != dlit); + m_inconsistent = false; + m_decisions.pop_back(); + m_qhead = m_trail.size(); + assign(~dlit); + } + +}; + diff --git a/src/sat/sat_unit_walk.h b/src/sat/sat_unit_walk.h new file mode 100644 index 000000000..8ab9bab70 --- /dev/null +++ b/src/sat/sat_unit_walk.h @@ -0,0 +1,79 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + sat_unit_walk.h + +Abstract: + + unit walk local search procedure. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-12-15. + +Revision History: + +--*/ +#ifndef SAT_UNIT_WALK_H_ +#define SAT_UNIT_WALK_H_ + +#include "sat/sat_solver.h" + +namespace sat { + + class unit_walk { + struct double2 { + double t, f; + }; + solver& s; + random_gen m_rand; + svector m_phase; + svector m_phase_tf; + indexed_uint_set m_freevars; + unsigned m_runs; + unsigned m_periods; + + // settings + unsigned m_max_runs; + unsigned m_max_periods; + unsigned m_max_conflicts; + bool m_sticky_phase; + + unsigned m_propagations; + unsigned m_flips; + unsigned m_max_trail; + unsigned m_qhead; + literal_vector m_trail; + bool m_inconsistent; + literal_vector m_decisions; + unsigned m_conflicts; + + void push(); + void backtrack(); + void init_runs(); + void init_phase(); + void init_propagation(); + void flip_phase(literal l); + lbool unit_propagation(); + void propagate(); + void propagate(literal lit); + literal choose_literal(); + void set_conflict(literal l1, literal l2); + void set_conflict(literal l1, literal l2, literal l3); + void set_conflict(clause const& c); + inline lbool value(literal lit) { return s.value(lit); } + void log_status(); + public: + + unit_walk(solver& s); + lbool operator()(); + std::ostream& display(std::ostream& out) const; + bool inconsistent() const { return m_inconsistent; } + void set_conflict(); + void assign(literal lit); + }; +}; + +#endif diff --git a/src/sat/sat_var_queue.h b/src/sat/sat_var_queue.h index a14eb4cff..0c22766ba 100644 --- a/src/sat/sat_var_queue.h +++ b/src/sat/sat_var_queue.h @@ -39,6 +39,15 @@ namespace sat { m_queue.decreased(v); } + void activity_changed_eh(bool_var v, bool up) { + if (m_queue.contains(v)) { + if (up) + m_queue.decreased(v); + else + m_queue.increased(v); + } + } + void mk_var_eh(bool_var v) { m_queue.reserve(v+1); m_queue.insert(v); @@ -61,6 +70,10 @@ namespace sat { bool empty() const { return m_queue.empty(); } bool_var next_var() { SASSERT(!empty()); return m_queue.erase_min(); } + + bool_var min_var() { SASSERT(!empty()); return m_queue.min_value(); } + + bool more_active(bool_var v1, bool_var v2) const { return m_queue.less_than(v1, v2); } }; }; diff --git a/src/sat/sat_watched.cpp b/src/sat/sat_watched.cpp index 6335d37fc..199d12797 100644 --- a/src/sat/sat_watched.cpp +++ b/src/sat/sat_watched.cpp @@ -27,10 +27,10 @@ namespace sat { for (; it != end; ++it) { if (it->is_clause() && it->get_clause_offset() == c) { watch_list::iterator it2 = it; - ++it; - for (; it != end; ++it) { + ++it; + for (; it != end; ++it, ++it2) { + SASSERT(!((it->is_clause() && it->get_clause_offset() == c))); *it2 = *it; - ++it2; } wlist.set_end(it2); return true; @@ -39,33 +39,90 @@ namespace sat { return false; } - void display(std::ostream & out, clause_allocator const & ca, watch_list const & wlist) { - watch_list::const_iterator it = wlist.begin(); - watch_list::const_iterator end = wlist.end(); - for (bool first = true; it != end; ++it) { + watched* find_binary_watch(watch_list & wlist, literal l) { + for (watched& w : wlist) { + if (w.is_binary_clause() && w.get_literal() == l) return &w; + } + return nullptr; + } + + watched const* find_binary_watch(watch_list const& wlist, literal l) { + for (watched const& w : wlist) { + if (w.is_binary_clause() && w.get_literal() == l) return &w; + } + return nullptr; + } + + void erase_binary_watch(watch_list& wlist, literal l) { + watch_list::iterator it = wlist.begin(), end = wlist.end(); + watch_list::iterator it2 = it; + bool found = false; + for (; it != end; ++it) { + if (it->is_binary_clause() && it->get_literal() == l && !found) { + found = true; + } + else { + *it2 = *it; + ++it2; + } + } + wlist.set_end(it2); + VERIFY(found); + } + + void erase_ternary_watch(watch_list& wlist, literal l1, literal l2) { + watched w(l1, l2); + watch_list::iterator it = wlist.begin(), end = wlist.end(); + watch_list::iterator it2 = it; + bool found = false; + for (; it != end; ++it) { + if (!found && w == *it) { + found = true; + } + else { + *it2 = *it; + ++it2; + } + } + wlist.set_end(it2); + //VERIFY(found); + } + + void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist) { + watch_list::iterator end = wlist.end(); + for (; it != end; ++it, ++it2) + *it2 = *it; + wlist.set_end(it2); + } + + + std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist) { + bool first = true; + for (watched const& w : wlist) { if (first) first = false; else out << " "; - switch (it->get_kind()) { + switch (w.get_kind()) { case watched::BINARY: - out << it->get_literal(); - if (it->is_learned()) + out << w.get_literal(); + if (w.is_learned()) out << "*"; break; case watched::TERNARY: - out << "(" << it->get_literal1() << " " << it->get_literal2() << ")"; + out << "(" << w.get_literal1() << " " << w.get_literal2() << ")"; break; case watched::CLAUSE: - out << "(" << it->get_blocked_literal() << " " << *(ca.get_clause(it->get_clause_offset())) << ")"; + out << "(" << w.get_blocked_literal() << " " << *(ca.get_clause(w.get_clause_offset())) << ")"; break; case watched::EXT_CONSTRAINT: - out << it->get_ext_constraint_idx(); + out << "ext: " << w.get_ext_constraint_idx(); break; default: UNREACHABLE(); } } + return out; } }; diff --git a/src/sat/sat_watched.h b/src/sat/sat_watched.h index 639d3e6a8..09ad22ffd 100644 --- a/src/sat/sat_watched.h +++ b/src/sat/sat_watched.h @@ -33,7 +33,7 @@ namespace sat { For binary clauses: we use a bit to store whether the binary clause was learned or not. - Remark: there is not Clause object for binary clauses. + Remark: there are no clause objects for binary clauses. */ class watched { public: @@ -41,7 +41,7 @@ namespace sat { BINARY = 0, TERNARY, CLAUSE, EXT_CONSTRAINT }; private: - unsigned m_val1; + size_t m_val1; unsigned m_val2; public: watched(literal l, bool learned): @@ -64,6 +64,8 @@ namespace sat { SASSERT(get_literal2() == l2); } + unsigned val2() const { return m_val2; } + watched(literal blocked_lit, clause_offset cls_off): m_val1(cls_off), m_val2(static_cast(CLAUSE) + (blocked_lit.to_uint() << 2)) { @@ -72,7 +74,7 @@ namespace sat { SASSERT(get_clause_offset() == cls_off); } - watched(ext_constraint_idx cnstr_idx): + explicit watched(ext_constraint_idx cnstr_idx): m_val1(cnstr_idx), m_val2(static_cast(EXT_CONSTRAINT)) { SASSERT(is_ext_constraint()); @@ -82,18 +84,21 @@ namespace sat { kind get_kind() const { return static_cast(m_val2 & 3); } bool is_binary_clause() const { return get_kind() == BINARY; } - literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(m_val1); } + literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(static_cast(m_val1)); } void set_literal(literal l) { SASSERT(is_binary_clause()); m_val1 = l.to_uint(); } - bool is_learned() const { SASSERT(is_binary_clause()); return (m_val2 >> 2) == 1; } - bool is_binary_non_learned_clause() const { return m_val2 == 0; } - void mark_not_learned() { SASSERT(is_learned()); m_val2 = static_cast(BINARY); SASSERT(!is_learned()); } - + bool is_learned() const { SASSERT(is_binary_clause()); return ((m_val2 >> 2) & 1) == 1; } + + bool is_binary_learned_clause() const { return is_binary_clause() && is_learned(); } + bool is_binary_non_learned_clause() const { return is_binary_clause() && !is_learned(); } + + void set_learned(bool l) { if (l) m_val2 |= 4u; else m_val2 &= ~4u; SASSERT(is_learned() == l); } + bool is_ternary_clause() const { return get_kind() == TERNARY; } - literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(m_val1); } + literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(static_cast(m_val1)); } literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 2); } bool is_clause() const { return get_kind() == CLAUSE; } - clause_offset get_clause_offset() const { SASSERT(is_clause()); return m_val1; } + clause_offset get_clause_offset() const { SASSERT(is_clause()); return static_cast(m_val1); } literal get_blocked_literal() const { SASSERT(is_clause()); return to_literal(m_val2 >> 2); } void set_clause_offset(clause_offset c) { SASSERT(is_clause()); m_val1 = c; } void set_blocked_literal(literal l) { SASSERT(is_clause()); m_val2 = static_cast(CLAUSE) + (l.to_uint() << 2); } @@ -103,7 +108,7 @@ namespace sat { } bool is_ext_constraint() const { return get_kind() == EXT_CONSTRAINT; } - ext_constraint_idx get_ext_constraint_idx() const { SASSERT(is_ext_constraint()); return m_val2; } + ext_constraint_idx get_ext_constraint_idx() const { SASSERT(is_ext_constraint()); return m_val1; } bool operator==(watched const & w) const { return m_val1 == w.m_val1 && m_val2 == w.m_val2; } bool operator!=(watched const & w) const { return !operator==(w); } @@ -126,11 +131,16 @@ namespace sat { typedef vector watch_list; + watched* find_binary_watch(watch_list & wlist, literal l); + watched const* find_binary_watch(watch_list const & wlist, literal l); bool erase_clause_watch(watch_list & wlist, clause_offset c); - inline void erase_ternary_watch(watch_list & wlist, literal l1, literal l2) { wlist.erase(watched(l1, l2)); } + void erase_ternary_watch(watch_list & wlist, literal l1, literal l2); + void set_ternary_learned(watch_list& wlist, literal l1, literal l2, bool learned); class clause_allocator; - void display(std::ostream & out, clause_allocator const & ca, watch_list const & wlist); + std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist); + + void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist); }; #endif diff --git a/src/sat/tactic/CMakeLists.txt b/src/sat/tactic/CMakeLists.txt index fed6a89c8..dbbfbe753 100644 --- a/src/sat/tactic/CMakeLists.txt +++ b/src/sat/tactic/CMakeLists.txt @@ -6,6 +6,7 @@ z3_add_component(sat_tactic COMPONENT_DEPENDENCIES sat tactic + solver TACTIC_HEADERS sat_tactic.h ) diff --git a/src/sat/tactic/atom2bool_var.cpp b/src/sat/tactic/atom2bool_var.cpp index 26f3448d3..b79eaa251 100644 --- a/src/sat/tactic/atom2bool_var.cpp +++ b/src/sat/tactic/atom2bool_var.cpp @@ -16,19 +16,24 @@ Author: Notes: --*/ -#include "sat/tactic/atom2bool_var.h" -#include "ast/ast_smt2_pp.h" + #include "util/ref_util.h" +#include "ast/ast_smt2_pp.h" #include "tactic/goal.h" +#include "sat/tactic/atom2bool_var.h" void atom2bool_var::mk_inv(expr_ref_vector & lit2expr) const { - obj_map::iterator it = m_mapping.begin(); - obj_map::iterator end = m_mapping.end(); - for (; it != end; ++it) { - sat::literal l(static_cast(it->m_value), false); - lit2expr.set(l.index(), it->m_key); + for (auto const& kv : m_mapping) { + sat::literal l(static_cast(kv.m_value), false); + lit2expr.set(l.index(), kv.m_key); l.neg(); - lit2expr.set(l.index(), m().mk_not(it->m_key)); + lit2expr.set(l.index(), m().mk_not(kv.m_key)); + } +} + +void atom2bool_var::mk_var_inv(app_ref_vector & var2expr) const { + for (auto const& kv : m_mapping) { + var2expr.set(kv.m_value, to_app(kv.m_key)); } } @@ -70,7 +75,7 @@ struct collect_boolean_interface_proc { continue; if (is_app(t) && to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0) { decl_kind k = to_app(t)->get_decl_kind(); - if (k == OP_OR || k == OP_NOT || k == OP_IFF || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) { + if (k == OP_OR || k == OP_NOT || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); diff --git a/src/sat/tactic/atom2bool_var.h b/src/sat/tactic/atom2bool_var.h index d360d3fe0..b7f533537 100644 --- a/src/sat/tactic/atom2bool_var.h +++ b/src/sat/tactic/atom2bool_var.h @@ -31,6 +31,7 @@ public: void insert(expr * n, sat::bool_var v) { expr2var::insert(n, v); } sat::bool_var to_bool_var(expr * n) const; void mk_inv(expr_ref_vector & lit2expr) const; + void mk_var_inv(app_ref_vector & var2expr) const; // return true if the mapping contains uninterpreted atoms. bool interpreted_atoms() const { return expr2var::interpreted_vars(); } }; diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 1f9dd91d1..5f0fbec16 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -26,16 +26,19 @@ Author: Notes: --*/ -#include "sat/tactic/goal2sat.h" -#include "ast/ast_smt2_pp.h" #include "util/ref_util.h" #include "util/cooperate.h" -#include "tactic/filter_model_converter.h" -#include "model/model_evaluator.h" +#include "ast/ast_smt2_pp.h" +#include "ast/ast_pp.h" +#include "ast/pb_decl_plugin.h" +#include "ast/ast_util.h" #include "ast/for_each_expr.h" +#include "sat/tactic/goal2sat.h" +#include "sat/ba_solver.h" +#include "model/model_evaluator.h" #include "model/model_v2_pp.h" #include "tactic/tactic.h" -#include "ast/ast_pp.h" +#include "tactic/generic_model_converter.h" #include struct goal2sat::imp { @@ -48,6 +51,8 @@ struct goal2sat::imp { m_t(t), m_root(r), m_sign(s), m_idx(idx) {} }; ast_manager & m; + pb_util pb; + sat::ba_solver* m_ext; svector m_frame_stack; svector m_result_stack; obj_map m_cache; @@ -61,22 +66,28 @@ struct goal2sat::imp { expr_ref_vector m_trail; expr_ref_vector m_interpreted_atoms; bool m_default_external; + bool m_xor_solver; + bool m_is_lemma; 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_solver(s), m_map(map), m_dep2asm(dep2asm), m_trail(m), m_interpreted_atoms(m), - m_default_external(default_external) { + m_default_external(default_external), + m_is_lemma(false) { updt_params(p); m_true = sat::null_bool_var; } void updt_params(params_ref const & p) { - m_ite_extra = p.get_bool("ite_extra", true); - m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_ite_extra = p.get_bool("ite_extra", true); + m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_xor_solver = p.get_bool("xor_solver", false); } void throw_op_not_handled(std::string const& s) { @@ -89,31 +100,33 @@ struct goal2sat::imp { m_solver.mk_clause(1, &l); } + void set_lemma_mode(bool f) { m_is_lemma = f; } + void mk_clause(sat::literal l1, sat::literal l2) { TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << "\n";); - m_solver.mk_clause(l1, l2); + m_solver.mk_clause(l1, l2, m_is_lemma); } void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3) { TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";); - m_solver.mk_clause(l1, l2, l3); + m_solver.mk_clause(l1, l2, l3, m_is_lemma); } void mk_clause(unsigned num, sat::literal * lits) { TRACE("goal2sat", tout << "mk_clause: "; for (unsigned i = 0; i < num; i++) tout << lits[i] << " "; tout << "\n";); - m_solver.mk_clause(num, lits); + m_solver.mk_clause(num, lits, m_is_lemma); } sat::bool_var mk_true() { if (m_true == sat::null_bool_var) { // create fake variable to represent true; - m_true = m_solver.mk_var(); + m_true = m_solver.mk_var(false); mk_clause(sat::literal(m_true, false)); // v is true } return m_true; } - void convert_atom(expr * t, bool root, bool sign) { + void convert_atom(expr * t, bool root, bool sign) { SASSERT(m.is_bool(t)); sat::literal l; sat::bool_var v = m_map.to_bool_var(t); @@ -129,7 +142,7 @@ struct goal2sat::imp { sat::bool_var v = m_solver.mk_var(ext); m_map.insert(t, v); l = sat::literal(v, sign); - TRACE("goal2sat", tout << "new_var: " << v << "\n" << mk_ismt2_pp(t, m) << "\n";); + TRACE("sat", tout << "new_var: " << v << ": " << mk_ismt2_pp(t, m) << "\n";); if (ext && !is_uninterp_const(t)) { m_interpreted_atoms.push_back(t); } @@ -138,6 +151,7 @@ struct goal2sat::imp { else { SASSERT(v != sat::null_bool_var); l = sat::literal(v, sign); + m_solver.set_eliminated(v, false); } SASSERT(l != sat::null_literal); if (root) @@ -146,6 +160,18 @@ struct goal2sat::imp { m_result_stack.push_back(l); } + bool convert_app(app* t, bool root, bool sign) { + if (t->get_family_id() == pb.get_family_id()) { + ensure_extension(); + m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); + return false; + } + else { + convert_atom(t, root, sign); + return true; + } + } + bool process_cached(app * t, bool root, bool sign) { sat::literal l; if (m_cache.find(t, l)) { @@ -160,6 +186,7 @@ struct goal2sat::imp { return false; } + bool visit(expr * t, bool root, bool sign) { if (!is_app(t)) { convert_atom(t, root, sign); @@ -168,14 +195,12 @@ struct goal2sat::imp { if (process_cached(to_app(t), root, sign)) return true; if (to_app(t)->get_family_id() != m.get_basic_family_id()) { - convert_atom(t, root, sign); - return true; + return convert_app(to_app(t), root, sign); } switch (to_app(t)->get_decl_kind()) { case OP_NOT: case OP_OR: case OP_AND: - case OP_IFF: m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; case OP_ITE: @@ -184,8 +209,10 @@ struct goal2sat::imp { m_frame_stack.push_back(frame(to_app(t), root, sign, 0)); return false; } - convert_atom(t, root, sign); - return true; + else { + convert_atom(t, root, sign); + return true; + } case OP_XOR: case OP_IMPLIES: case OP_DISTINCT: { @@ -283,7 +310,6 @@ struct goal2sat::imp { } } - void convert_ite(app * n, bool root, bool sign) { unsigned sz = m_result_stack.size(); SASSERT(sz >= 3); @@ -321,7 +347,7 @@ struct goal2sat::imp { } } - void convert_iff(app * t, bool root, bool sign) { + void convert_iff2(app * t, bool root, bool sign) { TRACE("goal2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_ismt2_pp(t, m) << "\n";); unsigned sz = m_result_stack.size(); SASSERT(sz >= 2); @@ -354,26 +380,344 @@ struct goal2sat::imp { } } + void convert_iff(app * t, bool root, bool sign) { + TRACE("goal2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_ismt2_pp(t, m) << "\n";); + unsigned sz = m_result_stack.size(); + unsigned num = get_num_args(t); + SASSERT(sz >= num && num >= 2); + if (num == 2) { + convert_iff2(t, root, sign); + return; + } + sat::literal_vector lits; + sat::bool_var v = m_solver.mk_var(true); + lits.push_back(sat::literal(v, true)); + convert_pb_args(num, lits); + // ensure that = is converted to xor + for (unsigned i = 1; i + 1 < lits.size(); ++i) { + lits[i].neg(); + } + ensure_extension(); + m_ext->add_xr(lits); + sat::literal lit(v, sign); + if (root) { + m_result_stack.reset(); + mk_clause(lit); + } + else { + m_result_stack.shrink(sz - num); + m_result_stack.push_back(lit); + } + } + + void convert_pb_args(unsigned num_args, sat::literal_vector& lits) { + unsigned sz = m_result_stack.size(); + for (unsigned i = 0; i < num_args; ++i) { + sat::literal lit(m_result_stack[sz - num_args + i]); + if (!m_solver.is_external(lit.var())) { + m_solver.set_external(lit.var()); + } + lits.push_back(lit); + } + } + + typedef std::pair wliteral; + + void check_unsigned(rational const& c) { + if (!c.is_unsigned()) { + throw default_exception("unsigned coefficient expected"); + } + } + + void convert_to_wlits(app* t, sat::literal_vector const& lits, svector& wlits) { + for (unsigned i = 0; i < lits.size(); ++i) { + rational c = pb.get_coeff(t, i); + check_unsigned(c); + wlits.push_back(std::make_pair(c.get_unsigned(), lits[i])); + } + } + + void convert_pb_args(app* t, svector& wlits) { + sat::literal_vector lits; + convert_pb_args(t->get_num_args(), lits); + convert_to_wlits(t, lits, wlits); + } + + void convert_pb_ge(app* t, bool root, bool sign) { + rational k = pb.get_k(t); + check_unsigned(k); + svector wlits; + convert_pb_args(t, wlits); + 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()); + } + else { + sat::bool_var v = m_solver.mk_var(true); + sat::literal lit(v, sign); + m_ext->add_pb_ge(v, wlits, k.get_unsigned()); + TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); + m_result_stack.shrink(sz - t->get_num_args()); + m_result_stack.push_back(lit); + } + } + + void convert_pb_le(app* t, bool root, bool sign) { + rational k = pb.get_k(t); + k.neg(); + svector wlits; + convert_pb_args(t, wlits); + for (wliteral& wl : wlits) { + wl.second.neg(); + k += rational(wl.first); + } + check_unsigned(k); + 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()); + } + else { + sat::bool_var v = m_solver.mk_var(true); + sat::literal lit(v, sign); + m_ext->add_pb_ge(v, wlits, k.get_unsigned()); + TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); + m_result_stack.shrink(sz - t->get_num_args()); + m_result_stack.push_back(lit); + } + } + + void convert_pb_eq(app* t, bool root, bool sign) { + IF_VERBOSE(0, verbose_stream() << "pbeq: " << mk_pp(t, m) << "\n";); + rational k = pb.get_k(t); + SASSERT(k.is_unsigned()); + svector wlits; + convert_pb_args(t, wlits); + sat::bool_var v1 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); + sat::bool_var v2 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); + m_ext->add_pb_ge(v1, wlits, k.get_unsigned()); + k.neg(); + for (wliteral& wl : wlits) { + wl.second.neg(); + k += rational(wl.first); + } + check_unsigned(k); + m_ext->add_pb_ge(v2, wlits, k.get_unsigned()); + if (root && !sign) { + m_result_stack.reset(); + } + else { + sat::literal l1(v1, false), l2(v2, false); + sat::bool_var v = m_solver.mk_var(); + sat::literal l(v, false); + mk_clause(~l, l1); + mk_clause(~l, l2); + mk_clause(~l1, ~l2, l); + m_cache.insert(t, l); + m_result_stack.shrink(m_result_stack.size() - t->get_num_args()); + if (sign) l.neg(); + m_result_stack.push_back(l); + if (root) { + m_result_stack.reset(); + mk_clause(l); + } + } + } + + void convert_at_least_k(app* t, rational const& k, bool root, bool sign) { + SASSERT(k.is_unsigned()); + sat::literal_vector lits; + unsigned sz = m_result_stack.size(); + convert_pb_args(t->get_num_args(), lits); + if (root) { + m_result_stack.reset(); + m_ext->add_at_least(sat::null_bool_var, lits, k.get_unsigned()); + } + else { + sat::bool_var v = m_solver.mk_var(true); + sat::literal lit(v, false); + m_ext->add_at_least(v, lits, k.get_unsigned()); + m_cache.insert(t, lit); + if (sign) lit.neg(); + TRACE("goal2sat", tout << "root: " << root << " lit: " << lit << "\n";); + m_result_stack.shrink(sz - t->get_num_args()); + m_result_stack.push_back(lit); + } + } + + void convert_at_most_k(app* t, rational const& k, bool root, bool sign) { + SASSERT(k.is_unsigned()); + sat::literal_vector lits; + unsigned sz = m_result_stack.size(); + convert_pb_args(t->get_num_args(), lits); + for (sat::literal& l : lits) { + l.neg(); + } + if (root) { + m_result_stack.reset(); + m_ext->add_at_least(sat::null_bool_var, lits, lits.size() - k.get_unsigned()); + } + else { + sat::bool_var v = m_solver.mk_var(true); + sat::literal lit(v, false); + m_ext->add_at_least(v, lits, lits.size() - k.get_unsigned()); + m_cache.insert(t, lit); + m_result_stack.shrink(sz - t->get_num_args()); + if (sign) lit.neg(); + m_result_stack.push_back(lit); + } + } + + void convert_eq_k(app* t, rational const& k, bool root, bool sign) { + SASSERT(k.is_unsigned()); + sat::literal_vector lits; + convert_pb_args(t->get_num_args(), lits); + sat::bool_var v1 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); + sat::bool_var v2 = (root && !sign) ? sat::null_bool_var : m_solver.mk_var(true); + m_ext->add_at_least(v1, lits, k.get_unsigned()); + for (sat::literal& l : lits) { + l.neg(); + } + m_ext->add_at_least(v2, lits, lits.size() - k.get_unsigned()); + + + if (root && !sign) { + m_result_stack.reset(); + } + else { + sat::literal l1(v1, false), l2(v2, false); + sat::bool_var v = m_solver.mk_var(); + sat::literal l(v, false); + mk_clause(~l, l1); + mk_clause(~l, l2); + mk_clause(~l1, ~l2, l); + m_cache.insert(t, l); + m_result_stack.shrink(m_result_stack.size() - t->get_num_args()); + if (sign) l.neg(); + m_result_stack.push_back(l); + if (root) { + mk_clause(l); + m_result_stack.reset(); + } + } + } + + void ensure_extension() { + if (!m_ext) { + sat::extension* ext = m_solver.get_extension(); + if (ext) { + m_ext = dynamic_cast(ext); + SASSERT(m_ext); + } + if (!m_ext) { + m_ext = alloc(sat::ba_solver); + m_solver.set_extension(m_ext); + } + } + } + void convert(app * t, bool root, bool sign) { - SASSERT(t->get_family_id() == m.get_basic_family_id()); - switch (to_app(t)->get_decl_kind()) { - case OP_OR: - convert_or(t, root, sign); - break; - case OP_AND: - convert_and(t, root, sign); - break; - case OP_ITE: - convert_ite(t, root, sign); - break; - case OP_IFF: - case OP_EQ: - convert_iff(t, root, sign); - break; - default: + if (t->get_family_id() == m.get_basic_family_id()) { + switch (to_app(t)->get_decl_kind()) { + case OP_OR: + convert_or(t, root, sign); + break; + case OP_AND: + convert_and(t, root, sign); + break; + case OP_ITE: + convert_ite(t, root, sign); + break; + case OP_EQ: + convert_iff(t, root, sign); + break; + default: + UNREACHABLE(); + } + } + else if (t->get_family_id() == pb.get_family_id()) { + ensure_extension(); + rational k; + switch (t->get_decl_kind()) { + case OP_AT_MOST_K: + k = pb.get_k(t); + convert_at_most_k(t, k, root, sign); + break; + case OP_AT_LEAST_K: + k = pb.get_k(t); + convert_at_least_k(t, k, root, sign); + break; + case OP_PB_LE: + if (pb.has_unit_coefficients(t)) { + k = pb.get_k(t); + convert_at_most_k(t, k, root, sign); + } + else { + convert_pb_le(t, root, sign); + } + break; + case OP_PB_GE: + if (pb.has_unit_coefficients(t)) { + k = pb.get_k(t); + convert_at_least_k(t, k, root, sign); + } + else { + convert_pb_ge(t, root, sign); + } + break; + case OP_PB_EQ: + if (pb.has_unit_coefficients(t)) { + k = pb.get_k(t); + convert_eq_k(t, k, root, sign); + } + else { + convert_pb_eq(t, root, sign); + } + break; + default: + UNREACHABLE(); + } + } + else { UNREACHABLE(); } } + + + unsigned get_num_args(app* t) { + + if (m.is_iff(t) && m_xor_solver) { + unsigned n = 2; + while (m.is_iff(t->get_arg(1))) { + ++n; + t = to_app(t->get_arg(1)); + } + return n; + } + else { + return t->get_num_args(); + } + } + + expr* get_arg(app* t, unsigned idx) { + if (m.is_iff(t) && m_xor_solver) { + while (idx >= 1) { + SASSERT(m.is_iff(t)); + t = to_app(t->get_arg(1)); + --idx; + } + if (m.is_iff(t)) { + return t->get_arg(idx); + } + else { + return t; + } + } + else { + return t->get_arg(idx); + } + } void process(expr * n) { //SASSERT(m_result_stack.empty()); @@ -405,9 +749,9 @@ struct goal2sat::imp { visit(t->get_arg(0), root, !sign); continue; } - unsigned num = t->get_num_args(); + unsigned num = get_num_args(t); while (fr.m_idx < num) { - expr * arg = t->get_arg(fr.m_idx); + expr * arg = get_arg(t, fr.m_idx); fr.m_idx++; if (!visit(arg, false, false)) goto loop; @@ -457,8 +801,7 @@ struct goal2sat::imp { fmls.reset(); m.linearize(g.dep(idx), deps); fmls.push_back(f); - for (unsigned i = 0; i < deps.size(); ++i) { - expr * d = deps[i]; + for (expr * d : deps) { expr * d1 = d; SASSERT(m.is_bool(d)); bool sign = m.is_not(d, d1); @@ -477,7 +820,7 @@ struct goal2sat::imp { } f = m.mk_or(fmls.size(), fmls.c_ptr()); } - TRACE("sat", tout << mk_pp(f, m) << "\n";); + TRACE("goal2sat", tout << f << "\n";); process(f); skip_dep: ; @@ -525,7 +868,7 @@ bool goal2sat::has_unsupported_bool(goal const & g) { return test(g); } -goal2sat::goal2sat():m_imp(0), m_interpreted_atoms(0) { +goal2sat::goal2sat():m_imp(nullptr), m_interpreted_atoms(nullptr) { } void goal2sat::collect_param_descrs(param_descrs & r) { @@ -539,14 +882,15 @@ struct goal2sat::scoped_set_imp { m_owner->m_imp = i; } ~scoped_set_imp() { - m_owner->m_imp = 0; + m_owner->m_imp = nullptr; } }; -void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external) { +void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external, bool is_lemma) { imp proc(g.m(), p, t, m, dep2asm, default_external); scoped_set_imp set(this, &proc); + proc.set_lemma_mode(is_lemma); proc(g); dealloc(m_interpreted_atoms); m_interpreted_atoms = alloc(expr_ref_vector, g.m()); @@ -560,116 +904,166 @@ void goal2sat::get_interpreted_atoms(expr_ref_vector& atoms) { } +sat2goal::mc::mc(ast_manager& m): m(m), m_var2expr(m) {} + +void sat2goal::mc::flush_smc(sat::solver& s, atom2bool_var const& map) { + s.flush(m_smc); + m_var2expr.resize(s.num_vars()); + map.mk_var_inv(m_var2expr); +} + +void sat2goal::mc::flush_gmc() { + sat::literal_vector updates; + m_smc.expand(updates); + m_smc.reset(); + if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); + // now gmc owns the model converter + sat::literal_vector clause; + expr_ref_vector tail(m); + expr_ref def(m); + for (unsigned i = 0; i < updates.size(); ++i) { + sat::literal l = updates[i]; + if (l == sat::null_literal) { + sat::literal lit0 = clause[0]; + for (unsigned i = 1; i < clause.size(); ++i) { + tail.push_back(lit2expr(~clause[i])); + } + def = m.mk_or(lit2expr(lit0), mk_and(tail)); + if (lit0.sign()) { + lit0.neg(); + def = m.mk_not(def); + } + m_gmc->add(lit2expr(lit0), def); + clause.reset(); + tail.reset(); + } + // short circuit for equivalences: + else if (clause.empty() && tail.empty() && + i + 5 < updates.size() && + updates[i] == ~updates[i + 3] && + updates[i + 1] == ~updates[i + 4] && + updates[i + 2] == sat::null_literal && + updates[i + 5] == sat::null_literal) { + sat::literal r = ~updates[i+1]; + if (l.sign()) { + l.neg(); + r.neg(); + } + m_gmc->add(lit2expr(l), lit2expr(r)); + i += 5; + } + else { + clause.push_back(l); + } + } +} + +model_converter* sat2goal::mc::translate(ast_translation& translator) { + mc* result = alloc(mc, translator.to()); + result->m_smc.copy(m_smc); + result->m_gmc = m_gmc ? dynamic_cast(m_gmc->translate(translator)) : nullptr; + for (app* e : m_var2expr) { + result->m_var2expr.push_back(translator(e)); + } + return result; +} + +void sat2goal::mc::set_env(ast_pp_util* visitor) { + flush_gmc(); + if (m_gmc) m_gmc->set_env(visitor); +} + +void sat2goal::mc::display(std::ostream& out) { + flush_gmc(); + if (m_gmc) m_gmc->display(out); +} + +void sat2goal::mc::get_units(obj_map& units) { + flush_gmc(); + if (m_gmc) m_gmc->get_units(units); +} + + +void sat2goal::mc::operator()(model_ref & md) { + model_evaluator ev(*md); + ev.set_model_completion(false); + + // create a SAT model using md + sat::model sat_md; + expr_ref val(m); + VERIFY(!m_var2expr.empty()); + for (expr * atom : m_var2expr) { + if (!atom) { + sat_md.push_back(l_undef); + continue; + } + ev(atom, val); + if (m.is_true(val)) + sat_md.push_back(l_true); + else if (m.is_false(val)) + sat_md.push_back(l_false); + else + sat_md.push_back(l_undef); + } + + // apply SAT model converter + m_smc(sat_md); + + // register value of non-auxiliary boolean variables back into md + unsigned sz = m_var2expr.size(); + for (sat::bool_var v = 0; v < sz; v++) { + app * atom = m_var2expr.get(v); + if (atom && is_uninterp_const(atom)) { + func_decl * d = atom->get_decl(); + lbool new_val = sat_md[v]; + if (new_val == l_true) + md->register_decl(d, m.mk_true()); + else if (new_val == l_false) + md->register_decl(d, m.mk_false()); + } + } + // apply externalized model converter + if (m_gmc) (*m_gmc)(md); + TRACE("sat_mc", tout << "after sat_mc\n"; model_v2_pp(tout, *md);); +} + + +void sat2goal::mc::operator()(expr_ref& fml) { + flush_gmc(); + if (m_gmc) (*m_gmc)(fml); +} + +void sat2goal::mc::insert(sat::bool_var v, app * atom, bool aux) { + SASSERT(!m_var2expr.get(v, nullptr)); + m_var2expr.reserve(v + 1); + m_var2expr.set(v, atom); + if (aux) { + SASSERT(is_uninterp_const(atom)); + SASSERT(m.is_bool(atom)); + if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); + m_gmc->hide(atom->get_decl()); + } +} + +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()); + m_var2expr.set(l.var(), aux); + if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); + m_gmc->hide(aux->get_decl()); + } + VERIFY(m_var2expr.get(l.var())); + expr_ref result(m_var2expr.get(l.var()), m); + if (l.sign()) { + result = m.mk_not(result); + } + return result; +} + + struct sat2goal::imp { - // Wrapper for sat::model_converter: converts it into an "AST level" model_converter. - class sat_model_converter : public model_converter { - sat::model_converter m_mc; - // TODO: the following mapping is storing a lot of useless information, and may be a performance bottleneck. - // We need to save only the expressions associated with variables that occur in m_mc. - // This information may be stored as a vector of pairs. - // The mapping is only created during the model conversion. - expr_ref_vector m_var2expr; - filter_model_converter_ref m_fmc; // filter for eliminating fresh variables introduced in the assertion-set --> sat conversion - - sat_model_converter(ast_manager & m): - m_var2expr(m) { - } - - public: - sat_model_converter(ast_manager & m, sat::solver const & s):m_var2expr(m) { - m_mc.copy(s.get_model_converter()); - m_fmc = alloc(filter_model_converter, m); - } - - ast_manager & m() { return m_var2expr.get_manager(); } - - void insert(expr * atom, bool aux) { - m_var2expr.push_back(atom); - if (aux) { - SASSERT(is_uninterp_const(atom)); - SASSERT(m().is_bool(atom)); - m_fmc->insert(to_app(atom)->get_decl()); - } - } - - virtual void operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); - TRACE("sat_mc", tout << "before sat_mc\n"; model_v2_pp(tout, *md); display(tout);); - // REMARK: potential problem - // model_evaluator can't evaluate quantifiers. Then, - // an eliminated variable that depends on a quantified expression can't be recovered. - // A similar problem also affects any model_converter that uses elim_var_model_converter. - // - // Possible solution: - // model_converters reject any variable elimination that depends on a quantified expression. - - model_evaluator ev(*md); - ev.set_model_completion(false); - - // create a SAT model using md - sat::model sat_md; - unsigned sz = m_var2expr.size(); - expr_ref val(m()); - for (sat::bool_var v = 0; v < sz; v++) { - expr * atom = m_var2expr.get(v); - ev(atom, val); - if (m().is_true(val)) - sat_md.push_back(l_true); - else if (m().is_false(val)) - sat_md.push_back(l_false); - else - sat_md.push_back(l_undef); - } - - // apply SAT model converter - m_mc(sat_md); - - // register value of non-auxiliary boolean variables back into md - sz = m_var2expr.size(); - for (sat::bool_var v = 0; v < sz; v++) { - expr * atom = m_var2expr.get(v); - if (is_uninterp_const(atom)) { - func_decl * d = to_app(atom)->get_decl(); - lbool new_val = sat_md[v]; - if (new_val == l_true) - md->register_decl(d, m().mk_true()); - else if (new_val == l_false) - md->register_decl(d, m().mk_false()); - } - } - - // apply filter model converter - (*m_fmc)(md); - TRACE("sat_mc", tout << "after sat_mc\n"; model_v2_pp(tout, *md);); - } - - virtual model_converter * translate(ast_translation & translator) { - sat_model_converter * res = alloc(sat_model_converter, translator.to()); - res->m_fmc = static_cast(m_fmc->translate(translator)); - unsigned sz = m_var2expr.size(); - for (unsigned i = 0; i < sz; i++) - res->m_var2expr.push_back(translator(m_var2expr.get(i))); - return res; - } - - void display(std::ostream & out) { - out << "(sat-model-converter\n"; - m_mc.display(out); - sat::bool_var_set vars; - m_mc.collect_vars(vars); - out << "(atoms"; - unsigned sz = m_var2expr.size(); - for (unsigned i = 0; i < sz; i++) { - if (vars.contains(i)) { - out << "\n (" << i << "\n " << mk_ismt2_pp(m_var2expr.get(i), m(), 2) << ")"; - } - } - out << ")\n"; - m_fmc->display(out); - out << ")\n"; - } - }; + typedef mc sat_model_converter; ast_manager & m; expr_ref_vector m_lit2expr; @@ -692,91 +1086,155 @@ struct sat2goal::imp { throw tactic_exception(TACTIC_MAX_MEMORY_MSG); } - void init_lit2expr(sat::solver const & s, atom2bool_var const & map, model_converter_ref & mc, bool produce_models) { - ref _mc; - if (produce_models) - _mc = alloc(sat_model_converter, m, s); - unsigned num_vars = s.num_vars(); - m_lit2expr.resize(num_vars * 2); - map.mk_inv(m_lit2expr); - sort * b = m.mk_bool_sort(); - for (sat::bool_var v = 0; v < num_vars; v++) { - checkpoint(); - sat::literal l(v, false); - if (m_lit2expr.get(l.index()) == 0) { - SASSERT(m_lit2expr.get((~l).index()) == 0); - app * aux = m.mk_fresh_const(0, b); - if (_mc) - _mc->insert(aux, true); - m_lit2expr.set(l.index(), aux); - m_lit2expr.set((~l).index(), m.mk_not(aux)); + expr * lit2expr(ref& mc, sat::literal l) { + if (!m_lit2expr.get(l.index())) { + 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()); + if (mc) { + mc->insert(l.var(), aux, true); + } } - else { - if (_mc) - _mc->insert(m_lit2expr.get(l.index()), false); - SASSERT(m_lit2expr.get((~l).index()) != 0); - } - } - mc = _mc.get(); - } - - expr * lit2expr(sat::literal l) { + sat::literal lit(l.var(), false); + m_lit2expr.set(lit.index(), aux); + m_lit2expr.set((~lit).index(), m.mk_not(aux)); + } return m_lit2expr.get(l.index()); } - void assert_clauses(sat::clause * const * begin, sat::clause * const * end, goal & r) { + void assert_pb(ref& mc, goal& r, sat::ba_solver::pb const& p) { + pb_util pb(m); ptr_buffer lits; - for (sat::clause * const * it = begin; it != end; it++) { + vector coeffs; + for (auto const& wl : p) { + lits.push_back(lit2expr(mc, wl.second)); + coeffs.push_back(rational(wl.first)); + } + rational k(p.k()); + expr_ref fml(pb.mk_ge(p.size(), coeffs.c_ptr(), lits.c_ptr(), k), m); + + if (p.lit() != sat::null_literal) { + fml = m.mk_eq(lit2expr(mc, p.lit()), fml); + } + r.assert_expr(fml); + } + + void assert_card(ref& mc, goal& r, sat::ba_solver::card const& c) { + pb_util pb(m); + ptr_buffer lits; + for (sat::literal l : c) { + lits.push_back(lit2expr(mc, l)); + } + expr_ref fml(pb.mk_at_least_k(c.size(), lits.c_ptr(), c.k()), m); + + if (c.lit() != sat::null_literal) { + fml = m.mk_eq(lit2expr(mc, c.lit()), fml); + } + r.assert_expr(fml); + } + + void assert_xor(ref& mc, goal & r, sat::ba_solver::xr const& x) { + ptr_buffer lits; + for (sat::literal l : x) { + lits.push_back(lit2expr(mc, l)); + } + expr_ref fml(m.mk_xor(x.size(), lits.c_ptr()), m); + + if (x.lit() != sat::null_literal) { + fml = m.mk_eq(lit2expr(mc, x.lit()), fml); + } + r.assert_expr(fml); + } + + void assert_clauses(ref& mc, sat::solver const & s, sat::clause_vector const& clauses, goal & r, bool asserted) { + ptr_buffer lits; + for (sat::clause* cp : clauses) { checkpoint(); lits.reset(); - sat::clause const & c = *(*it); - unsigned sz = c.size(); - for (unsigned i = 0; i < sz; i++) { - lits.push_back(lit2expr(c[i])); + sat::clause const & c = *cp; + if (asserted || m_learned || c.glue() <= s.get_config().m_gc_small_lbd) { + for (sat::literal l : c) { + lits.push_back(lit2expr(mc, l)); + } + r.assert_expr(m.mk_or(lits.size(), lits.c_ptr())); } - r.assert_expr(m.mk_or(lits.size(), lits.c_ptr())); } } - void operator()(sat::solver const & s, atom2bool_var const & map, goal & r, model_converter_ref & mc) { + sat::ba_solver* get_ba_solver(sat::solver const& s) { + return dynamic_cast(s.get_extension()); + } + + void operator()(sat::solver & s, atom2bool_var const & map, goal & r, ref & mc) { if (s.inconsistent()) { r.assert_expr(m.mk_false()); return; } - init_lit2expr(s, map, mc, r.models_enabled()); - // collect units - unsigned num_vars = s.num_vars(); - for (sat::bool_var v = 0; v < num_vars; v++) { - checkpoint(); - switch (s.value(v)) { - case l_true: - r.assert_expr(lit2expr(sat::literal(v, false))); - break; - case l_false: - r.assert_expr(lit2expr(sat::literal(v, true))); - break; - case l_undef: - break; - } + if (r.models_enabled() && !mc) { + mc = alloc(sat_model_converter, m); } + if (mc) mc->flush_smc(s, map); + m_lit2expr.resize(s.num_vars() * 2); + map.mk_inv(m_lit2expr); + // collect units + unsigned trail_sz = s.init_trail_size(); + for (unsigned i = 0; i < trail_sz; ++i) { + checkpoint(); + r.assert_expr(lit2expr(mc, s.trail_literal(i))); + } + // collect binary clauses svector bin_clauses; s.collect_bin_clauses(bin_clauses, m_learned); - svector::iterator it = bin_clauses.begin(); - svector::iterator end = bin_clauses.end(); - for (; it != end; ++it) { + for (sat::solver::bin_clause const& bc : bin_clauses) { checkpoint(); - r.assert_expr(m.mk_or(lit2expr(it->first), lit2expr(it->second))); + r.assert_expr(m.mk_or(lit2expr(mc, bc.first), lit2expr(mc, bc.second))); } // collect clauses - assert_clauses(s.begin_clauses(), s.end_clauses(), r); - if (m_learned) - assert_clauses(s.begin_learned(), s.end_learned(), r); + assert_clauses(mc, s, s.clauses(), r, true); + + sat::ba_solver* ext = get_ba_solver(s); + if (ext) { + for (auto* c : ext->constraints()) { + switch (c->tag()) { + case sat::ba_solver::card_t: + assert_card(mc, r, c->to_card()); + break; + case sat::ba_solver::pb_t: + assert_pb(mc, r, c->to_pb()); + break; + case sat::ba_solver::xr_t: + assert_xor(mc, r, c->to_xr()); + break; + } + } + } + } + + void add_clause(ref& mc, sat::literal_vector const& lits, expr_ref_vector& lemmas) { + expr_ref_vector lemma(m); + for (sat::literal l : lits) { + expr* e = lit2expr(mc, l); + if (!e) return; + lemma.push_back(e); + } + lemmas.push_back(mk_or(lemma)); + } + + void add_clause(ref& mc, sat::clause const& c, expr_ref_vector& lemmas) { + expr_ref_vector lemma(m); + for (sat::literal l : c) { + expr* e = lit2expr(mc, l); + if (!e) return; + lemma.push_back(e); + } + lemmas.push_back(mk_or(lemma)); } }; -sat2goal::sat2goal():m_imp(0) { +sat2goal::sat2goal():m_imp(nullptr) { } void sat2goal::collect_param_descrs(param_descrs & r) { @@ -790,14 +1248,15 @@ struct sat2goal::scoped_set_imp { m_owner->m_imp = i; } ~scoped_set_imp() { - m_owner->m_imp = 0; + m_owner->m_imp = nullptr; } }; -void sat2goal::operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p, - goal & g, model_converter_ref & mc) { +void sat2goal::operator()(sat::solver & t, atom2bool_var const & m, params_ref const & p, + goal & g, ref & mc) { imp proc(g.m(), p); scoped_set_imp set(this, &proc); proc(t, m, g, mc); } + diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index 199d79f9d..d86b2d7e4 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.h @@ -32,6 +32,7 @@ Notes: #include "tactic/goal.h" #include "sat/sat_solver.h" #include "tactic/model_converter.h" +#include "tactic/generic_model_converter.h" #include "sat/tactic/atom2bool_var.h" class goal2sat { @@ -50,6 +51,7 @@ public: static bool has_unsupported_bool(goal const & s); + /** \brief "Compile" the goal into the given sat solver. Store a mapping from atoms to boolean variables into m. @@ -60,7 +62,7 @@ public: \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), an unsupported operator is found, or memory consumption limit is reached (set with param :max-memory). */ - void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false); + void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m, dep2asm_map& dep2asm, bool default_external = false, bool is_lemma = false); void get_interpreted_atoms(expr_ref_vector& atoms); @@ -72,8 +74,35 @@ class sat2goal { imp * m_imp; struct scoped_set_imp; public: + + class mc : public model_converter { + ast_manager& m; + sat::model_converter m_smc; + generic_model_converter_ref m_gmc; + app_ref_vector m_var2expr; + + // flushes from m_smc to m_gmc; + void flush_gmc(); + + public: + mc(ast_manager& m); + virtual ~mc() {} + // 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; + void operator()(expr_ref& fml) override; + model_converter* translate(ast_translation& translator) override; + void set_env(ast_pp_util* visitor) override; + void display(std::ostream& out) override; + void get_units(obj_map& units) override; + app* var2expr(sat::bool_var v) const { return m_var2expr.get(v, nullptr); } + expr_ref lit2expr(sat::literal l); + void insert(sat::bool_var v, app * atom, bool aux); + }; + sat2goal(); + static void collect_param_descrs(param_descrs & r); /** @@ -84,7 +113,7 @@ public: \warning conversion throws a tactic_exception, if it is interrupted (by set_cancel), or memory consumption limit is reached (set with param :max-memory). */ - void operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p, goal & s, model_converter_ref & mc); + void operator()(sat::solver & t, atom2bool_var const & m, params_ref const & p, goal & s, ref & mc); }; diff --git a/src/sat/tactic/sat_tactic.cpp b/src/sat/tactic/sat_tactic.cpp index 4a6171f70..149a4e853 100644 --- a/src/sat/tactic/sat_tactic.cpp +++ b/src/sat/tactic/sat_tactic.cpp @@ -18,9 +18,10 @@ Notes: --*/ #include "ast/ast_pp.h" #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" #include "sat/tactic/goal2sat.h" #include "sat/sat_solver.h" +#include "solver/parallel_tactic.h" +#include "solver/parallel_params.hpp" #include "model/model_v2_pp.h" class sat_tactic : public tactic { @@ -34,17 +35,13 @@ class sat_tactic : public tactic { imp(ast_manager & _m, params_ref const & p): m(_m), - m_solver(p, m.limit(), 0), + m_solver(p, m.limit()), m_params(p) { SASSERT(!m.proofs_enabled()); } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; + goal_ref_buffer & result) { fail_if_proof_generation("sat", g); bool produce_models = g->models_enabled(); bool produce_core = g->unsat_core_enabled(); @@ -68,13 +65,8 @@ class sat_tactic : public tactic { TRACE("sat_dimacs", m_solver.display_dimacs(tout);); dep2assumptions(dep2asm, assumptions); lbool r = m_solver.check(assumptions.size(), assumptions.c_ptr()); - if (r == l_undef && m_solver.get_config().m_dimacs_display) { - for (auto const& kv : map) { - std::cout << "c " << kv.m_value << " " << mk_pp(kv.m_key, g->m()) << "\n"; - } - } if (r == l_false) { - expr_dependency * lcore = 0; + expr_dependency * lcore = nullptr; if (produce_core) { sat::literal_vector const& ucore = m_solver.get_core(); u_map asm2dep; @@ -85,7 +77,7 @@ class sat_tactic : public tactic { lcore = m.mk_join(lcore, m.mk_leaf(dep)); } } - g->assert_expr(m.mk_false(), 0, lcore); + g->assert_expr(m.mk_false(), nullptr, lcore); } else if (r == l_true && !map.interpreted_atoms()) { // register model @@ -109,7 +101,7 @@ class sat_tactic : public tactic { } } TRACE("sat_tactic", model_v2_pp(tout, *md);); - mc = model2model_converter(md.get()); + g->add(model2model_converter(md.get())); } } else { @@ -118,7 +110,9 @@ class sat_tactic : public tactic { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "\"formula constains interpreted atoms, recovering formula from sat solver...\"\n";); #endif m_solver.pop_to_base_level(); + ref mc; m_sat2goal(m_solver, map, m_params, *(g.get()), mc); + g->add(mc.get()); } g->inc_depth(); result.push_back(g.get()); @@ -148,7 +142,7 @@ class sat_tactic : public tactic { } ~scoped_set_imp() { - m_owner->m_imp = 0; + m_owner->m_imp = nullptr; } }; @@ -158,37 +152,34 @@ class sat_tactic : public tactic { public: sat_tactic(ast_manager & m, params_ref const & p): - m_imp(0), + m_imp(nullptr), m_params(p) { } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(sat_tactic, m, m_params); } - virtual ~sat_tactic() { + ~sat_tactic() override { SASSERT(m_imp == 0); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { goal2sat::collect_param_descrs(r); sat2goal::collect_param_descrs(r); sat::solver::collect_param_descrs(r); } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) override { imp proc(g->m(), m_params); scoped_set_imp set(this, &proc); try { - proc(g, result, mc, pc, core); + proc(g, result); proc.m_solver.collect_statistics(m_stats); } catch (sat::solver_exception & ex) { @@ -198,15 +189,15 @@ public: TRACE("sat_stats", m_stats.display_smt2(tout);); } - virtual void cleanup() { + void cleanup() override { SASSERT(m_imp == 0); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.copy(m_stats); } - virtual void reset_statistics() { + void reset_statistics() override { m_stats.reset(); } @@ -226,3 +217,4 @@ tactic * mk_sat_preprocessor_tactic(ast_manager & m, params_ref const & p) { return t; } + diff --git a/src/shell/datalog_frontend.cpp b/src/shell/datalog_frontend.cpp index b2b181977..6596cc3b7 100644 --- a/src/shell/datalog_frontend.cpp +++ b/src/shell/datalog_frontend.cpp @@ -41,7 +41,7 @@ static stopwatch g_overall_time; static stopwatch g_piece_timer; static unsigned t_parsing = 0; -static datalog::context * g_ctx = 0; +static datalog::context * g_ctx = nullptr; static datalog::rule_set * g_orig_rules; static datalog::instruction_block * g_code; static datalog::execution_context * g_ectx; @@ -212,7 +212,7 @@ unsigned read_datalog(char const * file) { if (early_termination) { IF_VERBOSE(1, verbose_stream() << "restarting saturation\n";); - uint64 new_timeout = static_cast(timeout)*ctx.initial_restart_timeout(); + uint64_t new_timeout = static_cast(timeout)*ctx.initial_restart_timeout(); if(new_timeout>UINT_MAX) { timeout=UINT_MAX; } @@ -246,7 +246,7 @@ unsigned read_datalog(char const * file) { false); } - catch (out_of_memory_error) { + catch (const out_of_memory_error &) { std::cout << "\n\nOUT OF MEMORY!\n\n"; display_statistics( std::cout, @@ -257,7 +257,7 @@ unsigned read_datalog(char const * file) { true); return ERR_MEMOUT; } - register_on_timeout_proc(0); + register_on_timeout_proc(nullptr); return 0; } diff --git a/src/shell/dimacs_frontend.cpp b/src/shell/dimacs_frontend.cpp index 999574f1e..3a2af72cf 100644 --- a/src/shell/dimacs_frontend.cpp +++ b/src/shell/dimacs_frontend.cpp @@ -21,12 +21,12 @@ Revision History: #include #include "util/timeout.h" #include "util/rlimit.h" +#include "util/gparams.h" #include "sat/dimacs.h" #include "sat/sat_solver.h" -#include "util/gparams.h" extern bool g_display_statistics; -static sat::solver * g_solver = 0; +static sat::solver * g_solver = nullptr; static clock_t g_start_time; static void display_statistics() { @@ -126,6 +126,41 @@ static void track_clauses(sat::solver const& src, } } +void verify_solution(char const * file_name) { + params_ref p = gparams::get_module("sat"); + p.set_bool("produce_models", true); + reslimit limit; + sat::solver solver(p, limit); + std::ifstream in(file_name); + if (in.bad() || in.fail()) { + std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; + exit(ERR_OPEN_FILE); + } + parse_dimacs(in, solver); + + sat::model const & m = g_solver->get_model(); + for (unsigned i = 1; i < m.size(); i++) { + sat::literal lit(i, false); + switch (m[i]) { + case l_false: lit.neg(); break; + case l_undef: break; + case l_true: break; + } + solver.mk_clause(1, &lit); + } + lbool r = solver.check(); + switch (r) { + case l_false: + std::cout << "model checking failed\n"; + break; + case l_true: + std::cout << "model validated\n"; + break; + default: + std::cout << "inconclusive model\n"; + break; + } +} unsigned read_dimacs(char const * file_name) { g_start_time = clock(); @@ -134,7 +169,7 @@ unsigned read_dimacs(char const * file_name) { params_ref p = gparams::get_module("sat"); p.set_bool("produce_models", true); reslimit limit; - sat::solver solver(p, limit, 0); + sat::solver solver(p, limit); g_solver = &solver; if (file_name) { @@ -152,7 +187,7 @@ unsigned read_dimacs(char const * file_name) { lbool r; vector tracking_clauses; - sat::solver solver2(p, limit, 0); + sat::solver solver2(p, limit); if (p.get_bool("dimacs.core", false)) { g_solver = &solver2; sat::literal_vector assumptions; @@ -165,6 +200,7 @@ unsigned read_dimacs(char const * file_name) { switch (r) { case l_true: std::cout << "sat\n"; + if (file_name) verify_solution(file_name); display_model(*g_solver); break; case l_undef: diff --git a/src/shell/lp_frontend.cpp b/src/shell/lp_frontend.cpp index 95f9e1eb3..ad9bcbd54 100644 --- a/src/shell/lp_frontend.cpp +++ b/src/shell/lp_frontend.cpp @@ -17,7 +17,7 @@ Author: #include "util/gparams.h" #include -static lp::lp_solver* g_solver = 0; +static lp::lp_solver* g_solver = nullptr; static void display_statistics() { if (g_solver && g_solver->settings().print_statistics) { @@ -49,14 +49,14 @@ struct front_end_resource_limit : public lp::lp_resource_limit { m_reslim(lim) {} - virtual bool get_cancel_flag() { return !m_reslim.inc(); } + bool get_cancel_flag() override { return !m_reslim.inc(); } }; void run_solver(lp_params & params, char const * mps_file_name) { reslimit rlim; - unsigned timeout = gparams::get().get_uint("timeout", 0); - unsigned rlimit = gparams::get().get_uint("rlimit", 0); + unsigned timeout = gparams::get_ref().get_uint("timeout", 0); + unsigned rlimit = gparams::get_ref().get_uint("rlimit", 0); front_end_resource_limit lp_limit(rlim); scoped_rlimit _rlimit(rlim, rlimit); @@ -95,8 +95,8 @@ void run_solver(lp_params & params, char const * mps_file_name) { // #pragma omp critical (g_display_stats) { display_statistics(); - register_on_timeout_proc(0); - g_solver = 0; + register_on_timeout_proc(nullptr); + g_solver = nullptr; } delete solver; } diff --git a/src/shell/main.cpp b/src/shell/main.cpp index f0e640d66..d367b8ec6 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -33,15 +33,16 @@ Revision History: #include "util/timeout.h" #include "util/z3_exception.h" #include "util/error_codes.h" +#include "util/file_path.h" #include "util/gparams.h" #include "util/env_params.h" #include "util/file_path.h" #include "shell/lp_frontend.h" -typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG, IN_MPS } input_kind; +typedef enum { IN_UNSPECIFIED, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_LP, IN_Z3_LOG, IN_MPS } input_kind; std::string g_aux_input_file; -char const * g_input_file = 0; +char const * g_input_file = nullptr; bool g_standard_input = false; input_kind g_input_kind = IN_UNSPECIFIED; bool g_display_statistics = false; @@ -71,10 +72,12 @@ void display_usage() { std::cout << "]. (C) Copyright 2006-2016 Microsoft Corp.\n"; std::cout << "Usage: z3 [options] [-file:]file\n"; std::cout << "\nInput format:\n"; - std::cout << " -smt use parser for SMT input format.\n"; std::cout << " -smt2 use parser for SMT 2 input format.\n"; std::cout << " -dl use parser for Datalog input format.\n"; std::cout << " -dimacs use parser for DIMACS input format.\n"; + std::cout << " -wcnf use parser for Weighted CNF DIMACS input format.\n"; + std::cout << " -opb use parser for PB optimization input format.\n"; + std::cout << " -lp use parser for a modest subset of CPLEX LP input format.\n"; std::cout << " -log use parser for Z3 log input format.\n"; std::cout << " -in read formula from standard input.\n"; std::cout << "\nMiscellaneous:\n"; @@ -113,7 +116,7 @@ void display_usage() { void parse_cmd_line_args(int argc, char ** argv) { int i = 1; - char * eq_pos = 0; + char * eq_pos = nullptr; while (i < argc) { char * arg = argv[i]; @@ -145,7 +148,7 @@ void parse_cmd_line_args(int argc, char ** argv) { // allow names such as --help if (*opt_name == '-') opt_name++; - char * opt_arg = 0; + char * opt_arg = nullptr; char * colon = strchr(arg, ':'); if (colon) { opt_arg = colon + 1; @@ -185,9 +188,12 @@ void parse_cmd_line_args(int argc, char ** argv) { else if (strcmp(opt_name, "wcnf") == 0) { g_input_kind = IN_WCNF; } - else if (strcmp(opt_name, "bpo") == 0) { + else if (strcmp(opt_name, "pbo") == 0) { g_input_kind = IN_OPB; } + else if (strcmp(opt_name, "lp") == 0) { + g_input_kind = IN_LP; + } else if (strcmp(opt_name, "log") == 0) { g_input_kind = IN_Z3_LOG; } @@ -200,7 +206,7 @@ void parse_cmd_line_args(int argc, char ** argv) { 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, "file") == 0) { @@ -209,7 +215,7 @@ void parse_cmd_line_args(int argc, char ** argv) { else if (strcmp(opt_name, "T") == 0) { if (!opt_arg) error("option argument (-T:timeout) is missing."); - long tm = strtol(opt_arg, 0, 10); + long tm = strtol(opt_arg, nullptr, 10); set_timeout(tm * 1000); } else if (strcmp(opt_name, "t") == 0) { @@ -319,6 +325,9 @@ int STD_CALL main(int argc, char ** argv) { else if (strcmp(ext, "opb") == 0) { g_input_kind = IN_OPB; } + else if (strcmp(ext, "lp") == 0) { + g_input_kind = IN_LP; + } else if (strcmp(ext, "log") == 0) { g_input_kind = IN_Z3_LOG; } @@ -340,10 +349,13 @@ int STD_CALL main(int argc, char ** argv) { return_value = read_dimacs(g_input_file); break; case IN_WCNF: - return_value = parse_opt(g_input_file, true); + return_value = parse_opt(g_input_file, wcnf_t); break; case IN_OPB: - return_value = parse_opt(g_input_file, false); + return_value = parse_opt(g_input_file, opb_t); + break; + case IN_LP: + return_value = parse_opt(g_input_file, lp_t); break; case IN_DATALOG: read_datalog(g_input_file); diff --git a/src/shell/opt_frontend.cpp b/src/shell/opt_frontend.cpp index 8154cbde4..0a2a8f4a1 100644 --- a/src/shell/opt_frontend.cpp +++ b/src/shell/opt_frontend.cpp @@ -7,35 +7,46 @@ Copyright (c) 2015 Microsoft Corporation #include #include #include -#include "opt/opt_context.h" -#include "ast/ast_util.h" -#include "ast/arith_decl_plugin.h" #include "util/gparams.h" #include "util/timeout.h" +#include "util/cancel_eh.h" +#include "util/scoped_timer.h" +#include "ast/ast_util.h" +#include "ast/arith_decl_plugin.h" +#include "ast/ast_pp.h" #include "ast/reg_decl_plugins.h" +#include "model/model_smt2_pp.h" +#include "opt/opt_context.h" +#include "shell/opt_frontend.h" #include "opt/opt_parse.h" extern bool g_display_statistics; static bool g_first_interrupt = true; -static opt::context* g_opt = 0; +static opt::context* g_opt = nullptr; static double g_start_time = 0; static unsigned_vector g_handles; static void display_results() { - if (g_opt) { - for (unsigned i = 0; i < g_handles.size(); ++i) { - expr_ref lo = g_opt->get_lower(g_handles[i]); - expr_ref hi = g_opt->get_upper(g_handles[i]); - if (lo == hi) { - std::cout << " " << lo << "\n"; - } - else { - std::cout << " [" << lo << ":" << hi << "]\n"; - } - } - } + IF_VERBOSE(1, + if (g_opt) { + model_ref mdl; + g_opt->get_model(mdl); + if (mdl) { + model_smt2_pp(verbose_stream(), g_opt->get_manager(), *mdl, 0); + } + for (unsigned h : g_handles) { + expr_ref lo = g_opt->get_lower(h); + expr_ref hi = g_opt->get_upper(h); + if (lo == hi) { + std::cout << " " << lo << "\n"; + } + else { + std::cout << " [" << lo << ":" << hi << "]\n"; + } + } + }); } static void display_statistics() { @@ -73,41 +84,48 @@ static void on_timeout() { } } -static unsigned parse_opt(std::istream& in, bool is_wcnf) { +static unsigned parse_opt(std::istream& in, opt_format f) { ast_manager m; reg_decl_plugins(m); opt::context opt(m); g_opt = &opt; params_ref p = gparams::get_module("opt"); opt.updt_params(p); - if (is_wcnf) { + switch (f) { + case wcnf_t: parse_wcnf(opt, in, g_handles); - } - else { + break; + case opb_t: parse_opb(opt, in, g_handles); + break; + case lp_t: + parse_lp(opt, in, g_handles); + break; } try { + cancel_eh eh(m.limit()); + unsigned timeout = std::stoul(gparams::get_value("timeout")); + unsigned rlimit = std::stoi(gparams::get_value("rlimit")); + scoped_timer timer(timeout, &eh); + scoped_rlimit _rlimit(m.limit(), rlimit); lbool r = opt.optimize(); switch (r) { case l_true: std::cout << "sat\n"; break; case l_false: std::cout << "unsat\n"; break; case l_undef: std::cout << "unknown\n"; break; } - DEBUG_CODE( - if (false && r == l_true) { - model_ref mdl; - opt.get_model(mdl); - expr_ref_vector hard(m); - opt.get_hard_constraints(hard); - for (unsigned i = 0; i < hard.size(); ++i) { - std::cout << "validate: " << i << "\n"; - expr_ref tmp(m); - VERIFY(mdl->eval(hard[i].get(), tmp)); - if (!m.is_true(tmp)) { - std::cout << tmp << "\n"; - } + + if (r != l_false && gparams::get_ref().get_bool("model_validate", false)) { + model_ref mdl; + opt.get_model(mdl); + expr_ref_vector hard(m); + opt.get_hard_constraints(hard); + for (expr* h : hard) { + if (!mdl->is_true(h)) { + std::cout << mk_pp(h, m) << " evaluates to: " << (*mdl)(h) << "\n"; } - }); + } + } } catch (z3_exception & ex) { std::cerr << ex.msg() << "\n"; @@ -115,13 +133,13 @@ static unsigned parse_opt(std::istream& in, bool is_wcnf) { #pragma omp critical (g_display_stats) { display_statistics(); - register_on_timeout_proc(0); - g_opt = 0; + register_on_timeout_proc(nullptr); + g_opt = nullptr; } return 0; } -unsigned parse_opt(char const* file_name, bool is_wcnf) { +unsigned parse_opt(char const* file_name, opt_format f) { g_first_interrupt = true; g_start_time = static_cast(clock()); register_on_timeout_proc(on_timeout); @@ -132,10 +150,10 @@ unsigned parse_opt(char const* file_name, bool is_wcnf) { std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; exit(ERR_OPEN_FILE); } - return parse_opt(in, is_wcnf); + return parse_opt(in, f); } else { - return parse_opt(std::cin, is_wcnf); + return parse_opt(std::cin, f); } } diff --git a/src/shell/opt_frontend.h b/src/shell/opt_frontend.h index 65ec606a5..1266ed76b 100644 --- a/src/shell/opt_frontend.h +++ b/src/shell/opt_frontend.h @@ -13,7 +13,9 @@ Author: #ifndef OPT_FRONTEND_H_ #define OPT_FRONTEND_H_ -unsigned parse_opt(char const* file_name, bool is_wcnf); +enum opt_format { opb_t, wcnf_t, lp_t }; + +unsigned parse_opt(char const* file_name, opt_format f); #endif /* OPT_FRONTEND_H_ */ diff --git a/src/shell/smtlib_frontend.cpp b/src/shell/smtlib_frontend.cpp index 8c716a81a..7218558fa 100644 --- a/src/shell/smtlib_frontend.cpp +++ b/src/shell/smtlib_frontend.cpp @@ -34,7 +34,7 @@ Revision History: extern bool g_display_statistics; static clock_t g_start_time; -static cmd_context * g_cmd_context = 0; +static cmd_context * g_cmd_context = nullptr; static void display_statistics() { clock_t end_time = clock(); @@ -102,7 +102,7 @@ unsigned read_smtlib2_commands(char const * file_name) { #pragma omp critical (g_display_stats) { display_statistics(); - g_cmd_context = 0; + g_cmd_context = nullptr; } return result ? 0 : 1; } diff --git a/src/smt/CMakeLists.txt b/src/smt/CMakeLists.txt index e102bd28b..fb1997fb4 100644 --- a/src/smt/CMakeLists.txt +++ b/src/smt/CMakeLists.txt @@ -70,6 +70,7 @@ z3_add_component(smt euclid fpa grobner + nlsat lp macros normal_forms diff --git a/src/smt/arith_eq_adapter.cpp b/src/smt/arith_eq_adapter.cpp index 307bdc671..593eb1d71 100644 --- a/src/smt/arith_eq_adapter.cpp +++ b/src/smt/arith_eq_adapter.cpp @@ -42,7 +42,7 @@ namespace smt { m_n2(n2) { } - virtual void undo(context & ctx) { + void undo(context & ctx) override { m_already_processed.erase(m_n1, m_n2); TRACE("arith_eq_adapter_profile", tout << "del #" << m_n1->get_owner_id() << " #" << m_n2->get_owner_id() << "\n";); } @@ -67,9 +67,9 @@ namespace smt { m_ge(ge) { } - virtual ~arith_eq_relevancy_eh() {} + ~arith_eq_relevancy_eh() override {} - virtual void operator()(relevancy_propagator & rp) { + void operator()(relevancy_propagator & rp) override { if (!rp.is_relevant(m_n1)) return; if (!rp.is_relevant(m_n2)) @@ -154,8 +154,8 @@ namespace smt { // Requires that the theory arithmetic internalizer accept non simplified terms of the form t1 - t2 // if t1 and t2 already have slacks (theory variables) associated with them. // It also accepts terms with repeated variables (Issue #429). - app * le = 0; - app * ge = 0; + app * le = nullptr; + app * ge = nullptr; if (m_util.is_numeral(t1)) std::swap(t1, t2); if (m_util.is_numeral(t2)) { diff --git a/src/smt/arith_eq_adapter.h b/src/smt/arith_eq_adapter.h index 4a8a293e3..42aae6821 100644 --- a/src/smt/arith_eq_adapter.h +++ b/src/smt/arith_eq_adapter.h @@ -53,7 +53,7 @@ namespace smt { expr * m_t1_eq_t2; expr * m_le; expr * m_ge; - data():m_t1_eq_t2(0), m_le(0), m_ge(0) {} + data():m_t1_eq_t2(nullptr), m_le(nullptr), m_ge(nullptr) {} data(expr * t1_eq_t2, expr * le, expr * ge):m_t1_eq_t2(t1_eq_t2), m_le(le), m_ge(ge) {} }; diff --git a/src/smt/asserted_formulas.cpp b/src/smt/asserted_formulas.cpp index 56600266a..d364404da 100644 --- a/src/smt/asserted_formulas.cpp +++ b/src/smt/asserted_formulas.cpp @@ -27,8 +27,9 @@ Revision History: #include "ast/macros/quasi_macros.h" #include "smt/asserted_formulas.h" -asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): +asserted_formulas::asserted_formulas(ast_manager & m, smt_params & sp, params_ref const& p): m(m), + m_smt_params(sp), m_params(p), m_rewriter(m), m_substitution(m), @@ -46,7 +47,6 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): m_refine_inj_axiom(*this), m_max_bv_sharing_fn(*this), m_elim_term_ite(*this), - m_pull_cheap_ite_trees(*this), m_pull_nested_quantifiers(*this), m_elim_bvs_from_quantifiers(*this), m_cheap_quant_fourier_motzkin(*this), @@ -66,20 +66,20 @@ asserted_formulas::asserted_formulas(ast_manager & m, smt_params & p): } void asserted_formulas::setup() { - switch (m_params.m_lift_ite) { + switch (m_smt_params.m_lift_ite) { case LI_FULL: - m_params.m_ng_lift_ite = LI_NONE; + m_smt_params.m_ng_lift_ite = LI_NONE; break; case LI_CONSERVATIVE: - if (m_params.m_ng_lift_ite == LI_CONSERVATIVE) - m_params.m_ng_lift_ite = LI_NONE; + if (m_smt_params.m_ng_lift_ite == LI_CONSERVATIVE) + m_smt_params.m_ng_lift_ite = LI_NONE; break; default: break; } - if (m_params.m_relevancy_lvl == 0) - m_params.m_relevancy_lemma = false; + if (m_smt_params.m_relevancy_lvl == 0) + m_smt_params.m_relevancy_lemma = false; } @@ -90,7 +90,7 @@ void asserted_formulas::push_assertion(expr * e, proof * pr, vectorget_num_args(); ++i) { expr* arg = to_app(e)->get_arg(i); - proof_ref _pr(m.proofs_enabled() ? m.mk_and_elim(pr, i) : 0, m); + proof_ref _pr(m.proofs_enabled() ? m.mk_and_elim(pr, i) : nullptr, m); push_assertion(arg, _pr, result); } } else if (m.is_not(e, e1) && m.is_or(e1)) { for (unsigned i = 0; i < to_app(e1)->get_num_args(); ++i) { expr* arg = to_app(e1)->get_arg(i); - proof_ref _pr(m.proofs_enabled() ? m.mk_not_or_elim(pr, i) : 0, m); + proof_ref _pr(m.proofs_enabled() ? m.mk_not_or_elim(pr, i) : nullptr, m); expr_ref narg(mk_not(m, arg), m); push_assertion(narg, _pr, result); } @@ -118,20 +118,24 @@ void asserted_formulas::push_assertion(expr * e, proof * pr, vector new_fmls; @@ -375,7 +379,7 @@ void asserted_formulas::nnf_cnf() { unsigned sz2 = push_todo.size(); for (unsigned k = 0; k < sz2; k++) { expr * n = push_todo.get(k); - pr = 0; + pr = nullptr; m_rewriter(n, r1, pr1); CASSERT("well_sorted",is_well_sorted(m, r1)); if (canceled()) { @@ -416,7 +420,7 @@ void asserted_formulas::simplify_fmls::operator()() { void asserted_formulas::reduce_and_solve() { - IF_IVERBOSE(10, verbose_stream() << "(smt.reducing)\n";); + IF_VERBOSE(10, verbose_stream() << "(smt.reducing)\n";); flush_cache(); // collect garbage m_reduce_asserted_formulas(); } @@ -496,7 +500,8 @@ unsigned asserted_formulas::propagate_values(unsigned i) { void asserted_formulas::update_substitution(expr* n, proof* pr) { expr* lhs, *rhs, *n1; - if (is_ground(n) && (m.is_eq(n, lhs, rhs) || m.is_iff(n, lhs, rhs))) { + proof_ref pr1(m); + if (is_ground(n) && m.is_eq(n, lhs, rhs)) { compute_depth(lhs); compute_depth(rhs); if (is_gt(lhs, rhs)) { @@ -506,13 +511,13 @@ void asserted_formulas::update_substitution(expr* n, proof* pr) { } if (is_gt(rhs, lhs)) { TRACE("propagate_values", tout << "insert " << mk_pp(rhs, m) << " -> " << mk_pp(lhs, m) << "\n";); - m_scoped_substitution.insert(rhs, lhs, m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr); + pr1 = m.proofs_enabled() ? m.mk_symmetry(pr) : nullptr; + m_scoped_substitution.insert(rhs, lhs, pr1); return; } TRACE("propagate_values", tout << "incompatible " << mk_pp(n, m) << "\n";); } - proof_ref pr1(m); - if (m.is_not(n, n1)) { + if (m.is_not(n, n1)) { pr1 = m.proofs_enabled() ? m.mk_iff_false(pr) : nullptr; m_scoped_substitution.insert(n1, m.mk_false(), pr1); } @@ -522,6 +527,7 @@ void asserted_formulas::update_substitution(expr* n, proof* pr) { } } + /** \brief implement a Knuth-Bendix ordering on expressions. */ @@ -597,15 +603,15 @@ void asserted_formulas::compute_depth(expr* e) { proof * asserted_formulas::get_inconsistency_proof() const { if (!inconsistent()) - return 0; + return nullptr; if (!m.proofs_enabled()) - return 0; + return nullptr; for (justified_expr const& j : m_formulas) { if (m.is_false(j.get_fml())) return j.get_proof(); } UNREACHABLE(); - return 0; + return nullptr; } void asserted_formulas::refine_inj_axiom_fn::simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { @@ -627,9 +633,9 @@ unsigned asserted_formulas::get_total_size() const { return r; } + #ifdef Z3DEBUG void pp(asserted_formulas & f) { f.display(std::cout); } #endif - diff --git a/src/smt/asserted_formulas.h b/src/smt/asserted_formulas.h index 88c9e13a7..9c82b1314 100644 --- a/src/smt/asserted_formulas.h +++ b/src/smt/asserted_formulas.h @@ -26,7 +26,6 @@ Revision History: #include "ast/rewriter/bit2int.h" #include "ast/rewriter/maximize_ac_sharing.h" #include "ast/rewriter/distribute_forall.h" -#include "ast/rewriter/pull_ite_tree.h" #include "ast/rewriter/push_app_ite.h" #include "ast/rewriter/inj_axiom.h" #include "ast/rewriter/bv_elim.h" @@ -44,7 +43,8 @@ Revision History: class asserted_formulas { ast_manager & m; - smt_params & m_params; + smt_params & m_smt_params; + params_ref m_params; th_rewriter m_rewriter; expr_substitution m_substitution; scoped_expr_substitution m_scoped_substitution; @@ -82,80 +82,80 @@ class asserted_formulas { class reduce_asserted_formulas_fn : public simplify_fmls { public: reduce_asserted_formulas_fn(asserted_formulas& af): simplify_fmls(af, "reduce-asserted") {} - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { af.m_rewriter(j.get_fml(), n, p); } + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { af.m_rewriter(j.get_fml(), n, p); } }; class find_macros_fn : public simplify_fmls { public: find_macros_fn(asserted_formulas& af): simplify_fmls(af, "find-macros") {} - virtual void operator()() { af.find_macros_core(); } - virtual bool should_apply() const { return af.m_params.m_macro_finder && af.has_quantifiers(); } - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); } + void operator()() override { af.find_macros_core(); } + bool should_apply() const override { return af.m_smt_params.m_macro_finder && af.has_quantifiers(); } + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { UNREACHABLE(); } }; class apply_quasi_macros_fn : public simplify_fmls { public: apply_quasi_macros_fn(asserted_formulas& af): simplify_fmls(af, "find-quasi-macros") {} - virtual void operator()() { af.apply_quasi_macros(); } - virtual bool should_apply() const { return af.m_params.m_quasi_macros && af.has_quantifiers(); } - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); } + void operator()() override { af.apply_quasi_macros(); } + bool should_apply() const override { return af.m_smt_params.m_quasi_macros && af.has_quantifiers(); } + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { UNREACHABLE(); } }; class nnf_cnf_fn : public simplify_fmls { public: nnf_cnf_fn(asserted_formulas& af): simplify_fmls(af, "nnf-cnf") {} - virtual void operator()() { af.nnf_cnf(); } - virtual bool should_apply() const { return af.m_params.m_nnf_cnf || (af.m_params.m_mbqi && af.has_quantifiers()); } - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); } + void operator()() override { af.nnf_cnf(); } + bool should_apply() const override { return af.m_smt_params.m_nnf_cnf || (af.m_smt_params.m_mbqi && af.has_quantifiers()); } + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { UNREACHABLE(); } }; class propagate_values_fn : public simplify_fmls { public: propagate_values_fn(asserted_formulas& af): simplify_fmls(af, "propagate-values") {} - virtual void operator()() { af.propagate_values(); } - virtual bool should_apply() const { return af.m_params.m_propagate_values; } - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { UNREACHABLE(); } + void operator()() override { af.propagate_values(); } + bool should_apply() const override { return af.m_smt_params.m_propagate_values; } + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { UNREACHABLE(); } }; class distribute_forall_fn : public simplify_fmls { distribute_forall m_functor; public: distribute_forall_fn(asserted_formulas& af): simplify_fmls(af, "distribute-forall"), m_functor(af.m) {} - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { m_functor(j.get_fml(), n); } - virtual bool should_apply() const { return af.m_params.m_distribute_forall && af.has_quantifiers(); } - virtual void post_op() { af.reduce_and_solve(); TRACE("asserted_formulas", af.display(tout);); } + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { m_functor(j.get_fml(), n); } + bool should_apply() const override { return af.m_smt_params.m_distribute_forall && af.has_quantifiers(); } + void post_op() override { af.reduce_and_solve(); TRACE("asserted_formulas", af.display(tout);); } }; class pattern_inference_fn : public simplify_fmls { pattern_inference_rw m_infer; public: - pattern_inference_fn(asserted_formulas& af): simplify_fmls(af, "pattern-inference"), m_infer(af.m, af.m_params) {} - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { m_infer(j.get_fml(), n, p); } - virtual bool should_apply() const { return af.m_params.m_ematching && af.has_quantifiers(); } + pattern_inference_fn(asserted_formulas& af): simplify_fmls(af, "pattern-inference"), m_infer(af.m, af.m_smt_params) {} + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { m_infer(j.get_fml(), n, p); } + bool should_apply() const override { return af.m_smt_params.m_ematching && af.has_quantifiers(); } }; class refine_inj_axiom_fn : public simplify_fmls { public: refine_inj_axiom_fn(asserted_formulas& af): simplify_fmls(af, "refine-injectivity") {} - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p); - virtual bool should_apply() const { return af.m_params.m_refine_inj_axiom && af.has_quantifiers(); } + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override; + bool should_apply() const override { return af.m_smt_params.m_refine_inj_axiom && af.has_quantifiers(); } }; class max_bv_sharing_fn : public simplify_fmls { public: max_bv_sharing_fn(asserted_formulas& af): simplify_fmls(af, "maximizing-bv-sharing") {} - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { af.m_bv_sharing(j.get_fml(), n, p); } - virtual bool should_apply() const { return af.m_params.m_max_bv_sharing; } - virtual void post_op() { af.m_reduce_asserted_formulas(); } + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { af.m_bv_sharing(j.get_fml(), n, p); } + bool should_apply() const override { return af.m_smt_params.m_max_bv_sharing; } + void post_op() override { af.m_reduce_asserted_formulas(); } }; class elim_term_ite_fn : public simplify_fmls { elim_term_ite_rw m_elim; public: elim_term_ite_fn(asserted_formulas& af): simplify_fmls(af, "elim-term-ite"), m_elim(af.m, af.m_defined_names) {} - virtual void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) { m_elim(j.get_fml(), n, p); } - virtual bool should_apply() const { return af.m_params.m_eliminate_term_ite && af.m_params.m_lift_ite != LI_FULL; } - virtual void post_op() { af.m_formulas.append(m_elim.new_defs()); af.reduce_and_solve(); m_elim.reset(); } + void simplify(justified_expr const& j, expr_ref& n, proof_ref& p) override { m_elim(j.get_fml(), n, p); } + bool should_apply() const override { return af.m_smt_params.m_eliminate_term_ite && af.m_smt_params.m_lift_ite != LI_FULL; } + void post_op() override { af.m_formulas.append(m_elim.new_defs()); af.reduce_and_solve(); m_elim.reset(); } }; #define MK_SIMPLIFIERA(NAME, FUNCTOR, MSG, APP, ARG, REDUCE) \ @@ -172,13 +172,12 @@ class asserted_formulas { #define MK_SIMPLIFIERF(NAME, FUNCTOR, MSG, APP, REDUCE) MK_SIMPLIFIERA(NAME, FUNCTOR, MSG, APP, (af.m), REDUCE) - MK_SIMPLIFIERF(pull_cheap_ite_trees, pull_cheap_ite_tree_rw, "pull-cheap-ite-trees", af.m_params.m_pull_cheap_ite_trees, false); - MK_SIMPLIFIERF(pull_nested_quantifiers, pull_nested_quant, "pull-nested-quantifiers", af.m_params.m_pull_nested_quantifiers && af.has_quantifiers(), false); - MK_SIMPLIFIERF(cheap_quant_fourier_motzkin, elim_bounds_rw, "cheap-fourier-motzkin", af.m_params.m_eliminate_bounds && af.has_quantifiers(), true); - MK_SIMPLIFIERF(elim_bvs_from_quantifiers, bv_elim_rw, "eliminate-bit-vectors-from-quantifiers", af.m_params.m_bb_quantifiers, true); - MK_SIMPLIFIERF(apply_bit2int, bit2int, "propagate-bit-vector-over-integers", af.m_params.m_simplify_bit2int, true); - MK_SIMPLIFIERA(lift_ite, push_app_ite_rw, "lift-ite", af.m_params.m_lift_ite != LI_NONE, (af.m, af.m_params.m_lift_ite == LI_CONSERVATIVE), true); - MK_SIMPLIFIERA(ng_lift_ite, ng_push_app_ite_rw, "lift-ite", af.m_params.m_ng_lift_ite != LI_NONE, (af.m, af.m_params.m_ng_lift_ite == LI_CONSERVATIVE), true); + MK_SIMPLIFIERF(pull_nested_quantifiers, pull_nested_quant, "pull-nested-quantifiers", af.m_smt_params.m_pull_nested_quantifiers && af.has_quantifiers(), false); + MK_SIMPLIFIERF(cheap_quant_fourier_motzkin, elim_bounds_rw, "cheap-fourier-motzkin", af.m_smt_params.m_eliminate_bounds && af.has_quantifiers(), true); + MK_SIMPLIFIERF(elim_bvs_from_quantifiers, bv_elim_rw, "eliminate-bit-vectors-from-quantifiers", af.m_smt_params.m_bb_quantifiers, true); + MK_SIMPLIFIERF(apply_bit2int, bit2int, "propagate-bit-vector-over-integers", af.m_smt_params.m_simplify_bit2int, true); + MK_SIMPLIFIERA(lift_ite, push_app_ite_rw, "lift-ite", af.m_smt_params.m_lift_ite != LI_NONE, (af.m, af.m_smt_params.m_lift_ite == LI_CONSERVATIVE), true); + MK_SIMPLIFIERA(ng_lift_ite, ng_push_app_ite_rw, "lift-ite", af.m_smt_params.m_ng_lift_ite != LI_NONE, (af.m, af.m_smt_params.m_ng_lift_ite == LI_CONSERVATIVE), true); reduce_asserted_formulas_fn m_reduce_asserted_formulas; @@ -187,7 +186,6 @@ class asserted_formulas { refine_inj_axiom_fn m_refine_inj_axiom; max_bv_sharing_fn m_max_bv_sharing_fn; elim_term_ite_fn m_elim_term_ite; - pull_cheap_ite_trees m_pull_cheap_ite_trees; pull_nested_quantifiers m_pull_nested_quantifiers; elim_bvs_from_quantifiers m_elim_bvs_from_quantifiers; cheap_quant_fourier_motzkin m_cheap_quant_fourier_motzkin; @@ -219,14 +217,14 @@ class asserted_formulas { bool is_gt(expr* lhs, expr* rhs); void compute_depth(expr* e); unsigned depth(expr* e) { return m_expr2depth[e]; } - bool pull_cheap_ite_trees(); void init(unsigned num_formulas, expr * const * formulas, proof * const * prs); public: - asserted_formulas(ast_manager & m, smt_params & p); + asserted_formulas(ast_manager & m, smt_params & smtp, params_ref const& p); ~asserted_formulas(); + void updt_params(params_ref const& p); bool has_quantifiers() const { return m_has_quantifiers; } void setup(); void assert_expr(expr * e, proof * in_pr); diff --git a/src/smt/cached_var_subst.cpp b/src/smt/cached_var_subst.cpp index 7c0997bc5..1f184c39c 100644 --- a/src/smt/cached_var_subst.cpp +++ b/src/smt/cached_var_subst.cpp @@ -44,7 +44,7 @@ void cached_var_subst::reset() { void cached_var_subst::operator()(quantifier * qa, unsigned num_bindings, smt::enode * const * bindings, expr_ref & result) { m_new_keys.reserve(num_bindings+1, 0); key * new_key = m_new_keys[num_bindings]; - if (new_key == 0) + if (new_key == nullptr) new_key = static_cast(m_region.allocate(sizeof(key) + sizeof(expr*)*num_bindings)); new_key->m_qa = qa; @@ -52,7 +52,7 @@ void cached_var_subst::operator()(quantifier * qa, unsigned num_bindings, smt::e for (unsigned i = 0; i < num_bindings; i++) new_key->m_bindings[i] = bindings[i]->get_owner(); - instances::entry * entry = m_instances.insert_if_not_there2(new_key, 0); + instances::entry * entry = m_instances.insert_if_not_there2(new_key, nullptr); if (entry->get_data().m_key != new_key) { SASSERT(entry->get_data().m_value != 0); // entry was already there diff --git a/src/smt/cost_evaluator.cpp b/src/smt/cost_evaluator.cpp index 94151f2b3..0719d0961 100644 --- a/src/smt/cost_evaluator.cpp +++ b/src/smt/cost_evaluator.cpp @@ -47,8 +47,7 @@ float cost_evaluator::eval(expr * f) const { return 1.0f; return 0.0f; case OP_ITE: return E(0) != 0.0f ? E(1) : E(2); - case OP_EQ: - case OP_IFF: return E(0) == E(1) ? 1.0f : 0.0f; + case OP_EQ: return E(0) == E(1) ? 1.0f : 0.0f; case OP_XOR: return E(0) != E(1) ? 1.0f : 0.0f; case OP_IMPLIES: if (E(0) == 0.0f) diff --git a/src/smt/dyn_ack.cpp b/src/smt/dyn_ack.cpp index baa8129bb..a006c9dd5 100644 --- a/src/smt/dyn_ack.cpp +++ b/src/smt/dyn_ack.cpp @@ -39,12 +39,12 @@ namespace smt { SASSERT(m_app1->get_id() < m_app2->get_id()); } - virtual char const * get_name() const { return "dyn-ack"; } + char const * get_name() const override { return "dyn-ack"; } - virtual void get_antecedents(conflict_resolution & cr) { + void get_antecedents(conflict_resolution & cr) override { } - virtual void display_debug_info(conflict_resolution & cr, std::ostream & out) { + void display_debug_info(conflict_resolution & cr, std::ostream & out) override { ast_manager & m = cr.get_manager(); out << "m_app1:\n" << mk_pp(m_app1, m) << "\n"; out << "m_app2:\n" << mk_pp(m_app2, m) << "\n"; @@ -70,7 +70,7 @@ namespace smt { } } - virtual proof * mk_proof(conflict_resolution & cr) { + proof * mk_proof(conflict_resolution & cr) override { ast_manager & m = cr.get_manager(); context & ctx = cr.get_context(); unsigned num_args = m_app1->get_num_args(); @@ -288,8 +288,8 @@ namespace smt { dyn_ack_clause_del_eh(dyn_ack_manager & m): m_manager(m) { } - virtual ~dyn_ack_clause_del_eh() {} - virtual void operator()(ast_manager & m, clause * cls) { + ~dyn_ack_clause_del_eh() override {} + void operator()(ast_manager & m, clause * cls) override { m_manager.del_clause_eh(cls); dealloc(this); } @@ -299,7 +299,7 @@ namespace smt { TRACE("dyn_ack", tout << "del_clause_eh: "; m_context.display_clause(tout, cls); tout << "\n";); m_context.m_stats.m_num_del_dyn_ack++; - app_pair p((app*)0,(app*)0); + app_pair p((app*)nullptr,(app*)nullptr); if (m_clause2app_pair.find(cls, p)) { SASSERT(p.first && p.second); m_instantiated.erase(p); @@ -371,7 +371,7 @@ namespace smt { lits.push_back(mk_eq(n1, n2)); clause_del_eh * del_eh = alloc(dyn_ack_clause_del_eh, *this); - justification * js = 0; + justification * js = nullptr; if (m_manager.proofs_enabled()) js = alloc(dyn_ack_justification, n1, n2); clause * cls = m_context.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh); @@ -423,7 +423,7 @@ namespace smt { lits.push_back(mk_eq(n1, n2)); clause_del_eh * del_eh = alloc(dyn_ack_clause_del_eh, *this); - justification * js = 0; + justification * js = nullptr; if (m_manager.proofs_enabled()) js = alloc(dyn_ack_justification, n1, n2); clause * cls = m_context.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, del_eh); diff --git a/src/smt/expr_context_simplifier.cpp b/src/smt/expr_context_simplifier.cpp index c420543e1..a57b0299f 100644 --- a/src/smt/expr_context_simplifier.cpp +++ b/src/smt/expr_context_simplifier.cpp @@ -110,13 +110,15 @@ void expr_context_simplifier::reduce_rec(app * a, expr_ref & result) { case OP_OR: reduce_or(a->get_num_args(), a->get_args(), result); return; - case OP_IFF: { - expr_ref tmp1(m_manager), tmp2(m_manager); - reduce_rec(a->get_arg(0), tmp1); - reduce_rec(a->get_arg(1), tmp2); - m_simp.mk_iff(tmp1.get(), tmp2.get(), result); - return; - } + case OP_EQ: + if (m_manager.is_iff(a)) { + expr_ref tmp1(m_manager), tmp2(m_manager); + reduce_rec(a->get_arg(0), tmp1); + reduce_rec(a->get_arg(1), tmp2); + m_simp.mk_iff(tmp1.get(), tmp2.get(), result); + return; + } + break; case OP_XOR: { expr_ref tmp1(m_manager), tmp2(m_manager); reduce_rec(a->get_arg(0), tmp1); @@ -311,7 +313,7 @@ bool expr_context_simplifier::is_false(expr* e) const { // expr_strong_context_simplifier::expr_strong_context_simplifier(smt_params& p, ast_manager& m): - m_manager(m), m_arith(m), m_fn(0,m), m_solver(m, p) { + m_manager(m), m_arith(m), m_fn(nullptr,m), m_solver(m, p) { sort* i_sort = m_arith.mk_int(); m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } @@ -358,7 +360,7 @@ void expr_strong_context_simplifier::simplify_basic(expr* fml, expr_ref& result) m_solver.push(); while (!todo.empty()) { - r = 0; + r = nullptr; ptr_buffer args; expr* e = todo.back(); unsigned pos = parent_ids.back(); @@ -405,7 +407,7 @@ void expr_strong_context_simplifier::simplify_basic(expr* fml, expr_ref& result) self_pos = self_ids.back(); sz = a->get_num_args(); - n2 = 0; + n2 = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); @@ -580,7 +582,7 @@ void expr_strong_context_simplifier::simplify_model_based(expr* fml, expr_ref& r } assignment_map.insert(a, value); } - else if (m.is_iff(a, n1, n2) || m.is_eq(a, n1, n2)) { + else if (m.is_eq(a, n1, n2)) { lbool v1 = assignment_map.find(n1); lbool v2 = assignment_map.find(n2); if (v1 == l_undef || v2 == l_undef) { @@ -620,7 +622,7 @@ void expr_strong_context_simplifier::simplify_model_based(expr* fml, expr_ref& r m_solver.push(); while (!todo.empty()) { - r = 0; + r = nullptr; ptr_buffer args; expr* e = todo.back(); unsigned pos = parent_ids.back(); @@ -681,7 +683,7 @@ void expr_strong_context_simplifier::simplify_model_based(expr* fml, expr_ref& r self_pos = self_ids.back(); sz = a->get_num_args(); - n2 = 0; + n2 = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); diff --git a/src/smt/fingerprints.cpp b/src/smt/fingerprints.cpp index 435350396..832f539df 100644 --- a/src/smt/fingerprints.cpp +++ b/src/smt/fingerprints.cpp @@ -24,7 +24,7 @@ namespace smt { m_data(d), m_data_hash(d_h), m_num_args(n), - m_args(0) { + m_args(nullptr) { m_args = new (r) enode*[n]; memcpy(m_args, args, sizeof(enode*) * n); } @@ -54,7 +54,7 @@ namespace smt { fingerprint * fingerprint_set::insert(void * data, unsigned data_hash, unsigned num_args, enode * const * args) { fingerprint * d = mk_dummy(data, data_hash, num_args, args); if (m_set.contains(d)) - return 0; + return nullptr; TRACE("fingerprint_bug", tout << "1) inserting: " << data_hash << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_owner_id(); tout << "\n";); @@ -64,7 +64,7 @@ namespace smt { TRACE("fingerprint_bug", tout << "failed: " << data_hash << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << d->m_args[i]->get_owner_id(); tout << "\n";); - return 0; + return nullptr; } TRACE("fingerprint_bug", tout << "2) inserting: " << data_hash << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_owner_id(); diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 96334e3ce..c35a0b180 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -188,7 +188,7 @@ namespace smt { 1) Variables: (f ... X ...) 2) Ground terms: (f ... t ...) 3) depth 2 joint: (f ... (g ... X ...) ...) - Joint2 stores the declartion g, and the position of variable X, and its idx. + Joint2 stores the declaration g, and the position of variable X, and its idx. \remark Z3 has no support for depth 3 joints (f ... (g ... (h ... X ...) ...) ....) */ @@ -211,7 +211,7 @@ namespace smt { approx_set m_lbl_set; // singleton set containing m_label /* The following field is an array of tagged pointers. - Each positon contains: + Each position contains: 1- null (no joint), NULL_TAG 2- a boxed integer (i.e., register that contains the variable bind) VAR_TAG 3- an enode pointer (ground term) GROUND_TERM_TAG @@ -423,20 +423,20 @@ namespace smt { instruction * curr = head; out << *curr; curr = curr->m_next; - while (curr != 0 && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { + while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { out << "\n"; out << *curr; curr = curr->m_next; } out << "\n"; - if (curr != 0) { + if (curr != nullptr) { display_children(out, static_cast(curr), indent + 1); } } void display_children(std::ostream & out, choose * first_child, unsigned indent) const { choose * curr = first_child; - while (curr != 0) { + while (curr != nullptr) { display_seq(out, curr, indent); curr = curr->m_alt; } @@ -485,7 +485,7 @@ namespace smt { m_filter_candidates(filter_candidates), m_num_regs(num_args + 1), m_num_choices(0), - m_root(0) { + m_root(nullptr) { DEBUG_CODE(m_context = 0;); #ifdef _PROFILE_MAM m_counter = 0; @@ -569,10 +569,9 @@ namespace smt { if (m_context) { ast_manager & m = m_context->get_manager(); out << "patterns:\n"; - ptr_vector::const_iterator it = m_patterns.begin(); - ptr_vector::const_iterator end = m_patterns.end(); - for (; it != end; ++it) - out << mk_pp(*it, m) << "\n"; + for (expr* p : m_patterns) { + out << mk_pp(p, m) << "\n"; + } } #endif out << "function: " << m_root_lbl->get_name(); @@ -607,7 +606,7 @@ namespace smt { void * mem = m_region.allocate(size); OP * r = new (mem) OP; r->m_opcode = op; - r->m_next = 0; + r->m_next = nullptr; #ifdef _PROFILE_MAM r->m_counter = 0; #endif @@ -696,7 +695,7 @@ namespace smt { choose * mk_noop() { choose * r = mk_instr(NOOP, sizeof(choose)); - r->m_alt = 0; + r->m_alt = nullptr; return r; } @@ -831,10 +830,8 @@ namespace smt { void init(code_tree * t, quantifier * qa, app * mp, unsigned first_idx) { SASSERT(m_ast_manager.is_pattern(mp)); #ifdef Z3DEBUG - svector::iterator it = m_mark.begin(); - svector::iterator end = m_mark.end(); - for (; it != end; ++it) { - SASSERT(*it == NOT_CHECKED); + for (auto cm : m_mark) { + SASSERT(cm == NOT_CHECKED); } #endif m_tree = t; @@ -865,9 +862,7 @@ namespace smt { That is, during execution time, the variables will be already bound */ bool all_args_are_bound_vars(app * n) { - unsigned num_args = n->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = n->get_arg(i); + for (expr* arg : *n) { if (!is_var(arg)) return false; if (m_vars[to_var(arg)->get_idx()] == -1) @@ -884,9 +879,7 @@ namespace smt { if (n->is_ground()) { return; } - unsigned num_args = n->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = n->get_arg(i); + for (expr* arg : *n) { if (is_var(arg)) { sz++; unsigned var_id = to_var(arg)->get_idx(); @@ -923,15 +916,12 @@ namespace smt { */ void linearise_core() { m_aux.reset(); - app * first_app = 0; + app * first_app = nullptr; unsigned first_app_reg; unsigned first_app_sz; unsigned first_app_num_unbound_vars; // generate first the non-BIND operations - unsigned_vector::iterator it = m_todo.begin(); - unsigned_vector::iterator end = m_todo.end(); - for (; it != end; ++it) { - unsigned reg = *it; + for (unsigned reg : m_todo) { expr * p = m_registers[reg]; SASSERT(!is_quantifier(p)); if (is_var(p)) { @@ -1111,7 +1101,7 @@ namespace smt { // multi_pattern support for (unsigned i = 1; i < num_args; i++) { // select the pattern with the biggest number of bound variables - app * best = 0; + app * best = nullptr; unsigned best_num_bvars = 0; unsigned best_j = 0; bool found_bounded_mp = false; @@ -1127,7 +1117,7 @@ namespace smt { found_bounded_mp = true; break; } - if (best == 0 || (num_bvars > best_num_bvars)) { + if (best == nullptr || (num_bvars > best_num_bvars)) { best = p; best_num_bvars = num_bvars; best_j = j; @@ -1249,10 +1239,7 @@ namespace smt { SASSERT(head->m_next == 0); m_seq.push_back(m_ct_manager.mk_yield(m_qa, m_mp, m_qa->get_num_decls(), reinterpret_cast(m_vars.begin()))); - ptr_vector::iterator it = m_seq.begin(); - ptr_vector::iterator end = m_seq.end(); - for (; it != end; ++it) { - instruction * curr = *it; + for (instruction * curr : m_seq) { head->m_next = curr; head = curr; } @@ -1286,16 +1273,16 @@ namespace smt { choose * find_best_child(choose * first_child) { unsigned num_too_simple = 0; - choose * best_child = 0; + choose * best_child = nullptr; unsigned max_compatibility = 0; choose * curr_child = first_child; - while (curr_child != 0) { + while (curr_child != nullptr) { bool simple = false; unsigned curr_compatibility = get_compatibility_measure(curr_child, simple); if (simple) { num_too_simple++; if (num_too_simple > FIND_BEST_CHILD_THRESHOLD) - return 0; // it is unlikely we will find a compatible node + return nullptr; // it is unlikely we will find a compatible node } if (curr_compatibility > max_compatibility) { best_child = curr_child; @@ -1310,7 +1297,7 @@ namespace smt { unsigned ireg = instr->m_ireg; expr * n = m_registers[ireg]; return - n != 0 && + n != nullptr && is_app(n) && // It is wasteful to use a bind of a ground term. // Actually, in the rest of the code I assume that. @@ -1450,7 +1437,7 @@ namespace smt { unsigned weight = 0; unsigned num_instr = 0; instruction * curr = child->m_next; - while (curr != 0 && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { + while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { num_instr++; switch (curr->m_opcode) { case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: @@ -1493,12 +1480,10 @@ namespace smt { } curr = curr->m_next; } - if (num_instr > SIMPLE_SEQ_THRESHOLD || (curr != 0 && curr->m_opcode == CHOOSE)) + if (num_instr > SIMPLE_SEQ_THRESHOLD || (curr != nullptr && curr->m_opcode == CHOOSE)) simple = false; - unsigned_vector::iterator it = m_to_reset.begin(); - unsigned_vector::iterator end = m_to_reset.end(); - for (; it != end; ++it) - m_registers[*it] = 0; + for (unsigned reg : m_to_reset) + m_registers[reg] = 0; return weight; } @@ -1509,7 +1494,7 @@ namespace smt { TRACE("mam_compiler_detail", tout << "processing head: " << *head << "\n";); instruction * curr = head->m_next; instruction * last = head; - while (curr != 0 && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { + while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { TRACE("mam_compiler_detail", tout << "processing instr: " << *curr << "\n";); switch (curr->m_opcode) { case BIND1: case BIND2: case BIND3: case BIND4: case BIND5: case BIND6: case BINDN: @@ -1680,7 +1665,7 @@ namespace smt { SASSERT(curr->m_opcode == CHOOSE); choose * first_child = static_cast(curr); choose * best_child = find_best_child(first_child); - if (best_child == 0) { + if (best_child == nullptr) { // There is no compatible child // Suppose the sequence is: // head -> c1 -> ... -> (cn == last) -> first_child; @@ -1716,23 +1701,19 @@ namespace smt { m_num_choices++; // set: head -> c1 -> c2 -> c3 -> new_child_head1 curr = head; - ptr_vector::iterator it1 = m_compatible.begin(); - ptr_vector::iterator end1 = m_compatible.end(); - for (; it1 != end1; ++it1) { - set_next(curr, *it1); - curr = *it1; + for (instruction* instr : m_compatible) { + set_next(curr, instr); + curr = instr; } set_next(curr, new_child_head1); // set: new_child_head1:CHOOSE(new_child_head2) -> i1 -> i2 -> first_child_head curr = new_child_head1; - ptr_vector::iterator it2 = m_incompatible.begin(); - ptr_vector::iterator end2 = m_incompatible.end(); - for (; it2 != end2; ++it2) { + for (instruction* inc : m_incompatible) { if (curr == new_child_head1) - curr->m_next = *it2; // new_child_head1 is a new node, I don't need to save trail + curr->m_next = inc; // new_child_head1 is a new node, I don't need to save trail else - set_next(curr, *it2); - curr = *it2; + set_next(curr, inc); + curr = inc; } set_next(curr, first_child_head); // build new_child_head2:NOOP -> linearise() @@ -1902,7 +1883,7 @@ namespace smt { curr = curr->get_next(); } while (curr != first); - return 0; + return nullptr; } enode * get_next_f_app(func_decl * lbl, unsigned num_expected_args, enode * first, enode * curr) { @@ -1914,7 +1895,7 @@ namespace smt { } curr = curr->get_next(); } - return 0; + return nullptr; } /** @@ -2094,7 +2075,7 @@ namespace smt { enode_vector * interpreter::mk_depth2_vector(joint2 * j2, func_decl * f, unsigned i) { enode * n = m_registers[j2->m_reg]->get_root(); if (n->get_num_parents() == 0) - return 0; + return nullptr; unsigned num_args = n->get_num_args(); enode_vector * v = mk_enode_vector(); enode_vector::const_iterator it1 = n->begin_parents(); @@ -2132,7 +2113,7 @@ namespace smt { // quick filter... check if any of the joint points have zero parents... for (unsigned i = 0; i < num_args; i++) { void * bare = c->m_joints[i]; - enode * n = 0; + enode * n = nullptr; switch (GET_TAG(bare)) { case NULL_TAG: goto non_depth1; @@ -2147,20 +2128,20 @@ namespace smt { } r = n->get_root(); if (m_use_filters && r->get_plbls().empty_intersection(c->m_lbl_set)) - return 0; + return nullptr; if (r->get_num_parents() == 0) - return 0; + return nullptr; non_depth1: ; } // traverse each joint and select the best one. - enode_vector * best_v = 0; + enode_vector * best_v = nullptr; for (unsigned i = 0; i < num_args; i++) { enode * bare = c->m_joints[i]; - enode_vector * curr_v = 0; + enode_vector * curr_v = nullptr; switch (GET_TAG(bare)) { case NULL_TAG: - curr_v = 0; + curr_v = nullptr; break; case GROUND_TERM_TAG: curr_v = mk_depth1_vector(UNTAG(enode *, bare), lbl, i); @@ -2172,14 +2153,14 @@ namespace smt { curr_v = mk_depth2_vector(UNTAG(joint2 *, bare), lbl, i); break; } - if (curr_v != 0) { - if (curr_v->size() < min_sz && (best_v == 0 || curr_v->size() < best_v->size())) { + if (curr_v != nullptr) { + if (curr_v->size() < min_sz && (best_v == nullptr || curr_v->size() < best_v->size())) { if (best_v) recycle_enode_vector(best_v); best_v = curr_v; if (best_v->empty()) { recycle_enode_vector(best_v); - return 0; + return nullptr; } } else { @@ -2191,10 +2172,10 @@ namespace smt { bp.m_instr = c; bp.m_old_max_generation = m_max_generation; bp.m_old_used_enodes_size = m_used_enodes.size(); - if (best_v == 0) { + if (best_v == nullptr) { TRACE("mam_bug", tout << "m_top: " << m_top << ", m_backtrack_stack.size(): " << m_backtrack_stack.size() << "\n"; tout << *c << "\n";); - bp.m_to_recycle = 0; + bp.m_to_recycle = nullptr; bp.m_it = m_context.begin_enodes_of(lbl); bp.m_end = m_context.end_enodes_of(lbl); } @@ -2211,7 +2192,7 @@ namespace smt { break; } if (bp.m_it == bp.m_end) - return 0; + return nullptr; m_top++; update_max_generation(*(bp.m_it)); return *(bp.m_it); @@ -2648,7 +2629,7 @@ namespace smt { m_num_args = static_cast(m_pc)->m_num_args; m_oreg = static_cast(m_pc)->m_oreg; m_app = init_continue(static_cast(m_pc), m_num_args); - if (m_app == 0) + if (m_app == nullptr) goto backtrack; m_pattern_instances.push_back(m_app); TRACE("mam_int", tout << "continue candidate:\n" << mk_ll_pp(m_app->get_owner(), m_ast_manager);); @@ -2844,7 +2825,7 @@ namespace smt { unsigned m_lbl_id; public: mk_tree_trail(ptr_vector & t, unsigned id):m_trees(t), m_lbl_id(id) {} - virtual void undo(mam_impl & m) { + void undo(mam_impl & m) override { dealloc(m_trees[m_lbl_id]); m_trees[m_lbl_id] = 0; } @@ -2911,7 +2892,7 @@ namespace smt { if (lbl_id < m_trees.size()) return m_trees[lbl_id]; else - return 0; + return nullptr; } ptr_vector::iterator begin_code_trees() { @@ -2974,11 +2955,11 @@ namespace smt { if (p1->m_label != p2->m_label || p1->m_arg_idx != p2->m_arg_idx || p1->m_pattern_idx != p2->m_pattern_idx || - (p1->m_child == 0) != (p2->m_child == 0)) { + (p1->m_child == nullptr) != (p2->m_child == nullptr)) { return false; } - if (p1->m_child == 0 && p2->m_child == 0) + if (p1->m_child == nullptr && p2->m_child == nullptr) return true; p1 = p1->m_child; @@ -3014,11 +2995,11 @@ namespace smt { m_arg_idx(p->m_arg_idx), m_ground_arg_idx(p->m_ground_arg_idx), m_ground_arg(p->m_ground_arg), - m_code(0), + m_code(nullptr), m_filter(h(p->m_label)), - m_sibling(0), - m_first_child(0), - m_todo(0) { + m_sibling(nullptr), + m_first_child(nullptr), + m_todo(nullptr) { #ifdef _PROFILE_PATH_TREE m_counter = 0; m_num_eq_visited = 0; @@ -3029,7 +3010,7 @@ namespace smt { void display(std::ostream & out, unsigned indent) { path_tree * curr = this; - while (curr != 0) { + while (curr != nullptr) { for (unsigned i = 0; i < indent; i++) out << " "; out << curr->m_label->get_name() << ":" << curr->m_arg_idx; if (curr->m_ground_arg) @@ -3104,7 +3085,7 @@ namespace smt { enode * m_enode; public: add_shared_enode_trail(enode * n):m_enode(n) {} - virtual void undo(mam_impl & m) { m.m_shared_enodes.erase(m_enode); } + void undo(mam_impl & m) override { m.m_shared_enodes.erase(m_enode); } }; #ifdef Z3DEBUG @@ -3122,7 +3103,7 @@ namespace smt { } void add_candidate(code_tree * t, enode * app) { - if (t != 0) { + if (t != nullptr) { TRACE("mam_candidate", tout << "adding candidate:\n" << mk_ll_pp(app->get_owner(), m_ast_manager);); if (!t->has_candidates()) m_to_match.push_back(t); @@ -3221,7 +3202,7 @@ namespace smt { for (unsigned j = 0; j < APPROX_SET_CAPACITY; j++) { m_pp[i][j].first = 0; m_pp[i][j].second = 0; - m_pc[i][j] = 0; + m_pc[i][j] = nullptr; } } } @@ -3240,10 +3221,10 @@ namespace smt { SASSERT(m_ast_manager.is_pattern(mp)); SASSERT(p != 0); unsigned pat_idx = p->m_pattern_idx; - path_tree * head = 0; - path_tree * curr = 0; - path_tree * prev = 0; - while (p != 0) { + path_tree * head = nullptr; + path_tree * curr = nullptr; + path_tree * prev = nullptr; + while (p != nullptr) { curr = new (m_region) path_tree(p, m_lbl_hasher); if (prev) prev->m_first_child = curr; @@ -3260,9 +3241,9 @@ namespace smt { void insert(path_tree * t, path * p, quantifier * qa, app * mp) { SASSERT(m_ast_manager.is_pattern(mp)); path_tree * head = t; - path_tree * prev_sibling = 0; + path_tree * prev_sibling = nullptr; bool found_label = false; - while (t != 0) { + while (t != nullptr) { if (t->m_label == p->m_label) { found_label = true; if (t->m_arg_idx == p->m_arg_idx && @@ -3270,8 +3251,8 @@ namespace smt { t->m_ground_arg_idx == p->m_ground_arg_idx ) { // found compatible node - if (t->m_first_child == 0) { - if (p->m_child == 0) { + if (t->m_first_child == nullptr) { + if (p->m_child == nullptr) { SASSERT(t->m_code != 0); insert_code(t, qa, mp, p->m_pattern_idx); } @@ -3281,7 +3262,7 @@ namespace smt { } } else { - if (p->m_child == 0) { + if (p->m_child == nullptr) { if (t->m_code) { insert_code(t, qa, mp, p->m_pattern_idx); } @@ -3367,10 +3348,7 @@ namespace smt { void update_vars(unsigned short var_id, path * p, quantifier * qa, app * mp) { paths & var_paths = m_var_paths[var_id]; bool found = false; - paths::iterator it = var_paths.begin(); - paths::iterator end = var_paths.end(); - for (; it != end; ++it) { - path * curr_path = *it; + for (path* curr_path : var_paths) { if (is_equal(p, curr_path)) found = true; func_decl * lbl1 = curr_path->m_label; @@ -3393,7 +3371,7 @@ namespace smt { return mk_enode(m_context, qa, to_app(arg)); } } - return 0; + return nullptr; } /** @@ -3460,7 +3438,7 @@ namespace smt { unsigned num_patterns = mp->get_num_args(); for (unsigned i = 0; i < num_patterns; i++) { app * pat = to_app(mp->get_arg(i)); - update_filters(pat, 0, qa, mp, i); + update_filters(pat, nullptr, qa, mp, i); } } @@ -3496,7 +3474,7 @@ namespace smt { \brief Collect new E-matching candidates using the inverted path index t. */ void collect_parents(enode * r, path_tree * t) { - if (t == 0) + if (t == nullptr) return; #ifdef _PROFILE_PATH_TREE t->m_watch.start(); @@ -3604,7 +3582,7 @@ namespace smt { // Filter 2. ( // curr_tree has no support for the filter based on a ground argument. - curr_tree->m_ground_arg == 0 || + curr_tree->m_ground_arg == nullptr || // checks whether the child of the parent is equal to the expected ground argument. is_eq(curr_tree->m_ground_arg, curr_parent->get_arg(curr_tree->m_ground_arg_idx)) )) { @@ -3614,7 +3592,7 @@ namespace smt { } if (curr_tree->m_first_child) { path_tree * child = curr_tree->m_first_child; - if (child->m_todo == 0) { + if (child->m_todo == nullptr) { child->m_todo = mk_tmp_vector(); m_todo.push_back(child); } @@ -3636,7 +3614,7 @@ namespace smt { } } recycle(t->m_todo); - t->m_todo = 0; + t->m_todo = nullptr; // remove both marks. unmark_enodes(to_unmark->size(), to_unmark->c_ptr()); unmark_enodes2(to_unmark2->size(), to_unmark2->c_ptr()); @@ -3661,18 +3639,12 @@ namespace smt { TRACE("incremental_matcher", tout << "pp: plbls1: " << plbls1 << ", plbls2: " << plbls2 << "\n";); TRACE("mam_info", tout << "pp: " << plbls1.size() * plbls2.size() << "\n";); if (!plbls1.empty() && !plbls2.empty()) { - approx_set::iterator it1 = plbls1.begin(); - approx_set::iterator end1 = plbls1.end(); - for (; it1 != end1; ++it1) { + for (unsigned plbl1 : plbls1) { if (m_context.get_cancel_flag()) { break; } - unsigned plbl1 = *it1; SASSERT(plbls1.may_contain(plbl1)); - approx_set::iterator it2 = plbls2.begin(); - approx_set::iterator end2 = plbls2.end(); - for (; it2 != end2; ++it2) { - unsigned plbl2 = *it2; + for (unsigned plbl2 : plbls2) { SASSERT(plbls2.may_contain(plbl2)); unsigned n_plbl1 = plbl1; unsigned n_plbl2 = plbl2; @@ -3812,18 +3784,18 @@ namespace smt { m_interpreter(ctx, *this, use_filters), m_trees(m_ast_manager, m_compiler, m_trail_stack), m_region(m_trail_stack.get_region()), - m_r1(0), - m_r2(0) { + m_r1(nullptr), + m_r2(nullptr) { DEBUG_CODE(m_trees.set_context(&ctx);); DEBUG_CODE(m_check_missing_instances = false;); reset_pp_pc(); } - virtual ~mam_impl() { + ~mam_impl() override { m_trail_stack.reset(); } - virtual void add_pattern(quantifier * qa, app * mp) { + void add_pattern(quantifier * qa, app * mp) override { SASSERT(m_ast_manager.is_pattern(mp)); TRACE("trigger_bug", tout << "adding pattern\n" << mk_ismt2_pp(qa, m_ast_manager) << "\n" << mk_ismt2_pp(mp, m_ast_manager) << "\n";); TRACE("mam_bug", tout << "adding pattern\n" << mk_pp(qa, m_ast_manager) << "\n" << mk_pp(mp, m_ast_manager) << "\n";); @@ -3846,11 +3818,11 @@ namespace smt { m_trees.add_pattern(qa, mp, i); } - virtual void push_scope() { + void push_scope() override { m_trail_stack.push_scope(); } - virtual void pop_scope(unsigned num_scopes) { + void pop_scope(unsigned num_scopes) override { if (!m_to_match.empty()) { ptr_vector::iterator it = m_to_match.begin(); ptr_vector::iterator end = m_to_match.end(); @@ -3864,7 +3836,7 @@ namespace smt { m_trail_stack.pop_scope(num_scopes); } - virtual void reset() { + void reset() override { m_trail_stack.reset(); m_trees.reset(); m_to_match.reset(); @@ -3875,7 +3847,7 @@ namespace smt { m_tmp_region.reset(); } - virtual void display(std::ostream& out) { + void display(std::ostream& out) override { out << "mam:\n"; m_lbl_hasher.display(out); ptr_vector::iterator it = m_trees.begin_code_trees(); @@ -3886,7 +3858,7 @@ namespace smt { } } - virtual void match() { + void match() override { TRACE("trigger_bug", tout << "match\n"; display(tout);); ptr_vector::iterator it = m_to_match.begin(); ptr_vector::iterator end = m_to_match.end(); @@ -3903,7 +3875,7 @@ namespace smt { } } - virtual void rematch(bool use_irrelevant) { + void rematch(bool use_irrelevant) override { ptr_vector::iterator it = m_trees.begin_code_trees(); ptr_vector::iterator end = m_trees.end_code_trees(); unsigned lbl = 0; @@ -3924,7 +3896,7 @@ namespace smt { } #ifdef Z3DEBUG - virtual bool check_missing_instances() { + bool check_missing_instances() override { TRACE("missing_instance", tout << "checking for missing instances...\n";); flet l(m_check_missing_instances, true); rematch(false); @@ -3932,7 +3904,7 @@ namespace smt { } #endif - virtual void on_match(quantifier * qa, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, ptr_vector & used_enodes) { + void on_match(quantifier * qa, app * pat, unsigned num_bindings, enode * const * bindings, unsigned max_generation, ptr_vector & used_enodes) override { TRACE("trigger_bug", tout << "found match " << mk_pp(qa, m_ast_manager) << "\n";); #ifdef Z3DEBUG if (m_check_missing_instances) { @@ -3955,13 +3927,13 @@ namespace smt { m_context.add_instance(qa, pat, num_bindings, bindings, max_generation, min_gen, max_gen, used_enodes); } - virtual bool is_shared(enode * n) const { + bool is_shared(enode * n) const override { return !m_shared_enodes.empty() && m_shared_enodes.contains(n); } // This method is invoked when n becomes relevant. // If lazy == true, then n is not added to the list of candidate enodes for matching. That is, the method just updates the lbls. - virtual void relevant_eh(enode * n, bool lazy) { + void relevant_eh(enode * n, bool lazy) override { TRACE("trigger_bug", tout << "relevant_eh:\n" << mk_ismt2_pp(n->get_owner(), m_ast_manager) << "\n"; tout << "mam: " << this << "\n";); TRACE("mam", tout << "relevant_eh: #" << n->get_owner_id() << "\n";); @@ -3984,11 +3956,11 @@ namespace smt { } } - virtual bool has_work() const { + bool has_work() const override { return !m_to_match.empty() || !m_new_patterns.empty(); } - virtual void add_eq_eh(enode * r1, enode * r2) { + void add_eq_eh(enode * r1, enode * r2) override { flet l1(m_r1, r1); flet l2(m_r2, r2); diff --git a/src/smt/old_interval.cpp b/src/smt/old_interval.cpp index d126c2f32..da589893b 100644 --- a/src/smt/old_interval.cpp +++ b/src/smt/old_interval.cpp @@ -157,8 +157,8 @@ interval::interval(v_dependency_manager & m): m_upper(true), m_lower_open(true), m_upper_open(true), - m_lower_dep(0), - m_upper_dep(0) { + m_lower_dep(nullptr), + m_upper_dep(nullptr) { } /** @@ -215,12 +215,12 @@ interval::interval(v_dependency_manager & m, rational const & val, bool open, bo m_lower_dep = d; m_upper = ext_numeral(true); m_upper_open = true; - m_upper_dep = 0; + m_upper_dep = nullptr; } else { m_lower = ext_numeral(false); m_lower_open = true; - m_lower_dep = 0; + m_lower_dep = nullptr; m_upper = ext_numeral(val); m_upper_open = open; m_upper_dep = d; @@ -252,8 +252,8 @@ interval & interval::operator+=(interval const & other) { m_upper += other.m_upper; m_lower_open |= other.m_lower_open; m_upper_open |= other.m_upper_open; - m_lower_dep = m_lower.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, other.m_lower_dep); - m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_upper_dep, other.m_upper_dep); + m_lower_dep = m_lower.is_infinite() ? nullptr : m_manager.mk_join(m_lower_dep, other.m_lower_dep); + m_upper_dep = m_upper.is_infinite() ? nullptr : m_manager.mk_join(m_upper_dep, other.m_upper_dep); return *this; } @@ -283,7 +283,7 @@ v_dependency * interval::join_opt(v_dependency * d1, v_dependency * d2, v_depend return join(d1, d2); if (opt2 == d1 || opt2 == d2) return join(d1, d2); - if (opt1 == 0 || opt2 == 0) + if (opt1 == nullptr || opt2 == nullptr) return join(d1, d2); // TODO: more opts... return join(d1, d2, opt1); @@ -331,8 +331,8 @@ interval & interval::operator*=(interval const & other) { m_upper_open = a_o || c_o; SASSERT(a.is_neg() && c.is_neg()); m_lower = new_lower; m_upper = new_upper; - m_lower_dep = m_lower.is_infinite() ? 0 : join(b_d, d_d); - m_upper_dep = m_upper.is_infinite() ? 0 : join_opt(a_d, c_d, b_d, d_d); + m_lower_dep = m_lower.is_infinite() ? nullptr : join(b_d, d_d); + m_upper_dep = m_upper.is_infinite() ? nullptr : join_opt(a_d, c_d, b_d, d_d); } else if (other.is_M()) { // a <= x <= b <= 0, y <= d, d > 0 --> a*d <= x*y (uses the fact that b is not positive) @@ -344,8 +344,8 @@ interval & interval::operator*=(interval const & other) { m_upper_open = a_o || c_o; m_lower = new_lower; m_upper = new_upper; - m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, d_d, b_d); - m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, c_d, b_d); + m_lower_dep = m_lower.is_infinite() ? nullptr : join(a_d, d_d, b_d); + m_upper_dep = m_upper.is_infinite() ? nullptr : join(a_d, c_d, b_d); } else { // a <= x <= b <= 0, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that x is neg (b is not positive) or y is pos (c is not negative)) @@ -359,8 +359,8 @@ interval & interval::operator*=(interval const & other) { m_upper_open = (is_N0_old || other.is_P0()) ? false : (b_o || c_o); m_lower = new_lower; m_upper = new_upper; - m_lower_dep = m_lower.is_infinite() ? 0 : join_opt(a_d, d_d, b_d, c_d); - m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, c_d); + m_lower_dep = m_lower.is_infinite() ? nullptr : join_opt(a_d, d_d, b_d, c_d); + m_upper_dep = m_upper.is_infinite() ? nullptr : join(b_d, c_d); } } else if (is_M()) { @@ -374,8 +374,8 @@ interval & interval::operator*=(interval const & other) { m_upper_open = a_o || c_o; SASSERT(a.is_neg() && c.is_neg()); m_lower = new_lower; m_upper = new_upper; - m_lower_dep = m_lower.is_infinite() ? 0 : join(b_d, c_d, d_d); - m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, c_d, d_d); + m_lower_dep = m_lower.is_infinite() ? nullptr : join(b_d, c_d, d_d); + m_upper_dep = m_upper.is_infinite() ? nullptr : join(a_d, c_d, d_d); } else if (other.is_M()) { TRACE("interval_bug", tout << "(M, M)\n";); @@ -404,8 +404,8 @@ interval & interval::operator*=(interval const & other) { m_upper = bd; m_upper_open = bd_o; } - m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, b_d, c_d, d_d); - m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, b_d, c_d, d_d); + m_lower_dep = m_lower.is_infinite() ? nullptr : join(a_d, b_d, c_d, d_d); + m_upper_dep = m_upper.is_infinite() ? nullptr : join(a_d, b_d, c_d, d_d); } else { // a < 0, a <= x, 0 <= c <= y <= d --> a*d <= x*y (uses the fact that c is not negative) @@ -418,8 +418,8 @@ interval & interval::operator*=(interval const & other) { m_upper_open = b_o || d_o; SASSERT(b.is_pos() && d.is_pos()); m_lower = new_lower; m_upper = new_upper; - m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, d_d, c_d); - m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, d_d, c_d); + m_lower_dep = m_lower.is_infinite() ? nullptr : join(a_d, d_d, c_d); + m_upper_dep = m_upper.is_infinite() ? nullptr : join(b_d, d_d, c_d); } } else { @@ -435,8 +435,8 @@ interval & interval::operator*=(interval const & other) { m_upper_open = (is_P0_old || other.is_N0()) ? false : a_o || d_o; m_lower = new_lower; m_upper = new_upper; - m_lower_dep = m_lower.is_infinite() ? 0 : join_opt(b_d, c_d, a_d, d_d); - m_upper_dep = m_upper.is_infinite() ? 0 : join(a_d, d_d); + m_lower_dep = m_lower.is_infinite() ? nullptr : join_opt(b_d, c_d, a_d, d_d); + m_upper_dep = m_upper.is_infinite() ? nullptr : join(a_d, d_d); } else if (other.is_M()) { // 0 <= a <= x <= b, c <= y --> b*c <= x*y (uses the fact that a is not negative) @@ -448,8 +448,8 @@ interval & interval::operator*=(interval const & other) { m_upper_open = b_o || d_o; m_lower = new_lower; m_upper = new_upper; - m_lower_dep = m_lower.is_infinite() ? 0 : join(b_d, c_d, a_d); - m_upper_dep = m_upper.is_infinite() ? 0 : join(b_d, d_d, a_d); + m_lower_dep = m_lower.is_infinite() ? nullptr : join(b_d, c_d, a_d); + m_upper_dep = m_upper.is_infinite() ? nullptr : join(b_d, d_d, a_d); } else { // 0 <= a <= x, 0 <= c <= y --> a*c <= x*y @@ -462,8 +462,8 @@ interval & interval::operator*=(interval const & other) { m_upper_open = b_o || d_o; SASSERT(b.is_pos() && d.is_pos()); m_lower = new_lower; m_upper = new_upper; - m_lower_dep = m_lower.is_infinite() ? 0 : join(a_d, c_d); - m_upper_dep = m_upper.is_infinite() ? 0 : join_opt(b_d, d_d, a_d, c_d); + m_lower_dep = m_lower.is_infinite() ? nullptr : join(a_d, c_d); + m_upper_dep = m_upper.is_infinite() ? nullptr : join_opt(b_d, d_d, a_d, c_d); } } TRACE("interval_bug", tout << "operator*= result: " << *this << "\n";); @@ -590,7 +590,7 @@ void interval::expt(unsigned n) { // 0 < a <= x <= b --> x^n <= b^n (use lower and upper bound -- need the fact that x is positive) m_lower.expt(n); m_upper.expt(n); - m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep); + m_upper_dep = m_upper.is_infinite() ? nullptr : m_manager.mk_join(m_lower_dep, m_upper_dep); } else if (m_upper.is_neg()) { // [l, u]^n = [u^n, l^n] if u < 0 @@ -601,7 +601,7 @@ void interval::expt(unsigned n) { std::swap(m_lower_dep, m_upper_dep); m_lower.expt(n); m_upper.expt(n); - m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep); + m_upper_dep = m_upper.is_infinite() ? nullptr : m_manager.mk_join(m_lower_dep, m_upper_dep); } else { // [l, u]^n = [0, max{l^n, u^n}] otherwise @@ -614,10 +614,10 @@ void interval::expt(unsigned n) { m_upper = m_lower; m_upper_open = m_lower_open; } - m_upper_dep = m_upper.is_infinite() ? 0 : m_manager.mk_join(m_lower_dep, m_upper_dep); + m_upper_dep = m_upper.is_infinite() ? nullptr : m_manager.mk_join(m_lower_dep, m_upper_dep); m_lower = ext_numeral(0); m_lower_open = false; - m_lower_dep = 0; + m_lower_dep = nullptr; } } else { diff --git a/src/smt/old_interval.h b/src/smt/old_interval.h index e9cb73b8f..1928a1c70 100644 --- a/src/smt/old_interval.h +++ b/src/smt/old_interval.h @@ -80,7 +80,7 @@ class old_interval { public: explicit old_interval(v_dependency_manager & m); explicit old_interval(v_dependency_manager & m, rational const & lower, bool l_open, v_dependency * l_dep, rational const & upper, bool u_open, v_dependency * u_dep); - explicit old_interval(v_dependency_manager & m, rational const & val, v_dependency * l_dep = 0, v_dependency * u_dep = 0); + explicit old_interval(v_dependency_manager & m, rational const & val, v_dependency * l_dep = nullptr, v_dependency * u_dep = nullptr); explicit old_interval(v_dependency_manager & m, rational const & val, bool open, bool lower, v_dependency * d); explicit old_interval(v_dependency_manager & m, ext_numeral const& lower, bool l_open, v_dependency * l_dep, ext_numeral const & upper, bool u_open, v_dependency * u_dep); old_interval(old_interval const & other); diff --git a/src/smt/params/preprocessor_params.cpp b/src/smt/params/preprocessor_params.cpp index 93ea794fa..3e1c6f0cd 100644 --- a/src/smt/params/preprocessor_params.cpp +++ b/src/smt/params/preprocessor_params.cpp @@ -41,12 +41,11 @@ void preprocessor_params::display(std::ostream & out) const { DISPLAY_PARAM(m_lift_ite); DISPLAY_PARAM(m_ng_lift_ite); - DISPLAY_PARAM(m_pull_cheap_ite_trees); + DISPLAY_PARAM(m_pull_cheap_ite); DISPLAY_PARAM(m_pull_nested_quantifiers); DISPLAY_PARAM(m_eliminate_term_ite); DISPLAY_PARAM(m_macro_finder); DISPLAY_PARAM(m_propagate_values); - DISPLAY_PARAM(m_propagate_booleans); DISPLAY_PARAM(m_refine_inj_axiom); DISPLAY_PARAM(m_eliminate_bounds); DISPLAY_PARAM(m_simplify_bit2int); diff --git a/src/smt/params/preprocessor_params.h b/src/smt/params/preprocessor_params.h index dc16d6244..f6724fada 100644 --- a/src/smt/params/preprocessor_params.h +++ b/src/smt/params/preprocessor_params.h @@ -32,12 +32,11 @@ struct preprocessor_params : public pattern_inference_params, public bit_blaster_params { lift_ite_kind m_lift_ite; lift_ite_kind m_ng_lift_ite; // lift ite for non ground terms - bool m_pull_cheap_ite_trees; + bool m_pull_cheap_ite; bool m_pull_nested_quantifiers; bool m_eliminate_term_ite; bool m_macro_finder; bool m_propagate_values; - bool m_propagate_booleans; bool m_refine_inj_axiom; bool m_eliminate_bounds; bool m_simplify_bit2int; @@ -54,12 +53,11 @@ public: preprocessor_params(params_ref const & p = params_ref()): m_lift_ite(LI_NONE), m_ng_lift_ite(LI_NONE), - m_pull_cheap_ite_trees(false), + m_pull_cheap_ite(false), m_pull_nested_quantifiers(false), m_eliminate_term_ite(false), m_macro_finder(false), m_propagate_values(true), - m_propagate_booleans(false), // TODO << check peformance m_refine_inj_axiom(true), m_eliminate_bounds(false), m_simplify_bit2int(false), diff --git a/src/smt/params/qi_params.cpp b/src/smt/params/qi_params.cpp index a9cff6e8c..91f354eda 100644 --- a/src/smt/params/qi_params.cpp +++ b/src/smt/params/qi_params.cpp @@ -35,12 +35,12 @@ void qi_params::updt_params(params_ref const & _p) { m_qi_lazy_threshold = p.qi_lazy_threshold(); m_qi_cost = p.qi_cost(); m_qi_max_eager_multipatterns = p.qi_max_multi_patterns(); + m_qi_quick_checker = static_cast(p.qi_quick_checker()); } #define DISPLAY_PARAM(X) out << #X"=" << X << std::endl; void qi_params::display(std::ostream & out) const { - DISPLAY_PARAM(m_qi_ematching); DISPLAY_PARAM(m_qi_cost); DISPLAY_PARAM(m_qi_new_gen); DISPLAY_PARAM(m_qi_eager_threshold); diff --git a/src/smt/params/qi_params.h b/src/smt/params/qi_params.h index 2cee6dc72..0f6c03f5b 100644 --- a/src/smt/params/qi_params.h +++ b/src/smt/params/qi_params.h @@ -29,7 +29,6 @@ enum quick_checker_mode { }; struct qi_params { - bool m_qi_ematching; std::string m_qi_cost; std::string m_qi_new_gen; double m_qi_eager_threshold; @@ -99,7 +98,7 @@ struct qi_params { m_mbqi_max_iterations(1000), m_mbqi_trace(false), m_mbqi_force_template(10), - m_mbqi_id(0) + m_mbqi_id(nullptr) { updt_params(p); } diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index a8eb81a2e..3ad20cf90 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -39,6 +39,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_timeout = p.timeout(); m_rlimit = p.rlimit(); m_max_conflicts = p.max_conflicts(); + m_restart_max = p.restart_max(); m_core_validate = p.core_validate(); m_logic = _p.get_sym("logic", m_logic); m_string_solver = p.string_solver(); diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index b01499c04..32b634626 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -99,6 +99,7 @@ struct smt_params : public preprocessor_params, unsigned m_phase_caching_off; bool m_minimize_lemmas; unsigned m_max_conflicts; + unsigned m_restart_max; bool m_simplify_clauses; unsigned m_tick; bool m_display_features; diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 937aa6a2b..816764896 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -21,6 +21,7 @@ def_module_params(module_name='smt', ('timeout', UINT, UINT_MAX, 'timeout (in milliseconds) (UINT_MAX and 0 mean no timeout)'), ('rlimit', UINT, 0, 'resource limit (0 means no limit)'), ('max_conflicts', UINT, UINT_MAX, 'maximum number of conflicts before giving up.'), + ('restart.max', UINT, UINT_MAX, 'maximal number of restarts.'), ('mbqi', BOOL, True, 'model based quantifier instantiation (MBQI)'), ('mbqi.max_cexs', UINT, 1, 'initial maximal number of counterexamples used in MBQI, each counterexample generates a quantifier instantiation'), ('mbqi.max_cexs_incr', UINT, 0, 'increment for MBQI_MAX_CEXS, the increment is performed after each round of MBQI'), @@ -35,10 +36,11 @@ def_module_params(module_name='smt', ('qi.lazy_threshold', DOUBLE, 20.0, 'threshold for lazy quantifier instantiation'), ('qi.cost', STRING, '(+ weight generation)', 'expression specifying what is the cost of a given quantifier instantiation'), ('qi.max_multi_patterns', UINT, 0, 'specify the number of extra multi patterns'), + ('qi.quick_checker', UINT, 0, 'specify quick checker mode, 0 - no quick checker, 1 - using unsat instances, 2 - using both unsat and no-sat instances'), ('bv.reflect', BOOL, True, 'create enode for every bit-vector term'), ('bv.enable_int2bv', BOOL, True, 'enable support for int2bv and bv2int operators'), ('arith.random_initial_value', BOOL, False, 'use random initial values in the simplex-based procedure for linear arithmetic'), - ('arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination'), + ('arith.solver', UINT, 2, 'arithmetic solver: 0 - no solver, 1 - bellman-ford based solver (diff. logic only), 2 - simplex based solver, 3 - floyd-warshall based solver (diff. logic only) and no theory combination 4 - utvpi, 5 - infinitary lra, 6 - lra solver'), ('arith.nl', BOOL, True, '(incomplete) nonlinear arithmetic support based on Groebner basis and interval propagation'), ('arith.nl.gb', BOOL, True, 'groebner Basis computation, this option is ignored when arith.nl=false'), ('arith.nl.branching', BOOL, True, 'branching on integer variables in non linear clusters'), @@ -52,6 +54,8 @@ def_module_params(module_name='smt', ('arith.ignore_int', BOOL, False, 'treat integer variables as real'), ('arith.dump_lemmas', BOOL, False, 'dump arithmetic theory lemmas to files'), ('arith.greatest_error_pivot', BOOL, False, 'Pivoting strategy'), + ('arith.eager_eq_axioms', BOOL, True, 'eager equality axioms'), + ('arith.auto_config_simplex', BOOL, False, 'force simplex solver in auto_config'), ('pb.conflict_frequency', UINT, 1000, 'conflict frequency for Pseudo-Boolean theory'), ('pb.learn_complements', BOOL, True, 'learn complement literals for Pseudo-Boolean theory'), ('pb.enable_compilation', BOOL, True, 'enable compilation into sorting circuits for Pseudo-Boolean'), @@ -74,11 +78,17 @@ def_module_params(module_name='smt', ('str.fast_length_tester_cache', BOOL, False, 'cache length tester constants instead of regenerating them'), ('str.fast_value_tester_cache', BOOL, True, 'cache value tester constants instead of regenerating them'), ('str.string_constant_cache', BOOL, True, 'cache all generated string constants generated from anywhere in theory_str'), - ('str.use_binary_search', BOOL, True, 'use a binary search heuristic for finding concrete length values for free variables in theory_str (set to False to use linear search)'), + ('str.use_binary_search', BOOL, False, 'use a binary search heuristic for finding concrete length values for free variables in theory_str (set to False to use linear search)'), ('str.binary_search_start', UINT, 64, 'initial upper bound for theory_str binary search'), ('theory_aware_branching', BOOL, False, 'Allow the context to use extra information from theory solvers regarding literal branching prioritization.'), ('str.finite_overlap_models', BOOL, False, 'attempt a finite model search for overlapping variables instead of completely giving up on the arrangement'), ('str.overlap_priority', DOUBLE, -0.1, 'theory-aware priority for overlapping variable cases; use smt.theory_aware_branching=true'), + ('str.regex_automata', BOOL, True, 'use automata-based reasoning for regular expressions (Z3str3 only)'), + ('str.regex_automata_difficulty_threshold', UINT, 1000, 'difficulty threshold for regex automata heuristics'), + ('str.regex_automata_intersection_difficulty_threshold', UINT, 1000, 'difficulty threshold for regex intersection heuristics'), + ('str.regex_automata_failed_automaton_threshold', UINT, 10, 'number of failed automaton construction attempts after which a full automaton is automatically built'), + ('str.regex_automata_failed_intersection_threshold', UINT, 10, 'number of failed automaton intersection attempts after which intersection is always computed'), + ('str.regex_automata_length_attempt_threshold', UINT, 10, 'number of length/path constraint attempts before checking unsatisfiability of regex terms'), ('core.minimize', BOOL, False, 'minimize unsat core produced by SMT context'), ('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'), diff --git a/src/smt/params/theory_arith_params.cpp b/src/smt/params/theory_arith_params.cpp index 250848db4..0918c9423 100644 --- a/src/smt/params/theory_arith_params.cpp +++ b/src/smt/params/theory_arith_params.cpp @@ -37,6 +37,9 @@ void theory_arith_params::updt_params(params_ref const & _p) { m_arith_bound_prop = static_cast(p.arith_propagation_mode()); m_arith_dump_lemmas = p.arith_dump_lemmas(); m_arith_reflect = p.arith_reflect(); + m_arith_eager_eq_axioms = p.arith_eager_eq_axioms(); + m_arith_auto_config_simplex = p.arith_auto_config_simplex(); + arith_rewriter_params ap(_p); m_arith_eq2ineq = ap.eq2ineq(); } diff --git a/src/smt/params/theory_arith_params.h b/src/smt/params/theory_arith_params.h index 1fe7e1163..eb1459058 100644 --- a/src/smt/params/theory_arith_params.h +++ b/src/smt/params/theory_arith_params.h @@ -23,12 +23,13 @@ Revision History: #include "util/params.h" enum arith_solver_id { - AS_NO_ARITH, - AS_DIFF_LOGIC, - AS_ARITH, - AS_DENSE_DIFF_LOGIC, - AS_UTVPI, - AS_OPTINF + AS_NO_ARITH, // 0 + AS_DIFF_LOGIC, // 1 + AS_ARITH, // 2 + AS_DENSE_DIFF_LOGIC, // 3 + AS_UTVPI, // 4 + AS_OPTINF, // 5 + AS_LRA // 6 }; enum bound_prop_mode { diff --git a/src/smt/params/theory_str_params.cpp b/src/smt/params/theory_str_params.cpp index afbfc33fc..92478bcd9 100644 --- a/src/smt/params/theory_str_params.cpp +++ b/src/smt/params/theory_str_params.cpp @@ -31,4 +31,10 @@ void theory_str_params::updt_params(params_ref const & _p) { m_UseBinarySearch = p.str_use_binary_search(); m_BinarySearchInitialUpperBound = p.str_binary_search_start(); m_OverlapTheoryAwarePriority = p.str_overlap_priority(); + m_RegexAutomata = p.str_regex_automata(); + m_RegexAutomata_DifficultyThreshold = p.str_regex_automata_difficulty_threshold(); + m_RegexAutomata_IntersectionDifficultyThreshold = p.str_regex_automata_intersection_difficulty_threshold(); + m_RegexAutomata_FailedAutomatonThreshold = p.str_regex_automata_failed_automaton_threshold(); + m_RegexAutomata_FailedIntersectionThreshold = p.str_regex_automata_failed_intersection_threshold(); + m_RegexAutomata_LengthAttemptThreshold = p.str_regex_automata_length_attempt_threshold(); } diff --git a/src/smt/params/theory_str_params.h b/src/smt/params/theory_str_params.h index c841609db..8c7816839 100644 --- a/src/smt/params/theory_str_params.h +++ b/src/smt/params/theory_str_params.h @@ -80,6 +80,43 @@ struct theory_str_params { double m_OverlapTheoryAwarePriority; + /* + * If RegexAutomata is set to true, + * Z3str3 will use automata-based methods to reason about + * regular expression constraints. + */ + bool m_RegexAutomata; + + /* + * RegexAutomata_DifficultyThreshold is the lowest difficulty above which Z3str3 + * will not eagerly construct an automaton for a regular expression term. + */ + unsigned m_RegexAutomata_DifficultyThreshold; + + /* + * RegexAutomata_IntersectionDifficultyThreshold is the lowest difficulty above which Z3str3 + * will not eagerly intersect automata to check unsatisfiability. + */ + unsigned m_RegexAutomata_IntersectionDifficultyThreshold; + + /* + * RegexAutomata_FailedAutomatonThreshold is the number of failed attempts to build an automaton + * after which a full automaton (i.e. with no length information) will be built regardless of difficulty. + */ + unsigned m_RegexAutomata_FailedAutomatonThreshold; + + /* + * RegexAutomaton_FailedIntersectionThreshold is the number of failed attempts to perform automaton + * intersection after which intersection will always be performed regardless of difficulty. + */ + unsigned m_RegexAutomata_FailedIntersectionThreshold; + + /* + * RegexAutomaton_LengthAttemptThreshold is the number of attempts to satisfy length/path constraints + * before which we begin checking unsatisfiability of a regex term. + */ + unsigned m_RegexAutomata_LengthAttemptThreshold; + theory_str_params(params_ref const & p = params_ref()): m_StrongArrangements(true), m_AggressiveLengthTesting(false), @@ -91,7 +128,13 @@ struct theory_str_params { m_FiniteOverlapModels(false), m_UseBinarySearch(false), m_BinarySearchInitialUpperBound(64), - m_OverlapTheoryAwarePriority(-0.1) + m_OverlapTheoryAwarePriority(-0.1), + m_RegexAutomata(true), + m_RegexAutomata_DifficultyThreshold(1000), + m_RegexAutomata_IntersectionDifficultyThreshold(1000), + m_RegexAutomata_FailedAutomatonThreshold(10), + m_RegexAutomata_FailedIntersectionThreshold(10), + m_RegexAutomata_LengthAttemptThreshold(10) { updt_params(p); } diff --git a/src/smt/proto_model/array_factory.cpp b/src/smt/proto_model/array_factory.cpp index d112331f0..919f18dc0 100644 --- a/src/smt/proto_model/array_factory.cpp +++ b/src/smt/proto_model/array_factory.cpp @@ -62,7 +62,7 @@ void array_factory::get_some_args_for(sort * s, ptr_buffer & args) { expr * array_factory::get_some_value(sort * s) { TRACE("array_factory", tout << mk_pp(s, m_manager) << "\n";); - value_set * set = 0; + value_set * set = nullptr; if (m_sort2value_set.find(s, set) && !set->empty()) return *(set->begin()); func_interp * fi; @@ -99,7 +99,7 @@ bool array_factory::mk_two_diff_values_for(sort * s) { } bool array_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { - value_set * set = 0; + value_set * set = nullptr; if (!m_sort2value_set.find(s, set) || set->size() == 0) { if (!mk_two_diff_values_for(s)) return false; @@ -111,7 +111,7 @@ bool array_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { if (set->size() == 1) { v1 = *(set->begin()); v2 = get_fresh_value(s); - return v2.get() != 0; + return v2.get() != nullptr; } else { SASSERT(set->size() >= 2); @@ -139,7 +139,7 @@ expr * array_factory::get_fresh_value(sort * s) { } sort * range = get_array_range(s); expr * range_val = m_model.get_fresh_value(range); - if (range_val != 0) { + if (range_val != nullptr) { // easy case func_interp * fi; expr * val = mk_array_interp(s, fi); @@ -170,7 +170,7 @@ expr * array_factory::get_fresh_value(sort * s) { if (!found) { expr * arg1 = m_model.get_fresh_value(d); expr * arg2 = m_model.get_fresh_value(d); - if (arg1 != 0 && arg2 != 0) { + if (arg1 != nullptr && arg2 != nullptr) { found = true; args1.push_back(arg1); args2.push_back(arg2); @@ -201,6 +201,6 @@ expr * array_factory::get_fresh_value(sort * s) { // failed to create a fresh array value TRACE("array_factory_bug", tout << "failed to build fresh array value\n";); - return 0; + return nullptr; } diff --git a/src/smt/proto_model/array_factory.h b/src/smt/proto_model/array_factory.h index 4d4f52944..b59df3f94 100644 --- a/src/smt/proto_model/array_factory.h +++ b/src/smt/proto_model/array_factory.h @@ -32,13 +32,13 @@ class array_factory : public struct_factory { public: array_factory(ast_manager & m, proto_model & md); - virtual ~array_factory() {} + ~array_factory() override {} - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; - virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); + bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; - virtual expr * get_fresh_value(sort * s); + expr * get_fresh_value(sort * s) override; }; #endif /* ARRAY_FACTORY_H_ */ diff --git a/src/smt/proto_model/datatype_factory.cpp b/src/smt/proto_model/datatype_factory.cpp index 550b694da..eded55cc3 100644 --- a/src/smt/proto_model/datatype_factory.cpp +++ b/src/smt/proto_model/datatype_factory.cpp @@ -27,7 +27,7 @@ datatype_factory::datatype_factory(ast_manager & m, proto_model & md): } expr * datatype_factory::get_some_value(sort * s) { - value_set * set = 0; + value_set * set = nullptr; if (m_sort2value_set.find(s, set) && !set->empty()) return *(set->begin()); func_decl * c = m_util.get_non_rec_constructor(s); @@ -46,7 +46,7 @@ expr * datatype_factory::get_some_value(sort * s) { \brief Return the last fresh (or almost) fresh value of sort s. */ expr * datatype_factory::get_last_fresh_value(sort * s) { - expr * val = 0; + expr * val = nullptr; if (m_last_fresh_value.find(s, val)) { TRACE("datatype", tout << "cached fresh value: " << mk_pp(val, m_manager) << "\n";); return val; @@ -98,7 +98,7 @@ expr * datatype_factory::get_almost_fresh_value(sort * s) { sort * s_arg = constructor->get_domain(i); if (!found_fresh_arg && (!m_util.is_datatype(s_arg) || !m_util.are_siblings(s, s_arg))) { expr * new_arg = m_model.get_fresh_value(s_arg); - if (new_arg != 0) { + if (new_arg != nullptr) { found_fresh_arg = true; args.push_back(new_arg); continue; @@ -131,7 +131,7 @@ expr * datatype_factory::get_almost_fresh_value(sort * s) { } } SASSERT(!m_util.is_recursive(s)); - return 0; + return nullptr; } @@ -160,7 +160,7 @@ expr * datatype_factory::get_fresh_value(sort * s) { sort * s_arg = constructor->get_domain(i); if (!found_fresh_arg && (!m_util.is_recursive(s) || !m_util.is_datatype(s_arg) || !m_util.are_siblings(s, s_arg))) { expr * new_arg = m_model.get_fresh_value(s_arg); - if (new_arg != 0) { + if (new_arg != nullptr) { found_fresh_arg = true; args.push_back(new_arg); continue; @@ -204,7 +204,7 @@ expr * datatype_factory::get_fresh_value(sort * s) { << found_sibling << "\n";); if (!found_sibling && m_util.is_datatype(s_arg) && m_util.are_siblings(s, s_arg)) { found_sibling = true; - expr * maybe_new_arg = 0; + expr * maybe_new_arg = nullptr; if (num_iterations <= 1) { maybe_new_arg = get_almost_fresh_value(s_arg); } @@ -245,6 +245,6 @@ expr * datatype_factory::get_fresh_value(sort * s) { // Search for value that was not created before. SASSERT(!m_util.is_recursive(s)); - return 0; + return nullptr; } diff --git a/src/smt/proto_model/datatype_factory.h b/src/smt/proto_model/datatype_factory.h index 9f64b8daa..85b264382 100644 --- a/src/smt/proto_model/datatype_factory.h +++ b/src/smt/proto_model/datatype_factory.h @@ -33,9 +33,9 @@ class datatype_factory : public struct_factory { public: datatype_factory(ast_manager & m, proto_model & md); - virtual ~datatype_factory() {} - virtual expr * get_some_value(sort * s); - virtual expr * get_fresh_value(sort * s); + ~datatype_factory() override {} + expr * get_some_value(sort * s) override; + expr * get_fresh_value(sort * s) override; }; #endif /* DATATYPE_FACTORY_H_ */ diff --git a/src/smt/proto_model/numeral_factory.h b/src/smt/proto_model/numeral_factory.h index f1b68223b..198ff0d32 100644 --- a/src/smt/proto_model/numeral_factory.h +++ b/src/smt/proto_model/numeral_factory.h @@ -26,17 +26,17 @@ Revision History: class numeral_factory : public simple_factory { public: numeral_factory(ast_manager & m, family_id fid):simple_factory(m, fid) {} - virtual ~numeral_factory() {} + ~numeral_factory() override {} }; class arith_factory : public numeral_factory { arith_util m_util; - virtual app * mk_value_core(rational const & val, sort * s); + app * mk_value_core(rational const & val, sort * s) override; public: arith_factory(ast_manager & m); - virtual ~arith_factory(); + ~arith_factory() override; app * mk_num_value(rational const & val, bool is_int); }; @@ -44,11 +44,11 @@ public: class bv_factory : public numeral_factory { bv_util m_util; - virtual app * mk_value_core(rational const & val, sort * s); + app * mk_value_core(rational const & val, sort * s) override; public: bv_factory(ast_manager & m); - virtual ~bv_factory(); + ~bv_factory() override; app * mk_num_value(rational const & val, unsigned bv_size); }; diff --git a/src/smt/proto_model/proto_model.cpp b/src/smt/proto_model/proto_model.cpp index 0a75ff700..8af3af277 100644 --- a/src/smt/proto_model/proto_model.cpp +++ b/src/smt/proto_model/proto_model.cpp @@ -53,11 +53,11 @@ void proto_model::register_aux_decl(func_decl * d) { */ void proto_model::reregister_decl(func_decl * f, func_interp * new_fi, func_decl * f_aux) { func_interp * fi = get_func_interp(f); - if (fi == 0) { + if (fi == nullptr) { register_decl(f, new_fi); } else { - if (f_aux != 0) { + if (f_aux != nullptr) { register_decl(f_aux, fi); m_aux_decls.insert(f_aux); } @@ -135,7 +135,7 @@ void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_au todo.pop_back(); func_decl * a_decl = to_app(a)->get_decl(); expr * ai = get_const_interp(a_decl); - if (ai == 0) { + if (ai == nullptr) { ai = get_some_value(a_decl->get_range()); register_decl(a_decl, ai); } @@ -148,7 +148,7 @@ void proto_model::cleanup_func_interp(func_interp * fi, func_decl_set & found_au bool visited = true; args.reset(); for (expr* t_arg : *t) { - expr * arg = 0; + expr * arg = nullptr; if (!cache.find(t_arg, arg)) { visited = false; todo.push_back(t_arg); @@ -342,11 +342,17 @@ void proto_model::compress() { \brief Complete the interpretation fi of f if it is partial. If f does not have an interpretation in the given model, then this is a noop. */ -void proto_model::complete_partial_func(func_decl * f) { +void proto_model::complete_partial_func(func_decl * f, bool use_fresh) { func_interp * fi = get_func_interp(f); if (fi && fi->is_partial()) { - expr * else_value = fi->get_max_occ_result(); - if (else_value == 0) + expr * else_value; + if (use_fresh) { + else_value = get_fresh_value(f->get_range()); + } + else { + else_value = fi->get_max_occ_result(); + } + if (else_value == nullptr) else_value = get_some_value(f->get_range()); fi->set_else(else_value); } @@ -355,14 +361,14 @@ void proto_model::complete_partial_func(func_decl * f) { /** \brief Set the (else) field of function interpretations... */ -void proto_model::complete_partial_funcs() { +void proto_model::complete_partial_funcs(bool use_fresh) { if (m_model_partial) return; // m_func_decls may be "expanded" when we invoke get_some_value. // So, we must not use iterators to traverse it. - for (unsigned i = 0; i < m_func_decls.size(); i++) { - complete_partial_func(m_func_decls[i]); + for (unsigned i = 0; i < m_func_decls.size(); ++i) { + complete_partial_func(m_func_decls.get(i), use_fresh); } } diff --git a/src/smt/proto_model/proto_model.h b/src/smt/proto_model/proto_model.h index 05af0091c..04e3a90fe 100644 --- a/src/smt/proto_model/proto_model.h +++ b/src/smt/proto_model/proto_model.h @@ -60,7 +60,7 @@ class proto_model : public model_core { public: proto_model(ast_manager & m, params_ref const & p = params_ref()); - virtual ~proto_model() {} + ~proto_model() override {} void register_factory(value_factory * f) { m_factories.register_plugin(f); } @@ -69,7 +69,7 @@ public: value_factory * get_factory(family_id fid); - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); @@ -93,15 +93,15 @@ public: void freeze_universe(sort * s); bool is_finite(sort * s) const; obj_hashtable const & get_known_universe(sort * s) const; - virtual ptr_vector const & get_universe(sort * s) const; - virtual unsigned get_num_uninterpreted_sorts() const; - virtual sort * get_uninterpreted_sort(unsigned idx) const; + ptr_vector const & get_universe(sort * s) const override; + unsigned get_num_uninterpreted_sorts() const override; + sort * get_uninterpreted_sort(unsigned idx) const override; // // Complete partial function interps // - void complete_partial_func(func_decl * f); - void complete_partial_funcs(); + void complete_partial_func(func_decl * f, bool use_fresh); + void complete_partial_funcs(bool use_fresh); // // Create final model object. diff --git a/src/smt/proto_model/struct_factory.cpp b/src/smt/proto_model/struct_factory.cpp index 8d85c6485..a31dd84dd 100644 --- a/src/smt/proto_model/struct_factory.cpp +++ b/src/smt/proto_model/struct_factory.cpp @@ -20,7 +20,7 @@ Revision History: #include "smt/proto_model/proto_model.h" struct_factory::value_set * struct_factory::get_value_set(sort * s) { - value_set * set = 0; + value_set * set = nullptr; if (!m_sort2value_set.find(s, set)) { set = alloc(value_set); m_sort2value_set.insert(s, set); diff --git a/src/smt/proto_model/struct_factory.h b/src/smt/proto_model/struct_factory.h index bfbd90ede..9fe54392c 100644 --- a/src/smt/proto_model/struct_factory.h +++ b/src/smt/proto_model/struct_factory.h @@ -43,11 +43,11 @@ protected: public: struct_factory(ast_manager & m, family_id fid, proto_model & md); - virtual ~struct_factory(); + ~struct_factory() override; - virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); + bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; - virtual void register_value(expr * array_value); + void register_value(expr * array_value) override; }; #endif /* STRUCT_FACTORY_H_ */ diff --git a/src/smt/proto_model/value_factory.cpp b/src/smt/proto_model/value_factory.cpp index e41696165..5a0d012dc 100644 --- a/src/smt/proto_model/value_factory.cpp +++ b/src/smt/proto_model/value_factory.cpp @@ -34,7 +34,7 @@ basic_factory::basic_factory(ast_manager & m): expr * basic_factory::get_some_value(sort * s) { if (m_manager.is_bool(s)) return m_manager.mk_false(); - return 0; + return nullptr; } bool basic_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { @@ -47,7 +47,7 @@ bool basic_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { } expr * basic_factory::get_fresh_value(sort * s) { - return 0; + return nullptr; } user_sort_factory::user_sort_factory(ast_manager & m): @@ -56,7 +56,7 @@ user_sort_factory::user_sort_factory(ast_manager & m): void user_sort_factory::freeze_universe(sort * s) { if (!m_finite.contains(s)) { - value_set * set = 0; + value_set * set = nullptr; m_sort2value_set.find(s, set); if (!m_sort2value_set.find(s, set) || set->m_values.empty()) { // we cannot freeze an empty universe. @@ -68,7 +68,7 @@ void user_sort_factory::freeze_universe(sort * s) { } obj_hashtable const & user_sort_factory::get_known_universe(sort * s) const { - value_set * set = 0; + value_set * set = nullptr; if (m_sort2value_set.find(s, set)) { return set->m_values; } @@ -77,7 +77,7 @@ obj_hashtable const & user_sort_factory::get_known_universe(sort * s) cons expr * user_sort_factory::get_some_value(sort * s) { if (is_finite(s)) { - value_set * set = 0; + value_set * set = nullptr; m_sort2value_set.find(s, set); SASSERT(set != 0); SASSERT(!set->m_values.empty()); @@ -88,7 +88,7 @@ expr * user_sort_factory::get_some_value(sort * s) { bool user_sort_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { if (is_finite(s)) { - value_set * set = 0; + value_set * set = nullptr; if (m_sort2value_set.find(s, set) && set->m_values.size() >= 2) { obj_hashtable::iterator it = set->m_values.begin(); v1 = *it; @@ -103,7 +103,7 @@ bool user_sort_factory::get_some_values(sort * s, expr_ref & v1, expr_ref & v2) expr * user_sort_factory::get_fresh_value(sort * s) { if (is_finite(s)) - return 0; + return nullptr; return simple_factory::get_fresh_value(s); } diff --git a/src/smt/proto_model/value_factory.h b/src/smt/proto_model/value_factory.h index e81c63306..77452d424 100644 --- a/src/smt/proto_model/value_factory.h +++ b/src/smt/proto_model/value_factory.h @@ -64,13 +64,13 @@ class basic_factory : public value_factory { public: basic_factory(ast_manager & m); - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; - virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); + bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; - virtual expr * get_fresh_value(sort * s); + expr * get_fresh_value(sort * s) override; - virtual void register_value(expr * n) { } + void register_value(expr * n) override { } }; /** @@ -95,7 +95,7 @@ protected: ptr_vector m_sets; value_set * get_value_set(sort * s) { - value_set * set = 0; + value_set * set = nullptr; if (!m_sort2value_set.find(s, set)) { set = alloc(value_set); m_sort2value_set.insert(s, set); @@ -133,13 +133,13 @@ public: m_sorts(m) { } - virtual ~simple_factory() { + ~simple_factory() override { std::for_each(m_sets.begin(), m_sets.end(), delete_proc()); } - virtual expr * get_some_value(sort * s) { - value_set * set = 0; - expr * result = 0; + expr * get_some_value(sort * s) override { + value_set * set = nullptr; + expr * result = nullptr; if (m_sort2value_set.find(s, set) && !set->m_values.empty()) result = *(set->m_values.begin()); else @@ -147,8 +147,8 @@ public: return result; } - virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { - value_set * set = 0; + bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override { + value_set * set = nullptr; if (m_sort2value_set.find(s, set)) { switch (set->m_values.size()) { case 0: @@ -176,12 +176,12 @@ public: return true; } - virtual expr * get_fresh_value(sort * s) { + expr * get_fresh_value(sort * s) override { value_set * set = get_value_set(s); bool is_new = false; - expr * result = 0; + expr * result = nullptr; sort_info* s_info = s->get_info(); - sort_size const* sz = s_info?&s_info->get_num_elements():0; + sort_size const* sz = s_info?&s_info->get_num_elements():nullptr; bool has_max = false; Number max_size(0); if (sz && sz->is_finite() && sz->size() < UINT_MAX) { @@ -195,14 +195,14 @@ public: result = mk_value(next, s, is_new); next++; if (has_max && next > max_size + start) { - return 0; + return nullptr; } } SASSERT(result != 0); return result; } - virtual void register_value(expr * n) { + void register_value(expr * n) override { sort * s = this->m_manager.get_sort(n); value_set * set = get_value_set(s); if (!set->m_values.contains(n)) { @@ -228,10 +228,10 @@ public: class user_sort_factory : public simple_factory { obj_hashtable m_finite; //!< set of sorts that are marked as finite. obj_hashtable m_empty_universe; - virtual app * mk_value_core(unsigned const & val, sort * s); + app * mk_value_core(unsigned const & val, sort * s) override; public: user_sort_factory(ast_manager & m); - virtual ~user_sort_factory() {} + ~user_sort_factory() override {} /** \brief Make the universe of \c s finite, by preventing new @@ -257,13 +257,13 @@ public: */ obj_hashtable const & get_finite_sorts() const { return m_finite; } - virtual expr * get_some_value(sort * s); + expr * get_some_value(sort * s) override; - virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2); + bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override; - virtual expr * get_fresh_value(sort * s); + expr * get_fresh_value(sort * s) override; - virtual void register_value(expr * n); + void register_value(expr * n) override; }; #endif /* VALUE_FACTORY_H_ */ diff --git a/src/smt/qi_queue.cpp b/src/smt/qi_queue.cpp index 2a8d9d199..4e639a7b2 100644 --- a/src/smt/qi_queue.cpp +++ b/src/smt/qi_queue.cpp @@ -128,7 +128,7 @@ namespace smt { unsigned qi_queue::get_new_gen(quantifier * q, unsigned generation, float cost) { // max_top_generation and min_top_generation are not available for computing inc_gen - set_values(q, 0, generation, 0, 0, cost); + set_values(q, nullptr, generation, 0, 0, cost); float r = m_evaluator(m_new_gen_function, m_vals.size(), m_vals.c_ptr()); return static_cast(r); } diff --git a/src/smt/smt2_extra_cmds.cpp b/src/smt/smt2_extra_cmds.cpp index 136805e43..d66849338 100644 --- a/src/smt/smt2_extra_cmds.cpp +++ b/src/smt/smt2_extra_cmds.cpp @@ -23,23 +23,23 @@ Notes: class include_cmd : public cmd { char const * m_filename; public: - include_cmd() : cmd("include"), m_filename(0) {} - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "include a file"; } - virtual unsigned get_arity() const { return 1; } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_STRING; } - virtual void set_next_arg(cmd_context & ctx, char const * val) { m_filename = val; } - virtual void failure_cleanup(cmd_context & ctx) {} - virtual void execute(cmd_context & ctx) { + include_cmd() : cmd("include"), m_filename(nullptr) {} + char const * get_usage() const override { return ""; } + char const * get_descr(cmd_context & ctx) const override { return "include a file"; } + unsigned get_arity() const override { return 1; } + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_STRING; } + void set_next_arg(cmd_context & ctx, char const * val) override { m_filename = val; } + void failure_cleanup(cmd_context & ctx) override {} + void execute(cmd_context & ctx) override { std::ifstream is(m_filename); if (is.bad() || is.fail()) throw cmd_exception(std::string("failed to open file '") + m_filename + "'"); parse_smt2_commands(ctx, is, false, params_ref(), m_filename); is.close(); } - virtual void prepare(cmd_context & ctx) { reset(ctx); } - virtual void reset(cmd_context & ctx) { m_filename = 0; } - virtual void finalize(cmd_context & ctx) { reset(ctx); } + void prepare(cmd_context & ctx) override { reset(ctx); } + void reset(cmd_context & ctx) override { m_filename = nullptr; } + void finalize(cmd_context & ctx) override { reset(ctx); } }; void install_smt2_extra_cmds(cmd_context & ctx) { diff --git a/src/smt/smt_almost_cg_table.cpp b/src/smt/smt_almost_cg_table.cpp index 4d24ea0d0..b891c002d 100644 --- a/src/smt/smt_almost_cg_table.cpp +++ b/src/smt/smt_almost_cg_table.cpp @@ -108,8 +108,8 @@ namespace smt { void almost_cg_table::insert(enode * n) { table::entry * entry = m_table.find_core(n); - if (entry == 0) { - list * new_lst = new (m_region) list(n, 0); + if (entry == nullptr) { + list * new_lst = new (m_region) list(n, nullptr); m_table.insert(n, new_lst); } else { @@ -119,7 +119,7 @@ namespace smt { } list * almost_cg_table::find(enode * n) { - list * result = 0; + list * result = nullptr; m_table.find(n, result); return result; } diff --git a/src/smt/smt_almost_cg_table.h b/src/smt/smt_almost_cg_table.h index 7807f705a..d514f1478 100644 --- a/src/smt/smt_almost_cg_table.h +++ b/src/smt/smt_almost_cg_table.h @@ -57,7 +57,7 @@ namespace smt { table m_table; public: - almost_cg_table(enode * r1 = 0, enode * r2 = 0); + almost_cg_table(enode * r1 = nullptr, enode * r2 = nullptr); void reset(enode * r1, enode * r2) { m_r1 = r1->get_root(); m_r2 = r2->get_root(); reset(); } void reset(); void insert(enode *); diff --git a/src/smt/smt_b_justification.h b/src/smt/smt_b_justification.h index 55669a7ef..7aeb268cb 100644 --- a/src/smt/smt_b_justification.h +++ b/src/smt/smt_b_justification.h @@ -91,7 +91,7 @@ namespace smt { } }; - const b_justification null_b_justification(static_cast(0)); + const b_justification null_b_justification(static_cast(nullptr)); inline std::ostream& operator<<(std::ostream& out, b_justification::kind k) { switch (k) { diff --git a/src/smt/smt_case_split_queue.cpp b/src/smt/smt_case_split_queue.cpp index 6aef5f0a9..5c51b906f 100644 --- a/src/smt/smt_case_split_queue.cpp +++ b/src/smt/smt_case_split_queue.cpp @@ -75,42 +75,42 @@ namespace smt { m_queue(1024, bool_var_act_lt(ctx.get_activity_vector())) { } - virtual void activity_increased_eh(bool_var v) { + void activity_increased_eh(bool_var v) override { if (m_queue.contains(v)) m_queue.decreased(v); } - virtual void mk_var_eh(bool_var v) { + void mk_var_eh(bool_var v) override { m_queue.reserve(v+1); SASSERT(!m_queue.contains(v)); m_queue.insert(v); } - virtual void del_var_eh(bool_var v) { + void del_var_eh(bool_var v) override { if (m_queue.contains(v)) m_queue.erase(v); } - virtual void unassign_var_eh(bool_var v) { + void unassign_var_eh(bool_var v) override { if (!m_queue.contains(v)) m_queue.insert(v); } - virtual void relevant_eh(expr * n) {} + void relevant_eh(expr * n) override {} - virtual void init_search_eh() {} + void init_search_eh() override {} - virtual void end_search_eh() {} + void end_search_eh() override {} - virtual void reset() { + void reset() override { m_queue.reset(); } - virtual void push_scope() {} + void push_scope() override {} - virtual void pop_scope(unsigned num_scopes) {} + void pop_scope(unsigned num_scopes) override {} - virtual void next_case_split(bool_var & next, lbool & phase) { + void next_case_split(bool_var & next, lbool & phase) override { phase = l_undef; if (m_context.get_random_value() < static_cast(m_params.m_random_var_freq * random_gen::max_value())) { @@ -129,7 +129,7 @@ namespace smt { next = null_bool_var; } - virtual void display(std::ostream & out) { + void display(std::ostream & out) override { bool first = true; for (unsigned v : m_queue) { if (m_context.get_assignment(v) == l_undef) { @@ -144,7 +144,7 @@ namespace smt { out << "\n"; } - virtual ~act_case_split_queue() {}; + ~act_case_split_queue() override {}; }; /** @@ -159,7 +159,7 @@ namespace smt { m_delayed_queue(1024, bool_var_act_lt(ctx.get_activity_vector())) { } - virtual void activity_increased_eh(bool_var v) { + void activity_increased_eh(bool_var v) override { act_case_split_queue::activity_increased_eh(v); if (m_queue.contains(v)) m_queue.decreased(v); @@ -167,7 +167,7 @@ namespace smt { m_delayed_queue.decreased(v); } - virtual void mk_var_eh(bool_var v) { + void mk_var_eh(bool_var v) override { m_queue.reserve(v+1); m_delayed_queue.reserve(v+1); SASSERT(!m_delayed_queue.contains(v)); @@ -178,28 +178,28 @@ namespace smt { m_queue.insert(v); } - virtual void del_var_eh(bool_var v) { + void del_var_eh(bool_var v) override { act_case_split_queue::del_var_eh(v); if (m_delayed_queue.contains(v)) m_delayed_queue.erase(v); } - virtual void relevant_eh(expr * n) {} + void relevant_eh(expr * n) override {} - virtual void init_search_eh() {} + void init_search_eh() override {} - virtual void end_search_eh() {} + void end_search_eh() override {} - virtual void reset() { + void reset() override { act_case_split_queue::reset(); m_delayed_queue.reset(); } - virtual void push_scope() {} + void push_scope() override {} - virtual void pop_scope(unsigned num_scopes) {} + void pop_scope(unsigned num_scopes) override {} - virtual void next_case_split(bool_var & next, lbool & phase) { + void next_case_split(bool_var & next, lbool & phase) override { act_case_split_queue::next_case_split(next, phase); if (next != null_bool_var) return; @@ -229,7 +229,7 @@ namespace smt { m_cache_domain(ctx.get_manager()) { } - virtual void mk_var_eh(bool_var v) { + void mk_var_eh(bool_var v) override { expr * n = m_context.bool_var2expr(v); double act; if (m_cache.find(n, act)) @@ -237,7 +237,7 @@ namespace smt { act_case_split_queue::mk_var_eh(v); } - virtual void del_var_eh(bool_var v) { + void del_var_eh(bool_var v) override { if (m_context.is_searching()) { double act = m_context.get_activity(v); if (act > 0.0) { @@ -249,14 +249,14 @@ namespace smt { act_case_split_queue::del_var_eh(v); } - virtual void init_search_eh() { + void init_search_eh() override { m_cache.reset(); m_cache_domain.reset(); } - virtual void end_search_eh() {} + void end_search_eh() override {} - virtual void reset() { + void reset() override { init_search_eh(); } }; @@ -322,15 +322,15 @@ namespace smt { m_head2(0) { } - virtual void activity_increased_eh(bool_var v) {} + void activity_increased_eh(bool_var v) override {} - virtual void mk_var_eh(bool_var v) {} + void mk_var_eh(bool_var v) override {} - virtual void del_var_eh(bool_var v) {} + void del_var_eh(bool_var v) override {} - virtual void unassign_var_eh(bool_var v) {} + void unassign_var_eh(bool_var v) override {} - virtual void relevant_eh(expr * n) { + void relevant_eh(expr * n) override { if (!m_manager.is_bool(n)) return; bool is_or = m_manager.is_or(n); @@ -361,15 +361,15 @@ namespace smt { m_queue2.push_back(n); } - virtual void init_search_eh() { + void init_search_eh() override { m_bs_num_bool_vars = m_context.get_num_bool_vars(); } - virtual void end_search_eh() { + void end_search_eh() override { m_bs_num_bool_vars = UINT_MAX; } - virtual void reset() { + void reset() override { m_queue.reset(); m_head = 0; m_queue2.reset(); @@ -377,7 +377,7 @@ namespace smt { m_scopes.reset(); } - virtual void push_scope() { + void push_scope() override { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_queue_trail = m_queue.size(); @@ -387,7 +387,7 @@ namespace smt { TRACE("case_split", tout << "head: " << m_head << "\n";); } - virtual void pop_scope(unsigned num_scopes) { + void pop_scope(unsigned num_scopes) override { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; @@ -419,7 +419,7 @@ namespace smt { val = l_true; } if ((is_or && val == l_true) || (is_and && val == l_false)) { - expr * undef_child = 0; + expr * undef_child = nullptr; if (!has_child_assigned_to(m_context, to_app(curr), val, undef_child, m_params.m_rel_case_split_order)) { if (m_manager.has_trace_stream()) { m_manager.trace_stream() << "[decide-and-or] #" << curr->get_id() << " #" << undef_child->get_id() << "\n"; @@ -443,7 +443,7 @@ namespace smt { next = null_bool_var; } - virtual void next_case_split(bool_var & next, lbool & phase) { + void next_case_split(bool_var & next, lbool & phase) override { next_case_split_core(m_queue, m_head, next, phase); if (next == null_bool_var) next_case_split_core(m_queue2, m_head2, next, phase); @@ -471,7 +471,7 @@ namespace smt { out << "\n"; } - virtual void display(std::ostream & out) { + void display(std::ostream & out) override { if (m_queue.empty() && m_queue2.empty()) return; out << "case-splits:\n"; @@ -507,9 +507,9 @@ namespace smt { m_delayed_queue(1024, bool_var_act_lt(ctx.get_activity_vector())) { } - virtual void activity_increased_eh(bool_var v) {} + void activity_increased_eh(bool_var v) override {} - virtual void mk_var_eh(bool_var v) { + void mk_var_eh(bool_var v) override { if (m_context.is_searching()) { SASSERT(v >= m_bs_num_bool_vars); m_delayed_queue.reserve(v+1); @@ -517,19 +517,19 @@ namespace smt { } } - virtual void del_var_eh(bool_var v) { + void del_var_eh(bool_var v) override { if (v >= m_bs_num_bool_vars && m_delayed_queue.contains(v)) m_delayed_queue.erase(v); } - virtual void unassign_var_eh(bool_var v) { + void unassign_var_eh(bool_var v) override { if (v < m_bs_num_bool_vars) return; if (!m_delayed_queue.contains(v)) m_delayed_queue.insert(v); } - virtual void relevant_eh(expr * n) { + void relevant_eh(expr * n) override { if (!m_manager.is_bool(n)) return; bool is_or = m_manager.is_or(n); @@ -558,22 +558,22 @@ namespace smt { m_queue.push_back(n); } - virtual void init_search_eh() { + void init_search_eh() override { m_bs_num_bool_vars = m_context.get_num_bool_vars(); } - virtual void end_search_eh() { + void end_search_eh() override { m_bs_num_bool_vars = UINT_MAX; } - virtual void reset() { + void reset() override { m_queue.reset(); m_head = 0; m_delayed_queue.reset(); m_scopes.reset(); } - virtual void push_scope() { + void push_scope() override { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_queue_trail = m_queue.size(); @@ -581,7 +581,7 @@ namespace smt { TRACE("case_split", tout << "head: " << m_head << "\n";); } - virtual void pop_scope(unsigned num_scopes) { + void pop_scope(unsigned num_scopes) override { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; @@ -611,7 +611,7 @@ namespace smt { val = l_true; } if ((is_or && val == l_true) || (is_and && val == l_false)) { - expr * undef_child = 0; + expr * undef_child = nullptr; if (!has_child_assigned_to(m_context, to_app(curr), val, undef_child, m_params.m_rel_case_split_order)) { TRACE("case_split", tout << "found AND/OR candidate: #" << curr->get_id() << " #" << undef_child->get_id() << "\n";); literal l = m_context.get_literal(undef_child); @@ -632,7 +632,7 @@ namespace smt { next = null_bool_var; } - virtual void next_case_split(bool_var & next, lbool & phase) { + void next_case_split(bool_var & next, lbool & phase) override { if (m_context.get_random_value() < static_cast(0.02 * random_gen::max_value())) { next = m_context.get_random_value() % m_context.get_num_b_internalized(); TRACE("random_split", tout << "next: " << next << " get_assignment(next): " << m_context.get_assignment(next) << "\n";); @@ -664,7 +664,7 @@ namespace smt { out << "\n"; } - virtual void display(std::ostream & out) { + void display(std::ostream & out) override { if (m_queue.empty()) return; out << "case-splits:\n"; @@ -747,19 +747,19 @@ namespace smt { m_head(0), m_bs_num_bool_vars(UINT_MAX), m_priority_queue2(0, generation_lt(*this)), - m_current_goal(0) { + m_current_goal(nullptr) { set_global_generation(); } - virtual void activity_increased_eh(bool_var v) {} + void activity_increased_eh(bool_var v) override {} - virtual void mk_var_eh(bool_var v) {} + void mk_var_eh(bool_var v) override {} - virtual void del_var_eh(bool_var v) {} + void del_var_eh(bool_var v) override {} - virtual void unassign_var_eh(bool_var v) {} + void unassign_var_eh(bool_var v) override {} - virtual void relevant_eh(expr * n) { + void relevant_eh(expr * n) override { if (get_generation(n) == 0 && m_current_generation != 0) set_generation_rec(n, m_current_generation); @@ -793,21 +793,21 @@ namespace smt { add_to_queue2(n); } - virtual void internalize_instance_eh(expr * e, unsigned gen) + void internalize_instance_eh(expr * e, unsigned gen) override { //lower_generation(e, gen); } - virtual void init_search_eh() { + void init_search_eh() override { m_bs_num_bool_vars = m_context.get_num_bool_vars(); set_global_generation(); } - virtual void end_search_eh() { + void end_search_eh() override { m_bs_num_bool_vars = UINT_MAX; } - virtual void reset() { + void reset() override { m_queue.reset(); m_head = 0; m_queue2.reset(); @@ -816,7 +816,7 @@ namespace smt { set_global_generation(); } - virtual void push_scope() { + void push_scope() override { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_queue_trail = m_queue.size(); @@ -827,7 +827,7 @@ namespace smt { TRACE("case_split", tout << "head: " << m_head << "\n";); } - virtual void pop_scope(unsigned num_scopes) { + void pop_scope(unsigned num_scopes) override { SASSERT(num_scopes <= m_scopes.size()); unsigned new_lvl = m_scopes.size() - num_scopes; scope & s = m_scopes[new_lvl]; @@ -875,7 +875,7 @@ namespace smt { val = l_true; } if ((is_or && val == l_true) || (is_and && val == l_false)) { - expr * undef_child = 0; + expr * undef_child = nullptr; if (!has_child_assigned_to(m_context, to_app(curr), val, undef_child, m_params.m_rel_case_split_order)) { if (m_manager.has_trace_stream()) { m_manager.trace_stream() << "[decide-and-or] #" << curr->get_id() << " #" << undef_child->get_id() << "\n"; @@ -898,7 +898,7 @@ namespace smt { next = null_bool_var; } - virtual void next_case_split(bool_var & next, lbool & phase) { + void next_case_split(bool_var & next, lbool & phase) override { phase = l_undef; next = null_bool_var; @@ -943,7 +943,7 @@ namespace smt { out << "\n"; } - virtual void display(std::ostream & out) { + void display(std::ostream & out) override { if (m_queue.empty() && m_queue2.empty()) return; out << "case-splits:\n"; @@ -951,7 +951,7 @@ namespace smt { //display_core(out, m_queue2, m_head2, 2); } - virtual void assign_lit_eh(literal l) { + void assign_lit_eh(literal l) override { // if (m_current_generation > stop_gen) // m_current_generation--; @@ -1128,41 +1128,41 @@ namespace smt { m_queue(1024, theory_aware_act_lt(ctx.get_activity_vector(), m_theory_var_priority)) { } - virtual void activity_increased_eh(bool_var v) { + void activity_increased_eh(bool_var v) override { if (m_queue.contains(v)) m_queue.decreased(v); } - virtual void mk_var_eh(bool_var v) { + void mk_var_eh(bool_var v) override { m_queue.reserve(v+1); m_queue.insert(v); } - virtual void del_var_eh(bool_var v) { + void del_var_eh(bool_var v) override { if (m_queue.contains(v)) m_queue.erase(v); } - virtual void unassign_var_eh(bool_var v) { + void unassign_var_eh(bool_var v) override { if (!m_queue.contains(v)) m_queue.insert(v); } - virtual void relevant_eh(expr * n) {} + void relevant_eh(expr * n) override {} - virtual void init_search_eh() {} + void init_search_eh() override {} - virtual void end_search_eh() {} + void end_search_eh() override {} - virtual void reset() { + void reset() override { m_queue.reset(); } - virtual void push_scope() {} + void push_scope() override {} - virtual void pop_scope(unsigned num_scopes) {} + void pop_scope(unsigned num_scopes) override {} - virtual void next_case_split(bool_var & next, lbool & phase) { + void next_case_split(bool_var & next, lbool & phase) override { int threshold = static_cast(m_params.m_random_var_freq * random_gen::max_value()); SASSERT(threshold >= 0); if (m_context.get_random_value() < threshold) { @@ -1187,7 +1187,7 @@ namespace smt { } } - virtual void add_theory_aware_branching_info(bool_var v, double priority, lbool phase) { + void add_theory_aware_branching_info(bool_var v, double priority, lbool phase) override { TRACE("theory_aware_branching", tout << "Add theory-aware branching information for l#" << v << ": priority=" << priority << std::endl;); m_theory_vars.insert(v); m_theory_var_phase.insert(v, phase); @@ -1203,7 +1203,7 @@ namespace smt { // m_theory_queue.insert(v); } - virtual void display(std::ostream & out) { + void display(std::ostream & out) override { bool first = true; bool_var_act_queue::const_iterator it = m_queue.begin(); bool_var_act_queue::const_iterator end = m_queue.end(); @@ -1222,7 +1222,7 @@ namespace smt { } - virtual ~theory_aware_branching_queue() {}; + ~theory_aware_branching_queue() override {}; }; diff --git a/src/smt/smt_cg_table.h b/src/smt/smt_cg_table.h index 1e3fd8b83..64c8328d0 100644 --- a/src/smt/smt_cg_table.h +++ b/src/smt/smt_cg_table.h @@ -307,17 +307,17 @@ namespace smt { enode * find(enode * n) const { SASSERT(n->get_num_args() > 0); - enode * r = 0; + enode * r = nullptr; void * t = const_cast(this)->get_table(n); switch (static_cast(GET_TAG(t))) { case UNARY: - return UNTAG(unary_table*, t)->find(n, r) ? r : 0; + return UNTAG(unary_table*, t)->find(n, r) ? r : nullptr; case BINARY: - return UNTAG(binary_table*, t)->find(n, r) ? r : 0; + return UNTAG(binary_table*, t)->find(n, r) ? r : nullptr; case BINARY_COMM: - return UNTAG(comm_table*, t)->find(n, r) ? r : 0; + return UNTAG(comm_table*, t)->find(n, r) ? r : nullptr; default: - return UNTAG(table*, t)->find(n, r) ? r : 0; + return UNTAG(table*, t)->find(n, r) ? r : nullptr; } } diff --git a/src/smt/smt_checker.cpp b/src/smt/smt_checker.cpp index a7a25037c..1b6a5d370 100644 --- a/src/smt/smt_checker.cpp +++ b/src/smt/smt_checker.cpp @@ -61,8 +61,20 @@ namespace smt { return is_true ? any_arg(a, true) : all_args(a, false); case OP_AND: return is_true ? all_args(a, true) : any_arg(a, false); - case OP_IFF: - if (is_true) { + case OP_EQ: + if (!m_manager.is_iff(a)) { + enode * lhs = get_enode_eq_to(a->get_arg(0)); + enode * rhs = get_enode_eq_to(a->get_arg(1)); + if (lhs && rhs && m_context.is_relevant(lhs) && m_context.is_relevant(rhs)) { + if (is_true && lhs->get_root() == rhs->get_root()) + return true; + // if (!is_true && m_context.is_ext_diseq(lhs, rhs, 2)) + if (!is_true && m_context.is_diseq(lhs, rhs)) + return true; + } + return false; + } + else if (is_true) { return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || @@ -86,18 +98,6 @@ namespace smt { } return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true); } - case OP_EQ: { - enode * lhs = get_enode_eq_to(a->get_arg(0)); - enode * rhs = get_enode_eq_to(a->get_arg(1)); - if (lhs && rhs && m_context.is_relevant(lhs) && m_context.is_relevant(rhs)) { - if (is_true && lhs->get_root() == rhs->get_root()) - return true; - // if (!is_true && m_context.is_ext_diseq(lhs, rhs, 2)) - if (!is_true && m_context.is_diseq(lhs, rhs)) - return true; - } - return false; - } default: break; } @@ -125,28 +125,28 @@ namespace smt { unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { enode * arg = get_enode_eq_to(n->get_arg(i)); - if (arg == 0) - return 0; + if (arg == nullptr) + return nullptr; buffer.push_back(arg); } enode * e = m_context.get_enode_eq_to(n->get_decl(), num, buffer.c_ptr()); - if (e == 0) - return 0; - return m_context.is_relevant(e) ? e : 0; + if (e == nullptr) + return nullptr; + return m_context.is_relevant(e) ? e : nullptr; } enode * checker::get_enode_eq_to(expr * n) { if (is_var(n)) { unsigned idx = to_var(n)->get_idx(); if (idx >= m_num_bindings) - return 0; + return nullptr; return m_bindings[m_num_bindings - idx - 1]; } if (m_context.e_internalized(n) && m_context.is_relevant(n)) return m_context.get_enode(n); if (!is_app(n) || to_app(n)->get_num_args() == 0) - return 0; - enode * r = 0; + return nullptr; + enode * r = nullptr; if (n->get_ref_count() > 1 && m_to_enode_cache.find(n, r)) return r; r = get_enode_eq_to_core(to_app(n)); @@ -179,7 +179,7 @@ namespace smt { m_context(c), m_manager(c.get_manager()), m_num_bindings(0), - m_bindings(0) { + m_bindings(nullptr) { } }; diff --git a/src/smt/smt_checker.h b/src/smt/smt_checker.h index 5e841f572..beb32239c 100644 --- a/src/smt/smt_checker.h +++ b/src/smt/smt_checker.h @@ -47,8 +47,8 @@ namespace smt { public: checker(context & c); - bool is_sat(expr * n, unsigned num_bindings = 0, enode * const * bindings = 0); - bool is_unsat(expr * n, unsigned num_bindings = 0, enode * const * bindings = 0); + bool is_sat(expr * n, unsigned num_bindings = 0, enode * const * bindings = nullptr); + bool is_unsat(expr * n, unsigned num_bindings = 0, enode * const * bindings = nullptr); }; }; diff --git a/src/smt/smt_clause.cpp b/src/smt/smt_clause.cpp index ccb336941..2b9b8dd3e 100644 --- a/src/smt/smt_clause.cpp +++ b/src/smt/smt_clause.cpp @@ -25,11 +25,11 @@ namespace smt { \brief Create a new clause. bool_var2expr_map is a mapping from bool_var -> expr, it is only used if save_atoms == true. */ - clause * clause::mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js, + clause * clause::mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js, clause_del_eh * del_eh, bool save_atoms, expr * const * bool_var2expr_map) { SASSERT(k == CLS_AUX || js == 0 || !js->in_region()); SASSERT(num_lits >= 2); - unsigned sz = get_obj_size(num_lits, k, save_atoms, del_eh != 0, js != 0); + unsigned sz = get_obj_size(num_lits, k, save_atoms, del_eh != nullptr, js != nullptr); void * mem = m.get_allocator().allocate(sz); clause * cls = new (mem) clause(); cls->m_num_literals = num_lits; @@ -38,8 +38,8 @@ namespace smt { cls->m_reinit = save_atoms; cls->m_reinternalize_atoms = save_atoms; cls->m_has_atoms = save_atoms; - cls->m_has_del_eh = del_eh != 0; - cls->m_has_justification = js != 0; + cls->m_has_del_eh = del_eh != nullptr; + cls->m_has_justification = js != nullptr; cls->m_deleted = false; SASSERT(!m.proofs_enabled() || js != 0); memcpy(cls->m_lits, lits, sizeof(literal) * num_lits); @@ -67,7 +67,7 @@ namespace smt { }}); return cls; } - + void clause::deallocate(ast_manager & m) { clause_del_eh * del_eh = get_del_eh(); if (del_eh) @@ -92,7 +92,7 @@ namespace smt { unsigned num_atoms = get_num_atoms(); for (unsigned i = 0; i < num_atoms; i++) { m.dec_ref(get_atom(i)); - const_cast(get_atoms_addr())[i] = 0; + const_cast(get_atoms_addr())[i] = nullptr; } } @@ -115,4 +115,3 @@ namespace smt { } }; - diff --git a/src/smt/smt_clause.h b/src/smt/smt_clause.h index d4b9ee02f..f0b352e05 100644 --- a/src/smt/smt_clause.h +++ b/src/smt/smt_clause.h @@ -149,8 +149,8 @@ namespace smt { void release_atoms(ast_manager & m); public: - static clause * mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js = 0, - clause_del_eh * del_eh = 0, bool save_atoms = false, expr * const * bool_var2expr_map = 0); + static clause * mk(ast_manager & m, unsigned num_lits, literal * lits, clause_kind k, justification * js = nullptr, + clause_del_eh * del_eh = nullptr, bool save_atoms = false, expr * const * bool_var2expr_map = nullptr); void deallocate(ast_manager & m); @@ -192,13 +192,13 @@ namespace smt { return m_lits[idx]; } - literal * begin_literals() { return m_lits; } + literal * begin() { return m_lits; } - literal * end_literals() { return m_lits + m_num_literals; } + literal * end() { return m_lits + m_num_literals; } - literal const * begin_literals() const { return m_lits; } + literal const * begin() const { return m_lits; } - literal const * end_literals() const { return m_lits + m_num_literals; } + literal const * end() const { return m_lits + m_num_literals; } unsigned get_activity() const { SASSERT(is_lemma()); @@ -211,11 +211,11 @@ namespace smt { } clause_del_eh * get_del_eh() const { - return m_has_del_eh ? *(get_del_eh_addr()) : 0; + return m_has_del_eh ? *(get_del_eh_addr()) : nullptr; } justification * get_justification() const { - return m_has_justification ? *(get_justification_addr()) : 0; + return m_has_justification ? *(get_justification_addr()) : nullptr; } unsigned get_num_atoms() const { @@ -253,7 +253,7 @@ namespace smt { clause_del_eh * del_eh = get_del_eh(); if (del_eh) { (*del_eh)(m, this); - *(const_cast(get_del_eh_addr())) = 0; + *(const_cast(get_del_eh_addr())) = nullptr; } } diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index a6432c960..f7bc60051 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -43,7 +43,7 @@ namespace smt { m_assigned_literals(assigned_literals), m_lemma_atoms(m), m_todo_js_qhead(0), - m_antecedents(0), + m_antecedents(nullptr), m_watches(watches), m_new_proofs(m), m_lemma_proof(m) @@ -204,7 +204,7 @@ namespace smt { eq2literals(p.first, p.second); } if (m_todo_js_qhead == m_todo_js.size()) { - m_antecedents = 0; + m_antecedents = nullptr; return; } } @@ -480,7 +480,7 @@ namespace smt { // save space for first uip m_lemma.push_back(null_literal); - m_lemma_atoms.push_back(0); + m_lemma_atoms.push_back(nullptr); unsigned num_marks = 0; if (not_l != null_literal) { @@ -758,7 +758,7 @@ namespace smt { return pr; } m_todo_pr.push_back(tp_elem(n1, n2)); - return 0; + return nullptr; } /** @@ -766,12 +766,12 @@ namespace smt { */ proof * conflict_resolution::norm_eq_proof(enode * n1, enode * n2, proof * pr) { if (!pr) - return 0; + return nullptr; SASSERT(m_manager.has_fact(pr)); app * fact = to_app(m_manager.get_fact(pr)); app * n1_owner = n1->get_owner(); app * n2_owner = n2->get_owner(); - bool is_eq = m_manager.is_eq(fact) || m_manager.is_iff(fact); + bool is_eq = m_manager.is_eq(fact); if (!is_eq || (fact->get_arg(0) != n2_owner && fact->get_arg(1) != n2_owner)) { CTRACE("norm_eq_proof_bug", !m_ctx.is_true(n2) && !m_ctx.is_false(n2), tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() << "\n"; @@ -794,7 +794,7 @@ namespace smt { TRACE("norm_eq_proof", tout << "#" << n1->get_owner_id() << " = #" << n2->get_owner_id() << "\n"; tout << mk_ll_pp(pr, m_manager, true, false);); - SASSERT(m_manager.is_eq(fact) || m_manager.is_iff(fact)); + SASSERT(m_manager.is_eq(fact)); SASSERT((fact->get_arg(0) == n1->get_owner() && fact->get_arg(1) == n2->get_owner()) || (fact->get_arg(1) == n1->get_owner() && fact->get_arg(0) == n2->get_owner())); if (fact->get_arg(0) == n1_owner && fact->get_arg(1) == n2_owner) @@ -814,7 +814,7 @@ namespace smt { switch (js.get_kind()) { case eq_justification::AXIOM: UNREACHABLE(); - return 0; + return nullptr; case eq_justification::EQUATION: TRACE("proof_gen_bug", tout << js.get_literal() << "\n"; m_ctx.display_literal_info(tout, js.get_literal());); return norm_eq_proof(n1, n2, get_proof(js.get_literal())); @@ -845,11 +845,11 @@ namespace smt { visited = false; } if (!visited) - return 0; + return nullptr; app * e1 = n1->get_owner(); app * e2 = n2->get_owner(); app * e2_prime = m_manager.mk_app(e2->get_decl(), e2->get_arg(1), e2->get_arg(0)); - proof * pr1 = 0; + proof * pr1 = nullptr; if (!prs.empty()) { pr1 = m_manager.mk_congruence(e1, e2_prime, prs.size(), prs.c_ptr()); m_new_proofs.push_back(pr1); @@ -877,14 +877,14 @@ namespace smt { } } if (!visited) - return 0; + return nullptr; proof * pr = m_manager.mk_congruence(n1->get_owner(), n2->get_owner(), prs.size(), prs.c_ptr()); m_new_proofs.push_back(pr); return pr; } default: UNREACHABLE(); - return 0; + return nullptr; } } @@ -899,7 +899,7 @@ namespace smt { return pr; } m_todo_pr.push_back(tp_elem(l)); - return 0; + return nullptr; } /** @@ -945,7 +945,7 @@ namespace smt { SASSERT(js); proof * pr = get_proof(js); ptr_buffer prs; - bool visited = pr != 0; + bool visited = pr != nullptr; TRACE("get_proof_bug", if (pr != 0) tout << js->get_name() << "\n";); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, if (pr != 0) tout << js->get_name() << "\n";); CTRACE("get_proof_bug_after", invocation_counter >= DUMP_AFTER_NUM_INVOCATIONS, if (pr != 0) js->display_debug_info(*this, tout);); @@ -973,7 +973,7 @@ namespace smt { visited = false; } if (!visited) - return 0; + return nullptr; expr_ref l_exr(m_manager); m_ctx.literal2expr(l, l_exr); TRACE("get_proof_bug", @@ -1033,8 +1033,9 @@ namespace smt { return pr; } SASSERT(js != 0); + TRACE("proof_gen_bug", tout << js << "\n";); m_todo_pr.push_back(tp_elem(js)); - return 0; + return nullptr; } void conflict_resolution::init_mk_proof() { @@ -1061,7 +1062,7 @@ namespace smt { SASSERT(js.get_kind() != b_justification::AXIOM); if (js.get_kind() == b_justification::CLAUSE) { clause * cls = js.get_clause(); - bool visited = get_proof(cls->get_justification()) != 0; + bool visited = get_proof(cls->get_justification()) != nullptr; unsigned num_lits = cls->get_num_literals(); unsigned i = 0; if (l != false_literal) { @@ -1070,20 +1071,20 @@ namespace smt { } else { SASSERT(cls->get_literal(1) == l); - if (get_proof(~cls->get_literal(0)) == 0) + if (get_proof(~cls->get_literal(0)) == nullptr) visited = false; i = 2; } } for (; i < num_lits; i++) { SASSERT(cls->get_literal(i) != l); - if (get_proof(~cls->get_literal(i)) == 0) + if (get_proof(~cls->get_literal(i)) == nullptr) visited = false; } return visited; } else - return get_proof(js.get_justification()) != 0; + return get_proof(js.get_justification()) != nullptr; } void conflict_resolution::mk_proof(literal l, b_justification js) { @@ -1114,11 +1115,11 @@ namespace smt { UNREACHABLE(); break; case eq_justification::EQUATION: - if (get_proof(js.get_literal()) == 0) + if (get_proof(js.get_literal()) == nullptr) visited = false; break; case eq_justification::JUSTIFICATION: - if (get_proof(js.get_justification()) == 0) + if (get_proof(js.get_justification()) == nullptr) visited = false; break; case eq_justification::CONGRUENCE: { @@ -1132,16 +1133,16 @@ namespace smt { enode * c1_2 = n1->get_arg(1); enode * c2_1 = n2->get_arg(0); enode * c2_2 = n2->get_arg(1); - if (c1_1 != c2_2 && get_proof(c1_1, c2_2) == 0) + if (c1_1 != c2_2 && get_proof(c1_1, c2_2) == nullptr) visited = false; - if (c1_2 != c2_1 && get_proof(c1_2, c2_1) == 0) + if (c1_2 != c2_1 && get_proof(c1_2, c2_1) == nullptr) visited = false; } else { for (unsigned i = 0; i < num_args; i++) { enode * c1 = n1->get_arg(i); enode * c2 = n2->get_arg(i); - if (c1 != c2 && get_proof(c1, c2) == 0) + if (c1 != c2 && get_proof(c1, c2) == nullptr) visited = false; } } @@ -1204,7 +1205,7 @@ namespace smt { } prs2.pop_back(); } - proof * pr = 0; + proof * pr = nullptr; SASSERT(!prs1.empty()); if (prs1.size() == 1) pr = prs1[0]; @@ -1290,13 +1291,13 @@ namespace smt { } SASSERT(visit_b_justification(consequent, conflict)); - proof * pr = 0; + proof * pr = nullptr; if (not_l == null_literal) { pr = get_proof(false_literal, conflict); SASSERT(pr); } else { - proof * prs[2] = { 0, 0}; + proof * prs[2] = { nullptr, nullptr}; m_lit2proof.find(not_l, prs[0]); SASSERT(prs[0]); prs[1] = get_proof(consequent, conflict); @@ -1310,13 +1311,13 @@ namespace smt { m_ctx.literal2expr(lit, l_expr); lits.push_back(l_expr); } - expr * fact = 0; + expr * fact = nullptr; switch (lits.size()) { - case 0: fact = 0; break; + case 0: fact = nullptr; break; case 1: fact = lits[0]; break; default: fact = m_manager.mk_or(lits.size(), lits.c_ptr()); } - if (fact == 0) + if (fact == nullptr) m_lemma_proof = pr; else m_lemma_proof = m_manager.mk_lemma(pr, fact); diff --git a/src/smt/smt_conflict_resolution.h b/src/smt/smt_conflict_resolution.h index b5b857184..90390ae7e 100644 --- a/src/smt/smt_conflict_resolution.h +++ b/src/smt/smt_conflict_resolution.h @@ -96,7 +96,7 @@ namespace smt { }; tp_elem(literal l):m_kind(LITERAL), m_lidx(l.index()) {} tp_elem(enode * lhs, enode * rhs):m_kind(EQUALITY), m_lhs(lhs), m_rhs(rhs) {} - tp_elem(justification * js):m_kind(JUSTIFICATION), m_js(js) {} + tp_elem(justification * js):m_kind(JUSTIFICATION), m_js(js) { SASSERT(js);} }; svector m_todo_pr; diff --git a/src/smt/smt_consequences.cpp b/src/smt/smt_consequences.cpp index 0bf2a2939..4cb331661 100644 --- a/src/smt/smt_consequences.cpp +++ b/src/smt/smt_consequences.cpp @@ -244,7 +244,7 @@ namespace smt { } literal lit = mk_diseq(k, v); literals.push_back(lit); - mk_clause(literals.size(), literals.c_ptr(), 0); + mk_clause(literals.size(), literals.c_ptr(), nullptr); TRACE("context", display_literals_verbose(tout, literals.size(), literals.c_ptr());); } } @@ -637,15 +637,14 @@ namespace smt { model_ref mdl; for (unsigned i = 0; i < unfixed.size(); ++i) { push(); - for (unsigned j = 0; j < assumptions.size(); ++j) { - assert_expr(assumptions[j]); - } + for (expr* a : assumptions) + assert_expr(a); TRACE("context", tout << "checking unfixed: " << mk_pp(unfixed[i], m) << "\n";); lbool is_sat = check(); SASSERT(is_sat != l_false); if (is_sat == l_true) { get_model(mdl); - mdl->eval(unfixed[i], tmp); + tmp = (*mdl)(unfixed[i]); if (m.is_value(tmp)) { tmp = m.mk_not(m.mk_eq(unfixed[i], tmp)); assert_expr(tmp); diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 3b69d4846..48033f9b5 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -45,13 +45,13 @@ namespace smt { m_fparams(p), m_params(_p), m_setup(*this, p), - m_asserted_formulas(m, p), + m_asserted_formulas(m, p, _p), m_qmanager(alloc(quantifier_manager, *this, p, _p)), m_model_generator(alloc(model_generator, m)), m_relevancy_propagator(mk_relevancy_propagator(*this)), m_random(p.m_random_seed), m_flushing(false), - m_progress_callback(0), + m_progress_callback(nullptr), m_next_progress_sample(0), m_fingerprints(m_region), m_b_internalized_stack(m), @@ -59,7 +59,7 @@ namespace smt { m_final_check_idx(0), m_cg_table(m), m_dyn_ack_manager(*this, p), - m_is_diseq_tmp(0), + m_is_diseq_tmp(nullptr), m_units_to_reassert(m_manager), m_qhead(0), m_simp_qhead(0), @@ -132,6 +132,10 @@ namespace smt { return !m_manager.limit().inc(); } + void context::updt_params(params_ref const& p) { + m_params.append(p); + m_asserted_formulas.updt_params(p); + } void context::copy(context& src_ctx, context& dst_ctx) { ast_manager& dst_m = dst_ctx.get_manager(); @@ -176,7 +180,7 @@ namespace smt { for (unsigned i = 0; i < src_ctx.m_assigned_literals.size(); ++i) { literal lit; lit = TRANSLATE(src_ctx.m_assigned_literals[i]); - dst_ctx.mk_clause(1, &lit, 0, CLS_AUX, 0); + dst_ctx.mk_clause(1, &lit, nullptr, CLS_AUX, nullptr); } #if 0 literal_vector lits; @@ -199,8 +203,8 @@ namespace smt { literal l1 = to_literal(l_idx); literal neg_l1 = ~l1; watch_list const & wl = *it; - literal const * it2 = wl.begin_literals(); - literal const * end2 = wl.end_literals(); + literal const * it2 = wl.begin(); + literal const * end2 = wl.end(); for (; it2 != end2; ++it2) { literal l2 = *it2; if (l1.index() < l2.index()) { @@ -232,8 +236,8 @@ namespace smt { } context * context::mk_fresh(symbol const * l, smt_params * p) { - context * new_ctx = alloc(context, m_manager, p == 0 ? m_fparams : *p); - new_ctx->set_logic(l == 0 ? m_setup.get_logic() : *l); + context * new_ctx = alloc(context, m_manager, p == nullptr ? m_fparams : *p); + new_ctx->set_logic(l == nullptr ? m_setup.get_logic() : *l); copy_plugins(*this, *new_ctx); return new_ctx; } @@ -381,8 +385,8 @@ namespace smt { it2++; } else { - literal * it3 = cls->begin_literals() + 2; - literal * end3 = cls->end_literals(); + literal * it3 = cls->begin() + 2; + literal * end3 = cls->end(); for(; it3 != end3; ++it3) { if (get_assignment(*it3) != l_false) { // swap literal *it3 with literal at position 0 @@ -476,7 +480,7 @@ namespace smt { m_r2_num_parents(r2_num_parents) { } - virtual void undo(context & ctx) { + void undo(context & ctx) override { ctx.undo_add_eq(m_r1, m_n1, m_r2_num_parents); } }; @@ -623,10 +627,7 @@ namespace smt { */ void context::remove_parents_from_cg_table(enode * r1) { // Remove parents from the congruence table - enode_vector::iterator it = r1->begin_parents(); - enode_vector::iterator end = r1->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : enode::parents(r1)) { #if 0 { static unsigned num_eqs = 0; @@ -671,10 +672,7 @@ namespace smt { */ void context::reinsert_parents_into_cg_table(enode * r1, enode * r2, enode * n1, enode * n2, eq_justification js) { enode_vector & r2_parents = r2->m_parents; - enode_vector::iterator it = r1->begin_parents(); - enode_vector::iterator end = r1->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : enode::parents(r1)) { if (!parent->is_marked()) continue; parent->unset_mark(); @@ -741,9 +739,9 @@ namespace smt { enode * curr = n->m_trans.m_target; enode * prev = n; eq_justification js = n->m_trans.m_justification; - prev->m_trans.m_target = 0; + prev->m_trans.m_target = nullptr; prev->m_trans.m_justification = null_eq_justification; - while (curr != 0) { + while (curr != nullptr) { SASSERT(prev->trans_reaches(n)); enode * new_curr = curr->m_trans.m_target; eq_justification new_js = curr->m_trans.m_justification; @@ -798,7 +796,7 @@ namespace smt { theory_var context::get_closest_var(enode * n, theory_id th_id) { if (th_id == null_theory_id) return null_theory_var; - while (n != 0) { + while (n != nullptr) { theory_var v = n->get_th_var(th_id); if (v != null_theory_var) return v; @@ -826,7 +824,7 @@ namespace smt { if (js.get_kind() == eq_justification::JUSTIFICATION) from_th = js.get_justification()->get_from_theory(); - if (r2->m_th_var_list.get_next() == 0 && r1->m_th_var_list.get_next() == 0) { + if (r2->m_th_var_list.get_next() == nullptr && r1->m_th_var_list.get_next() == nullptr) { // Common case: r2 and r1 have at most one theory var. theory_id t2 = r2->m_th_var_list.get_th_id(); theory_id t1 = r1->m_th_var_list.get_th_id(); @@ -1004,10 +1002,7 @@ namespace smt { r2->m_parents.shrink(r2_num_parents); // try to reinsert parents of r1 that are not cgr - it = r1->begin_parents(); - end = r1->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : enode::parents(r1)) { TRACE("add_eq_parents", tout << "visiting: #" << parent->get_owner_id() << "\n";); if (parent->is_cgc_enabled()) { enode * cg = parent->m_cg; @@ -1023,7 +1018,7 @@ namespace smt { } // restore theory vars - if (r2->m_th_var_list.get_next() == 0) { + if (r2->m_th_var_list.get_next() == nullptr) { // common case: r2 has at most one variable theory_var v2 = r2->m_th_var_list.get_th_var(); if (v2 != null_theory_var) { @@ -1043,7 +1038,7 @@ namespace smt { // r1 -> .. -> n1 -> n2 -> ... -> r2 SASSERT(r1->trans_reaches(r2)); SASSERT(r1->trans_reaches(n1)); - n1->m_trans.m_target = 0; + n1->m_trans.m_target = nullptr; n1->m_trans.m_justification = null_eq_justification; invert_trans(r1); // --------------- @@ -1067,7 +1062,7 @@ namespace smt { */ void context::restore_theory_vars(enode * r2, enode * r1) { SASSERT(r2->get_root() == r2); - theory_var_list * new_l2 = 0; + theory_var_list * new_l2 = nullptr; theory_var_list * l2 = r2->get_th_var_list(); while (l2) { theory_var v2 = l2->get_th_var(); @@ -1091,11 +1086,11 @@ namespace smt { } if (new_l2) { - new_l2->set_next(0); + new_l2->set_next(nullptr); } else { r2->m_th_var_list.set_th_var(null_theory_var); - r2->m_th_var_list.set_next(0); + r2->m_th_var_list.set_next(nullptr); } } @@ -1128,7 +1123,7 @@ namespace smt { } // Propagate disequalities to theories - if (r1->m_th_var_list.get_next() == 0 && r2->m_th_var_list.get_next() == 0) { + if (r1->m_th_var_list.get_next() == nullptr && r2->m_th_var_list.get_next() == nullptr) { // common case: r2 and r1 have at most one theory var. theory_id t1 = r1->m_th_var_list.get_th_id(); theory_var v1 = m_fparams.m_new_core2th_eq ? get_closest_var(n1, t1) : r1->m_th_var_list.get_th_var(); @@ -1202,10 +1197,7 @@ namespace smt { bool context::is_diseq_slow(enode * n1, enode * n2) const { if (n1->get_num_parents() > n2->get_num_parents()) std::swap(n1, n2); - enode_vector::iterator it = n1->begin_parents(); - enode_vector::iterator end = n1->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : enode::parents(n1)) { if (parent->is_eq() && is_relevant(parent->get_owner()) && get_assignment(enode2bool_var(parent)) == l_false && ((parent->get_arg(0)->get_root() == n1->get_root() && parent->get_arg(1)->get_root() == n2->get_root()) || (parent->get_arg(1)->get_root() == n1->get_root() && parent->get_arg(0)->get_root() == n2->get_root()))) { @@ -1237,10 +1229,7 @@ namespace smt { return false; if (r1->get_num_parents() < SMALL_NUM_PARENTS) { TRACE("is_ext_diseq", tout << mk_bounded_pp(n1->get_owner(), m_manager) << " " << mk_bounded_pp(n2->get_owner(), m_manager) << " " << depth << "\n";); - enode_vector::iterator it1 = r1->begin_parents(); - enode_vector::iterator end1 = r1->end_parents(); - for (; it1 != end1; ++it1) { - enode * p1 = *it1; + for (enode * p1 : enode::parents(r1)) { if (!is_relevant(p1)) continue; if (p1->is_eq()) @@ -1250,10 +1239,7 @@ namespace smt { func_decl * f = p1->get_decl(); TRACE("is_ext_diseq", tout << "p1: " << mk_bounded_pp(p1->get_owner(), m_manager) << "\n";); unsigned num_args = p1->get_num_args(); - enode_vector::iterator it2 = r2->begin_parents(); - enode_vector::iterator end2 = r2->end_parents(); - for (; it2 != end2; ++it2) { - enode * p2 = *it2; + for (enode * p2 : enode::parents(r2)) { if (!is_relevant(p2)) continue; if (p2->is_eq()) @@ -1291,10 +1277,7 @@ namespace smt { } almost_cg_table & table = *(m_almost_cg_tables[depth]); table.reset(r1, r2); - enode_vector::iterator it1 = r1->begin_parents(); - enode_vector::iterator end1 = r1->end_parents(); - for (; it1 != end1; ++it1) { - enode * p1 = *it1; + for (enode * p1 : enode::parents(r1)) { if (!is_relevant(p1)) continue; if (p1->is_eq()) @@ -1305,10 +1288,7 @@ namespace smt { } if (table.empty()) return false; - enode_vector::iterator it2 = r2->begin_parents(); - enode_vector::iterator end2 = r2->end_parents(); - for (; it2 != end2; ++it2) { - enode * p2 = *it2; + for (enode * p2 : enode::parents(r2)) { if (!is_relevant(p2)) continue; if (p2->is_eq()) @@ -1451,7 +1431,7 @@ namespace smt { bool_var m_var; public: set_var_theory_trail(bool_var v):m_var(v) {} - virtual void undo(context & ctx) { + void undo(context & ctx) override { bool_var_data & d = ctx.m_bdata[m_var]; d.reset_notify_theory(); } @@ -1515,10 +1495,7 @@ namespace smt { } TRACE("push_new_th_diseqs", tout << "#" << r->get_owner_id() << " v" << v << "\n";); theory_id th_id = th->get_id(); - enode_vector::iterator it = r->begin_parents(); - enode_vector::iterator end = r->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : r->get_parents()) { CTRACE("parent_bug", parent == 0, tout << "#" << r->get_owner_id() << ", num_parents: " << r->get_num_parents() << "\n"; display(tout);); if (parent->is_eq()) { bool_var bv = get_bool_var_of_id(parent->get_owner_id()); @@ -1646,11 +1623,11 @@ namespace smt { m_qmanager->relevant_eh(e); } - theory * propagated_th = 0; + theory * propagated_th = nullptr; family_id fid = to_app(n)->get_family_id(); if (fid != m_manager.get_basic_family_id()) { theory * th = get_theory(fid); - if (th != 0) { + if (th != nullptr) { th->relevant_eh(to_app(n)); propagated_th = th; // <<< mark that relevancy_eh was already invoked for theory th. } @@ -1791,7 +1768,7 @@ namespace smt { } } - void context::set_conflict(b_justification js, literal not_l) { + void context::set_conflict(const b_justification & js, literal not_l) { if (!inconsistent()) { TRACE("set_conflict", display_literal_verbose(tout, not_l); display(tout, js); ); m_conflict = js; @@ -1822,11 +1799,31 @@ namespace smt { m_bvar_inc *= INV_ACTIVITY_LIMIT; } + expr* context::next_decision() { + bool_var var; + lbool phase; + m_case_split_queue->next_case_split(var, phase); + if (var == null_bool_var) return m_manager.mk_true(); + m_case_split_queue->unassign_var_eh(var); + return bool_var2expr(var); + } + /** \brief Execute next clase split, return false if there are no more case splits to be performed. */ bool context::decide() { + + if (at_search_level() && !m_tmp_clauses.empty()) { + switch (decide_clause()) { + case l_true: // already satisfied + break; + case l_undef: // made a decision + return true; + case l_false: // inconsistent + return false; + } + } bool_var var; lbool phase; m_case_split_queue->next_case_split(var, phase); @@ -1982,6 +1979,15 @@ namespace smt { m_watches[(~cls->get_literal(idx)).index()].remove_clause(cls); } + /** + \brief Remove boolean variable from watch lists. + */ + void context::remove_watch(bool_var v) { + literal lit(v); + m_watches[lit.index()].reset(); + m_watches[(~lit).index()].reset(); + } + /** \brief Update the index used for backward subsumption. */ @@ -2157,7 +2163,7 @@ namespace smt { \brief See cache_generation(unsigned new_scope_lvl) */ void context::cache_generation(clause const * cls, unsigned new_scope_lvl) { - cache_generation(cls->get_num_literals(), cls->begin_literals(), new_scope_lvl); + cache_generation(cls->get_num_literals(), cls->begin(), new_scope_lvl); } /** @@ -2193,9 +2199,7 @@ namespace smt { TRACE("cached_generation", tout << "caching: #" << n->get_id() << " " << e->get_generation() << "\n";); m_cached_generation.insert(n, e->get_generation()); } - unsigned num_args = to_app(n)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = to_app(n)->get_arg(i); + for (expr * arg : *to_app(n)) { if (is_app(arg) || is_quantifier(arg)) todo.push_back(arg); } @@ -2412,7 +2416,7 @@ namespace smt { if (!bs.m_inconsistent) { m_conflict = null_b_justification; m_not_l = null_literal; - m_unsat_proof = 0; + m_unsat_proof = nullptr; } m_base_scopes.shrink(new_lvl); } @@ -2532,7 +2536,7 @@ namespace smt { if (m_manager.proofs_enabled() && !simp_lits.empty()) { SASSERT(m_scope_lvl == m_base_lvl); justification * js = cls->get_justification(); - justification * new_js = 0; + justification * new_js = nullptr; if (js->in_region()) new_js = mk_justification(unit_resolution_justification(m_region, js, @@ -2586,7 +2590,7 @@ namespace smt { } } justification * cls_js = cls->get_justification(); - justification * js = 0; + justification * js = nullptr; if (!cls_js || cls_js->in_region()) { // If cls_js is 0 or is allocated in a region, then // we can allocate the new justification in a region too. @@ -2598,7 +2602,7 @@ namespace smt { else { js = alloc(unit_resolution_justification, cls_js, simp_lits.size(), simp_lits.c_ptr()); // js took ownership of the justification object. - cls->set_justification(0); + cls->set_justification(nullptr); m_justifications.push_back(js); } set_justification(v0, m_bdata[v0], b_justification(js)); @@ -2643,10 +2647,11 @@ namespace smt { } TRACE("simplify_clauses_detail", tout << "before:\n"; display_clauses(tout, m_lemmas);); + IF_VERBOSE(2, verbose_stream() << "(smt.simplifying-clause-set"; verbose_stream().flush();); + SASSERT(check_clauses(m_lemmas)); SASSERT(check_clauses(m_aux_clauses)); - IF_VERBOSE(2, verbose_stream() << "(smt.simplifying-clause-set"; verbose_stream().flush();); // m_simp_counter is used to balance the cost of simplify_clause. // @@ -2851,7 +2856,7 @@ namespace smt { #endif void context::register_plugin(theory * th) { - if (m_theories.get_plugin(th->get_family_id()) != 0) { + if (m_theories.get_plugin(th->get_family_id()) != nullptr) { dealloc(th); return; // context already has a theory for the given family id. } @@ -2905,20 +2910,21 @@ namespace smt { void context::flush() { flet l1(m_flushing, true); TRACE("flush", tout << "m_scope_lvl: " << m_scope_lvl << "\n";); - m_relevancy_propagator = 0; + m_relevancy_propagator = nullptr; m_model_generator->reset(); for (theory* t : m_theory_set) t->flush_eh(); undo_trail_stack(0); - m_qmanager = 0; + m_qmanager = nullptr; del_clauses(m_aux_clauses, 0); del_clauses(m_lemmas, 0); del_justifications(m_justifications, 0); + reset_tmp_clauses(); if (m_is_diseq_tmp) { m_is_diseq_tmp->del_eh(m_manager, false); m_manager.dec_ref(m_is_diseq_tmp->get_owner()); enode::del_dummy(m_is_diseq_tmp); - m_is_diseq_tmp = 0; + m_is_diseq_tmp = nullptr; } std::for_each(m_almost_cg_tables.begin(), m_almost_cg_tables.end(), delete_proc()); } @@ -2929,7 +2935,7 @@ namespace smt { TRACE("begin_assert_expr", tout << mk_pp(e, m_manager) << "\n";); TRACE("begin_assert_expr_ll", tout << mk_ll_pp(e, m_manager) << "\n";); pop_to_base_lvl(); - if (pr == 0) + if (pr == nullptr) m_asserted_formulas.assert_expr(e); else m_asserted_formulas.assert_expr(e, pr); @@ -2937,7 +2943,7 @@ namespace smt { } void context::assert_expr(expr * e) { - assert_expr(e, 0); + assert_expr(e, nullptr); } void context::assert_expr(expr * e, proof * pr) { @@ -2951,7 +2957,7 @@ namespace smt { case_split_insert_trail(literal l): l(l) { } - virtual void undo(context & ctx) { + void undo(context & ctx) override { ctx.undo_th_case_split(l); } }; @@ -2966,7 +2972,7 @@ namespace smt { for (unsigned j = i+1; j < num_lits; ++j) { literal l1 = lits[i]; literal l2 = lits[j]; - mk_clause(~l1, ~l2, (justification*) 0); + mk_clause(~l1, ~l2, (justification*) nullptr); } } } else { @@ -3056,12 +3062,47 @@ namespace smt { bool context::reduce_assertions() { if (!m_asserted_formulas.inconsistent()) { - SASSERT(at_base_level()); + // SASSERT(at_base_level()); m_asserted_formulas.reduce(); } return m_asserted_formulas.inconsistent(); } + static bool is_valid_assumption(ast_manager & m, expr * assumption) { + expr* arg; + if (!m.is_bool(assumption)) + return false; + if (is_uninterp_const(assumption)) + return true; + if (m.is_not(assumption, arg) && is_uninterp_const(arg)) + return true; + if (!is_app(assumption)) + return false; + if (to_app(assumption)->get_num_args() == 0) + return true; + if (m.is_not(assumption, arg) && is_app(arg) && to_app(arg)->get_num_args() == 0) + return true; + return false; + } + + void context::internalize_proxies(expr_ref_vector const& asms, vector>& asm2proxy) { + for (expr* e : asms) { + if (is_valid_assumption(m_manager, e)) { + asm2proxy.push_back(std::make_pair(e, expr_ref(e, m_manager))); + } + else { + expr_ref proxy(m_manager), fml(m_manager); + proxy = m_manager.mk_fresh_const("proxy", m_manager.mk_bool_sort()); + fml = m_manager.mk_implies(proxy, e); + m_asserted_formulas.assert_expr(fml); + asm2proxy.push_back(std::make_pair(e, proxy)); + } + } + // The new assertions are of the form 'proxy => assumption' + // so clause simplification is sound even as these are removed after pop_scope. + internalize_assertions(); + } + void context::internalize_assertions() { if (get_cancel_flag()) return; TRACE("internalize_assertions", tout << "internalize_assertions()...\n";); @@ -3084,7 +3125,7 @@ namespace smt { } if (m_asserted_formulas.inconsistent() && !inconsistent()) { proof * pr = m_asserted_formulas.get_inconsistency_proof(); - if (pr == 0) { + if (pr == nullptr) { set_conflict(b_justification::mk_axiom()); } else { @@ -3094,32 +3135,16 @@ namespace smt { } TRACE("internalize_assertions", tout << "after internalize_assertions()...\n"; tout << "inconsistent: " << inconsistent() << "\n";); - } - - bool is_valid_assumption(ast_manager & m, expr * assumption) { - expr* arg; - if (!m.is_bool(assumption)) - return false; - if (is_uninterp_const(assumption)) - return true; - if (m.is_not(assumption, arg) && is_uninterp_const(arg)) - return true; - if (!is_app(assumption)) - return false; - if (to_app(assumption)->get_num_args() == 0) - return true; - if (m.is_not(assumption, arg) && is_app(arg) && to_app(arg)->get_num_args() == 0) - return true; - return false; + TRACE("after_internalize_assertions", display(tout);); } /** \brief Assumptions must be uninterpreted boolean constants (aka propositional variables). */ - bool context::validate_assumptions(unsigned num_assumptions, expr * const * assumptions) { - for (unsigned i = 0; i < num_assumptions; i++) { - SASSERT(assumptions[i]); - if (!is_valid_assumption(m_manager, assumptions[i])) { + bool context::validate_assumptions(expr_ref_vector const& asms) { + for (expr* a : asms) { + SASSERT(a); + if (!is_valid_assumption(m_manager, a)) { warning_msg("an assumption must be a propositional variable or the negation of one"); return false; } @@ -3127,11 +3152,69 @@ namespace smt { return true; } - void context::init_assumptions(unsigned num_assumptions, expr * const * assumptions) { + void context::init_clause(expr_ref_vector const& _clause) { + literal_vector lits; + for (expr* lit : _clause) { + internalize_formula(lit, true); + mark_as_relevant(lit); + lits.push_back(get_literal(lit)); + } + clause* clausep = nullptr; + if (lits.size() >= 2) { + justification* js = nullptr; + if (m_manager.proofs_enabled()) { + proof * pr = mk_clause_def_axiom(lits.size(), lits.c_ptr(), nullptr); + js = mk_justification(justification_proof_wrapper(*this, pr)); + } + clausep = clause::mk(m_manager, lits.size(), lits.c_ptr(), CLS_AUX, js); + } + m_tmp_clauses.push_back(std::make_pair(clausep, lits)); + } + + void context::reset_tmp_clauses() { + for (auto& p : m_tmp_clauses) { + if (p.first) del_clause(p.first); + } + m_tmp_clauses.reset(); + } + + lbool context::decide_clause() { + if (m_tmp_clauses.empty()) return l_true; + for (auto & tmp_clause : m_tmp_clauses) { + literal_vector& lits = tmp_clause.second; + for (literal l : lits) { + switch (get_assignment(l)) { + case l_false: + break; + case l_true: + goto next_clause; + default: + shuffle(lits.size(), lits.c_ptr(), m_random); + push_scope(); + assign(l, b_justification::mk_axiom(), true); + return l_undef; + } + } + + if (lits.size() == 1) { + set_conflict(b_justification(), ~lits[0]); + } + else { + set_conflict(b_justification(tmp_clause.first), null_literal); + } + VERIFY(!resolve_conflict()); + return l_false; + next_clause: + ; + } + return l_true; + } + + void context::init_assumptions(expr_ref_vector const& asms) { reset_assumptions(); m_literal2assumption.reset(); m_unsat_core.reset(); - if (num_assumptions > 0) { + if (!asms.empty()) { // We must give a chance to the theories to propagate before we create a new scope... propagate(); // Internal backtracking scopes (created with push_scope()) must only be created when we are @@ -3141,17 +3224,21 @@ namespace smt { if (get_cancel_flag()) return; push_scope(); - for (unsigned i = 0; i < num_assumptions; i++) { - expr * curr_assumption = assumptions[i]; + vector> asm2proxy; + internalize_proxies(asms, asm2proxy); + for (auto const& p: asm2proxy) { + expr_ref curr_assumption = p.second; + expr* orig_assumption = p.first; + if (m_manager.is_true(curr_assumption)) continue; SASSERT(is_valid_assumption(m_manager, curr_assumption)); proof * pr = m_manager.mk_asserted(curr_assumption); internalize_assertion(curr_assumption, pr, 0); literal l = get_literal(curr_assumption); - m_literal2assumption.insert(l.index(), curr_assumption); + m_literal2assumption.insert(l.index(), orig_assumption); // mark_as_relevant(l); <<< not needed // internalize_assertion marked l as relevant. SASSERT(is_relevant(l)); - TRACE("assumptions", tout << l << ":" << mk_pp(curr_assumption, m_manager) << "\n";); + TRACE("assumptions", tout << l << ":" << curr_assumption << " " << mk_pp(orig_assumption, m_manager) << "\n";); if (m_manager.proofs_enabled()) assign(l, mk_justification(justification_proof_wrapper(*this, pr))); else @@ -3161,8 +3248,9 @@ namespace smt { } } m_search_lvl = m_scope_lvl; - SASSERT(!(num_assumptions > 0) || m_search_lvl > m_base_lvl); - SASSERT(!(num_assumptions == 0) || m_search_lvl == m_base_lvl); + SASSERT(asms.empty() || m_search_lvl > m_base_lvl); + SASSERT(!asms.empty() || m_search_lvl == m_base_lvl); + TRACE("after_internalization", display(tout);); } void context::reset_assumptions() { @@ -3171,7 +3259,8 @@ namespace smt { m_assumptions.reset(); } - lbool context::mk_unsat_core() { + lbool context::mk_unsat_core(lbool r) { + if (r != l_false) return r; SASSERT(inconsistent()); if (!tracking_assumptions()) { SASSERT(m_assumptions.empty()); @@ -3188,24 +3277,20 @@ namespace smt { SASSERT(m_literal2assumption.contains(l.index())); if (!already_found_assumptions.contains(l.index())) { already_found_assumptions.insert(l.index()); - m_unsat_core.push_back(m_literal2assumption[l.index()]); + expr* orig_assumption = m_literal2assumption[l.index()]; + m_unsat_core.push_back(orig_assumption); + TRACE("assumptions", tout << l << ": " << mk_pp(orig_assumption, m_manager) << "\n";); } } reset_assumptions(); pop_to_base_lvl(); // undo the push_scope() performed by init_assumptions m_search_lvl = m_base_lvl; std::sort(m_unsat_core.c_ptr(), m_unsat_core.c_ptr() + m_unsat_core.size(), ast_lt_proc()); - TRACE("unsat_core_bug", tout << "unsat core:\n"; - unsigned sz = m_unsat_core.size(); - for (unsigned i = 0; i < sz; i++) { - tout << mk_pp(m_unsat_core.get(i), m_manager) << "\n"; - }); + TRACE("unsat_core_bug", tout << "unsat core:\n" << m_unsat_core << "\n";); validate_unsat_core(); // theory validation of unsat core - ptr_vector::iterator th_it = m_theory_set.begin(); - ptr_vector::iterator th_end = m_theory_set.end(); - for (; th_it != th_end; ++th_it) { - lbool theory_result = (*th_it)->validate_unsat_core(m_unsat_core); + for (theory* th : m_theory_set) { + lbool theory_result = th->validate_unsat_core(m_unsat_core); if (theory_result == l_undef) { return l_undef; } @@ -3225,6 +3310,10 @@ namespace smt { m_last_search_failure = MEMOUT; return false; } + reset_tmp_clauses(); + m_unsat_core.reset(); + m_stats.m_num_checks++; + pop_to_base_lvl(); return true; } @@ -3248,8 +3337,7 @@ namespace smt { and before internalizing any formulas. */ lbool context::setup_and_check(bool reset_cancel) { - if (!check_preamble(reset_cancel)) - return l_undef; + if (!check_preamble(reset_cancel)) return l_undef; SASSERT(m_scope_lvl == 0); SASSERT(!m_setup.already_configured()); setup_context(m_fparams.m_auto_config); @@ -3262,20 +3350,8 @@ namespace smt { } internalize_assertions(); - lbool r = l_undef; TRACE("before_search", display(tout);); - if (m_asserted_formulas.inconsistent()) { - r = l_false; - } - else { - if (inconsistent()) { - VERIFY(!resolve_conflict()); // build the proof - r = l_false; - } - else { - r = search(); - } - } + lbool r = search(); r = check_finalize(r); return r; } @@ -3289,17 +3365,15 @@ namespace smt { } void context::setup_context(bool use_static_features) { - if (m_setup.already_configured()) + if (m_setup.already_configured() || inconsistent()) return; m_setup(get_config_mode(use_static_features)); setup_components(); #ifndef _EXTERNAL_RELEASE if (m_fparams.m_display_installed_theories) { std::cout << "(theories"; - ptr_vector::iterator it = m_theory_set.begin(); - ptr_vector::iterator end = m_theory_set.end(); - for (; it != end; ++it) { - std::cout << " " << (*it)->get_name(); + for (theory* th : m_theory_set) { + std::cout << " " << th->get_name(); } std::cout << ")" << std::endl; } @@ -3316,102 +3390,67 @@ namespace smt { m_fparams.m_relevancy_lemma = false; // setup all the theories - ptr_vector::iterator it = m_theory_set.begin(); - ptr_vector::iterator end = m_theory_set.end(); - for (; it != end; ++it) - (*it)->setup(); + for (theory* th : m_theory_set) + th->setup(); } void context::add_theory_assumptions(expr_ref_vector & theory_assumptions) { - ptr_vector::iterator it = m_theory_set.begin(); - ptr_vector::iterator end = m_theory_set.end(); - for (; it != end; ++it) { - (*it)->add_theory_assumptions(theory_assumptions); + for (theory* th : m_theory_set) { + th->add_theory_assumptions(theory_assumptions); } } - lbool context::check(unsigned ext_num_assumptions, expr * const * ext_assumptions, bool reset_cancel, bool already_did_theory_assumptions) { - m_stats.m_num_checks++; - TRACE("check_bug", tout << "STARTING check(num_assumptions, assumptions)\n"; - tout << "inconsistent: " << inconsistent() << ", m_unsat_core.empty(): " << m_unsat_core.empty() << "\n"; - m_asserted_formulas.display(tout); - tout << "-----------------------\n"; - display(tout);); - if (!m_unsat_core.empty()) - m_unsat_core.reset(); - if (!check_preamble(reset_cancel)) - return l_undef; - - expr_ref_vector all_assumptions(m_manager, ext_num_assumptions, ext_assumptions); - if (!already_did_theory_assumptions) { - add_theory_assumptions(all_assumptions); - } - - unsigned num_assumptions = all_assumptions.size(); - expr * const * assumptions = all_assumptions.c_ptr(); - - if (!validate_assumptions(num_assumptions, assumptions)) - return l_undef; - TRACE("check_bug", tout << "inconsistent: " << inconsistent() << ", m_unsat_core.empty(): " << m_unsat_core.empty() << "\n";); - TRACE("unsat_core_bug", for (unsigned i = 0; i < num_assumptions; i++) { tout << mk_pp(assumptions[i], m_manager) << "\n";}); - pop_to_base_lvl(); - TRACE("before_search", display(tout);); + lbool context::check(unsigned num_assumptions, expr * const * assumptions, bool reset_cancel, bool already_did_theory_assumptions) { + if (!check_preamble(reset_cancel)) return l_undef; SASSERT(at_base_level()); - lbool r = l_undef; - if (inconsistent()) { - r = l_false; - } - else { - setup_context(false); - internalize_assertions(); - TRACE("after_internalize_assertions", display(tout);); - if (m_asserted_formulas.inconsistent()) { - r = l_false; - } - else { - init_assumptions(num_assumptions, assumptions); - TRACE("after_internalization", display(tout);); - if (inconsistent()) { - VERIFY(!resolve_conflict()); // build the proof - lbool result = mk_unsat_core(); - if (result == l_undef) { - r = l_undef; - } else { - r = l_false; - } - } - else { - r = search(); - if (r == l_false) { - lbool result = mk_unsat_core(); - if (result == l_undef) { - r = l_undef; - } - } - } - } - } + setup_context(false); + expr_ref_vector asms(m_manager, num_assumptions, assumptions); + if (!already_did_theory_assumptions) add_theory_assumptions(asms); + // introducing proxies: if (!validate_assumptions(asms)) return l_undef; + TRACE("unsat_core_bug", tout << asms << "\n";); + internalize_assertions(); + init_assumptions(asms); + TRACE("before_search", display(tout);); + lbool r = search(); + r = mk_unsat_core(r); r = check_finalize(r); return r; } + lbool context::check(expr_ref_vector const& cube, vector const& clauses) { + if (!check_preamble(true)) return l_undef; + TRACE("before_search", display(tout);); + setup_context(false); + expr_ref_vector asms(cube); + add_theory_assumptions(asms); + // introducing proxies: if (!validate_assumptions(asms)) return l_undef; + for (auto const& clause : clauses) if (!validate_assumptions(clause)) return l_undef; + internalize_assertions(); + init_assumptions(asms); + for (auto const& clause : clauses) init_clause(clause); + lbool r = search(); + r = mk_unsat_core(r); + r = check_finalize(r); + return r; + } + void context::init_search() { for (theory* th : m_theory_set) { th->init_search_eh(); } m_qmanager->init_search_eh(); - m_assumption_core.reset(); m_incomplete_theories.reset(); m_num_conflicts = 0; m_num_conflicts_since_restart = 0; m_num_conflicts_since_lemma_gc = 0; + m_num_restarts = 0; m_restart_threshold = m_fparams.m_restart_initial; m_restart_outer_threshold = m_fparams.m_restart_initial; m_agility = 0.0; m_luby_idx = 1; m_lemma_gc_threshold = m_fparams.m_lemma_gc_initial; m_last_search_failure = OK; - m_unsat_proof = 0; + m_unsat_proof = nullptr; m_unsat_core .reset(); m_dyn_ack_manager .init_search_eh(); m_final_check_idx = 0; @@ -3462,6 +3501,12 @@ namespace smt { exit(1); } #endif + if (m_asserted_formulas.inconsistent()) + return l_false; + if (inconsistent()) { + VERIFY(!resolve_conflict()); + return l_false; + } timeit tt(get_verbosity_level() >= 100, "smt.stats"); scoped_mk_model smk(*this); SASSERT(at_search_level()); @@ -3485,17 +3530,13 @@ namespace smt { if (!restart(status, curr_lvl)) { break; - } + } } - TRACE("search_lite", tout << "status: " << status << "\n";); TRACE("guessed_literals", expr_ref_vector guessed_lits(m_manager); get_guessed_literals(guessed_lits); - unsigned sz = guessed_lits.size(); - for (unsigned i = 0; i < sz; i++) { - tout << mk_pp(guessed_lits.get(i), m_manager) << "\n"; - }); + tout << guessed_lits << "\n";); end_search(); return status; } @@ -3535,7 +3576,7 @@ namespace smt { inc_limits(); if (status == l_true || !m_fparams.m_restart_adaptive || m_agility < m_fparams.m_restart_agility_threshold) { SASSERT(!inconsistent()); - IF_VERBOSE(2, verbose_stream() << "(smt.restarting :propagations " << m_stats.m_num_propagations + IF_VERBOSE(2, verbose_stream() << "(smt.restarting :propagations " << m_stats.m_num_propagations << " :decisions " << m_stats.m_num_decisions << " :conflicts " << m_stats.m_num_conflicts << " :restart " << m_restart_threshold; if (m_fparams.m_restart_strategy == RS_IN_OUT_GEOMETRIC) { @@ -3544,17 +3585,17 @@ namespace smt { if (m_fparams.m_restart_adaptive) { verbose_stream() << " :agility " << m_agility; } - verbose_stream() << ")" << std::endl; verbose_stream().flush();); + verbose_stream() << ")\n"); // execute the restart m_stats.m_num_restarts++; + m_num_restarts++; if (m_scope_lvl > curr_lvl) { pop_scope(m_scope_lvl - curr_lvl); SASSERT(at_search_level()); } - ptr_vector::iterator it = m_theory_set.begin(); - ptr_vector::iterator end = m_theory_set.end(); - for (; it != end && !inconsistent(); ++it) - (*it)->restart_eh(); + for (theory* th : m_theory_set) { + if (!inconsistent()) th->restart_eh(); + } TRACE("mbqi_bug_detail", tout << "before instantiating quantifiers...\n";); if (!inconsistent()) { m_qmanager->restart_eh(); @@ -3564,6 +3605,11 @@ namespace smt { status = l_false; return false; } + if (m_num_restarts >= m_fparams.m_restart_max) { + status = l_undef; + m_last_search_failure = NUM_CONFLICTS; + return false; + } } if (m_fparams.m_simplify_clauses) simplify_clauses(); @@ -3649,6 +3695,8 @@ namespace smt { simplify_clauses(); if (!decide()) { + if (inconsistent()) + return l_false; final_check_status fcs = final_check(); TRACE("final_check_result", tout << "fcs: " << fcs << " last_search_failure: " << m_last_search_failure << "\n";); switch (fcs) { @@ -3706,6 +3754,7 @@ namespace smt { TRACE("final_check", tout << "final_check inconsistent: " << inconsistent() << "\n"; display(tout); display_normalized_enodes(tout);); CASSERT("relevancy", check_relevancy()); + if (m_fparams.m_model_on_final_check) { mk_proto_model(l_undef); model_pp(std::cout, *m_proto_model); @@ -3869,12 +3918,15 @@ namespace smt { svector expr_signs; for (unsigned i = 0; i < num_lits; i++) { literal l = lits[i]; + if (get_assignment(l) != l_false) { + std::cout << l << " " << get_assignment(l) << "\n"; + } SASSERT(get_assignment(l) == l_false); expr_lits.push_back(bool_var2expr(l.var())); expr_signs.push_back(l.sign()); } #endif - proof * pr = 0; + proof * pr = nullptr; if (m_manager.proofs_enabled()) { pr = m_conflict_resolution->get_lemma_proof(); // check_proof(pr); @@ -3947,14 +3999,14 @@ namespace smt { } } #endif - justification * js = 0; + justification * js = nullptr; if (m_manager.proofs_enabled()) { js = alloc(justification_proof_wrapper, *this, pr, false); } #if 0 { static unsigned counter = 0; - static uint64 total = 0; + static uint64_t total = 0; static unsigned max = 0; counter++; total += num_lits; @@ -4070,10 +4122,7 @@ namespace smt { bool include = false; if (at_lbls) { // include if there is a label with the '@' sign. - buffer::const_iterator it = lbls.begin(); - buffer::const_iterator end = lbls.end(); - for (; it != end; ++it) { - symbol const & s = *it; + for (symbol const& s : lbls) { if (s.contains('@')) { include = true; break; @@ -4142,7 +4191,7 @@ namespace smt { bool_var m_var; public: set_true_first_trail(bool_var v):m_var(v) {} - virtual void undo(context & ctx) { + void undo(context & ctx) override { ctx.m_bdata[m_var].reset_true_first_flag(); } }; @@ -4234,10 +4283,7 @@ namespace smt { theory_var_list * l = n->get_th_var_list(); theory_id th_id = l->get_th_id(); - enode_vector::const_iterator it = n->begin_parents(); - enode_vector::const_iterator end = n->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode * parent : enode::parents(n)) { family_id fid = parent->get_owner()->get_family_id(); if (fid != th_id && fid != m_manager.get_basic_family_id()) { TRACE("is_shared", tout << mk_pp(n->get_owner(), m_manager) << "\nis shared because of:\n" << mk_pp(parent->get_owner(), m_manager) << "\n";); @@ -4282,7 +4328,7 @@ namespace smt { sort * s = m_manager.get_sort(n->get_owner()); family_id fid = s->get_family_id(); theory * th = get_theory(fid); - if (th == 0) + if (th == nullptr) return false; return th->get_value(n, value); } @@ -4318,27 +4364,25 @@ namespace smt { m_proto_model = m_model_generator->mk_model(); m_qmanager->adjust_model(m_proto_model.get()); TRACE("mbqi_bug", tout << "before complete_partial_funcs:\n"; model_pp(tout, *m_proto_model);); - m_proto_model->complete_partial_funcs(); + m_proto_model->complete_partial_funcs(false); TRACE("mbqi_bug", tout << "before cleanup:\n"; model_pp(tout, *m_proto_model);); m_proto_model->cleanup(); if (m_fparams.m_model_compact) m_proto_model->compress(); TRACE("mbqi_bug", tout << "after cleanup:\n"; model_pp(tout, *m_proto_model);); - } - else { - + IF_VERBOSE(11, model_pp(verbose_stream(), *m_proto_model);); } } proof * context::get_proof() { if (!m_manager.proofs_enabled()) - return 0; + return nullptr; return m_unsat_proof; } void context::get_model(model_ref & m) const { if (inconsistent()) - m = 0; + m = nullptr; else m = const_cast(m_model.get()); } diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index e50c03c8a..e85348a07 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -168,6 +168,8 @@ namespace smt { expr_ref_vector m_units_to_reassert; svector m_units_to_reassert_sign; literal_vector m_assigned_literals; + typedef std::pair tmp_clause; + vector m_tmp_clauses; unsigned m_qhead; unsigned m_simp_qhead; int m_simp_counter; //!< can become negative @@ -203,11 +205,11 @@ namespace smt { struct scoped_mk_model { context & m_ctx; scoped_mk_model(context & ctx):m_ctx(ctx) { - m_ctx.m_proto_model = 0; - m_ctx.m_model = 0; + m_ctx.m_proto_model = nullptr; + m_ctx.m_model = nullptr; } ~scoped_mk_model() { - if (m_ctx.m_proto_model.get() != 0) { + if (m_ctx.m_proto_model.get() != nullptr) { m_ctx.m_model = m_ctx.m_proto_model->mk_model(); try { m_ctx.add_rec_funs_to_model(); @@ -215,7 +217,7 @@ namespace smt { catch (...) { // no op } - m_ctx.m_proto_model = 0; // proto_model is not needed anymore. + m_ctx.m_proto_model = nullptr; // proto_model is not needed anymore. } } }; @@ -262,6 +264,8 @@ namespace smt { return m_params; } + void updt_params(params_ref const& p); + bool get_cancel_flag(); region & get_region() { @@ -317,6 +321,7 @@ namespace smt { } #endif + clause_vector const& get_lemmas() const { return m_lemmas; } literal get_literal(expr * n) const; @@ -514,12 +519,12 @@ namespace smt { enode_vector::const_iterator begin_enodes_of(func_decl const * decl) const { unsigned id = decl->get_decl_id(); - return id < m_decl2enodes.size() ? m_decl2enodes[id].begin() : 0; + return id < m_decl2enodes.size() ? m_decl2enodes[id].begin() : nullptr; } enode_vector::const_iterator end_enodes_of(func_decl const * decl) const { unsigned id = decl->get_decl_id(); - return id < m_decl2enodes.size() ? m_decl2enodes[id].end() : 0; + return id < m_decl2enodes.size() ? m_decl2enodes[id].end() : nullptr; } ptr_vector::const_iterator begin_enodes() const { @@ -619,8 +624,6 @@ namespace smt { void remove_cls_occs(clause * cls); - void mark_as_deleted(clause * cls); - void del_clause(clause * cls); void del_clauses(clause_vector & v, unsigned old_size); @@ -645,6 +648,14 @@ namespace smt { void reassert_units(unsigned units_to_reassert_lim); + public: + // \brief exposed for PB solver to participate in GC + + void remove_watch(bool_var v); + + void mark_as_deleted(clause * cls); + + // ----------------------------------- // // Internalization @@ -746,7 +757,7 @@ namespace smt { friend class mk_bool_var_trail; class mk_bool_var_trail : public trail { public: - virtual void undo(context & ctx) { ctx.undo_mk_bool_var(); } + void undo(context & ctx) override { ctx.undo_mk_bool_var(); } }; mk_bool_var_trail m_mk_bool_var_trail; @@ -755,7 +766,7 @@ namespace smt { friend class mk_enode_trail; class mk_enode_trail : public trail { public: - virtual void undo(context & ctx) { ctx.undo_mk_enode(); } + void undo(context & ctx) override { ctx.undo_mk_enode(); } }; mk_enode_trail m_mk_enode_trail; @@ -822,17 +833,17 @@ namespace smt { void internalize(expr * n, bool gate_ctx, unsigned generation); - clause * mk_clause(unsigned num_lits, literal * lits, justification * j, clause_kind k = CLS_AUX, clause_del_eh * del_eh = 0); + clause * mk_clause(unsigned num_lits, literal * lits, justification * j, clause_kind k = CLS_AUX, clause_del_eh * del_eh = nullptr); void mk_clause(literal l1, literal l2, justification * j); void mk_clause(literal l1, literal l2, literal l3, justification * j); - void mk_th_axiom(theory_id tid, unsigned num_lits, literal * lits, unsigned num_params = 0, parameter * params = 0); + void mk_th_axiom(theory_id tid, unsigned num_lits, literal * lits, unsigned num_params = 0, parameter * params = nullptr); - void mk_th_axiom(theory_id tid, literal l1, literal l2, unsigned num_params = 0, parameter * params = 0); + void mk_th_axiom(theory_id tid, literal l1, literal l2, unsigned num_params = 0, parameter * params = nullptr); - void mk_th_axiom(theory_id tid, literal l1, literal l2, literal l3, unsigned num_params = 0, parameter * params = 0); + void mk_th_axiom(theory_id tid, literal l1, literal l2, literal l3, unsigned num_params = 0, parameter * params = nullptr); /* * Provide a hint to the core solver that the specified literals form a "theory case split". @@ -883,10 +894,11 @@ namespace smt { failure m_last_search_failure; ptr_vector m_incomplete_theories; //!< theories that failed to produce a model bool m_searching; - ptr_vector m_assumption_core; unsigned m_num_conflicts; unsigned m_num_conflicts_since_restart; unsigned m_num_conflicts_since_lemma_gc; + unsigned m_num_restarts; + unsigned m_num_simplifications; unsigned m_restart_threshold; unsigned m_restart_outer_threshold; unsigned m_luby_idx; @@ -897,7 +909,7 @@ namespace smt { void trace_assign(literal l, b_justification j, bool decision) const; public: - void assign(literal l, b_justification j, bool decision = false) { + void assign(literal l, const b_justification & j, bool decision = false) { SASSERT(l != false_literal); SASSERT(l != null_literal); switch (get_assignment(l)) { @@ -998,9 +1010,9 @@ namespace smt { void assign_quantifier(quantifier * q); - void set_conflict(b_justification js, literal not_l); + void set_conflict(const b_justification & js, literal not_l); - void set_conflict(b_justification js) { + void set_conflict(const b_justification & js) { set_conflict(js, null_literal); } @@ -1028,6 +1040,8 @@ namespace smt { enode * get_enode_eq_to(func_decl * f, unsigned num_args, enode * const * args); + expr* next_decision(); + protected: bool decide(); @@ -1091,15 +1105,23 @@ namespace smt { void assert_assumption(expr * a); - bool validate_assumptions(unsigned num_assumptions, expr * const * assumptions); + bool validate_assumptions(expr_ref_vector const& asms); - void init_assumptions(unsigned num_assumptions, expr * const * assumptions); + void init_assumptions(expr_ref_vector const& asms); + + void init_clause(expr_ref_vector const& clause); + + lbool decide_clause(); + + void reset_tmp_clauses(); void reset_assumptions(); + void reset_clause(); + void add_theory_assumptions(expr_ref_vector & theory_assumptions); - lbool mk_unsat_core(); + lbool mk_unsat_core(lbool result); void validate_unsat_core(); @@ -1387,7 +1409,7 @@ namespace smt { void flush(); config_mode get_config_mode(bool use_static_features) const; virtual void setup_context(bool use_static_features); - void setup_components(void); + void setup_components(); void pop_to_base_lvl(); void pop_to_search_lvl(); #ifdef Z3DEBUG @@ -1460,7 +1482,7 @@ namespace smt { If l == 0, then the logic of this context is used in the new context. If p == 0, then this->m_params is used */ - context * mk_fresh(symbol const * l = 0, smt_params * p = 0); + context * mk_fresh(symbol const * l = nullptr, smt_params * p = nullptr); static void copy(context& src, context& dst); @@ -1482,7 +1504,9 @@ namespace smt { void pop(unsigned num_scopes); - lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0, bool reset_cancel = true, bool already_did_theory_assumptions = false); + lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr, bool reset_cancel = true, bool already_did_theory_assumptions = false); + + lbool check(expr_ref_vector const& cube, vector const& clauses); lbool get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed); @@ -1511,6 +1535,8 @@ namespace smt { void internalize_assertion(expr * n, proof * pr, unsigned generation); + void internalize_proxies(expr_ref_vector const& asms, vector>& asm2proxy); + void internalize_instance(expr * body, proof * pr, unsigned generation) { internalize_assertion(body, pr, generation); if (relevancy()) diff --git a/src/smt/smt_context_inv.cpp b/src/smt/smt_context_inv.cpp index cf09c996a..7f409622b 100644 --- a/src/smt/smt_context_inv.cpp +++ b/src/smt/smt_context_inv.cpp @@ -43,13 +43,9 @@ namespace smt { } bool context::check_clauses(clause_vector const & v) const { - clause_vector::const_iterator it = v.begin(); - clause_vector::const_iterator end = v.end(); - for (; it != end; ++it) { - clause * cls = *it; + for (clause* cls : v) if (!cls->deleted()) check_clause(cls); - } return true; } @@ -92,10 +88,7 @@ namespace smt { bool context::check_lit_occs(literal l) const { clause_set const & v = m_lit_occs[l.index()]; - clause_set::iterator it = v.begin(); - clause_set::iterator end = v.end(); - for (; it != end; ++it) { - clause * cls = *it; + for (clause * cls : v) { unsigned num = cls->get_num_literals(); unsigned i = 0; for (; i < num; i++) @@ -138,10 +131,8 @@ namespace smt { } bool context::check_enodes() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - check_enode(*it); + for (enode* n : m_enodes) { + check_enode(n); } return true; } @@ -157,11 +148,9 @@ namespace smt { } bool context::check_missing_clause_propagation(clause_vector const & v) const { - clause_vector::const_iterator it = v.begin(); - clause_vector::const_iterator end = v.end(); - for (; it != end; ++it) { - CTRACE("missing_propagation", is_unit_clause(*it), display_clause_detail(tout, *it); tout << "\n";); - SASSERT(!is_unit_clause(*it)); + for (clause * cls : v) { + CTRACE("missing_propagation", is_unit_clause(cls), display_clause_detail(tout, cls); tout << "\n";); + SASSERT(!is_unit_clause(cls)); } return true; } @@ -188,10 +177,7 @@ namespace smt { } bool context::check_missing_eq_propagation() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * n = *it; + for (enode* n : m_enodes) { SASSERT(!n->is_true_eq() || get_assignment(n) == l_true); if (n->is_eq() && get_assignment(n) == l_true) { SASSERT(n->is_true_eq()); @@ -201,13 +187,8 @@ namespace smt { } bool context::check_missing_congruence() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * n = *it; - ptr_vector::const_iterator it2 = m_enodes.begin(); - for (; it2 != end; ++it2) { - enode * n2 = *it2; + for (enode* n : m_enodes) { + for (enode* n2 : m_enodes) { if (n->get_root() != n2->get_root()) { if (n->is_true_eq() && n2->is_true_eq()) continue; @@ -222,10 +203,7 @@ namespace smt { } bool context::check_missing_bool_enode_propagation() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * n = *it; + for (enode* n : m_enodes) { if (m_manager.is_bool(n->get_owner()) && get_assignment(n) == l_undef) { enode * first = n; do { @@ -286,10 +264,7 @@ namespace smt { For all a, b. root(a) == root(b) ==> get_assignment(a) == get_assignment(b) */ bool context::check_eqc_bool_assignment() const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * e = *it; + for (enode* e : m_enodes) { if (m_manager.is_bool(e->get_owner())) { enode * r = e->get_root(); CTRACE("eqc_bool", get_assignment(e) != get_assignment(r), @@ -343,24 +318,24 @@ namespace smt { TRACE("check_th_diseq_propagation", tout << "checking theory: " << m_manager.get_family_name(th_id) << "\n";); // if the theory doesn't use diseqs, then the diseqs are not propagated. if (th->use_diseqs() && rhs->get_th_var(th_id) != null_theory_var) { + bool found = false; // lhs and rhs are attached to theory th_id - svector::const_iterator it = m_propagated_th_diseqs.begin(); - svector::const_iterator end = m_propagated_th_diseqs.end(); - for (; it != end; ++it) { - if (it->m_th_id == th_id) { - enode * lhs_prime = th->get_enode(it->m_lhs)->get_root(); - enode * rhs_prime = th->get_enode(it->m_rhs)->get_root(); + for (new_th_eq const& eq : m_propagated_th_diseqs) { + if (eq.m_th_id == th_id) { + enode * lhs_prime = th->get_enode(eq.m_lhs)->get_root(); + enode * rhs_prime = th->get_enode(eq.m_rhs)->get_root(); TRACE("check_th_diseq_propagation", - tout << m_manager.get_family_name(it->m_th_id) << "\n";); + tout << m_manager.get_family_name(eq.m_th_id) << "\n";); if ((lhs == lhs_prime && rhs == rhs_prime) || (rhs == lhs_prime && lhs == rhs_prime)) { TRACE("check_th_diseq_propagation", tout << "ok v" << v << " " << get_assignment(v) << "\n";); + found = true; break; } } } - if (it == end) { + if (!found) { // missed theory diseq propagation display(std::cout); std::cout << "checking theory: " << m_manager.get_family_name(th_id) << "\n"; @@ -369,8 +344,7 @@ namespace smt { std::cout << "lhs: #" << lhs->get_owner_id() << ", rhs: #" << rhs->get_owner_id() << "\n"; std::cout << mk_bounded_pp(lhs->get_owner(), m_manager) << " " << mk_bounded_pp(rhs->get_owner(), m_manager) << "\n"; } - - SASSERT(it != end); + VERIFY(found); } l = l->get_next(); } @@ -381,11 +355,9 @@ namespace smt { } bool context::check_missing_diseq_conflict() const { - svector::const_iterator it = m_diseq_vector.begin(); - svector::const_iterator end = m_diseq_vector.end(); - for (; it != end; ++it) { - enode * n1 = it->first; - enode * n2 = it->second; + for (enode_pair const& p : m_diseq_vector) { + enode * n1 = p.first; + enode * n2 = p.second; if (n1->get_root() == n2->get_root()) { TRACE("diseq_bug", tout << "n1: #" << n1->get_owner_id() << ", n2: #" << n2->get_owner_id() << @@ -420,10 +392,7 @@ namespace smt { return true; } ast_manager& m = m_manager; - literal_vector::const_iterator it = m_assigned_literals.begin(); - literal_vector::const_iterator end = m_assigned_literals.end(); - for (; it != end; ++it) { - literal lit = *it; + for (literal lit : m_assigned_literals) { if (!is_relevant(lit)) { continue; } @@ -435,7 +404,7 @@ namespace smt { if (is_quantifier(n) && m.is_rec_fun_def(to_quantifier(n))) { continue; } - switch (get_assignment(*it)) { + switch (get_assignment(lit)) { case l_undef: break; case l_true: diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index f5ba52128..19247c1d9 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -19,7 +19,7 @@ Revision History: #include "smt/smt_context.h" #include "ast/ast_ll_pp.h" #include "ast/ast_pp.h" -#include "ast/ast_smt_pp.h" +#include "ast/ast_pp_util.h" #include "util/stats.h" namespace smt { @@ -43,11 +43,10 @@ namespace smt { return out << "RESOURCE_LIMIT"; case THEORY: if (!m_incomplete_theories.empty()) { - ptr_vector::const_iterator it = m_incomplete_theories.begin(); - ptr_vector::const_iterator end = m_incomplete_theories.end(); - for (bool first = true; it != end; ++it) { + bool first = true; + for (theory* th : m_incomplete_theories) { if (first) first = false; else out << " "; - out << (*it)->get_name(); + out << th->get_name(); } } else { @@ -173,12 +172,10 @@ namespace smt { void context::display_binary_clauses(std::ostream & out) const { bool first = true; - vector::const_iterator it = m_watches.begin(); - vector::const_iterator end = m_watches.end(); - for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { - literal l1 = to_literal(l_idx); + unsigned l_idx = 0; + for (watch_list const& wl : m_watches) { + literal l1 = to_literal(l_idx++); literal neg_l1 = ~l1; - watch_list const & wl = *it; literal const * it2 = wl.begin_literals(); literal const * end2 = wl.end_literals(); for (; it2 != end2; ++it2) { @@ -291,10 +288,7 @@ namespace smt { } void context::display_theories(std::ostream & out) const { - ptr_vector::const_iterator it = m_theory_set.begin(); - ptr_vector::const_iterator end = m_theory_set.end(); - for (; it != end; ++it) { - theory * th = *it; + for (theory* th : m_theory_set) { th->display(out); } } @@ -347,10 +341,7 @@ namespace smt { } void context::display_parent_eqs(std::ostream & out, enode * n) const { - enode_vector::iterator it = n->begin_parents(); - enode_vector::iterator end = n->end_parents(); - for (; it != end; ++it) { - enode * parent = *it; + for (enode* parent : n->get_parents()) { if (parent->is_eq()) display_eq_detail(out, parent); } @@ -393,10 +384,8 @@ namespace smt { #endif m_qmanager->collect_statistics(st); m_asserted_formulas.collect_statistics(st); - ptr_vector::const_iterator it = m_theory_set.begin(); - ptr_vector::const_iterator end = m_theory_set.end(); - for (; it != end; ++it) { - (*it)->collect_statistics(st); + for (theory* th : m_theory_set) { + th->collect_statistics(st); } } @@ -413,19 +402,23 @@ namespace smt { } void context::display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, literal consequent, symbol const& logic) const { - ast_smt_pp pp(m_manager); - pp.set_benchmark_name("lemma"); - pp.set_status("unsat"); - pp.set_logic(logic); + ast_pp_util visitor(m_manager); + expr_ref_vector fmls(m_manager); + visitor.collect(fmls); + expr_ref n(m_manager); for (unsigned i = 0; i < num_antecedents; i++) { literal l = antecedents[i]; - expr_ref n(m_manager); literal2expr(l, n); - pp.add_assumption(n); + fmls.push_back(n); } - expr_ref n(m_manager); - literal2expr(~consequent, n); - pp.display_smt2(out, n); + if (consequent != false_literal) { + literal2expr(~consequent, n); + fmls.push_back(n); + } + if (logic != symbol::null) out << "(set-logic " << logic << ")\n"; + visitor.collect(fmls); + visitor.display_decls(out); + visitor.display_asserts(out, fmls, true); } static unsigned g_lemma_id = 0; @@ -448,25 +441,29 @@ namespace smt { void context::display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, unsigned num_eq_antecedents, enode_pair const * eq_antecedents, literal consequent, symbol const& logic) const { - ast_smt_pp pp(m_manager); - pp.set_benchmark_name("lemma"); - pp.set_status("unsat"); - pp.set_logic(logic); + ast_pp_util visitor(m_manager); + expr_ref_vector fmls(m_manager); + visitor.collect(fmls); + expr_ref n(m_manager); for (unsigned i = 0; i < num_antecedents; i++) { literal l = antecedents[i]; - expr_ref n(m_manager); literal2expr(l, n); - pp.add_assumption(n); + fmls.push_back(n); } for (unsigned i = 0; i < num_eq_antecedents; i++) { enode_pair const & p = eq_antecedents[i]; - expr_ref eq(m_manager); - eq = m_manager.mk_eq(p.first->get_owner(), p.second->get_owner()); - pp.add_assumption(eq); + n = m_manager.mk_eq(p.first->get_owner(), p.second->get_owner()); + fmls.push_back(n); } - expr_ref n(m_manager); - literal2expr(~consequent, n); - pp.display_smt2(out, n); + if (consequent != false_literal) { + literal2expr(~consequent, n); + fmls.push_back(n); + } + + if (logic != symbol::null) out << "(set-logic " << logic << ")\n"; + visitor.collect(fmls); + visitor.display_decls(out); + visitor.display_asserts(out, fmls, true); } void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, @@ -490,10 +487,7 @@ namespace smt { */ void context::display_normalized_enodes(std::ostream & out) const { out << "normalized enodes:\n"; - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * n = *it; + for (enode * n : m_enodes) { out << "#"; out.width(5); out << std::left << n->get_owner_id() << " #"; @@ -524,28 +518,23 @@ namespace smt { } void context::display_enodes_lbls(std::ostream & out) const { - ptr_vector::const_iterator it = m_enodes.begin(); - ptr_vector::const_iterator end = m_enodes.end(); - for (; it != end; ++it) { - enode * n = *it; + for (enode* n : m_enodes) { n->display_lbls(out); } } void context::display_decl2enodes(std::ostream & out) const { out << "decl2enodes:\n"; - vector::const_iterator it1 = m_decl2enodes.begin(); - vector::const_iterator end1 = m_decl2enodes.end(); - for (unsigned id = 0; it1 != end1; ++it1, ++id) { - enode_vector const & v = *it1; + unsigned id = 0; + for (enode_vector const& v : m_decl2enodes) { if (!v.empty()) { out << "id " << id << " ->"; - enode_vector::const_iterator it2 = v.begin(); - enode_vector::const_iterator end2 = v.end(); - for (; it2 != end2; ++it2) - out << " #" << (*it2)->get_owner_id(); + for (enode* n : v) { + out << " #" << n->get_owner_id(); + } out << "\n"; } + ++id; } } @@ -588,20 +577,20 @@ namespace smt { case b_justification::BIN_CLAUSE: { literal l2 = j.get_literal(); out << "bin-clause "; - display_literal(out, l2); + display_literal_verbose(out, l2); break; } case b_justification::CLAUSE: { clause * cls = j.get_clause(); out << "clause "; - if (cls) display_literals(out, cls->get_num_literals(), cls->begin_literals()); + if (cls) out << literal_vector(cls->get_num_literals(), cls->begin()); break; } case b_justification::JUSTIFICATION: { out << "justification " << j.get_justification()->get_from_theory() << ": "; literal_vector lits; const_cast(*m_conflict_resolution).justification2literals(j.get_justification(), lits); - display_literals(out, lits); + display_literals_verbose(out, lits); break; } default: diff --git a/src/smt/smt_enode.cpp b/src/smt/smt_enode.cpp index 1452dc610..e09e83f6b 100644 --- a/src/smt/smt_enode.cpp +++ b/src/smt/smt_enode.cpp @@ -32,7 +32,7 @@ namespace smt { n->m_owner = owner; n->m_root = n; n->m_next = n; - n->m_cg = 0; + n->m_cg = nullptr; n->m_class_size = 1; n->m_generation = generation; n->m_func_decl_id = UINT_MAX; @@ -130,11 +130,11 @@ namespace smt { if (m_th_var_list.get_th_var() == null_theory_var) { m_th_var_list.set_th_var(v); m_th_var_list.set_th_id(id); - m_th_var_list.set_next(0); + m_th_var_list.set_next(nullptr); } else { theory_var_list * l = &m_th_var_list; - while (l->get_next() != 0) { + while (l->get_next() != nullptr) { SASSERT(l->get_th_id() != id); l = l->get_next(); } @@ -172,11 +172,11 @@ namespace smt { SASSERT(get_th_var(id) != null_theory_var); if (m_th_var_list.get_th_id() == id) { theory_var_list * next = m_th_var_list.get_next(); - if (next == 0) { + if (next == nullptr) { // most common case m_th_var_list.set_th_var(null_theory_var); m_th_var_list.set_th_id(null_theory_id); - m_th_var_list.set_next(0); + m_th_var_list.set_next(nullptr); } else { m_th_var_list = *next; @@ -405,7 +405,7 @@ namespace smt { tmp_enode::tmp_enode(): m_app(0), m_capacity(0), - m_enode_data(0) { + m_enode_data(nullptr) { SASSERT(m_app.get_app()->get_decl() == 0); set_capacity(5); } diff --git a/src/smt/smt_enode.h b/src/smt/smt_enode.h index f471314fa..6c8d156b4 100644 --- a/src/smt/smt_enode.h +++ b/src/smt/smt_enode.h @@ -33,7 +33,7 @@ namespace smt { enode * m_target; eq_justification m_justification; trans_justification(): - m_target(0), + m_target(nullptr), m_justification(null_eq_justification) { } }; @@ -116,7 +116,7 @@ namespace smt { theory_var_list * get_th_var_list() { - return m_th_var_list.get_th_var() == null_theory_var ? 0 : &m_th_var_list; + return m_th_var_list.get_th_var() == null_theory_var ? nullptr : &m_th_var_list; } friend class set_merge_tf_trail; @@ -216,6 +216,28 @@ namespace smt { return m_args; } + class const_args { + enode const& n; + public: + const_args(enode const& n):n(n) {} + const_args(enode const* n):n(*n) {} + enode_vector::const_iterator begin() const { return n.m_args; } + enode_vector::const_iterator end() const { return n.m_args + n.get_num_args(); } + }; + + class args { + enode & n; + public: + args(enode & n):n(n) {} + args(enode * n):n(*n) {} + enode_vector::iterator begin() const { return n.m_args; } + enode_vector::iterator end() const { return n.m_args + n.get_num_args(); } + }; + + const_args get_const_args() const { return const_args(this); } + + // args get_args() { return args(this); } + // unsigned get_id() const { // return m_id; // } @@ -285,6 +307,28 @@ namespace smt { return m_commutative; } + class const_parents { + enode const& n; + public: + const_parents(enode const& _n):n(_n) {} + const_parents(enode const* _n):n(*_n) {} + enode_vector::const_iterator begin() const { return n.begin_parents(); } + enode_vector::const_iterator end() const { return n.end_parents(); } + }; + + class parents { + enode& n; + public: + parents(enode & _n):n(_n) {} + parents(enode * _n):n(*_n) {} + enode_vector::iterator begin() const { return n.begin_parents(); } + enode_vector::iterator end() const { return n.end_parents(); } + }; + + parents get_parents() { return parents(this); } + + const_parents get_const_parents() const { return const_parents(this); } + unsigned get_num_parents() const { return m_parents.size(); } @@ -306,7 +350,7 @@ namespace smt { } theory_var_list const * get_th_var_list() const { - return m_th_var_list.get_th_var() == null_theory_var ? 0 : &m_th_var_list; + return m_th_var_list.get_th_var() == null_theory_var ? nullptr : &m_th_var_list; } bool has_th_vars() const { diff --git a/src/smt/smt_eq_justification.h b/src/smt/smt_eq_justification.h index af538a130..952caa2a5 100644 --- a/src/smt/smt_eq_justification.h +++ b/src/smt/smt_eq_justification.h @@ -78,7 +78,7 @@ namespace smt { } }; - const eq_justification null_eq_justification(static_cast(0)); + const eq_justification null_eq_justification(static_cast(nullptr)); }; #endif /* SMT_EQ_JUSTIFICATION_H_ */ diff --git a/src/smt/smt_for_each_relevant_expr.h b/src/smt/smt_for_each_relevant_expr.h index b81023349..3626e37c7 100644 --- a/src/smt/smt_for_each_relevant_expr.h +++ b/src/smt/smt_for_each_relevant_expr.h @@ -93,8 +93,8 @@ namespace smt { for_each_relevant_expr(ctx), m_buffer(b) { } - virtual ~collect_relevant_label_lits() {} - virtual void operator()(expr * n); + ~collect_relevant_label_lits() override {} + void operator()(expr * n) override; }; class collect_relevant_labels : public for_each_relevant_expr { @@ -104,8 +104,8 @@ namespace smt { for_each_relevant_expr(ctx), m_buffer(b) { } - virtual ~collect_relevant_labels() {} - virtual void operator()(expr * n); + ~collect_relevant_labels() override {} + void operator()(expr * n) override; }; }; diff --git a/src/smt/smt_implied_equalities.cpp b/src/smt/smt_implied_equalities.cpp index d021708fc..9119119d3 100644 --- a/src/smt/smt_implied_equalities.cpp +++ b/src/smt/smt_implied_equalities.cpp @@ -96,7 +96,7 @@ namespace smt { ++m_stats_calls; m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(s, t))); - bool is_eq = l_false == m_solver.check_sat(0,0); + bool is_eq = l_false == m_solver.check_sat(0,nullptr); m_solver.pop(1); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";); if (is_eq) { @@ -123,7 +123,7 @@ namespace smt { m_stats_timer.start(); m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(s, t))); - bool is_eq = l_false == m_solver.check_sat(0,0); + bool is_eq = l_false == m_solver.check_sat(0,nullptr); m_solver.pop(1); m_stats_timer.stop(); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << (is_eq?"eq":"unrelated") << "\n";); @@ -155,7 +155,7 @@ namespace smt { m_solver.push(); unsigned arity = get_array_arity(srt); expr_ref_vector args(m); - args.push_back(0); + args.push_back(nullptr); for (unsigned i = 0; i < arity; ++i) { sort* srt_i = get_array_domain(srt, i); expr* idx = m.mk_fresh_const("index", srt_i); @@ -163,10 +163,10 @@ namespace smt { } for (unsigned i = 0; i < terms.size(); ++i) { args[0] = terms[i].term; - terms[i].term = m.mk_app(m_array_util.get_family_id(), OP_SELECT, 0, 0, args.size(), args.c_ptr()); + terms[i].term = m.mk_app(m_array_util.get_family_id(), OP_SELECT, 0, nullptr, args.size(), args.c_ptr()); } assert_relevant(terms); - VERIFY(m_solver.check_sat(0,0) != l_false); + VERIFY(m_solver.check_sat(0,nullptr) != l_false); model_ref model1; m_solver.get_model(model1); SASSERT(model1.get()); @@ -199,7 +199,7 @@ namespace smt { for (unsigned i = 0; i < terms.size(); ++i) { expr* t = terms[i].term; - model->eval(t, vl); + vl = (*model)(t); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " |-> " << mk_pp(vl, m) << "\n";); reduce_value(model, vl); if (!m.is_value(vl)) { @@ -215,7 +215,7 @@ namespace smt { expr* s = terms[vec[j]].term; m_solver.push(); m_solver.assert_expr(m.mk_not(m.mk_eq(t, s))); - lbool is_sat = m_solver.check_sat(0,0); + lbool is_sat = m_solver.check_sat(0,nullptr); m_solver.pop(1); TRACE("get_implied_equalities", tout << mk_pp(t, m) << " = " << mk_pp(s, m) << " " << is_sat << "\n";); if (is_sat == l_false) { @@ -284,7 +284,7 @@ namespace smt { } lbool reduce_cond(model_ref& model, expr* e) { - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; if (m.is_eq(e, e1, e2) && m_array_util.is_as_array(e1) && m_array_util.is_as_array(e2)) { if (e1 == e2) { return l_true; @@ -335,7 +335,7 @@ namespace smt { m_solver.push(); assert_relevant(num_terms, terms); - lbool is_sat = m_solver.check_sat(0,0); + lbool is_sat = m_solver.check_sat(0,nullptr); if (is_sat != l_false) { model_ref model; diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index ffaee434f..f9ee900ff 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -34,9 +34,10 @@ namespace smt { switch (to_app(n)->get_decl_kind()) { case OP_AND: case OP_OR: - case OP_IFF: case OP_ITE: return true; + case OP_EQ: + return m.is_bool(to_app(n)->get_arg(0)); default: return false; } @@ -121,7 +122,7 @@ namespace smt { bool visited = true; family_id fid = to_app(n)->get_family_id(); theory * th = m_theories.get_plugin(fid); - bool def_int = th == 0 || th->default_internalizer(); + bool def_int = th == nullptr || th->default_internalizer(); if (!def_int) { ptr_buffer descendants; get_foreign_descendants(to_app(n), fid, descendants); @@ -229,7 +230,7 @@ namespace smt { add_or_rel_watches(to_app(n)); break; } - case OP_IFF: { + case OP_EQ: { expr * lhs = to_app(n)->get_arg(0); expr * rhs = to_app(n)->get_arg(1); internalize(lhs, true); @@ -301,7 +302,7 @@ namespace smt { e->mark_as_interpreted(); app_ref eq(m_manager.mk_eq(fapp, val), m_manager); TRACE("assert_distinct", tout << "eq: " << mk_pp(eq, m_manager) << "\n";); - assert_default(eq, 0); + assert_default(eq, nullptr); mark_as_relevant(eq.get()); // TODO: we may want to hide the auxiliary values val and the function f from the model. } @@ -369,7 +370,7 @@ namespace smt { else { TRACE("internalize_bug", tout << "creating enode for #" << n->get_id() << "\n";); mk_enode(to_app(n), - true, /* supress arguments, we not not use CC for this kind of enode */ + true, /* suppress arguments, we not not use CC for this kind of enode */ true, /* bool enode must be merged with true/false, since it is not in the context of a gate */ false /* CC is not enabled */ ); set_enode_flag(v, false); @@ -381,7 +382,7 @@ namespace smt { return; } - if (m_manager.is_eq(n)) + if (m_manager.is_eq(n) && !m_manager.is_iff(n)) internalize_eq(to_app(n), gate_ctx); else if (m_manager.is_distinct(n)) internalize_distinct(to_app(n), gate_ctx); @@ -453,7 +454,7 @@ namespace smt { // must be associated with an enode. if (!e_internalized(n)) { mk_enode(to_app(n), - true, /* supress arguments, we not not use CC for this kind of enode */ + true, /* suppress arguments, we not not use CC for this kind of enode */ true /* bool enode must be merged with true/false, since it is not in the context of a gate */, false /* CC is not enabled */); } @@ -538,9 +539,7 @@ namespace smt { bool _is_gate = is_gate(m_manager, n) || m_manager.is_not(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, _is_gate); } @@ -596,8 +595,9 @@ namespace smt { mk_or_cnstr(to_app(n)); add_or_rel_watches(to_app(n)); break; - case OP_IFF: - mk_iff_cnstr(to_app(n)); + case OP_EQ: + if (m_manager.is_iff(n)) + mk_iff_cnstr(to_app(n)); break; case OP_ITE: mk_ite_cnstr(to_app(n)); @@ -611,7 +611,6 @@ namespace smt { case OP_XOR: UNREACHABLE(); case OP_OEQ: - case OP_INTERP: UNREACHABLE(); default: break; @@ -631,7 +630,7 @@ namespace smt { set_merge_tf_trail(enode * n): m_node(n) { } - virtual void undo(context & ctx) { + void undo(context & ctx) override { m_node->m_merge_tf = false; } }; @@ -667,7 +666,7 @@ namespace smt { set_enode_flag_trail(bool_var v): m_var(v) { } - virtual void undo(context & ctx) { + void undo(context & ctx) override { bool_var_data & data = ctx.m_bdata[m_var]; data.reset_enode_flag(); } @@ -695,7 +694,7 @@ namespace smt { void context::internalize_term(app * n) { if (e_internalized(n)) { theory * th = m_theories.get_plugin(n->get_family_id()); - if (th != 0) { + if (th != nullptr) { // This code is necessary because some theories may decide // not to create theory variables for a nested application. // Example: @@ -739,7 +738,7 @@ namespace smt { app_ref eq1(mk_eq_atom(n, t), m_manager); app_ref eq2(mk_eq_atom(n, e), m_manager); mk_enode(n, - true /* supress arguments, I don't want to apply CC on ite terms */, + true /* suppress arguments, I don't want to apply CC on ite terms */, false /* it is a term, so it should not be merged with true/false */, false /* CC is not enabled */); internalize(c, true); @@ -797,7 +796,7 @@ namespace smt { } enode * e = mk_enode(n, - false, /* do not supress args */ + false, /* do not suppress args */ false, /* it is a term, so it should not be merged with true/false */ true); apply_sort_cnstr(n, e); @@ -1271,7 +1270,7 @@ namespace smt { case CLS_AUX: { literal_buffer simp_lits; if (!simplify_aux_clause_literals(num_lits, lits, simp_lits)) - return 0; // clause is equivalent to true; + return nullptr; // clause is equivalent to true; DEBUG_CODE({ for (unsigned i = 0; i < simp_lits.size(); i++) { SASSERT(get_assignment(simp_lits[i]) == l_true); @@ -1284,7 +1283,7 @@ namespace smt { } case CLS_AUX_LEMMA: { if (!simplify_aux_lemma_literals(num_lits, lits)) - return 0; // clause is equivalent to true + return nullptr; // clause is equivalent to true // simplify_aux_lemma_literals does not delete literals assigned to false, so // it is not necessary to create a unit_resolution_justification break; @@ -1303,14 +1302,14 @@ namespace smt { if (j && !j->in_region()) m_justifications.push_back(j); TRACE("mk_clause", tout << "empty clause... setting conflict\n";); - set_conflict(j == 0 ? b_justification::mk_axiom() : b_justification(j)); + set_conflict(j == nullptr ? b_justification::mk_axiom() : b_justification(j)); SASSERT(inconsistent()); - return 0; + return nullptr; case 1: if (j && !j->in_region()) m_justifications.push_back(j); assign(lits[0], j); - return 0; + return nullptr; case 2: if (use_binary_clause_opt(lits[0], lits[1], lemma)) { literal l1 = lits[0]; @@ -1321,7 +1320,7 @@ namespace smt { assign(l1, b_justification(~l2)); m_stats.m_num_mk_bin_clause++; - return 0; + return nullptr; } default: { m_stats.m_num_mk_clause++; @@ -1345,6 +1344,7 @@ namespace smt { cls->swap_lits(1, w2_idx); TRACE("mk_th_lemma", display_clause(tout, cls); tout << "\n";); } + // display_clause(std::cout, cls); std::cout << "\n"; m_lemmas.push_back(cls); add_watch_literal(cls, 0); add_watch_literal(cls, 1); @@ -1404,7 +1404,7 @@ namespace smt { } void context::mk_th_axiom(theory_id tid, unsigned num_lits, literal * lits, unsigned num_params, parameter * params) { - justification * js = 0; + justification * js = nullptr; TRACE("mk_th_axiom", display_literals_verbose(tout, num_lits, lits); tout << "\n";); @@ -1449,12 +1449,12 @@ namespace smt { void context::mk_gate_clause(unsigned num_lits, literal * lits) { if (m_manager.proofs_enabled()) { - proof * pr = mk_clause_def_axiom(num_lits, lits, 0); + proof * pr = mk_clause_def_axiom(num_lits, lits, nullptr); TRACE("gate_clause", tout << mk_ll_pp(pr, m_manager);); mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); } else { - mk_clause(num_lits, lits, 0); + mk_clause(num_lits, lits, nullptr); } } @@ -1487,7 +1487,7 @@ namespace smt { mk_clause(num_lits, lits, mk_justification(justification_proof_wrapper(*this, pr))); } else { - mk_clause(num_lits, lits, 0); + mk_clause(num_lits, lits, nullptr); } } @@ -1506,7 +1506,7 @@ namespace smt { relevancy_eh * eh = m_relevancy_propagator->mk_and_relevancy_eh(n); unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { - // if one child is assigned to false, the the and-parent must be notified + // if one child is assigned to false, the and-parent must be notified literal l = get_literal(n->get_arg(i)); add_rel_watch(~l, eh); } @@ -1518,7 +1518,7 @@ namespace smt { relevancy_eh * eh = m_relevancy_propagator->mk_or_relevancy_eh(n); unsigned num = n->get_num_args(); for (unsigned i = 0; i < num; i++) { - // if one child is assigned to true, the the or-parent must be notified + // if one child is assigned to true, the or-parent must be notified literal l = get_literal(n->get_arg(i)); add_rel_watch(l, eh); } @@ -1612,7 +1612,7 @@ namespace smt { SASSERT(m_th_var != null_theory_var); } - virtual void undo(context & ctx) { + void undo(context & ctx) override { theory_var v = m_enode->get_th_var(m_th_id); SASSERT(v != null_theory_var); SASSERT(m_th_var == v); @@ -1637,7 +1637,7 @@ namespace smt { m_old_th_var(old_var) { } - virtual void undo(context & ctx) { + void undo(context & ctx) override { SASSERT(m_enode->get_th_var(m_th_id) != null_theory_var); m_enode->replace_th_var(m_old_th_var, m_th_id); } diff --git a/src/smt/smt_justification.cpp b/src/smt/smt_justification.cpp index 440da7297..4ac176a2b 100644 --- a/src/smt/smt_justification.cpp +++ b/src/smt/smt_justification.cpp @@ -90,17 +90,17 @@ namespace smt { SASSERT(m_antecedent); ptr_buffer prs; proof * pr = cr.get_proof(m_antecedent); - bool visited = pr != 0; + bool visited = pr != nullptr; prs.push_back(pr); for (unsigned i = 0; i < m_num_literals; i++) { proof * pr = cr.get_proof(m_literals[i]); - if (pr == 0) + if (pr == nullptr) visited = false; else prs.push_back(pr); } if (!visited) - return 0; + return nullptr; ast_manager & m = cr.get_manager(); TRACE("unit_resolution_justification_bug", tout << "in mk_proof\n"; @@ -150,7 +150,7 @@ namespace smt { } if (!visited) - return 0; + return nullptr; expr * lhs = m_node1->get_root()->get_owner(); expr * rhs = m_node2->get_root()->get_owner(); @@ -178,7 +178,7 @@ namespace smt { proof * pr2 = m.mk_rewrite(m.get_fact(pr1), lit); return m.mk_modus_ponens(pr1, pr2); } - return 0; + return nullptr; } void eq_propagation_justification::get_antecedents(conflict_resolution & cr) { @@ -241,7 +241,7 @@ namespace smt { mk_pp(m.get_fact(pr), m) << "\n";); return pr; } - return 0; + return nullptr; } simple_justification::simple_justification(region & r, unsigned num_lits, literal const * lits): @@ -266,7 +266,7 @@ namespace smt { bool visited = true; for (unsigned i = 0; i < m_num_literals; i++) { proof * pr = cr.get_proof(m_literals[i]); - if (pr == 0) + if (pr == nullptr) visited = false; else result.push_back(pr); @@ -284,15 +284,15 @@ namespace smt { lits.push_back(l); } if (lits.size() == 1) - return m.mk_th_lemma(m_th_id, lits.get(0), 0, 0, m_params.size(), m_params.c_ptr()); + return m.mk_th_lemma(m_th_id, lits.get(0), 0, nullptr, m_params.size(), m_params.c_ptr()); else - return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, 0, m_params.size(), m_params.c_ptr()); + return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, nullptr, m_params.size(), m_params.c_ptr()); } proof * theory_propagation_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) - return 0; + return nullptr; context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref fact(m); @@ -303,7 +303,7 @@ namespace smt { proof * theory_conflict_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) - return 0; + return nullptr; ast_manager & m = cr.get_manager(); return m.mk_th_lemma(m_th_id, m.mk_false(), prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } @@ -334,7 +334,7 @@ namespace smt { for (unsigned i = 0; i < m_num_eqs; i++) { enode_pair const & p = m_eqs[i]; proof * pr = cr.get_proof(p.first, p.second); - if (pr == 0) + if (pr == nullptr) visited = false; else result.push_back(pr); @@ -345,7 +345,7 @@ namespace smt { proof * ext_theory_propagation_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) - return 0; + return nullptr; context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref fact(m); @@ -356,7 +356,7 @@ namespace smt { proof * ext_theory_conflict_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) - return 0; + return nullptr; ast_manager & m = cr.get_manager(); return m.mk_th_lemma(m_th_id, m.mk_false(), prs.size(), prs.c_ptr(), m_params.size(), m_params.c_ptr()); } @@ -364,7 +364,7 @@ namespace smt { proof * ext_theory_eq_propagation_justification::mk_proof(conflict_resolution & cr) { ptr_buffer prs; if (!antecedent2proof(cr, prs)) - return 0; + return nullptr; ast_manager & m = cr.get_manager(); context & ctx = cr.get_context(); expr * fact = ctx.mk_eq_atom(m_lhs->get_owner(), m_rhs->get_owner()); @@ -415,9 +415,9 @@ namespace smt { lits.push_back(l); } if (lits.size() == 1) - return m.mk_th_lemma(m_th_id, lits.get(0), 0, 0, m_params.size(), m_params.c_ptr()); + return m.mk_th_lemma(m_th_id, lits.get(0), 0, nullptr, m_params.size(), m_params.c_ptr()); else - return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, 0, m_params.size(), m_params.c_ptr()); + return m.mk_th_lemma(m_th_id, m.mk_or(lits.size(), lits.c_ptr()), 0, nullptr, m_params.size(), m_params.c_ptr()); } }; diff --git a/src/smt/smt_justification.h b/src/smt/smt_justification.h index 0af8e61ff..8354c8860 100644 --- a/src/smt/smt_justification.h +++ b/src/smt/smt_justification.h @@ -102,15 +102,15 @@ namespace smt { public: justification_proof_wrapper(context & ctx, proof * pr, bool in_region = true); - virtual bool has_del_eh() const { + bool has_del_eh() const override { return true; } - virtual void del_eh(ast_manager & m); + void del_eh(ast_manager & m) override; - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "proof-wrapper"; } + char const * get_name() const override { return "proof-wrapper"; } }; class unit_resolution_justification : public justification { @@ -122,21 +122,21 @@ namespace smt { unit_resolution_justification(justification * js, unsigned num_lits, literal const * lits); - ~unit_resolution_justification(); + ~unit_resolution_justification() override; - virtual bool has_del_eh() const { + bool has_del_eh() const override { return !in_region() && m_antecedent && m_antecedent->has_del_eh(); } - virtual void del_eh(ast_manager & m) { + void del_eh(ast_manager & m) override { if (!in_region() && m_antecedent) m_antecedent->del_eh(m); } - virtual void get_antecedents(conflict_resolution & cr); + void get_antecedents(conflict_resolution & cr) override; - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "unit-resolution"; } + char const * get_name() const override { return "unit-resolution"; } }; class eq_conflict_justification : public justification { @@ -150,11 +150,11 @@ namespace smt { m_js(js) { } - virtual void get_antecedents(conflict_resolution & cr); + void get_antecedents(conflict_resolution & cr) override; - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "eq-conflict"; } + char const * get_name() const override { return "eq-conflict"; } }; /** @@ -166,11 +166,11 @@ namespace smt { eq_root_propagation_justification(enode * n):m_node(n) { } - virtual void get_antecedents(conflict_resolution & cr); + void get_antecedents(conflict_resolution & cr) override; - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "eq-root"; } + char const * get_name() const override { return "eq-root"; } }; /** @@ -184,11 +184,11 @@ namespace smt { SASSERT(n1 != n2); } - virtual void get_antecedents(conflict_resolution & cr); + void get_antecedents(conflict_resolution & cr) override; - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "eq-propagation"; } + char const * get_name() const override { return "eq-propagation"; } }; /** @@ -201,11 +201,11 @@ namespace smt { mp_iff_justification(enode * n1, enode * n2):m_node1(n1), m_node2(n2) { } - virtual void get_antecedents(conflict_resolution & cr); + void get_antecedents(conflict_resolution & cr) override; - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "mp-iff"; } + char const * get_name() const override { return "mp-iff"; } }; /** @@ -221,11 +221,11 @@ namespace smt { public: simple_justification(region & r, unsigned num_lits, literal const * lits); - virtual void get_antecedents(conflict_resolution & cr); + void get_antecedents(conflict_resolution & cr) override; - virtual proof * mk_proof(conflict_resolution & cr) = 0; + proof * mk_proof(conflict_resolution & cr) override = 0; - virtual char const * get_name() const { return "simple"; } + char const * get_name() const override { return "simple"; } }; @@ -240,13 +240,13 @@ namespace smt { unsigned num_params, parameter* params): simple_justification(r, num_lits, lits), m_th_id(fid), m_params(num_params, params) {} - virtual ~simple_theory_justification() {} + ~simple_theory_justification() override {} - virtual bool has_del_eh() const { return !m_params.empty(); } + bool has_del_eh() const override { return !m_params.empty(); } - virtual void del_eh(ast_manager & m) { m_params.reset(); } + void del_eh(ast_manager & m) override { m_params.reset(); } - virtual theory_id get_from_theory() const { return m_th_id; } + theory_id get_from_theory() const override { return m_th_id; } }; @@ -255,39 +255,39 @@ namespace smt { theory_axiom_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, - unsigned num_params = 0, parameter* params = 0): + unsigned num_params = 0, parameter* params = nullptr): simple_theory_justification(fid, r, num_lits, lits, num_params, params) {} - virtual void get_antecedents(conflict_resolution & cr) {} + void get_antecedents(conflict_resolution & cr) override {} - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "theory-axiom"; } + char const * get_name() const override { return "theory-axiom"; } }; class theory_propagation_justification : public simple_theory_justification { literal m_consequent; public: theory_propagation_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, literal consequent, - unsigned num_params = 0, parameter* params = 0): + unsigned num_params = 0, parameter* params = nullptr): simple_theory_justification(fid, r, num_lits, lits, num_params, params), m_consequent(consequent) {} - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "theory-propagation"; } + char const * get_name() const override { return "theory-propagation"; } }; class theory_conflict_justification : public simple_theory_justification { public: theory_conflict_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, - unsigned num_params = 0, parameter* params = 0): + unsigned num_params = 0, parameter* params = nullptr): simple_theory_justification(fid, r, num_lits, lits, num_params, params) {} - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "theory-conflict"; } + char const * get_name() const override { return "theory-conflict"; } }; /** @@ -304,11 +304,11 @@ namespace smt { ext_simple_justification(region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs); - virtual void get_antecedents(conflict_resolution & cr); + void get_antecedents(conflict_resolution & cr) override; - virtual proof * mk_proof(conflict_resolution & cr) = 0; + proof * mk_proof(conflict_resolution & cr) override = 0; - virtual char const * get_name() const { return "ext-simple"; } + char const * get_name() const override { return "ext-simple"; } }; /** @@ -322,16 +322,16 @@ namespace smt { public: ext_theory_simple_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, - unsigned num_params = 0, parameter* params = 0): + unsigned num_params = 0, parameter* params = nullptr): ext_simple_justification(r, num_lits, lits, num_eqs, eqs), m_th_id(fid), m_params(num_params, params) {} - virtual ~ext_theory_simple_justification() {} + ~ext_theory_simple_justification() override {} - virtual bool has_del_eh() const { return !m_params.empty(); } + bool has_del_eh() const override { return !m_params.empty(); } - virtual void del_eh(ast_manager & m) { m_params.reset(); } + void del_eh(ast_manager & m) override { m_params.reset(); } - virtual theory_id get_from_theory() const { return m_th_id; } + theory_id get_from_theory() const override { return m_th_id; } }; class ext_theory_propagation_justification : public ext_theory_simple_justification { @@ -341,25 +341,25 @@ namespace smt { unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, literal consequent, - unsigned num_params = 0, parameter* params = 0): + unsigned num_params = 0, parameter* params = nullptr): ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params), m_consequent(consequent) {} - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "ext-theory-propagation"; } + char const * get_name() const override { return "ext-theory-propagation"; } }; class ext_theory_conflict_justification : public ext_theory_simple_justification { public: ext_theory_conflict_justification(family_id fid, region & r, unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, - unsigned num_params = 0, parameter* params = 0): + unsigned num_params = 0, parameter* params = nullptr): ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params) {} - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "ext-theory-conflict"; } + char const * get_name() const override { return "ext-theory-conflict"; } }; class ext_theory_eq_propagation_justification : public ext_theory_simple_justification { @@ -371,12 +371,12 @@ namespace smt { unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, enode * lhs, enode * rhs, - unsigned num_params = 0, parameter* params = 0): + unsigned num_params = 0, parameter* params = nullptr): ext_theory_simple_justification(fid, r, num_lits, lits, num_eqs, eqs, num_params, params), m_lhs(lhs), m_rhs(rhs) {} - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "ext-theory-eq-propagation"; } + char const * get_name() const override { return "ext-theory-eq-propagation"; } }; /** @@ -392,21 +392,21 @@ namespace smt { public: theory_lemma_justification(family_id fid, context & ctx, unsigned num_lits, literal const * lits, - unsigned num_params = 0, parameter* params = 0); + unsigned num_params = 0, parameter* params = nullptr); - virtual ~theory_lemma_justification(); + ~theory_lemma_justification() override; - virtual bool has_del_eh() const { + bool has_del_eh() const override { return true; } - virtual void del_eh(ast_manager & m); + void del_eh(ast_manager & m) override; - virtual void get_antecedents(conflict_resolution & cr) {} + void get_antecedents(conflict_resolution & cr) override {} - virtual proof * mk_proof(conflict_resolution & cr); + proof * mk_proof(conflict_resolution & cr) override; - virtual char const * get_name() const { return "theory-lemma"; } + char const * get_name() const override { return "theory-lemma"; } }; }; diff --git a/src/smt/smt_kernel.cpp b/src/smt/smt_kernel.cpp index a7948725c..b03604b5b 100644 --- a/src/smt/smt_kernel.cpp +++ b/src/smt/smt_kernel.cpp @@ -115,6 +115,10 @@ namespace smt { return m_kernel.check(num_assumptions, assumptions); } + lbool check(expr_ref_vector const& cube, vector const& clause) { + return m_kernel.check(cube, clause); + } + lbool get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed) { return m_kernel.get_consequences(assumptions, vars, conseq, unfixed); } @@ -123,7 +127,6 @@ namespace smt { return m_kernel.preferred_sat(asms, cores); } - lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_kernel.find_mutexes(vars, mutexes); } @@ -175,11 +178,15 @@ namespace smt { void get_guessed_literals(expr_ref_vector & result) { m_kernel.get_guessed_literals(result); } - + + expr* next_decision() { + return m_kernel.next_decision(); + } + void collect_statistics(::statistics & st) const { m_kernel.collect_statistics(st); } - + void reset_statistics() { } @@ -196,9 +203,7 @@ namespace smt { } void updt_params(params_ref const & p) { - // We don't need params2smt_params anymore. smt_params has support for reading params_ref. - // The update is performed at smt_kernel "users". - // params2smt_params(p, fparams()); + m_kernel.updt_params(p); } }; @@ -218,7 +223,6 @@ namespace smt { imp::copy(*src.m_imp, *dst.m_imp); } - bool kernel::set_logic(symbol logic) { return m_imp->set_logic(logic); } @@ -263,9 +267,9 @@ namespace smt { } void kernel::reset() { - ast_manager & _m = m(); + ast_manager & _m = m(); smt_params & fps = m_imp->fparams(); - params_ref ps = m_imp->params(); + params_ref ps = m_imp->params(); #pragma omp critical (smt_kernel) { m_imp->~imp(); @@ -287,6 +291,11 @@ namespace smt { return r; } + lbool kernel::check(expr_ref_vector const& cube, vector const& clauses) { + return m_imp->check(cube, clauses); + } + + lbool kernel::get_consequences(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq, expr_ref_vector& unfixed) { return m_imp->get_consequences(assumptions, vars, conseq, unfixed); } @@ -347,6 +356,10 @@ namespace smt { m_imp->get_guessed_literals(result); } + expr* kernel::next_decision() { + return m_imp->next_decision(); + } + void kernel::display(std::ostream & out) const { m_imp->display(out); } diff --git a/src/smt/smt_kernel.h b/src/smt/smt_kernel.h index d10cab4f3..d78f71e20 100644 --- a/src/smt/smt_kernel.h +++ b/src/smt/smt_kernel.h @@ -126,12 +126,14 @@ namespace smt { /** \brief Satisfiability check. */ - lbool check(unsigned num_assumptions = 0, expr * const * assumptions = 0); + lbool check(unsigned num_assumptions = 0, expr * const * assumptions = nullptr); lbool check(expr_ref_vector const& asms) { return check(asms.size(), asms.c_ptr()); } lbool check(app_ref_vector const& asms) { return check(asms.size(), (expr* const*)asms.c_ptr()); } + lbool check(expr_ref_vector const& cube, vector const& clauses); + /** \brief extract consequences among variables. */ @@ -212,6 +214,11 @@ namespace smt { */ void get_guessed_literals(expr_ref_vector & result); + /** + \brief return the next case split literal. + */ + expr* next_decision(); + /** \brief (For debubbing purposes) Prints the state of the kernel */ diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index cd59014aa..5ab52b57c 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -33,19 +33,19 @@ namespace smt { m(m), m_params(p), m_autil(m), - m_qm(0), - m_context(0), - m_root2value(0), + m_qm(nullptr), + m_context(nullptr), + m_root2value(nullptr), m_model_finder(mf), m_max_cexs(1), m_iteration_idx(0), - m_curr_model(0), + m_curr_model(nullptr), m_pinned_exprs(m) { } model_checker::~model_checker() { - m_aux_context = 0; // delete aux context before fparams - m_fparams = 0; + m_aux_context = nullptr; // delete aux context before fparams + m_fparams = nullptr; } quantifier * model_checker::get_flat_quantifier(quantifier * q) { @@ -72,7 +72,7 @@ namespace smt { m_value2expr.insert(val, n->get_owner()); } } - expr * t = 0; + expr * t = nullptr; m_value2expr.find(val, t); return t; } @@ -111,10 +111,10 @@ namespace smt { ptr_buffer subst_args; unsigned num_decls = q->get_num_decls(); subst_args.resize(num_decls, 0); - sks.resize(num_decls, 0); + sks.resize(num_decls, nullptr); for (unsigned i = 0; i < num_decls; i++) { sort * s = q->get_decl_sort(num_decls - i - 1); - expr * sk = m.mk_fresh_const(0, s); + expr * sk = m.mk_fresh_const(nullptr, s); sks[num_decls - i - 1] = sk; subst_args[num_decls - i - 1] = sk; if (m_curr_model->is_finite(s)) { @@ -132,7 +132,7 @@ namespace smt { } bool model_checker::add_instance(quantifier * q, model * cex, expr_ref_vector & sks, bool use_inv) { - if (cex == 0) { + if (cex == nullptr) { TRACE("model_checker", tout << "no model is available\n";); return false; } @@ -158,7 +158,7 @@ namespace smt { if (use_inv) { unsigned sk_term_gen; expr * sk_term = m_model_finder.get_inv(q, i, sk_value, sk_term_gen); - if (sk_term != 0) { + if (sk_term != nullptr) { TRACE("model_checker", tout << "Found inverse " << mk_pp(sk_term, m) << "\n";); SASSERT(!m.is_model_value(sk_term)); if (sk_term_gen > max_generation) @@ -172,7 +172,7 @@ namespace smt { } else { expr * sk_term = get_term_from_ctx(sk_value); - if (sk_term != 0) { + if (sk_term != nullptr) { sk_value = sk_term; } } @@ -323,7 +323,7 @@ namespace smt { bool is_undef = false; expr_ref_vector args(m); unsigned num_decls = q->get_num_decls(); - args.resize(num_decls, 0); + args.resize(num_decls, nullptr); var_subst sub(m); expr_ref tmp(m), result(m); for (; it != end; ++it) { @@ -438,7 +438,7 @@ namespace smt { } else if (!check(q)) { if (m_params.m_mbqi_trace || get_verbosity_level() >= 5) { - verbose_stream() << "(smt.mbqi :failed " << q->get_qid() << ")\n"; + IF_VERBOSE(0, verbose_stream() << "(smt.mbqi :failed " << q->get_qid() << ")\n"); } TRACE("model_checker", tout << "checking quantifier " << mk_pp(q, m) << " failed\n";); num_failures++; @@ -502,7 +502,7 @@ namespace smt { expr_ref inst_expr(m); instantiate(m, q, inst->m_bindings, inst_expr); tout << "(assert " << mk_ismt2_pp(inst_expr, m) << ")\n";); - m_context->add_instance(q, 0, num_decls, bindings.c_ptr(), gen, gen, gen, dummy); + m_context->add_instance(q, nullptr, num_decls, bindings.c_ptr(), gen, gen, gen, dummy); TRACE("model_checker_bug_detail", tout << "after instantiating, inconsistent: " << m_context->inconsistent() << "\n";); } } diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index 28a65f898..73f85faf6 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -120,7 +120,7 @@ namespace smt { } expr * get_inv(expr * v) const { - expr * t = 0; + expr * t = nullptr; m_inv.find(v, t); return t; } @@ -140,7 +140,7 @@ namespace smt { if (!t_val) break; TRACE("model_finder", tout << mk_pp(t, m_manager) << " " << mk_pp(t_val, m_manager) << "\n";); - expr * old_t = 0; + expr * old_t = nullptr; if (m_inv.find(t_val, old_t)) { unsigned old_t_gen = 0; SASSERT(m_elems.contains(old_t)); @@ -223,14 +223,14 @@ namespace smt { public: node(unsigned id, sort * s): m_id(id), - m_find(0), + m_find(nullptr), m_eqc_size(1), m_sort(s), m_mono_proj(false), m_signed_proj(false), - m_set(0), - m_else(0), - m_proj(0) { + m_set(nullptr), + m_else(nullptr), + m_proj(nullptr) { } ~node() { @@ -242,7 +242,7 @@ namespace smt { sort * get_sort() const { return m_sort; } - bool is_root() const { return m_find == 0; } + bool is_root() const { return m_find == nullptr; } node * get_root() const { node * curr = const_cast(this); @@ -428,7 +428,7 @@ namespace smt { } node * mk_node(key2node & m, ast * n, unsigned i, sort * s) { - node * r = 0; + node * r = nullptr; ast_idx_pair k(n, i); if (m.find(k, r)) { SASSERT(r->get_sort() == s); @@ -469,11 +469,11 @@ namespace smt { m_arith(m), m_bv(m), m_next_node_id(0), - m_context(0), + m_context(nullptr), m_ks(m), - m_model(0), + m_model(nullptr), m_eval_cache_range(m), - m_new_constraints(0) { + m_new_constraints(nullptr) { } virtual ~auf_solver() { @@ -523,10 +523,10 @@ namespace smt { instantiation_set const * get_uvar_inst_set(quantifier * q, unsigned i) const { SASSERT(!has_quantifiers(q->get_expr())); ast_idx_pair k(q, i); - node * r = 0; + node * r = nullptr; if (m_uvars.find(k, r)) return r->get_instantiation_set(); - return 0; + return nullptr; } void mk_instantiation_sets() { @@ -537,7 +537,7 @@ namespace smt { } } - // For each instantiation_set, reemove entries that do not evaluate to values. + // For each instantiation_set, remove entries that do not evaluate to values. void cleanup_instantiation_sets() { ptr_vector to_delete; for (node * curr : m_nodes) { @@ -566,14 +566,14 @@ namespace smt { } } - virtual expr * eval(expr * n, bool model_completion) { - expr * r = 0; + expr * eval(expr * n, bool model_completion) override { + expr * r = nullptr; if (m_eval_cache[model_completion].find(n, r)) { return r; } expr_ref tmp(m); if (!m_model->eval(n, tmp, model_completion)) { - r = 0; + r = nullptr; TRACE("model_finder", tout << "eval\n" << mk_pp(n, m) << "\n-----> null\n";); } else { @@ -602,7 +602,7 @@ namespace smt { for (node* a : avoid_set) { node * n = a->get_root(); - if (!n->is_mono_proj() && n->get_else() != 0) { + if (!n->is_mono_proj() && n->get_else() != nullptr) { expr * val = eval(n->get_else(), true); SASSERT(val != 0); r.push_back(val); @@ -621,7 +621,7 @@ namespace smt { instantiation_set const * s = n->get_instantiation_set(); obj_map const & elems = s->get_elems(); - expr * t_result = 0; + expr * t_result = nullptr; unsigned gen_result = UINT_MAX; for (auto const& kv : elems) { expr * t = kv.m_key; @@ -635,7 +635,7 @@ namespace smt { break; } } - if (!found && (t_result == 0 || gen < gen_result)) { + if (!found && (t_result == nullptr || gen < gen_result)) { t_result = t; gen_result = gen; } @@ -652,7 +652,7 @@ namespace smt { */ app * get_k_for(sort * s) { SASSERT(is_infinite(s)); - app * r = 0; + app * r = nullptr; if (m_sort2k.find(s, r)) return r; r = m.mk_fresh_const("k", s); @@ -674,11 +674,11 @@ namespace smt { SASSERT(is_infinite(s)); func_decl * k_decl = k->get_decl(); expr * r = m_model->get_const_interp(k_decl); - if (r != 0) + if (r != nullptr) return r; r = m_model->get_fresh_value(s); - if (r == 0) - return 0; + if (r == nullptr) + return nullptr; m_model->register_decl(k_decl, r); SASSERT(m_model->get_const_interp(k_decl) == r); TRACE("model_finder", tout << mk_pp(r, m) << "\n";); @@ -694,7 +694,7 @@ namespace smt { TRACE("assert_k_diseq_exceptions", tout << "assert_k_diseq_exceptions, " << "k: " << mk_pp(k, m) << "\nexceptions:\n"; for (expr * e : exceptions) tout << mk_pp(e, m) << "\n";); expr * k_interp = get_k_interp(k); - if (k_interp == 0) + if (k_interp == nullptr) return false; for (expr * ex : exceptions) { expr * ex_val = eval(ex, true); @@ -720,7 +720,7 @@ namespace smt { ptr_buffer ex_vals; collect_exceptions_values(n, ex_vals); expr * e = pick_instance_diff_exceptions(n, ex_vals); - if (e != 0) { + if (e != nullptr) { n->set_else(e); return; } @@ -735,7 +735,7 @@ namespace smt { } } // TBD: add support for the else of bitvectors. - // Idea: get the term t with the minimal interpreation and use t - 1. + // Idea: get the term t with the minimal interpretation and use t - 1. } n->set_else((*(elems.begin())).m_key); } @@ -912,8 +912,9 @@ namespace smt { func_decl * f = to_func_decl(kv.m_key.first); if (!r.contains(f)) { func_interp * fi = m_model->get_func_interp(f); - if (fi == 0) { + if (fi == nullptr) { fi = alloc(func_interp, m, f->get_arity()); + TRACE("model_finder", tout << "register " << f->get_name() << "\n";); m_model->register_decl(f, fi); SASSERT(fi->is_partial()); } @@ -955,7 +956,7 @@ namespace smt { if (elems.empty()) { // The method get_some_value cannot be used if n->get_sort() is an uninterpreted sort or is a sort built using uninterpreted sorts // (e.g., (Array S S) where S is uninterpreted). The problem is that these sorts do not have a fixed interpretation. - // Moreover, a model assigns an arbitrary intepretation to these sorts using "model_values" a model value. + // Moreover, a model assigns an arbitrary interpretation to these sorts using "model_values" a model value. // If these module values "leak" inside the logical context, they may affect satisfiability. // sort * ns = n->get_sort(); @@ -1007,16 +1008,16 @@ namespace smt { This may happen because the evaluator uses model_completion. In the beginning of fix_model() we collected all f with partial interpretations. During the process of computing the - projections we used the evalutator with model_completion, + projections we used the evaluator with model_completion, and it may have fixed the "else" case of some partial interpretations. This is ok, because in the "limit" the "else" of the interpretation is irrelevant after the projections are applied. */ func_decl * get_f_i_proj(func_decl * f, unsigned i) { - node * r = 0; + node * r = nullptr; ast_idx_pair k(f, i); if (!m_A_f_is.find(k, r)) - return 0; + return nullptr; return r->get_proj(); } @@ -1027,7 +1028,7 @@ namespace smt { void complete_partial_funcs(func_decl_set const & partial_funcs) { for (func_decl * f : partial_funcs) { // Complete the current interpretation - m_model->complete_partial_func(f); + m_model->complete_partial_func(f, true); unsigned arity = f->get_arity(); func_interp * fi = m_model->get_func_interp(f); @@ -1039,7 +1040,7 @@ namespace smt { for (unsigned i = 0; i < arity; i++) { var * v = m.mk_var(i, f->get_domain(i)); func_decl * pi = get_f_i_proj(f, i); - if (pi != 0) { + if (pi != nullptr) { args.push_back(m.mk_app(pi, v)); has_proj = true; } @@ -1134,24 +1135,24 @@ namespace smt { unsigned m_var_j; public: f_var(func_decl * f, unsigned i, unsigned j):m_f(f), m_arg_i(i), m_var_j(j) {} - virtual ~f_var() {} + ~f_var() override {} - virtual char const * get_kind() const { + char const * get_kind() const override { return "f_var"; } - virtual bool is_equal(qinfo const * qi) const { + bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; f_var const * other = static_cast(qi); return m_f == other->m_f && m_arg_i == other->m_arg_i && m_var_j == other->m_var_j; } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { out << "(" << m_f->get_name() << ":" << m_arg_i << " -> v!" << m_var_j << ")"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_A_f_i(m_f, m_arg_i); node * n2 = s.get_uvar(q, m_var_j); CTRACE("model_finder", n1->get_sort() != n2->get_sort(), @@ -1170,7 +1171,7 @@ namespace smt { n1->merge(n2); } - virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { + void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { node * A_f_i = s.get_A_f_i(m_f, m_arg_i); enode_vector::const_iterator it = ctx->begin_enodes_of(m_f); enode_vector::const_iterator end = ctx->end_enodes_of(m_f); @@ -1193,7 +1194,7 @@ namespace smt { } } - virtual void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) { + void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) override { if (m_f != mhead) return; uvar_inst_sets.reserve(m_var_j+1, 0); @@ -1222,31 +1223,31 @@ namespace smt { f_var(f, i, j), m_offset(offset, m) { } - virtual ~f_var_plus_offset() {} + ~f_var_plus_offset() override {} - virtual char const * get_kind() const { + char const * get_kind() const override { return "f_var_plus_offset"; } - virtual bool is_equal(qinfo const * qi) const { + bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; f_var_plus_offset const * other = static_cast(qi); return m_f == other->m_f && m_arg_i == other->m_arg_i && m_var_j == other->m_var_j && m_offset.get() == other->m_offset.get(); } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { out << "(" << m_f->get_name() << ":" << m_arg_i << " - " << mk_bounded_pp(m_offset.get(), m_offset.get_manager()) << " -> v!" << m_var_j << ")"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { // just create the nodes /* node * A_f_i = */ s.get_A_f_i(m_f, m_arg_i); /* node * S_j = */ s.get_uvar(q, m_var_j); } - virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { + void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { // S_j is not necessary equal to A_f_i. node * A_f_i = s.get_A_f_i(m_f, m_arg_i)->get_root(); node * S_j = s.get_uvar(q, m_var_j)->get_root(); @@ -1318,7 +1319,7 @@ namespace smt { } } - virtual void populate_inst_sets2(quantifier * q, auf_solver & s, context * ctx) { + void populate_inst_sets2(quantifier * q, auf_solver & s, context * ctx) override { node * A_f_i = s.get_A_f_i(m_f, m_arg_i)->get_root(); node * S_j = s.get_uvar(q, m_var_j)->get_root(); // If A_f_i == S_j, then there is no finite fixpoint, so we do nothing here. @@ -1331,7 +1332,7 @@ namespace smt { } } - virtual void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) { + void populate_inst_sets(quantifier * q, func_decl * mhead, ptr_vector & uvar_inst_sets, context * ctx) override { // ignored when in macro } @@ -1384,31 +1385,31 @@ namespace smt { func_decl * get_array_func_decl(app * ground_array, auf_solver & s) { expr * ground_array_interp = s.eval(ground_array, false); - if (ground_array_interp != 0 && m_array.is_as_array(ground_array_interp)) + if (ground_array_interp != nullptr && m_array.is_as_array(ground_array_interp)) return m_array.get_as_array_func_decl(ground_array_interp); - return 0; + return nullptr; } public: select_var(ast_manager & m, app * s, unsigned i, unsigned j):m_manager(m), m_array(m), m_select(s), m_arg_i(i), m_var_j(j) {} - virtual ~select_var() {} + ~select_var() override {} - virtual char const * get_kind() const { + char const * get_kind() const override { return "select_var"; } - virtual bool is_equal(qinfo const * qi) const { + bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; select_var const * other = static_cast(qi); return m_select == other->m_select && m_arg_i == other->m_arg_i && m_var_j == other->m_var_j; } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { out << "(" << mk_bounded_pp(m_select, m_manager) << ":" << m_arg_i << " -> v!" << m_var_j << ")"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { ptr_buffer arrays; get_auf_arrays(get_array(), ctx, arrays); TRACE("select_var", @@ -1428,7 +1429,7 @@ namespace smt { } } - virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { + void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { ptr_buffer arrays; get_auf_arrays(get_array(), ctx, arrays); for (enode * curr : arrays) { @@ -1461,20 +1462,20 @@ namespace smt { std::swap(m_var_i, m_var_j); } - virtual ~var_pair() {} + ~var_pair() override {} - virtual bool is_equal(qinfo const * qi) const { + bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; var_pair const * other = static_cast(qi); return m_var_i == other->m_var_i && m_var_j == other->m_var_j; } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { out << "(" << get_kind() << ":v!" << m_var_i << ":v!" << m_var_j << ")"; } - virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { + void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { // do nothing } }; @@ -1482,9 +1483,9 @@ namespace smt { class x_eq_y : public var_pair { public: x_eq_y(unsigned i, unsigned j):var_pair(i, j) {} - virtual char const * get_kind() const { return "x_eq_y"; } + char const * get_kind() const override { return "x_eq_y"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->insert_avoid(n2); @@ -1496,9 +1497,9 @@ namespace smt { class x_neq_y : public var_pair { public: x_neq_y(unsigned i, unsigned j):var_pair(i, j) {} - virtual char const * get_kind() const { return "x_neq_y"; } + char const * get_kind() const override { return "x_neq_y"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->merge(n2); @@ -1508,9 +1509,9 @@ namespace smt { class x_leq_y : public var_pair { public: x_leq_y(unsigned i, unsigned j):var_pair(i, j) {} - virtual char const * get_kind() const { return "x_leq_y"; } + char const * get_kind() const override { return "x_leq_y"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->merge(n2); @@ -1522,9 +1523,9 @@ namespace smt { class x_sleq_y : public x_leq_y { public: x_sleq_y(unsigned i, unsigned j):x_leq_y(i, j) {} - virtual char const * get_kind() const { return "x_sleq_y"; } + char const * get_kind() const override { return "x_sleq_y"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); node * n2 = s.get_uvar(q, m_var_j); n1->merge(n2); @@ -1540,16 +1541,16 @@ namespace smt { public: var_expr_pair(ast_manager & m, unsigned i, expr * t): m_var_i(i), m_t(t, m) {} - ~var_expr_pair() {} + ~var_expr_pair() override {} - virtual bool is_equal(qinfo const * qi) const { + bool is_equal(qinfo const * qi) const override { if (qi->get_kind() != get_kind()) return false; var_expr_pair const * other = static_cast(qi); return m_var_i == other->m_var_i && m_t.get() == other->m_t.get(); } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { out << "(" << get_kind() << ":v!" << m_var_i << ":" << mk_bounded_pp(m_t.get(), m_t.get_manager()) << ")"; } }; @@ -1558,19 +1559,19 @@ namespace smt { public: x_eq_t(ast_manager & m, unsigned i, expr * t): var_expr_pair(m, i, t) {} - virtual char const * get_kind() const { return "x_eq_t"; } + char const * get_kind() const override { return "x_eq_t"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { node * n1 = s.get_uvar(q, m_var_i); n1->insert_exception(m_t); } - virtual void populate_inst_sets(quantifier * q, auf_solver & slv, context * ctx) { + void populate_inst_sets(quantifier * q, auf_solver & slv, context * ctx) override { unsigned num_vars = q->get_num_decls(); ast_manager & m = ctx->get_manager(); sort * s = q->get_decl_sort(num_vars - m_var_i - 1); if (m.is_uninterp(s)) { - // For uninterpreted sorst, we add all terms in the context. + // For uninterpreted sorts, we add all terms in the context. // See Section 4.1 in the paper "Complete Quantifier Instantiation" node * S_q_i = slv.get_uvar(q, m_var_i); ptr_vector::const_iterator it = ctx->begin_enodes(); @@ -1589,14 +1590,14 @@ namespace smt { public: x_neq_t(ast_manager & m, unsigned i, expr * t): var_expr_pair(m, i, t) {} - virtual char const * get_kind() const { return "x_neq_t"; } + char const * get_kind() const override { return "x_neq_t"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { // make sure that S_q_i is create. s.get_uvar(q, m_var_i); } - virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { + void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { node * S_q_i = s.get_uvar(q, m_var_i); S_q_i->insert(m_t, 0); } @@ -1606,15 +1607,15 @@ namespace smt { public: x_gle_t(ast_manager & m, unsigned i, expr * t): var_expr_pair(m, i, t) {} - virtual char const * get_kind() const { return "x_gle_t"; } + char const * get_kind() const override { return "x_gle_t"; } - virtual void process_auf(quantifier * q, auf_solver & s, context * ctx) { + void process_auf(quantifier * q, auf_solver & s, context * ctx) override { // make sure that S_q_i is create. node * n1 = s.get_uvar(q, m_var_i); n1->set_mono_proj(); } - virtual void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) { + void populate_inst_sets(quantifier * q, auf_solver & s, context * ctx) override { node * S_q_i = s.get_uvar(q, m_var_i); S_q_i->insert(m_t, 0); } @@ -1656,7 +1657,7 @@ namespace smt { expr * get_cond() const { return m_cond; } - bool is_unconditional() const { return m_cond == 0 || m_manager.is_true(m_cond); } + bool is_unconditional() const { return m_cond == nullptr || m_manager.is_true(m_cond); } bool satisfy_atom() const { return m_satisfy_atom; } @@ -1736,12 +1737,12 @@ namespace smt { m_flat_q(m), m_is_auf(true), m_has_x_eq_y(false), - m_the_one(0), - m_uvar_inst_sets(0) { + m_the_one(nullptr), + m_uvar_inst_sets(nullptr) { if (has_quantifiers(q->get_expr())) { static bool displayed_flat_msg = false; if (!displayed_flat_msg) { - // [Leo]: This warning message is not usefult. + // [Leo]: This warning message is not useful. // warning_msg("For problems containing quantifiers, the model finding capabilities of Z3 work better when the formula does not contain nested quantifiers. You can use PULL_NESTED_QUANTIFIERS=true to eliminate nested quantifiers."); displayed_flat_msg = true; } @@ -1784,13 +1785,8 @@ namespace smt { return !m_cond_macros.empty(); } - macro_iterator begin_macros() const { - return m_cond_macros.begin(); - } + ptr_vector const& macros() const { return m_cond_macros; } - macro_iterator end_macros() const { - return m_cond_macros.end(); - } void set_the_one(func_decl * m) { m_the_one = m; @@ -1850,30 +1846,30 @@ namespace smt { void populate_macro_based_inst_sets(context * ctx, evaluator & ev) { SASSERT(m_the_one != 0); - if (m_uvar_inst_sets != 0) + if (m_uvar_inst_sets != nullptr) return; m_uvar_inst_sets = alloc(ptr_vector); for (qinfo* qi : m_qinfo_vect) qi->populate_inst_sets(m_flat_q, m_the_one, *m_uvar_inst_sets, ctx); for (instantiation_set * s : *m_uvar_inst_sets) { - if (s != 0) + if (s != nullptr) s->mk_inverse(ev); } } instantiation_set * get_macro_based_inst_set(unsigned vidx, context * ctx, evaluator & ev) { - if (m_the_one == 0) - return 0; + if (m_the_one == nullptr) + return nullptr; populate_macro_based_inst_sets(ctx, ev); return m_uvar_inst_sets->get(vidx, 0); } void reset_the_one() { - m_the_one = 0; + m_the_one = nullptr; if (m_uvar_inst_sets) { std::for_each(m_uvar_inst_sets->begin(), m_uvar_inst_sets->end(), delete_proc()); dealloc(m_uvar_inst_sets); - m_uvar_inst_sets = 0; + m_uvar_inst_sets = nullptr; } } }; @@ -2104,7 +2100,7 @@ namespace smt { } /** - \brief Process unintrepreted applications. + \brief Process uninterpreted applications. */ void process_u_app(app * t) { unsigned num_args = t->get_num_args(); @@ -2130,7 +2126,7 @@ namespace smt { /** \brief A term \c t is said to be a auf_select if - it is of ther form + it is of the form (select a i) Where: @@ -2151,7 +2147,7 @@ namespace smt { } /** - \brief Process intrepreted applications. + \brief Process interpreted applications. */ void process_i_app(app * t) { if (is_auf_select(t)) { @@ -2319,9 +2315,6 @@ namespace smt { case OP_ITE: process_ite(to_app(curr), pol); break; - case OP_IFF: - process_iff(to_app(curr)); - break; case OP_EQ: if (m_manager.is_bool(to_app(curr)->get_arg(0))) { process_iff(to_app(curr)); @@ -2389,7 +2382,7 @@ namespace smt { m_array_util(m), m_arith_util(m), m_bv_util(m), - m_info(0) { + m_info(nullptr) { } @@ -2416,7 +2409,7 @@ namespace smt { collect_macro_candidates(q); - m_info = 0; + m_info = nullptr; } }; @@ -2431,7 +2424,7 @@ namespace smt { proto_model * m_model; quantifier_info * get_qinfo(quantifier * q) const { - quantifier_info * qi = 0; + quantifier_info * qi = nullptr; m_q2info.find(q, qi); SASSERT(qi != 0); return qi; @@ -2440,11 +2433,12 @@ namespace smt { void set_else_interp(func_decl * f, expr * f_else) { SASSERT(f_else != 0); func_interp * fi = m_model->get_func_interp(f); - if (fi == 0) { + if (fi == nullptr) { fi = alloc(func_interp, m_manager, f->get_arity()); m_model->register_decl(f, fi); } fi->set_else(f_else); + TRACE("model_finder", tout << f->get_name() << " " << mk_pp(f_else, m_manager) << "\n";); } virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) = 0; @@ -2453,7 +2447,7 @@ namespace smt { base_macro_solver(ast_manager & m, obj_map const & q2i): m_manager(m), m_q2info(q2i), - m_model(0) { + m_model(nullptr) { } virtual ~base_macro_solver() {} @@ -2499,10 +2493,7 @@ namespace smt { bool process(quantifier * q, ptr_vector const & qs) { quantifier_info * qi = get_qinfo(q); - quantifier_info::macro_iterator it = qi->begin_macros(); - quantifier_info::macro_iterator end = qi->end_macros(); - for (; it != end; ++it) { - cond_macro * m = *it; + for (cond_macro* m : qi->macros()) { if (!m->satisfy_atom()) continue; func_decl * f = m->get_f(); @@ -2512,7 +2503,7 @@ namespace smt { SASSERT(f_else != 0); // Remark: I can ignore the conditions of m because // I know the (partial) interpretation of f satisfied the ground part. - // MBQI will force extra instantiations if the the (partial) interpretation of f + // MBQI will force extra instantiations if the (partial) interpretation of f // does not satisfy the quantifier. // In all other cases the "else" of f will satisfy the quantifier. set_else_interp(f, f_else); @@ -2524,7 +2515,7 @@ namespace smt { return false; } - virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) { + bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) override { bool removed = false; for (quantifier* q : qs) { if (process(q, qs)) @@ -2548,10 +2539,10 @@ namespace smt { where Q_{f_i} is the set of quantifiers that contain the function f_i. Let f_i = def_i be macros (in this solver conditions are ignored). Let Q_{f_i = def_i} be the set of quantifiers where f_i = def_i is a macro. - Then, the set Q can be satisfied using f_1 = def_1 ... f_n = d_n + Then, the set Q can be satisfied using f_1 = def_1 ... f_n = def_n when - Q_{f_1} union ... union Q_{f_n} = Q_{f_1 = def_1} ... Q_{f_n = d_n} (*) + Q_{f_1} union ... union Q_{f_n} = Q_{f_1 = def_1} ... Q_{f_n = def_n} (*) So, given a set of macros f_1 = def_1, ..., f_n = d_n, it is very easy to check whether they can be used to satisfy all quantifiers that use f_1, ..., f_n in @@ -2596,7 +2587,7 @@ namespace smt { void insert_q_f(quantifier * q, func_decl * f) { SASSERT(!m_forbidden.contains(f)); - quantifier_set * s = 0; + quantifier_set * s = nullptr; if (!m_q_f.find(f, s)) { s = alloc(quantifier_set); m_q_f.insert(f, s); @@ -2607,7 +2598,7 @@ namespace smt { } void insert_f2def(func_decl * f, expr * def) { - expr_set * s = 0; + expr_set * s = nullptr; if (!m_f2defs.find(f, s)) { s = alloc(expr_set); m_f2defs.insert(f, s); @@ -2619,7 +2610,7 @@ namespace smt { void insert_q_f_def(quantifier * q, func_decl * f, expr * def) { SASSERT(!m_forbidden.contains(f)); - quantifier_set * s = 0; + quantifier_set * s = nullptr; if (!m_q_f_def.find(f, def, s)) { s = alloc(quantifier_set); m_q_f_def.insert(f, def, s); @@ -2630,26 +2621,16 @@ namespace smt { s->insert(q); } - quantifier_set * get_q_f(func_decl * f) { - quantifier_set * s = 0; - m_q_f.find(f, s); - SASSERT(s != 0); - return s; - } + quantifier_set * get_q_f(func_decl * f) { return m_q_f[f]; } quantifier_set * get_q_f_def(func_decl * f, expr * def) { - quantifier_set * s = 0; + quantifier_set * s = nullptr; m_q_f_def.find(f, def, s); SASSERT(s != 0); return s; } - expr_set * get_f_defs(func_decl * f) { - expr_set * s = 0; - m_f2defs.find(f, s); - SASSERT(s != 0); - return s; - } + expr_set * get_f_defs(func_decl * f) { return m_f2defs[f]; } void reset_q_fs() { std::for_each(m_qsets.begin(), m_qsets.end(), delete_proc()); @@ -2666,10 +2647,7 @@ namespace smt { bool is_candidate(quantifier * q) const { quantifier_info * qi = get_qinfo(q); - quantifier_info::macro_iterator it = qi->begin_macros(); - quantifier_info::macro_iterator end = qi->end_macros(); - for (; it != end; ++it) { - cond_macro * m = *it; + for (cond_macro * m : qi->macros()) { if (m->satisfy_atom() && !m_forbidden.contains(m->get_f())) return true; } @@ -2712,10 +2690,7 @@ namespace smt { if (!m_forbidden.contains(f)) insert_q_f(q, f); } - quantifier_info::macro_iterator it3 = qi->begin_macros(); - quantifier_info::macro_iterator end3 = qi->end_macros(); - for (; it3 != end3; ++it3) { - cond_macro * m = *it3; + for (cond_macro * m : qi->macros()) { if (m->satisfy_atom() && !m_forbidden.contains(m->get_f())) { insert_q_f_def(q, m->get_f(), m->get_def()); m_candidates.insert(m->get_f()); @@ -2762,7 +2737,7 @@ namespace smt { void operator()(quantifier * q, bool ins) { quantifier_info * qi = m_owner->get_qinfo(q); - qi->set_the_one(0); + qi->set_the_one(nullptr); } ev_handler(hint_solver * o): @@ -2842,11 +2817,7 @@ namespace smt { void get_candidates_from_residue(func_decl_set & candidates) { for (quantifier * q : m_residue) { quantifier_info * qi = get_qinfo(q); - - quantifier_info::macro_iterator it2 = qi->begin_macros(); - quantifier_info::macro_iterator end2 = qi->end_macros(); - for (; it2 != end2; ++it2) { - cond_macro * m = *it2; + for (cond_macro * m : qi->macros()) { func_decl * f = m->get_f(); if (m->satisfy_atom() && !m_forbidden.contains(f) && !m_fs.contains(f)) { candidates.insert(f); @@ -2875,6 +2846,7 @@ namespace smt { m_satisfied.push_scope(); m_residue.push_scope(); + TRACE("model_finder", tout << f->get_name() << " " << mk_pp(def, m_manager) << "\n";); m_fs.insert(f, def); if (update_satisfied_residue(f, def)) { @@ -2889,12 +2861,56 @@ namespace smt { } } + /** + \brief check if satisfied subset introduces a cyclic dependency. + + f_1 = def_1(f_2), ..., f_n = def_n(f_1) + */ + + expr_mark m_visited; + obj_hashtable m_acyclic; + bool is_cyclic() { + m_acyclic.reset(); + while (true) { + unsigned sz = m_acyclic.size(); + if (sz == m_fs.size()) return false; // there are no cyclic dependencies + for (auto const& kv : m_fs) { + func_decl * f = kv.m_key; + if (m_acyclic.contains(f)) continue; + if (is_acyclic(kv.m_value)) + m_acyclic.insert(f); + } + if (sz == m_acyclic.size()) return true; // no progress, so dependency cycle found. + } + } + + struct occurs {}; + struct occurs_check { + hint_solver& m_cls; + occurs_check(hint_solver& hs): m_cls(hs) {} + void operator()(app* n) { if (m_cls.m_fs.contains(n->get_decl()) && !m_cls.m_acyclic.contains(n->get_decl())) throw occurs(); } + void operator()(var* n) {} + void operator()(quantifier* n) {} + }; + bool is_acyclic(expr* def) { + m_visited.reset(); + occurs_check oc(*this); + try { + for_each_expr(oc, m_visited, def); + } + catch (occurs) { + return false; + } + return true; + } + /** \brief Try to reduce m_residue (if not empty) by selecting a function f that is a macro in the residue. */ void greedy(unsigned depth) { if (m_residue.empty()) { + if (is_cyclic()) return; TRACE("model_finder_hint", tout << "found subset that is satisfied by macros\n"; display_search_state(tout);); @@ -2937,7 +2953,7 @@ namespace smt { } /** - \brief Use m_fs to set the interpreation of the function symbols that were used to satisfy the + \brief Use m_fs to set the interpretation of the function symbols that were used to satisfy the quantifiers in m_satisfied. */ void set_interp() { @@ -2957,7 +2973,7 @@ namespace smt { m_fs.reset(); } - virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) { + bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) override { reset(); ptr_vector qcandidates; preprocess(qs, qcandidates, new_qs); @@ -2988,7 +3004,7 @@ namespace smt { m_satisfied(ev_handler(this)) { } - virtual ~hint_solver() { + ~hint_solver() override { reset(); } @@ -3007,11 +3023,11 @@ namespace smt { qi_params const * m_qi_params; bool add_macro(func_decl * f, expr * f_else) { - TRACE("non_auf_macro_solver", tout << "trying to add macro for " << f->get_name() << "\n" << mk_pp(f_else, m_manager) << "\n";); + TRACE("model_finder", tout << "trying to add macro for " << f->get_name() << "\n" << mk_pp(f_else, m_manager) << "\n";); func_decl_set * s = m_dependencies.mk_func_decl_set(); m_dependencies.collect_ng_func_decls(f_else, s); if (!m_dependencies.insert(f, s)) { - TRACE("non_auf_macro_solver", tout << "failed to add macro\n";); + TRACE("model_finder", tout << "failed to add macro\n";); return false; // cyclic dependency } set_else_interp(f, f_else); @@ -3020,7 +3036,7 @@ namespace smt { // Return true if r1 is a better macro than r2. bool is_better_macro(cond_macro * r1, cond_macro * r2) { - if (r2 == 0 || !r1->is_hint()) + if (r2 == nullptr || !r1->is_hint()) return true; if (!r2->is_hint()) return false; @@ -3031,12 +3047,9 @@ namespace smt { } cond_macro * get_macro_for(func_decl * f, quantifier * q) { - cond_macro * r = 0; + cond_macro * r = nullptr; quantifier_info * qi = get_qinfo(q); - quantifier_info::macro_iterator it = qi->begin_macros(); - quantifier_info::macro_iterator end = qi->end_macros(); - for (; it != end; ++it) { - cond_macro * m = *it; + for (cond_macro * m : qi->macros()) { if (m->get_f() == f && !m->is_hint() && is_better_macro(m, r)) r = m; } @@ -3048,13 +3061,10 @@ namespace smt { void collect_candidates(ptr_vector const & qs, obj_map & full_macros, func_decl_set & cond_macros) { for (quantifier * q : qs) { quantifier_info * qi = get_qinfo(q); - quantifier_info::macro_iterator it2 = qi->begin_macros(); - quantifier_info::macro_iterator end2 = qi->end_macros(); - for (; it2 != end2; ++it2) { - cond_macro * m = *it2; + for (cond_macro * m : qi->macros()) { if (!m->is_hint()) { func_decl * f = m->get_f(); - TRACE("non_auf_macro_solver", tout << "considering macro for: " << f->get_name() << "\n"; + TRACE("model_finder", tout << "considering macro for: " << f->get_name() << "\n"; m->display(tout); tout << "\n";); SASSERT(m_qi_params != 0); if (m->is_unconditional() && (!qi->is_auf() || m->get_weight() >= m_qi_params->m_mbqi_force_template)) { @@ -3094,14 +3104,14 @@ namespace smt { if (m->is_unconditional()) return; // f is part of a full macro... ignoring it. to_remove.push_back(q); - if (fi_else.get() == 0) { + if (fi_else.get() == nullptr) { fi_else = m->get_def(); } else { fi_else = m_manager.mk_ite(m->get_cond(), m->get_def(), fi_else); } } - if (fi_else.get() != 0 && add_macro(f, fi_else)) { + if (fi_else.get() != nullptr && add_macro(f, fi_else)) { for (quantifier * q : to_remove) { get_qinfo(q)->set_the_one(f); removed.insert(q); @@ -3115,7 +3125,7 @@ namespace smt { } } - virtual bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) { + bool process(ptr_vector const & qs, ptr_vector & new_qs, ptr_vector & residue) override { obj_map full_macros; func_decl_set cond_macros; obj_hashtable removed; @@ -3139,7 +3149,7 @@ namespace smt { non_auf_macro_solver(ast_manager & m, obj_map const & q2i, func_decl_dependencies & d): base_macro_solver(m, q2i), m_dependencies(d), - m_qi_params(0) { + m_qi_params(nullptr) { } void set_params(qi_params const & p) { @@ -3157,7 +3167,7 @@ namespace smt { model_finder::model_finder(ast_manager & m): m_manager(m), - m_context(0), + m_context(nullptr), m_analyzer(alloc(quantifier_analyzer, *this, m)), m_auf_solver(alloc(auf_solver, m)), m_dependencies(m), @@ -3182,7 +3192,7 @@ namespace smt { } mf::quantifier_info * model_finder::get_quantifier_info(quantifier * q) const { - quantifier_info * info = 0; + quantifier_info * info = nullptr; m_q2info.find(q, info); SASSERT(info != 0); return info; @@ -3348,7 +3358,7 @@ namespace smt { instantiation_set const * r = m_auf_solver->get_uvar_inst_set(flat_q, flat_q->get_num_decls() - q->get_num_decls() + i); TRACE("model_finder", tout << "q: #" << q->get_id() << "\n" << mk_pp(q,m_manager) << "\nflat_q: " << mk_pp(flat_q, m_manager) << "\ni: " << i << " " << flat_q->get_num_decls() - q->get_num_decls() + i << "\n";); - if (r != 0) + if (r != nullptr) return r; // quantifier was not processed by AUF solver... // it must have been satisfied by "macro"/"hint". @@ -3368,10 +3378,10 @@ namespace smt { */ expr * model_finder::get_inv(quantifier * q, unsigned i, expr * val, unsigned & generation) const { instantiation_set const * s = get_uvar_inst_set(q, i); - if (s == 0) - return 0; + if (s == nullptr) + return nullptr; expr * t = s->get_inv(val); - if (t != 0) { + if (t != nullptr) { generation = s->get_generation(t); } return t; @@ -3399,7 +3409,7 @@ namespace smt { for (unsigned i = 0; i < num_decls; i++) { expr * sk = sks.get(num_decls - i - 1); instantiation_set const * s = get_uvar_inst_set(q, i); - if (s == 0) + if (s == nullptr) continue; // nothing to do obj_map const & inv = s->get_inv_map(); if (inv.empty()) diff --git a/src/smt/smt_model_generator.cpp b/src/smt/smt_model_generator.cpp index df6865f1e..9ba2c6165 100644 --- a/src/smt/smt_model_generator.cpp +++ b/src/smt/smt_model_generator.cpp @@ -29,10 +29,10 @@ namespace smt { model_generator::model_generator(ast_manager & m): m_manager(m), - m_context(0), + m_context(nullptr), m_fresh_idx(1), m_asts(m_manager), - m_model(0) { + m_model(nullptr) { } model_generator::~model_generator() { @@ -44,7 +44,7 @@ namespace smt { m_fresh_idx = 1; m_root2value.reset(); m_asts.reset(); - m_model = 0; + m_model = nullptr; } void model_generator::init_model() { @@ -89,7 +89,7 @@ namespace smt { if (r == r->get_root() && m_context->is_relevant(r)) { roots.push_back(r); sort * s = m_manager.get_sort(r->get_owner()); - model_value_proc * proc = 0; + model_value_proc * proc = nullptr; if (m_manager.is_bool(s)) { CTRACE("model", m_context->get_assignment(r) == l_undef, tout << mk_pp(r->get_owner(), m_manager) << "\n";); @@ -177,7 +177,7 @@ namespace smt { if (m_manager.get_sort(r->get_owner()) != s) continue; SASSERT(r == r->get_root()); - model_value_proc * proc = 0; + model_value_proc * proc = nullptr; root2proc.find(r, proc); SASSERT(proc); if (proc->is_fresh()) @@ -196,7 +196,7 @@ namespace smt { enode * n = src.get_enode(); SASSERT(n == n->get_root()); bool visited = true; - model_value_proc * proc = 0; + model_value_proc * proc = nullptr; root2proc.find(n, proc); SASSERT(proc); buffer dependencies; @@ -283,7 +283,7 @@ namespace smt { sz = roots.size(); for (unsigned i = 0; i < sz; i++) { enode * r = roots[i]; - model_value_proc * proc = 0; + model_value_proc * proc = nullptr; root2proc.find(r, proc); SASSERT(proc); if (!proc->is_fresh()) @@ -345,7 +345,7 @@ namespace smt { TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << "\n";); dependencies.reset(); dependency_values.reset(); - model_value_proc * proc = 0; + model_value_proc * proc = nullptr; VERIFY(root2proc.find(n, proc)); SASSERT(proc); proc->get_dependencies(dependencies); @@ -364,7 +364,7 @@ namespace smt { enode * child = d.get_enode(); TRACE("mg_top_sort", tout << "#" << n->get_owner_id() << " (" << mk_pp(n->get_owner(), m_manager) << "): " << mk_pp(child->get_owner(), m_manager) << " " << mk_pp(child->get_root()->get_owner(), m_manager) << "\n";); child = child->get_root(); - app * val = 0; + app * val = nullptr; m_root2value.find(child, val); SASSERT(val); dependency_values.push_back(val); @@ -387,6 +387,7 @@ namespace smt { enode * n = *it3; if (is_uninterp_const(n->get_owner()) && m_context->is_relevant(n)) { func_decl * d = n->get_owner()->get_decl(); + TRACE("mg_top_sort", tout << d->get_name() << " " << (m_hidden_ufs.contains(d)?"hidden":"visible") << "\n";); if (m_hidden_ufs.contains(d)) continue; expr * val = get_value(n); m_model->register_decl(d, val); @@ -395,7 +396,7 @@ namespace smt { } app * model_generator::get_value(enode * n) const { - app * val = 0; + app * val = nullptr; m_root2value.find(n->get_root(), val); SASSERT(val); return val; @@ -438,7 +439,7 @@ namespace smt { args.push_back(arg); } func_interp * fi = m_model->get_func_interp(f); - if (fi == 0) { + if (fi == nullptr) { fi = alloc(func_interp, m_manager, f->get_arity()); m_model->register_decl(f, fi); } @@ -454,7 +455,7 @@ namespace smt { tout << "value: #" << n->get_owner_id() << "\n" << mk_ismt2_pp(result, m_manager) << "\n";); if (m_context->get_last_search_failure() == smt::THEORY) { // if the theory solvers are incomplete, then we cannot assume the e-graph is close under congruence - if (fi->get_entry(args.c_ptr()) == 0) + if (fi->get_entry(args.c_ptr()) == nullptr) fi->insert_new_entry(args.c_ptr(), result); } else { diff --git a/src/smt/smt_model_generator.h b/src/smt/smt_model_generator.h index ee133482d..7466b8877 100644 --- a/src/smt/smt_model_generator.h +++ b/src/smt/smt_model_generator.h @@ -80,7 +80,7 @@ namespace smt { unsigned m_idx; expr * m_value; public: - extra_fresh_value(sort * s, unsigned idx):m_sort(s), m_idx(idx), m_value(0) {} + extra_fresh_value(sort * s, unsigned idx):m_sort(s), m_idx(idx), m_value(nullptr) {} sort * get_sort() const { return m_sort; } unsigned get_idx() const { return m_idx; } void set_value(expr * n) { SASSERT(m_value == 0); m_value = n; } @@ -100,7 +100,7 @@ namespace smt { extra_fresh_value * m_value; //!< When m_fresh == true, contains the sort of the fresh value }; public: - model_value_dependency():m_fresh(true), m_value(0) {} + model_value_dependency():m_fresh(true), m_value(nullptr) {} model_value_dependency(enode * n):m_fresh(false), m_enode(n->get_root()) {} model_value_dependency(extra_fresh_value * v):m_fresh(true), m_value(v) {} bool is_fresh_value() const { return m_fresh; } @@ -159,16 +159,16 @@ namespace smt { app * m_value; public: expr_wrapper_proc(app * v):m_value(v) {} - virtual app * mk_value(model_generator & m, ptr_vector & values) { return m_value; } + app * mk_value(model_generator & m, ptr_vector & values) override { return m_value; } }; class fresh_value_proc : public model_value_proc { extra_fresh_value * m_value; public: fresh_value_proc(extra_fresh_value * v):m_value(v) {} - virtual void get_dependencies(buffer & result) { result.push_back(m_value); } - virtual app * mk_value(model_generator & m, ptr_vector & values) { return to_app(values[0]); } - virtual bool is_fresh() const { return true; } + void get_dependencies(buffer & result) override { result.push_back(m_value); } + app * mk_value(model_generator & m, ptr_vector & values) override { return to_app(values[0]); } + bool is_fresh() const override { return true; } }; /** diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index c6f83c611..1f6811a0f 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -143,7 +143,7 @@ namespace smt { tout << "inserted: " << (f != 0) << "\n"; ); - return f != 0; + return f != nullptr; } void init_search_eh() { @@ -222,7 +222,7 @@ namespace smt { final_check_status final_check_eh(bool full) { if (full) { - IF_VERBOSE(100, verbose_stream() << "(smt.final-check \"quantifiers\")\n";); + IF_VERBOSE(100, if (!m_quantifiers.empty()) verbose_stream() << "(smt.final-check \"quantifiers\")\n";); final_check_status result = m_qi_queue.final_check_eh() ? FC_DONE : FC_CONTINUE; final_check_status presult = m_plugin->final_check_eh(full); if (presult != FC_DONE) @@ -300,7 +300,7 @@ namespace smt { bool quantifier_manager::add_instance(quantifier * q, unsigned num_bindings, enode * const * bindings, unsigned generation) { ptr_vector tmp; - return add_instance(q, 0, num_bindings, bindings, generation, generation, generation, tmp); + return add_instance(q, nullptr, num_bindings, bindings, generation, generation, generation, tmp); } void quantifier_manager::init_search_eh() { @@ -408,17 +408,17 @@ namespace smt { bool m_active; public: default_qm_plugin(): - m_qm(0), - m_context(0), + m_qm(nullptr), + m_context(nullptr), m_new_enode_qhead(0), m_lazy_matching_idx(0), m_active(false) { } - virtual ~default_qm_plugin() { + ~default_qm_plugin() override { } - virtual void set_manager(quantifier_manager & qm) { + void set_manager(quantifier_manager & qm) override { SASSERT(m_qm == 0); m_qm = &qm; m_context = &(qm.get_context()); @@ -434,11 +434,11 @@ namespace smt { m_model_checker->set_qm(qm); } - virtual quantifier_manager_plugin * mk_fresh() { return alloc(default_qm_plugin); } + quantifier_manager_plugin * mk_fresh() override { return alloc(default_qm_plugin); } - virtual bool model_based() const { return m_fparams->m_mbqi; } + bool model_based() const override { return m_fparams->m_mbqi; } - virtual bool mbqi_enabled(quantifier *q) const { + bool mbqi_enabled(quantifier *q) const override { if (!m_fparams->m_mbqi_id) return true; const symbol &s = q->get_qid(); size_t len = strlen(m_fparams->m_mbqi_id); @@ -450,16 +450,16 @@ namespace smt { /* Quantifier id's must begin with the prefix specified by parameter mbqi.id to be instantiated with MBQI. The default value is the empty string, so all quantifiers are instantiated. */ - virtual void add(quantifier * q) { + void add(quantifier * q) override { if (m_fparams->m_mbqi && mbqi_enabled(q)) { m_active = true; m_model_finder->register_quantifier(q); } } - virtual void del(quantifier * q) { } + void del(quantifier * q) override { } - virtual void push() { + void push() override { m_mam->push_scope(); m_lazy_mam->push_scope(); if (m_fparams->m_mbqi) { @@ -467,7 +467,7 @@ namespace smt { } } - virtual void pop(unsigned num_scopes) { + void pop(unsigned num_scopes) override { m_mam->pop_scope(num_scopes); m_lazy_mam->pop_scope(num_scopes); if (m_fparams->m_mbqi) { @@ -475,7 +475,7 @@ namespace smt { } } - virtual void init_search_eh() { + void init_search_eh() override { m_lazy_matching_idx = 0; if (m_fparams->m_mbqi) { m_model_finder->init_search_eh(); @@ -483,7 +483,7 @@ namespace smt { } } - virtual void assign_eh(quantifier * q) { + void assign_eh(quantifier * q) override { m_active = true; ast_manager& m = m_context->get_manager(); if (!m_fparams->m_ematching) { @@ -531,23 +531,23 @@ namespace smt { return m_fparams->m_ematching && !m_qm->empty(); } - virtual void add_eq_eh(enode * e1, enode * e2) { + void add_eq_eh(enode * e1, enode * e2) override { if (use_ematching()) m_mam->add_eq_eh(e1, e2); } - virtual void relevant_eh(enode * e) { + void relevant_eh(enode * e) override { if (use_ematching()) { m_mam->relevant_eh(e, false); m_lazy_mam->relevant_eh(e, true); } } - virtual bool can_propagate() const { + bool can_propagate() const override { return m_mam->has_work(); } - virtual void restart_eh() { + void restart_eh() override { if (m_fparams->m_mbqi) { m_model_finder->restart_eh(); m_model_checker->restart_eh(); @@ -555,17 +555,17 @@ namespace smt { TRACE("mam_stats", m_mam->display(tout);); } - virtual bool is_shared(enode * n) const { + bool is_shared(enode * n) const override { return m_active && (m_mam->is_shared(n) || m_lazy_mam->is_shared(n)); } - virtual void adjust_model(proto_model * m) { + void adjust_model(proto_model * m) override { if (m_fparams->m_mbqi) { m_model_finder->fix_model(m); } } - virtual void propagate() { + void propagate() override { m_mam->match(); if (!m_context->relevancy() && use_ematching()) { ptr_vector::const_iterator it = m_context->begin_enodes(); @@ -585,8 +585,8 @@ namespace smt { } } - virtual quantifier_manager::check_model_result - check_model(proto_model * m, obj_map const & root2value) { + quantifier_manager::check_model_result + check_model(proto_model * m, obj_map const & root2value) override { if (m_fparams->m_mbqi) { IF_VERBOSE(10, verbose_stream() << "(smt.mbqi)\n";); if (m_model_checker->check(m, root2value)) { @@ -599,7 +599,7 @@ namespace smt { return quantifier_manager::UNKNOWN; } - virtual final_check_status final_check_eh(bool full) { + final_check_status final_check_eh(bool full) override { if (!full) { if (m_fparams->m_qi_lazy_instantiation) return final_check_quant(); diff --git a/src/smt/smt_quantifier.h b/src/smt/smt_quantifier.h index d89f3f6a4..ad5f58e49 100644 --- a/src/smt/smt_quantifier.h +++ b/src/smt/smt_quantifier.h @@ -152,7 +152,7 @@ namespace smt { virtual bool mbqi_enabled(quantifier *q) const {return true;} /** - \brief Give a change to the plugin to adjust the interpretation of unintepreted functions. + \brief Give a change to the plugin to adjust the interpretation of uninterpreted functions. It can basically change the "else" of each uninterpreted function. */ virtual void adjust_model(proto_model * m) = 0; diff --git a/src/smt/smt_quantifier_stat.cpp b/src/smt/smt_quantifier_stat.cpp index 7d73ea27c..8c7fd196e 100644 --- a/src/smt/smt_quantifier_stat.cpp +++ b/src/smt/smt_quantifier_stat.cpp @@ -82,11 +82,13 @@ namespace smt { if (depth > 0) m_case_split_factor *= (num_args + 1); break; - case OP_IFF: - if (depth == 0) - m_case_split_factor *= 4; - else - m_case_split_factor *= 9; + case OP_EQ: + if (m_manager.is_iff(n)) { + if (depth == 0) + m_case_split_factor *= 4; + else + m_case_split_factor *= 9; + } break; case OP_ITE: if (depth == 0) diff --git a/src/smt/smt_quantifier_stat.h b/src/smt/smt_quantifier_stat.h index 308534833..5c31032f9 100644 --- a/src/smt/smt_quantifier_stat.h +++ b/src/smt/smt_quantifier_stat.h @@ -119,7 +119,7 @@ namespace smt { expr * m_expr; unsigned m_depth:31; bool m_depth_only:1; //!< track only the depth of this entry. - entry():m_expr(0), m_depth(0), m_depth_only(false) {} + entry():m_expr(nullptr), m_depth(0), m_depth_only(false) {} entry(expr * n, unsigned depth = 0, bool depth_only = false):m_expr(n), m_depth(depth), m_depth_only(depth_only) {} }; ast_manager & m_manager; diff --git a/src/smt/smt_quick_checker.cpp b/src/smt/smt_quick_checker.cpp index 72c28fd7d..72a720d98 100644 --- a/src/smt/smt_quick_checker.cpp +++ b/src/smt/smt_quick_checker.cpp @@ -108,7 +108,7 @@ namespace smt { if (n->get_family_id() != m_manager.get_basic_family_id()) collect(arg, n->get_decl(), j); else - collect(arg, 0, 0); + collect(arg, nullptr, 0); } } } @@ -157,7 +157,7 @@ namespace smt { flet l(m_conservative, conservative); init(q); TRACE("collector", tout << "model checking: #" << q->get_id() << "\n" << mk_pp(q, m_manager) << "\n";); - collect(q->get_expr(), 0, 0); + collect(q->get_expr(), nullptr, 0); save_result(candidates); } @@ -251,7 +251,7 @@ namespace smt { TRACE("quick_checker_sizes", tout << "found new candidate\n"; for (unsigned i = 0; i < m_num_bindings; i++) tout << "#" << m_bindings[i]->get_owner_id() << " "; tout << "\n";); unsigned max_generation = get_max_generation(m_num_bindings, m_bindings.c_ptr()); - if (m_context.add_instance(q, 0 /* no pattern was used */, m_num_bindings, m_bindings.c_ptr(), max_generation, + if (m_context.add_instance(q, nullptr /* no pattern was used */, m_num_bindings, m_bindings.c_ptr(), max_generation, 0, // min_top_generation is only available for instances created by the MAM 0, // max_top_generation is only available for instances created by the MAM empty_used_enodes)) @@ -311,11 +311,6 @@ namespace smt { return is_true ? any_arg(a, true) : all_args(a, false); case OP_AND: return is_true ? all_args(a, true) : any_arg(a, false); - case OP_IFF: - if (is_true) - return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false)); - else - return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true)); case OP_ITE: if (check(a->get_arg(0), true)) return check(a->get_arg(1), is_true); @@ -324,6 +319,13 @@ namespace smt { else return check(a->get_arg(1), is_true) && check(a->get_arg(2), is_true); case OP_EQ: + if (m_manager.is_iff(a)) { + if (is_true) + return (check(a->get_arg(0), true) && check(a->get_arg(1), true)) || (check(a->get_arg(0), false) && check(a->get_arg(1), false)); + else + return (check(a->get_arg(0), true) && check(a->get_arg(1), false)) || (check(a->get_arg(0), false) && check(a->get_arg(1), true)); + } + if (is_true) { return canonize(a->get_arg(0)) == canonize(a->get_arg(1)); } diff --git a/src/smt/smt_quick_checker.h b/src/smt/smt_quick_checker.h index 21a570c3c..d3af8e2e0 100644 --- a/src/smt/smt_quick_checker.h +++ b/src/smt/smt_quick_checker.h @@ -50,7 +50,7 @@ namespace smt { expr * m_expr; func_decl * m_parent; unsigned m_parent_pos; - entry(expr * n = 0, func_decl * d = 0, unsigned p = 0):m_expr(n), m_parent(d), m_parent_pos(p) {} + entry(expr * n = nullptr, func_decl * d = nullptr, unsigned p = 0):m_expr(n), m_parent(d), m_parent_pos(p) {} unsigned hash() const { return m_parent ? mk_mix(m_expr->get_id(), m_parent->get_id(), m_parent_pos) : m_expr->get_id(); } bool operator==(entry const & e) const { return m_expr == e.m_expr && m_parent == e.m_parent && m_parent_pos == e.m_parent_pos; } }; diff --git a/src/smt/smt_relevancy.cpp b/src/smt/smt_relevancy.cpp index 00457c643..7db5110ee 100644 --- a/src/smt/smt_relevancy.cpp +++ b/src/smt/smt_relevancy.cpp @@ -52,24 +52,24 @@ namespace smt { app * m_parent; public: and_relevancy_eh(app * p):m_parent(p) {} - virtual ~and_relevancy_eh() {} - virtual void operator()(relevancy_propagator & rp); + ~and_relevancy_eh() override {} + void operator()(relevancy_propagator & rp) override; }; class or_relevancy_eh : public relevancy_eh { app * m_parent; public: or_relevancy_eh(app * p):m_parent(p) {} - virtual ~or_relevancy_eh() {} - virtual void operator()(relevancy_propagator & rp); + ~or_relevancy_eh() override {} + void operator()(relevancy_propagator & rp) override; }; class ite_relevancy_eh : public relevancy_eh { app * m_parent; public: ite_relevancy_eh(app * p):m_parent(p) {} - virtual ~ite_relevancy_eh() {} - virtual void operator()(relevancy_propagator & rp); + ~ite_relevancy_eh() override {} + void operator()(relevancy_propagator & rp) override; }; class ite_term_relevancy_eh : public relevancy_eh { @@ -78,8 +78,8 @@ namespace smt { app * m_else_eq; public: ite_term_relevancy_eh(app * p, app * then_eq, app * else_eq):m_parent(p), m_then_eq(then_eq), m_else_eq(else_eq) {} - virtual ~ite_term_relevancy_eh() {} - virtual void operator()(relevancy_propagator & rp); + ~ite_term_relevancy_eh() override {} + void operator()(relevancy_propagator & rp) override; }; relevancy_propagator::relevancy_propagator(context & ctx): @@ -154,33 +154,33 @@ namespace smt { relevancy_propagator(ctx), m_qhead(0), m_relevant_exprs(ctx.get_manager()), m_propagating(false) {} - virtual ~relevancy_propagator_imp() { + ~relevancy_propagator_imp() override { undo_trail(0); } relevancy_ehs * get_handlers(expr * n) { - relevancy_ehs * r = 0; + relevancy_ehs * r = nullptr; m_relevant_ehs.find(n, r); SASSERT(m_relevant_ehs.contains(n) || r == 0); return r; } void set_handlers(expr * n, relevancy_ehs * ehs) { - if (ehs == 0) + if (ehs == nullptr) m_relevant_ehs.erase(n); else m_relevant_ehs.insert(n, ehs); } relevancy_ehs * get_watches(expr * n, bool val) { - relevancy_ehs * r = 0; + relevancy_ehs * r = nullptr; m_watches[val ? 1 : 0].find(n, r); SASSERT(m_watches[val ? 1 : 0].contains(n) || r == 0); return r; } void set_watches(expr * n, bool val, relevancy_ehs * ehs) { - if (ehs == 0) + if (ehs == nullptr) m_watches[val ? 1 : 0].erase(n); else m_watches[val ? 1 : 0].insert(n, ehs); @@ -191,7 +191,7 @@ namespace smt { m_trail.push_back(t); } - virtual void add_handler(expr * source, relevancy_eh * eh) { + void add_handler(expr * source, relevancy_eh * eh) override { if (!enabled()) return; if (is_relevant_core(source)) { @@ -204,7 +204,7 @@ namespace smt { } } - virtual void add_watch(expr * n, bool val, relevancy_eh * eh) { + void add_watch(expr * n, bool val, relevancy_eh * eh) override { if (!enabled()) return; lbool lval = m_context.find_assignment(n); @@ -224,7 +224,7 @@ namespace smt { } } - virtual void add_watch(expr * n, bool val, expr * target) { + void add_watch(expr * n, bool val, expr * target) override { if (!enabled()) return; lbool lval = m_context.find_assignment(n); @@ -244,18 +244,18 @@ namespace smt { bool is_relevant_core(expr * n) const { return m_is_relevant.contains(n); } - virtual bool is_relevant(expr * n) const { + bool is_relevant(expr * n) const override { return !enabled() || is_relevant_core(n); } - virtual void push() { + void push() override { m_scopes.push_back(scope()); scope & s = m_scopes.back(); s.m_relevant_exprs_lim = m_relevant_exprs.size(); s.m_trail_lim = m_trail.size(); } - virtual void pop(unsigned num_scopes) { + void pop(unsigned num_scopes) override { SASSERT(m_context.get_scope_level() == m_scopes.size()); unsigned lvl = m_scopes.size(); SASSERT(num_scopes <= lvl); @@ -325,12 +325,12 @@ namespace smt { \brief Mark the given expression as relevant if it is not already marked. */ - virtual void mark_as_relevant(expr * n) { + void mark_as_relevant(expr * n) override { if (!enabled()) return; if (!is_relevant_core(n)) { enode * e = m_context.find_enode(n); - if (e != 0) { + if (e != nullptr) { enode * curr = e; do { set_relevant(curr->get_owner()); @@ -376,7 +376,7 @@ namespace smt { case l_undef: break; case l_true: { - expr * true_arg = 0; + expr * true_arg = nullptr; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); @@ -400,7 +400,7 @@ namespace smt { lbool val = m_context.find_assignment(n); switch (val) { case l_false: { - expr * false_arg = 0; + expr * false_arg = nullptr; unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { expr * arg = n->get_arg(i); @@ -450,7 +450,7 @@ namespace smt { [m_qhead, m_relevant_exprs.size()) in the stack of relevant expressions. */ - virtual void propagate() { + void propagate() override { if (m_propagating) { return; } @@ -487,18 +487,18 @@ namespace smt { } relevancy_ehs * ehs = get_handlers(n); - while (ehs != 0) { + while (ehs != nullptr) { ehs->head()->operator()(*this, n); ehs = ehs->tail(); } } } - virtual bool can_propagate() const { + bool can_propagate() const override { return m_qhead < m_relevant_exprs.size(); } - virtual void assign_eh(expr * n, bool val) { + void assign_eh(expr * n, bool val) override { if (!enabled()) return; ast_manager & m = get_manager(); @@ -510,13 +510,13 @@ namespace smt { propagate_relevant_and(to_app(n)); } relevancy_ehs * ehs = get_watches(n, val); - while (ehs != 0) { + while (ehs != nullptr) { ehs->head()->operator()(*this, n, val); ehs = ehs->tail(); } } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { if (enabled() && !m_relevant_exprs.empty()) { out << "relevant exprs:\n"; for (unsigned i = 0; i < m_relevant_exprs.size(); i++) { @@ -527,7 +527,7 @@ namespace smt { } #ifdef Z3DEBUG - bool check_relevancy_app(app * n) const { + bool check_relevancy_app(app * n) const { SASSERT(is_relevant(n)); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) { @@ -537,7 +537,7 @@ namespace smt { return true; } - virtual bool check_relevancy_or(app * n, bool root) const { + bool check_relevancy_or(app * n, bool root) const override { lbool val = root ? l_true : m_context.find_assignment(n); if (val == l_false) return check_relevancy_app(n); @@ -600,7 +600,7 @@ namespace smt { return true; } - bool check_relevancy(expr_ref_vector const & v) const { + bool check_relevancy(expr_ref_vector const & v) const override { SASSERT(!can_propagate()); ast_manager & m = get_manager(); unsigned sz = v.size(); diff --git a/src/smt/smt_relevancy.h b/src/smt/smt_relevancy.h index 75019b110..edaa237b8 100644 --- a/src/smt/smt_relevancy.h +++ b/src/smt/smt_relevancy.h @@ -50,8 +50,8 @@ namespace smt { expr * m_target; public: simple_relevancy_eh(expr * t):m_target(t) {} - virtual ~simple_relevancy_eh() {} - virtual void operator()(relevancy_propagator & rp); + ~simple_relevancy_eh() override {} + void operator()(relevancy_propagator & rp) override; }; /** @@ -63,8 +63,8 @@ namespace smt { expr * m_target; public: pair_relevancy_eh(expr * s1, expr * s2, expr * t):m_source1(s1), m_source2(s2), m_target(t) {} - virtual ~pair_relevancy_eh() {} - virtual void operator()(relevancy_propagator & rp); + ~pair_relevancy_eh() override {} + void operator()(relevancy_propagator & rp) override; }; /** diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 5245e8020..294aa7b08 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -507,7 +507,7 @@ namespace smt { m_params.m_nnf_cnf = false; if (st.m_max_ite_tree_depth > 50) { m_params.m_arith_eq2ineq = false; - m_params.m_pull_cheap_ite_trees = true; + m_params.m_pull_cheap_ite = true; m_params.m_arith_propagate_eqs = true; m_params.m_relevancy_lvl = 2; m_params.m_relevancy_lemma = false; @@ -574,19 +574,19 @@ namespace smt { m_params.m_bv_cc = false; m_params.m_bb_ext_gates = true; m_params.m_nnf_cnf = false; - m_params.m_propagate_booleans = true; m_context.register_plugin(alloc(smt::theory_bv, m_manager, m_params, m_params)); - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_QF_AX() { + TRACE("setup", tout << "QF_AX\n";); m_params.m_array_mode = AR_SIMPLE; m_params.m_nnf_cnf = false; - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_QF_AX(static_features const & st) { - m_params.m_array_mode = AR_SIMPLE; + m_params.m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; m_params.m_nnf_cnf = false; if (st.m_num_clauses == st.m_num_units) { m_params.m_relevancy_lvl = 0; @@ -595,7 +595,7 @@ namespace smt { else { m_params.m_relevancy_lvl = 2; } - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_QF_AUFLIA() { @@ -607,11 +607,11 @@ namespace smt { m_params.m_restart_factor = 1.5; m_params.m_phase_selection = PS_CACHING_CONSERVATIVE2; setup_i_arith(); - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_QF_AUFLIA(static_features const & st) { - m_params.m_array_mode = AR_SIMPLE; + m_params.m_array_mode = st.m_has_ext_arrays ? AR_FULL : AR_SIMPLE; if (st.m_has_real) throw default_exception("Benchmark has real variables but it is marked as QF_AUFLIA (arrays, uninterpreted functions and linear integer arithmetic)."); m_params.m_nnf_cnf = false; @@ -631,7 +631,7 @@ namespace smt { // m_context.register_plugin(new smt::theory_si_arith(m_manager, m_params)); // else setup_i_arith(); - m_context.register_plugin(alloc(smt::theory_array, m_manager, m_params)); + setup_arrays(); } void setup::setup_AUFLIA(bool simple_array) { @@ -643,7 +643,6 @@ namespace smt { m_params.m_restart_factor = 1.5; m_params.m_eliminate_bounds = true; m_params.m_qi_quick_checker = MC_UNSAT; - m_params.m_propagate_booleans = true; m_params.m_qi_lazy_threshold = 20; // m_params.m_qi_max_eager_multipatterns = 10; /// <<< HACK m_params.m_mbqi = true; // enabling MBQI and MACRO_FINDER by default :-) @@ -671,7 +670,6 @@ namespace smt { m_params.m_phase_selection = PS_ALWAYS_FALSE; m_params.m_eliminate_bounds = true; m_params.m_qi_quick_checker = MC_UNSAT; - m_params.m_propagate_booleans = true; m_params.m_qi_eager_threshold = 5; // Added for MBQI release m_params.m_qi_lazy_threshold = 20; @@ -742,12 +740,11 @@ namespace smt { } void setup::setup_i_arith() { + // m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); } void setup::setup_r_arith() { - // to disable theory lra - // m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params)); } @@ -813,6 +810,9 @@ namespace smt { case AS_OPTINF: m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params)); break; + case AS_LRA: + setup_r_arith(); + break; default: if (m_params.m_arith_int_only && int_only) m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); @@ -1001,17 +1001,17 @@ namespace smt { } if (st.num_theories() == 1 && st.m_has_arrays) { - setup_QF_AX(); + setup_QF_AX(st); return; } - if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && st.m_has_bv) { + if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && !st.m_has_ext_arrays && st.m_has_bv) { setup_QF_AUFBV(); return; } if (st.num_theories() == 2 && st.has_uf() && st.m_has_arrays && st.m_has_int) { - setup_QF_AUFLIA(); + setup_QF_AUFLIA(st); return; } diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index 1f9ad3ef7..48f6053fc 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -30,9 +30,35 @@ Notes: namespace smt { class smt_solver : public solver_na2as { + + struct cuber { + smt_solver& m_solver; + unsigned m_round; + expr_ref m_result; + cuber(smt_solver& s): + m_solver(s), + m_round(0), + m_result(s.get_manager()) {} + expr_ref cube() { + switch (m_round) { + case 0: + m_result = m_solver.m_context.next_decision(); + break; + case 1: + m_result = m_solver.get_manager().mk_not(m_result); + break; + default: + m_result = m_solver.get_manager().mk_false(); + break; + } + ++m_round; + return m_result; + } + }; + smt_params m_smt_params; smt::kernel m_context; - progress_callback * m_callback; + cuber* m_cuber; symbol m_logic; bool m_minimizing_core; bool m_core_extend_patterns; @@ -45,6 +71,7 @@ namespace smt { solver_na2as(m), m_smt_params(p), m_context(m, m_smt_params), + m_cuber(nullptr), m_minimizing_core(false), m_core_extend_patterns(false), m_core_extend_patterns_max_distance(UINT_MAX), @@ -55,68 +82,91 @@ namespace smt { updt_params(p); } - virtual solver * translate(ast_manager & m, params_ref const & p) { + solver * translate(ast_manager & m, params_ref const & p) override { ast_translation translator(get_manager(), m); smt_solver * result = alloc(smt_solver, m, p, m_logic); smt::kernel::copy(m_context, result->m_context); - for (auto & kv : m_name2assertion) - result->m_name2assertion.insert(translator(kv.m_key), - translator(kv.m_value)); + + if (mc0()) + result->set_model_converter(mc0()->translate(translator)); + + for (auto & kv : m_name2assertion) { + expr* val = translator(kv.m_value); + expr* t = translator(kv.m_key); + result->m_name2assertion.insert(t, val); + result->solver_na2as::assert_expr(val, t); + m.inc_ref(val); + } return result; } - virtual ~smt_solver() { + ~smt_solver() override { dec_ref_values(get_manager(), m_name2assertion); + dealloc(m_cuber); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { solver::updt_params(p); - m_smt_params.updt_params(p); - m_context.updt_params(p); - smt_params_helper smth(p); + m_smt_params.updt_params(solver::get_params()); + m_context.updt_params(solver::get_params()); + smt_params_helper smth(solver::get_params()); m_core_extend_patterns = smth.core_extend_patterns(); m_core_extend_patterns_max_distance = smth.core_extend_patterns_max_distance(); m_core_extend_nonlocal_patterns = smth.core_extend_nonlocal_patterns(); } - virtual void collect_param_descrs(param_descrs & r) { + params_ref m_params_save; + smt_params m_smt_params_save; + + void push_params() override { + m_params_save = params_ref(); + m_params_save.copy(solver::get_params()); + m_smt_params_save = m_smt_params; + } + + void pop_params() override { + m_smt_params = m_smt_params_save; + solver::reset_params(m_params_save); + } + + void collect_param_descrs(param_descrs & r) override { m_context.collect_param_descrs(r); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { m_context.collect_statistics(st); } - virtual lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) { + lbool get_consequences_core(expr_ref_vector const& assumptions, expr_ref_vector const& vars, expr_ref_vector& conseq) override { expr_ref_vector unfixed(m_context.m()); return m_context.get_consequences(assumptions, vars, conseq, unfixed); } - virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_context.find_mutexes(vars, mutexes); } - virtual void assert_expr(expr * t) { + void assert_expr_core(expr * t) override { m_context.assert_expr(t); } - virtual void assert_expr(expr * t, expr * a) { + void assert_expr_core2(expr * t, expr * a) override { if (m_name2assertion.contains(a)) { throw default_exception("named assertion defined twice"); } - solver_na2as::assert_expr(t, a); + solver_na2as::assert_expr_core2(t, a); get_manager().inc_ref(t); m_name2assertion.insert(a, t); } - virtual void push_core() { + void push_core() override { m_context.push(); } - virtual void pop_core(unsigned n) { + void pop_core(unsigned n) override { unsigned cur_sz = m_assumptions.size(); if (n > 0 && cur_sz > 0) { unsigned lvl = m_scopes.size(); @@ -135,11 +185,16 @@ namespace smt { m_context.pop(n); } - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { TRACE("solver_na2as", tout << "smt_solver::check_sat_core: " << num_assumptions << "\n";); return m_context.check(num_assumptions, assumptions); } + + lbool check_sat_cc_core(expr_ref_vector const& cube, vector const& clauses) override { + return m_context.check(cube, clauses); + } + struct scoped_minimize_core { smt_solver& s; expr_ref_vector m_assumptions; @@ -154,17 +209,17 @@ namespace smt { } }; - virtual void get_unsat_core(ptr_vector & r) { + void get_unsat_core(expr_ref_vector & r) override { unsigned sz = m_context.get_unsat_core_size(); for (unsigned i = 0; i < sz; i++) { r.push_back(m_context.get_unsat_core_expr(i)); } - if (m_minimizing_core && smt_params_helper(get_params()).core_minimize()) { + if (!m_minimizing_core && smt_params_helper(get_params()).core_minimize()) { scoped_minimize_core scm(*this); mus mus(*this); mus.add_soft(r.size(), r.c_ptr()); - ptr_vector r2; + expr_ref_vector r2(m); if (l_true == mus.get_mus(r2)) { r.reset(); r.append(r2); @@ -177,44 +232,63 @@ namespace smt { add_nonlocal_pattern_literals_to_core(r); } - virtual void get_model(model_ref & m) { + void get_model_core(model_ref & m) override { m_context.get_model(m); } - virtual proof * get_proof() { + proof * get_proof() override { return m_context.get_proof(); } - virtual std::string reason_unknown() const { + std::string reason_unknown() const override { return m_context.last_failure_as_string(); } - virtual void set_reason_unknown(char const* msg) { + void set_reason_unknown(char const* msg) override { m_context.set_reason_unknown(msg); } - virtual void get_labels(svector & r) { + void get_labels(svector & r) override { buffer tmp; - m_context.get_relevant_labels(0, tmp); + m_context.get_relevant_labels(nullptr, tmp); r.append(tmp.size(), tmp.c_ptr()); } - virtual ast_manager & get_manager() const { return m_context.m(); } + ast_manager & get_manager() const override { return m_context.m(); } - virtual void set_progress_callback(progress_callback * callback) { - m_callback = callback; + void set_progress_callback(progress_callback * callback) override { m_context.set_progress_callback(callback); } - virtual unsigned get_num_assertions() const { + unsigned get_num_assertions() const override { return m_context.size(); } - virtual expr * get_assertion(unsigned idx) const { + expr * get_assertion(unsigned idx) const override { SASSERT(idx < get_num_assertions()); return m_context.get_formula(idx); } + expr_ref_vector cube(expr_ref_vector& vars, unsigned cutoff) override { + ast_manager& m = get_manager(); + if (!m_cuber) { + m_cuber = alloc(cuber, *this); + } + expr_ref result = m_cuber->cube(); + expr_ref_vector lits(m); + if (m.is_false(result)) { + dealloc(m_cuber); + m_cuber = nullptr; + } + if (m.is_true(result)) { + dealloc(m_cuber); + m_cuber = nullptr; + return lits; + } + lits.push_back(result); + return lits; + } + struct collect_fds_proc { ast_manager & m; func_decl_set & m_fds; @@ -259,7 +333,7 @@ namespace smt { for_each_expr(p, visited, e); } - void compute_assrtn_fds(ptr_vector & core, vector & assrtn_fds) { + void compute_assrtn_fds(expr_ref_vector & core, vector & assrtn_fds) { assrtn_fds.resize(m_name2assertion.size()); unsigned i = 0; for (auto & kv : m_name2assertion) { @@ -280,7 +354,7 @@ namespace smt { return false; } - void add_pattern_literals_to_core(ptr_vector & core) { + void add_pattern_literals_to_core(expr_ref_vector & core) { ast_manager & m = get_manager(); expr_ref_vector new_core_literals(m); @@ -339,7 +413,7 @@ namespace smt { for_each_expr(p, visited, e); } - void add_nonlocal_pattern_literals_to_core(ptr_vector & core) { + void add_nonlocal_pattern_literals_to_core(expr_ref_vector & core) { ast_manager & m = get_manager(); for (auto const& kv : m_name2assertion) { expr_ref name(kv.m_key, m); @@ -351,8 +425,8 @@ namespace smt { collect_body_func_decls(assrtn, body_fds); for (func_decl *fd : pattern_fds) { - if (!body_fds.contains(fd)) { - core.insert(name); + if (!body_fds.contains(fd) && !core.contains(name)) { + core.push_back(name); break; } } @@ -368,7 +442,7 @@ solver * mk_smt_solver(ast_manager & m, params_ref const & p, symbol const & log class smt_solver_factory : public solver_factory { public: - virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { return mk_smt_solver(m, p, logic); } }; diff --git a/src/smt/smt_theory.cpp b/src/smt/smt_theory.cpp index 515503d41..b5ad68adc 100644 --- a/src/smt/smt_theory.cpp +++ b/src/smt/smt_theory.cpp @@ -130,8 +130,8 @@ namespace smt { theory::theory(family_id fid): m_id(fid), - m_context(0), - m_manager(0) { + m_context(nullptr), + m_manager(nullptr) { } theory::~theory() { diff --git a/src/smt/smt_theory.h b/src/smt/smt_theory.h index 2ee0db322..9d5a4f49f 100644 --- a/src/smt/smt_theory.h +++ b/src/smt/smt_theory.h @@ -235,7 +235,7 @@ namespace smt { disequality propagation. */ virtual justification * why_is_diseq(theory_var v1, theory_var v2) { - return 0; + return nullptr; } /** @@ -369,7 +369,7 @@ namespace smt { theory_var other = null_theory_var; TRACE("assume_eqs", tout << "#" << n->get_owner_id() << " is_relevant_and_shared: " << is_relevant_and_shared(n) << "\n";); - if (n != 0 && is_relevant_and_shared(n)) { + if (n != nullptr && is_relevant_and_shared(n)) { other = table.insert_if_not_there(v); if (other != v) { enode * n2 = get_enode(other); @@ -423,7 +423,7 @@ namespace smt { \brief Return a functor that can build the value (interpretation) for n. */ virtual model_value_proc * mk_value(enode * n, model_generator & mg) { - return 0; + return nullptr; } virtual bool include_func_interp(func_decl* f) { diff --git a/src/smt/smt_theory_var_list.h b/src/smt/smt_theory_var_list.h index aa2816786..69180ad66 100644 --- a/src/smt/smt_theory_var_list.h +++ b/src/smt/smt_theory_var_list.h @@ -32,10 +32,10 @@ namespace smt { theory_var_list(): m_th_id(null_theory_id), m_th_var(null_theory_var), - m_next(0) { + m_next(nullptr) { } - theory_var_list(theory_id t, theory_var v, theory_var_list * n = 0): + theory_var_list(theory_id t, theory_var v, theory_var_list * n = nullptr): m_th_id(t), m_th_var(v), m_next(n) { diff --git a/src/smt/smt_types.h b/src/smt/smt_types.h index 6300bd43c..e062a4ad6 100644 --- a/src/smt/smt_types.h +++ b/src/smt/smt_types.h @@ -21,6 +21,7 @@ Revision History: #include "util/list.h" #include "util/vector.h" +#include "util/hashtable.h" #include "util/lbool.h" class model; diff --git a/src/smt/tactic/ctx_solver_simplify_tactic.cpp b/src/smt/tactic/ctx_solver_simplify_tactic.cpp index edc4b4ff5..4230b1317 100644 --- a/src/smt/tactic/ctx_solver_simplify_tactic.cpp +++ b/src/smt/tactic/ctx_solver_simplify_tactic.cpp @@ -43,11 +43,11 @@ public: m_fn = m.mk_func_decl(symbol(0xbeef101), i_sort, m.mk_bool_sort()); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(ctx_solver_simplify_tactic, m, m_params); } - virtual ~ctx_solver_simplify_tactic() { + ~ctx_solver_simplify_tactic() override { obj_map::iterator it = m_fns.begin(), end = m_fns.end(); for (; it != end; ++it) { m.dec_ref(it->m_value); @@ -55,33 +55,28 @@ public: m_fns.reset(); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_solver.updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { m_solver.collect_param_descrs(r); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.update("solver-simplify-steps", m_num_steps); } - virtual void reset_statistics() { m_num_steps = 0; } + void reset_statistics() override { m_num_steps = 0; } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - - mc = 0; pc = 0; core = 0; + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { reduce(*(in.get())); in->inc_depth(); result.push_back(in.get()); } - virtual void cleanup() { + void cleanup() override { reset_statistics(); m_solver.reset(); } @@ -127,7 +122,7 @@ protected: m_solver.pop(1); }); g.reset(); - g.assert_expr(fml, 0, 0); + g.assert_expr(fml, nullptr, nullptr); IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-solver-simplify :num-steps " << m_num_steps << ")\n";); SASSERT(g.is_well_sorted()); } @@ -141,7 +136,7 @@ protected: m_parent(p), m_self(s), m_idx(i), m_expr(e) {} expr_pos(): - m_parent(0), m_self(0), m_idx(0), m_expr(0) + m_parent(0), m_self(0), m_idx(0), m_expr(nullptr) {} }; @@ -194,7 +189,7 @@ protected: a = to_app(e); sz = a->get_num_args(); - n2 = 0; + n2 = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* arg = a->get_arg(i); diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index 57ac7b34b..cc0f0f207 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -16,19 +16,21 @@ Author: Notes: --*/ -#include "tactic/tactic.h" -#include "tactic/tactical.h" +#include "util/lp/lp_params.hpp" +#include "ast/rewriter/rewriter_types.h" +#include "ast/ast_util.h" #include "smt/smt_kernel.h" #include "smt/params/smt_params.h" #include "smt/params/smt_params_helper.hpp" -#include "util/lp/lp_params.hpp" -#include "ast/rewriter/rewriter_types.h" -#include "tactic/filter_model_converter.h" -#include "ast/ast_util.h" -#include "solver/solver2tactic.h" #include "smt/smt_solver.h" +#include "tactic/tactic.h" +#include "tactic/tactical.h" +#include "tactic/generic_model_converter.h" +#include "solver/solver2tactic.h" #include "solver/solver.h" #include "solver/mus.h" +#include "solver/parallel_tactic.h" +#include "solver/parallel_params.hpp" typedef obj_map expr2expr_map; @@ -47,17 +49,17 @@ class smt_tactic : public tactic { public: smt_tactic(params_ref const & p): m_params_ref(p), - m_ctx(0), - m_callback(0) { + m_ctx(nullptr), + m_callback(nullptr) { updt_params_core(p); TRACE("smt_tactic", tout << "p: " << p << "\n";); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(smt_tactic, m_params_ref); } - virtual ~smt_tactic() { + ~smt_tactic() override { SASSERT(m_ctx == 0); } @@ -74,7 +76,7 @@ public: m_fail_if_inconclusive = p.get_bool("fail_if_inconclusive", true); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { TRACE("smt_tactic", tout << "updt_params: " << p << "\n";); updt_params_core(p); fparams().updt_params(p); @@ -86,7 +88,7 @@ public: SASSERT(p.get_bool("auto_config", fparams().m_auto_config) == fparams().m_auto_config); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("candidate_models", CPK_BOOL, "(default: false) create candidate models even when quantifier or theory reasoning is incomplete."); r.insert("fail_if_inconclusive", CPK_BOOL, "(default: true) fail if found unsat (sat) for under (over) approximated goal."); smt_params_helper::collect_param_descrs(r); @@ -94,25 +96,25 @@ public: } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { if (m_ctx) m_ctx->collect_statistics(st); // ctx is still running... else st.copy(m_stats); } - virtual void cleanup() { + void cleanup() override { } - virtual void reset_statistics() { + void reset_statistics() override { m_stats.reset(); } - virtual void set_logic(symbol const & l) { + void set_logic(symbol const & l) override { m_logic = l; } - virtual void set_progress_callback(progress_callback * callback) { + void set_progress_callback(progress_callback * callback) override { m_callback = callback; } @@ -136,7 +138,7 @@ public: ~scoped_init_ctx() { smt::kernel * d = m_owner.m_ctx; - m_owner.m_ctx = 0; + m_owner.m_ctx = nullptr; if (d) dealloc(d); @@ -144,14 +146,10 @@ public: }; - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { IF_VERBOSE(10, verbose_stream() << "(smt.tactic start)\n";); - mc = 0; pc = 0; core = 0; SASSERT(in->is_well_sorted()); ast_manager & m = in->m(); TRACE("smt_tactic", tout << this << "\nAUTO_CONFIG: " << fparams().m_auto_config << " HIDIV0: " << fparams().m_hi_div0 << " " @@ -169,7 +167,7 @@ public: expr_ref_vector clauses(m); expr2expr_map bool2dep; ptr_vector assumptions; - ref fmc; + ref fmc; if (in->unsat_core_enabled()) { extract_clauses_and_dependencies(in, clauses, assumptions, bool2dep, fmc); TRACE("mus", in->display_with_dependencies(tout); @@ -221,8 +219,12 @@ public: m_ctx->get_model(md); buffer r; m_ctx->get_relevant_labels(0, r); - mc = model_and_labels2model_converter(md.get(), r); + labels_vec rv; + rv.append(r.size(), r.c_ptr()); + model_converter_ref mc; + mc = model_and_labels2model_converter(md.get(), rv); mc = concat(fmc.get(), mc.get()); + in->add(mc.get()); } return; } @@ -233,8 +235,8 @@ public: } // formula is unsat, reset the goal, and store false there. in->reset(); - proof * pr = 0; - expr_dependency * lcore = 0; + proof * pr = nullptr; + expr_dependency * lcore = nullptr; if (in->proofs_enabled()) pr = m_ctx->get_proof(); if (in->unsat_core_enabled()) { @@ -270,7 +272,9 @@ public: m_ctx->get_model(md); buffer r; m_ctx->get_relevant_labels(0, r); - mc = model_and_labels2model_converter(md.get(), r); + labels_vec rv; + rv.append(r.size(), r.c_ptr()); + in->add(model_and_labels2model_converter(md.get(), rv)); } return; default: @@ -299,3 +303,18 @@ tactic * mk_smt_tactic_using(bool auto_config, params_ref const & _p) { return using_params(r, p); } +tactic * mk_psmt_tactic(ast_manager& m, params_ref const& p, symbol const& logic) { + parallel_params pp(p); + return pp.enable() ? mk_parallel_tactic(mk_smt_solver(m, p, logic), p) : mk_smt_tactic(p); +} + +tactic * mk_psmt_tactic_using(ast_manager& m, bool auto_config, params_ref const& _p, symbol const& logic) { + parallel_params pp(_p); + params_ref p = _p; + p.set_bool("auto_config", auto_config); + return using_params(pp.enable() ? mk_parallel_tactic(mk_smt_solver(m, p, logic), p) : mk_smt_tactic(p), p); +} + +tactic * mk_parallel_smt_tactic(ast_manager& m, params_ref const& p) { + return mk_parallel_tactic(mk_smt_solver(m, p, symbol::null), p); +} diff --git a/src/smt/tactic/smt_tactic.h b/src/smt/tactic/smt_tactic.h index c7b91d032..fbee950c2 100644 --- a/src/smt/tactic/smt_tactic.h +++ b/src/smt/tactic/smt_tactic.h @@ -31,8 +31,13 @@ tactic * mk_smt_tactic(params_ref const & p = params_ref()); // syntax sugar for using_params(mk_smt_tactic(), p) where p = (:auto_config, auto_config) tactic * mk_smt_tactic_using(bool auto_config = true, params_ref const & p = params_ref()); +tactic * mk_psmt_tactic(ast_manager& m, params_ref const& p, symbol const& logic = symbol::null); +tactic * mk_psmt_tactic_using(ast_manager& m, bool auto_config, params_ref const& p, symbol const& logic = symbol::null); +tactic * mk_parallel_smt_tactic(ast_manager& m, params_ref const& p); + /* ADD_TACTIC("smt", "apply a SAT based SMT solver.", "mk_smt_tactic(p)") + ADD_TACTIC("psmt", "builtin strategy for SMT tactic in parallel.", "mk_parallel_smt_tactic(m, p)") */ #endif diff --git a/src/smt/tactic/unit_subsumption_tactic.cpp b/src/smt/tactic/unit_subsumption_tactic.cpp index 68a34cea8..7e960196d 100644 --- a/src/smt/tactic/unit_subsumption_tactic.cpp +++ b/src/smt/tactic/unit_subsumption_tactic.cpp @@ -37,22 +37,19 @@ struct unit_subsumption_tactic : public tactic { m_clauses(m) { } - void cleanup() {} + void cleanup() override {} - virtual void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) { + void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result) override { reduce_core(in, result); } - virtual void updt_params(params_ref const& p) { + void updt_params(params_ref const& p) override { m_params = p; // m_context.updt_params(p); does not exist. } - virtual tactic* translate(ast_manager& m) { + tactic* translate(ast_manager& m) override { return alloc(unit_subsumption_tactic, m, m_params); } @@ -109,9 +106,7 @@ struct unit_subsumption_tactic : public tactic { } void insert_result(goal_ref& result) { - for (unsigned i = 0; i < m_deleted.size(); ++i) { - result->update(m_deleted[i], m.mk_true()); // TBD proof? - } + for (auto d : m_deleted) result->update(d, m.mk_true()); // TBD proof? } void init(goal_ref const& g) { diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 806b2a05b..992a87dab 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -31,6 +31,7 @@ Revision History: #include "smt/params/theory_arith_params.h" #include "smt/arith_eq_adapter.h" #include "smt/proto_model/numeral_factory.h" +#include "smt/smt_context.h" #include "util/obj_pair_hashtable.h" #include "smt/old_interval.h" #include "math/grobner/grobner.h" @@ -306,16 +307,16 @@ namespace smt { public: atom(bool_var bv, theory_var v, inf_numeral const & k, atom_kind kind); atom_kind get_atom_kind() const { return static_cast(m_atom_kind); } - virtual ~atom() {} + ~atom() override {} inline inf_numeral const & get_k() const { return m_k; } bool_var get_bool_var() const { return m_bvar; } bool is_true() const { return m_is_true; } void assign_eh(bool is_true, inf_numeral const & epsilon); - virtual bool has_justification() const { return true; } - virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { + bool has_justification() const override { return true; } + void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override { a.push_lit(literal(get_bool_var(), !m_is_true), coeff, proofs_enabled); } - virtual void display(theory_arith const& th, std::ostream& out) const; + void display(theory_arith const& th, std::ostream& out) const override; }; class eq_bound : public bound { @@ -328,13 +329,13 @@ namespace smt { m_rhs(rhs) { SASSERT(m_lhs->get_root() == m_rhs->get_root()); } - virtual ~eq_bound() {} - virtual bool has_justification() const { return true; } - virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) { + ~eq_bound() override {} + bool has_justification() const override { return true; } + void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override { SASSERT(m_lhs->get_root() == m_rhs->get_root()); a.push_eq(enode_pair(m_lhs, m_rhs), coeff, proofs_enabled); } - virtual void display(theory_arith const& th, std::ostream& out) const; + void display(theory_arith const& th, std::ostream& out) const override; }; class derived_bound : public bound { @@ -344,14 +345,14 @@ namespace smt { friend class theory_arith; public: derived_bound(theory_var v, inf_numeral const & val, bound_kind k):bound(v, val, k, false) {} - virtual ~derived_bound() {} + ~derived_bound() override {} literal_vector const& lits() const { return m_lits; } eq_vector const& eqs() const { return m_eqs; } - virtual bool has_justification() const { return true; } - virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled); + bool has_justification() const override { return true; } + void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override; virtual void push_lit(literal l, numeral const&) { m_lits.push_back(l); } virtual void push_eq(enode_pair const& p, numeral const&) { m_eqs.push_back(p); } - virtual void display(theory_arith const& th, std::ostream& out) const; + void display(theory_arith const& th, std::ostream& out) const override; }; @@ -361,12 +362,12 @@ namespace smt { friend class theory_arith; public: justified_derived_bound(theory_var v, inf_numeral const & val, bound_kind k):derived_bound(v, val, k) {} - virtual ~justified_derived_bound() {} - virtual bool has_justification() const { return true; } - virtual void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled); - virtual void push_lit(literal l, numeral const& coeff); + ~justified_derived_bound() override {} + bool has_justification() const override { return true; } + void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override; + void push_lit(literal l, numeral const& coeff) override; - virtual void push_eq(enode_pair const& p, numeral const& coeff); + void push_eq(enode_pair const& p, numeral const& coeff) override; }; typedef int_hashtable > literal_idx_set; @@ -510,7 +511,7 @@ namespace smt { typedef int_hashtable var_value_table; var_value_table m_var_value_table; - virtual theory_var mk_var(enode * n); + theory_var mk_var(enode * n) override; void found_unsupported_op(app * n); void found_underspecified_op(app * n); @@ -561,16 +562,16 @@ namespace smt { bound * upper(theory_var v) const { return m_bounds[1][v]; } inf_numeral const & lower_bound(theory_var v) const { SASSERT(lower(v) != 0); return lower(v)->get_value(); } inf_numeral const & upper_bound(theory_var v) const { SASSERT(upper(v) != 0); return upper(v)->get_value(); } - bool below_lower(theory_var v) const { bound * l = lower(v); return l != 0 && get_value(v) < l->get_value(); } - bool above_upper(theory_var v) const { bound * u = upper(v); return u != 0 && get_value(v) > u->get_value(); } - bool below_upper(theory_var v) const { bound * u = upper(v); return u == 0 || get_value(v) < u->get_value(); } - bool above_lower(theory_var v) const { bound * l = lower(v); return l == 0 || get_value(v) > l->get_value(); } + bool below_lower(theory_var v) const { bound * l = lower(v); return l != nullptr && get_value(v) < l->get_value(); } + bool above_upper(theory_var v) const { bound * u = upper(v); return u != nullptr && get_value(v) > u->get_value(); } + bool below_upper(theory_var v) const { bound * u = upper(v); return u == nullptr || get_value(v) < u->get_value(); } + bool above_lower(theory_var v) const { bound * l = lower(v); return l == nullptr || get_value(v) > l->get_value(); } bool at_bound(theory_var v) const; - bool at_lower(theory_var v) const { bound * l = lower(v); return l != 0 && get_value(v) == l->get_value(); } - bool at_upper(theory_var v) const { bound * u = upper(v); return u != 0 && get_value(v) == u->get_value(); } - bool is_free(theory_var v) const { return lower(v) == 0 && upper(v) == 0; } - bool is_non_free(theory_var v) const { return lower(v) != 0 || upper(v) != 0; } - bool is_bounded(theory_var v) const { return lower(v) != 0 && upper(v) != 0; } + bool at_lower(theory_var v) const { bound * l = lower(v); return l != nullptr && get_value(v) == l->get_value(); } + bool at_upper(theory_var v) const { bound * u = upper(v); return u != nullptr && get_value(v) == u->get_value(); } + bool is_free(theory_var v) const { return lower(v) == nullptr && upper(v) == nullptr; } + bool is_non_free(theory_var v) const { return lower(v) != nullptr || upper(v) != nullptr; } + bool is_bounded(theory_var v) const { return lower(v) != nullptr && upper(v) != nullptr; } bool is_free(expr * n) const { SASSERT(get_context().e_internalized(n) && get_context().get_enode(n)->get_th_var(get_id()) != null_theory_var); return is_free(get_context().get_enode(n)->get_th_var(get_id())); @@ -635,24 +636,24 @@ namespace smt { struct compare_atoms { bool operator()(atom* a1, atom* a2) const { return a1->get_k() < a2->get_k(); } }; - virtual bool default_internalizer() const { return false; } - virtual bool internalize_atom(app * n, bool gate_ctx); - virtual bool internalize_term(app * term); - virtual void internalize_eq_eh(app * atom, bool_var v); - virtual void apply_sort_cnstr(enode * n, sort * s); + bool default_internalizer() const override { return false; } + bool internalize_atom(app * n, bool gate_ctx) override; + bool internalize_term(app * term) override; + void internalize_eq_eh(app * atom, bool_var v) override; + void apply_sort_cnstr(enode * n, sort * s) override; - virtual void assign_eh(bool_var v, bool is_true); - virtual void new_eq_eh(theory_var v1, theory_var v2); - virtual bool use_diseqs() const; - virtual void new_diseq_eh(theory_var v1, theory_var v2); + void assign_eh(bool_var v, bool is_true) override; + void new_eq_eh(theory_var v1, theory_var v2) override; + bool use_diseqs() const override; + void new_diseq_eh(theory_var v1, theory_var v2) override; - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; - virtual void relevant_eh(app * n); + void relevant_eh(app * n) override; - virtual void restart_eh(); - virtual void init_search_eh(); + void restart_eh() override; + void init_search_eh() override; /** \brief True if the assignment may be changed during final check. assume_eqs, check_int_feasibility, @@ -660,7 +661,7 @@ namespace smt { satisfy their respective constraints. However, when they do that the may create inconsistencies in the other modules. I use m_liberal_final_check to avoid infinite - loops where the modules keep changing the assigment and no + loops where the modules keep changing the assignment and no progress is made. If m_liberal_final_check is set to false, these modules will avoid mutating the assignment to satisfy constraints. @@ -669,18 +670,18 @@ namespace smt { */ bool m_liberal_final_check; final_check_status final_check_core(); - virtual final_check_status final_check_eh(); + final_check_status final_check_eh() override; - virtual bool can_propagate(); - virtual void propagate(); + bool can_propagate() override; + void propagate() override; bool propagate_core(); void failed(); - virtual void flush_eh(); - virtual void reset_eh(); + void flush_eh() override; + void reset_eh() override; - virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const; + bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override; // ----------------------------------- // @@ -867,7 +868,7 @@ namespace smt { void propagate_cheap_eq(unsigned rid); void propagate_eq_to_core(theory_var x, theory_var y, antecedents& antecedents); - virtual bool is_shared(theory_var v) const; + bool is_shared(theory_var v) const override; // ----------------------------------- // @@ -909,7 +910,7 @@ namespace smt { /** \brief See comment in theory::mk_eq_atom */ - virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return m_util.mk_eq(lhs, rhs); } + app * mk_eq_atom(expr * lhs, expr * rhs) override { return m_util.mk_eq(lhs, rhs); } // ----------------------------------- // @@ -1039,13 +1040,13 @@ namespace smt { // ----------------------------------- public: theory_arith(ast_manager & m, theory_arith_params & params); - virtual ~theory_arith(); + ~theory_arith() override; - virtual theory * mk_fresh(context * new_ctx); + theory * mk_fresh(context * new_ctx) override; - virtual void setup(); + void setup() override; - virtual char const * get_name() const { return "arithmetic"; } + char const * get_name() const override { return "arithmetic"; } // ----------------------------------- // @@ -1058,15 +1059,15 @@ namespace smt { void compute_epsilon(); void refine_epsilon(); - virtual void init_model(model_generator & m); - virtual model_value_proc * mk_value(enode * n, model_generator & mg); + void init_model(model_generator & m) override; + model_value_proc * mk_value(enode * n, model_generator & mg) override; // ----------------------------------- // // Model checker // // ----------------------------------- - virtual bool get_value(enode * n, expr_ref & r); + bool get_value(enode * n, expr_ref & r) override; bool get_lower(enode* n, expr_ref& r); bool get_upper(enode* n, expr_ref& r); @@ -1078,10 +1079,10 @@ namespace smt { // Optimization // // ----------------------------------- - virtual inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared); - virtual inf_eps_rational value(theory_var v); - virtual theory_var add_objective(app* term); - expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val); + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_numeral const& val); + inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; + inf_eps_rational value(theory_var v) override; + theory_var add_objective(app* term) override; void enable_record_conflict(expr* bound); void record_conflict(unsigned num_lits, literal const * lits, unsigned num_eqs, enode_pair const * eqs, @@ -1102,8 +1103,8 @@ namespace smt { // // ----------------------------------- public: - virtual void collect_statistics(::statistics & st) const; - virtual void display(std::ostream & out) const; + void collect_statistics(::statistics & st) const override; + void display(std::ostream & out) const override; protected: void display_row(std::ostream & out, unsigned r_id, bool compact = true) const; void display_row(std::ostream & out, row const & r, bool compact = true) const; diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index a1558e5e5..daf285a54 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -23,7 +23,7 @@ Revision History: #include "smt/theory_arith.h" #include "smt/smt_farkas_util.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace smt { @@ -279,7 +279,7 @@ namespace smt { return it; } } - return 0; + return nullptr; } template @@ -357,7 +357,7 @@ namespace smt { template parameter * theory_arith::antecedents_t::params(char const* name) { - if (empty()) return 0; + if (empty()) return nullptr; init(); m_params[0] = parameter(symbol(name)); return m_params.c_ptr(); @@ -444,19 +444,19 @@ namespace smt { template bool theory_arith::at_bound(theory_var v) const { bound * l = lower(v); - if (l != 0 && get_value(v) == l->get_value()) + if (l != nullptr && get_value(v) == l->get_value()) return true; bound * u = upper(v); - return u != 0 && get_value(v) == u->get_value(); + return u != nullptr && get_value(v) == u->get_value(); } template bool theory_arith::is_fixed(theory_var v) const { bound * l = lower(v); - if (l == 0) + if (l == nullptr) return false; bound * u = upper(v); - if (u == 0) + if (u == nullptr) return false; return l->get_value() == u->get_value(); } @@ -483,7 +483,7 @@ namespace smt { while (true) { column const & c = m_columns[v]; if (c.size() == 0) - return 0; + return nullptr; int quasi_base_rid = -1; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); @@ -533,7 +533,7 @@ namespace smt { typename theory_arith::col_entry const * theory_arith::get_row_for_eliminating(theory_var v) const { column const & c = m_columns[v]; if (c.size() == 0) - return 0; + return nullptr; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); for (; it != end; ++it) { @@ -556,7 +556,7 @@ namespace smt { return it; } } - return 0; + return nullptr; } template @@ -1117,14 +1117,14 @@ namespace smt { This allows to handle inequalities with non-standard numbers. */ template - expr_ref theory_arith::mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val) { + expr_ref theory_arith::mk_ge(generic_model_converter& fm, theory_var v, inf_numeral const& val) { ast_manager& m = get_manager(); context& ctx = get_context(); 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()); if (!ctx.b_internalized(b)) { - fm.insert(b->get_decl()); + fm.hide(b->get_decl()); bool_var bv = ctx.mk_bool_var(b); ctx.set_var_theory(bv, get_id()); // ctx.set_enode_flag(bv, true); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index b00648c36..cbd41f08c 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -27,7 +27,7 @@ Revision History: #include "ast/ast_smt2_pp.h" namespace smt { - + template void theory_arith::found_unsupported_op(app * n) { if (!m_found_unsupported_op) { @@ -40,7 +40,7 @@ namespace smt { template void theory_arith::found_underspecified_op(app * n) { if (!m_found_underspecified_op) { - TRACE("arith", tout << "found underspecificed expression:\n" << mk_pp(n, get_manager()) << "\n";); + TRACE("arith", tout << "found underspecified expression:\n" << mk_pp(n, get_manager()) << "\n";); get_context().push_trail(value_trail(m_found_underspecified_op)); m_found_underspecified_op = true; } @@ -69,33 +69,7 @@ namespace smt { #if 0 return m_util.is_int(e); #else - if (m_util.is_int(e)) return true; - if (is_uninterp(e)) return false; - m_todo.reset(); - m_todo.push_back(e); - rational r; - unsigned i = 0; - while (!m_todo.empty()) { - ++i; - if (i > 100) { - return false; - } - e = m_todo.back(); - m_todo.pop_back(); - if (m_util.is_to_real(e)) { - // pass - } - else if (m_util.is_numeral(e, r) && r.is_int()) { - // pass - } - else if (m_util.is_add(e) || m_util.is_mul(e)) { - m_todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); - } - else { - return false; - } - } - return true; + return m_util.is_int_expr(e); #endif } @@ -133,7 +107,7 @@ namespace smt { m_nl_monomials.push_back(r); SASSERT(check_vector_sizes()); SASSERT(m_var_occs[r].empty()); - TRACE("mk_arith_var", + TRACE("mk_arith_var", tout << "#" << n->get_owner_id() << " :=\n" << mk_ll_pp(n->get_owner(), get_manager()) << "\n"; tout << "is_attached_to_var: " << is_attached_to_var(n) << ", var: " << n->get_th_var(get_id()) << "\n";); get_context().attach_th_var(n, this, r); @@ -175,15 +149,15 @@ namespace smt { } /** - \brief Create an enode for n. + \brief Create an enode for n. */ template enode * theory_arith::mk_enode(app * n) { context & ctx = get_context(); - if (ctx.e_internalized(n)) + if (ctx.e_internalized(n)) return ctx.get_enode(n); else - return ctx.mk_enode(n, !reflect(n), false, enable_cgc_for(n)); + return ctx.mk_enode(n, !reflect(n), false, enable_cgc_for(n)); } /** @@ -239,13 +213,13 @@ namespace smt { row_entry & r_entry = r.add_row_entry(r_idx); int c_idx; col_entry & c_entry = c.add_col_entry(c_idx); - + r_entry.m_var = v; r_entry.m_coeff = coeff; if (invert) r_entry.m_coeff .neg(); r_entry.m_col_idx = c_idx; - + c_entry.m_row_id = r_id; c_entry.m_row_idx = r_idx; } @@ -266,7 +240,7 @@ namespace smt { return; } } - rational _val; + rational _val; expr* arg1, *arg2; if (m_util.is_mul(m, arg1, arg2) && m_util.is_numeral(arg1, _val) && is_app(arg1) && is_app(arg2)) { SASSERT(m->get_num_args() == 2); @@ -315,7 +289,7 @@ namespace smt { // HACK: n was already internalized by the internalize_internal_monomial or internalize_internal_add call above. // This can happen when one of calls invoke (indirectly) mk_axiom. // For example, they contain a nested to_int(t) term. - // TODO: reimplement mk_axiom. The current implementation is flaky. + // TODO: reimplement mk_axiom. The current implementation is flaky. // I should cache the axioms that need to be created. They should only be internalized after we finished internalizing the // current atom. Several other theories have similar problems. del_row(r_id); @@ -348,7 +322,7 @@ namespace smt { } /** - \brief Internalize the terms of the form (* c (* t1 ... tn)) and (* t1 ... tn). + \brief Internalize the terms of the form (* c (* t1 ... tn)) and (* t1 ... tn). Return an alias for the term. */ template @@ -389,7 +363,7 @@ namespace smt { return expr2var(n); ctx.internalize(n->get_arg(0), false); ctx.internalize(n->get_arg(1), false); - enode * e = mk_enode(n); + enode * e = mk_enode(n); return mk_var(e); } @@ -469,16 +443,17 @@ namespace smt { if (negated) l_conseq.neg(); TRACE("arith_axiom", tout << mk_pp(ante, m) << "\n" << mk_pp(conseq, m) << "\n"; - tout << s_ante << "\n" << s_conseq << "\n";); + tout << s_ante << "\n" << s_conseq << "\n"; + tout << l_ante << "\n" << l_conseq << "\n";); // literal lits[2] = {l_ante, l_conseq}; - mk_clause(l_ante, l_conseq, 0, 0); + mk_clause(l_ante, l_conseq, 0, nullptr); if (ctx.relevancy()) { if (l_ante == false_literal) { ctx.mark_as_relevant(l_conseq); } else { - // We must mark the antecedent as relevant, otherwise the + // We must mark the antecedent as relevant, otherwise the // core will not propagate it to the theory of arithmetic. // In a previous version, we were not doing that. // The core was assigning it to true, this assignment was inconsistent with @@ -495,7 +470,7 @@ namespace smt { if (!m_util.is_zero(q)) { ast_manager & m = get_manager(); expr_ref div(m), zero(m), eqz(m), eq(m); - TRACE("div_axiom_bug", tout << "expanding div_axiom for: " << mk_pp(p, m) << " / " << mk_pp(q, m) << "\n";); + TRACE("div_axiom_bug", tout << "expanding div_axiom for: " << mk_pp(p, m) << " / " << mk_pp(q, m) << "\n";); div = m_util.mk_div(p, q); zero = m_util.mk_numeral(rational(0), false); eqz = m.mk_eq(q, zero); @@ -524,7 +499,7 @@ namespace smt { eq = m.mk_eq(m_util.mk_add(m_util.mk_mul(divisor, div), mod), dividend); lower = m_util.mk_ge(mod, zero); upper = m_util.mk_le(mod, abs_divisor); - TRACE("div_axiom_bug", + TRACE("div_axiom_bug", tout << "eqz: " << eqz << " neq: " << eq << "\n"; tout << "lower: " << lower << "\n"; tout << "upper: " << upper << "\n";); @@ -533,13 +508,14 @@ namespace smt { mk_axiom(eqz, lower, !is_numeral); mk_axiom(eqz, upper, !is_numeral); rational k; - if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && + context& ctx = get_context(); + (void)ctx; + if (m_params.m_arith_enum_const_mod && m_util.is_numeral(divisor, k) && k.is_pos() && k < rational(8)) { rational j(0); #if 1 literal_buffer lits; expr_ref mod_j(m); - context& ctx = get_context(); while(j < k) { mod_j = m.mk_eq(mod, m_util.mk_numeral(j, true)); ctx.internalize(mod_j, false); @@ -549,7 +525,7 @@ namespace smt { j += rational(1); } ctx.mk_th_axiom(get_id(), lits.size(), lits.begin()); - + #else // performs slightly worse. literal_buffer lits; @@ -566,7 +542,37 @@ namespace smt { j += rational(1); } #endif - } + } +#if 0 + // e-matching is too restrictive for multiplication. + // also suffers from use-after free so formulas have to be pinned in solver. + // + if (!m_util.is_numeral(divisor)) { + // + // forall x . (or (= y 0) (= (div (* x y) y) x)) + // forall x . (=> (= y 0) (= (div (* x y) y) (div 0 0))) + // + sort* intS = m_util.mk_int(); + var_ref v(m.mk_var(0, intS), m); + app_ref mul(m_util.mk_mul(divisor, v), m); + app_ref div(m_util.mk_idiv(mul, divisor), m); + expr_ref divp1(m.mk_pattern(div), m); + app_ref mul2(m_util.mk_mul(v, divisor), m); + app_ref div2(m_util.mk_idiv(mul2, divisor), m); + expr_ref divp2(m.mk_pattern(div2), m); + expr_ref fml1(m.mk_or(m.mk_not(eqz), m.mk_eq(div, m_util.mk_idiv(zero, zero))), m); + expr_ref fml2(m.mk_or(eqz, m.mk_eq(div, v)), m); + symbol name("?x"); + expr* pats[2] = { divp1, divp2 }; + expr_ref fml(m); + fml = m.mk_forall(1, &intS, &name, fml1, 0, symbol::null, symbol::null, 2, pats, 0, nullptr); + proof_ref pr(m.mk_asserted(fml), m); + ctx.internalize_assertion(fml, pr, 0); + fml = m.mk_forall(1, &intS, &name, fml2, 0, symbol::null, symbol::null, 2, pats, 0, nullptr); + pr = m.mk_asserted(fml); + ctx.internalize_assertion(fml, pr, 0); + } +#endif } } @@ -585,17 +591,17 @@ namespace smt { mk_axiom(dltz, eq1); dltz = m.mk_not(dltz); // !n < 0 || rem(a,n) = -mod(a, n) - mk_axiom(dltz, eq2); + mk_axiom(dltz, eq2); } // - // create the term: s := to_real(to_int(x)) - x - // add the bounds 0 <= s < 1 + // create the term: s := x - to_real(to_int(x)) + // add the bounds 0 <= s < 1 // template void theory_arith::mk_to_int_axiom(app * n) { SASSERT(m_util.is_to_int(n)); - ast_manager & m = get_manager(); + ast_manager & m = get_manager(); expr* x = n->get_arg(0); // to_int (to_real x) = x @@ -603,11 +609,15 @@ namespace smt { mk_axiom(m.mk_false(), m.mk_eq(to_app(x)->get_arg(0), n)); return; } - expr* to_r = m_util.mk_to_real(n); - expr_ref lo(m_util.mk_le(to_r, x), m); - expr_ref hi(m_util.mk_lt(x, m_util.mk_add(to_r, m_util.mk_numeral(rational(1), false))), m); - mk_axiom(m.mk_false(), lo); - mk_axiom(m.mk_false(), hi); + expr_ref to_r(m_util.mk_to_real(n), m); + expr_ref diff(m_util.mk_add(x, m_util.mk_mul(m_util.mk_real(-1), to_r)), m); + + expr_ref lo(m_util.mk_ge(diff, m_util.mk_real(0)), m); + expr_ref hi(m_util.mk_ge(diff, m_util.mk_real(1)), m); + hi = m.mk_not(hi); + + mk_axiom(m.mk_false(), lo, false); + mk_axiom(m.mk_false(), hi, false); } template @@ -626,7 +636,7 @@ namespace smt { // // Create the axiom (iff (is_int x) (= x (to_real (to_int x)))) - // + // template void theory_arith::mk_is_int_axiom(app * n) { @@ -713,7 +723,7 @@ namespace smt { ++m_top; } ~scoped_row_vars() { - --m_top; + --m_top; } }; @@ -733,7 +743,7 @@ namespace smt { SASSERT(!m_util.is_sub(n)); SASSERT(!m_util.is_uminus(n)); - + if (m_util.is_add(n)) return internalize_add(n); else if (m_util.is_mul(n)) @@ -742,9 +752,9 @@ namespace smt { return internalize_div(n); else if (m_util.is_idiv(n)) return internalize_idiv(n); - else if (m_util.is_mod(n)) + else if (m_util.is_mod(n)) return internalize_mod(n); - else if (m_util.is_rem(n)) + else if (m_util.is_rem(n)) return internalize_rem(n); else if (m_util.is_to_real(n)) return internalize_to_real(n); @@ -839,7 +849,7 @@ namespace smt { /** \brief Collect variables in the given row that have the given kind, but a different from the row main var (i.e., var that owns the row). - + The inv of the coefficients is also stored in result */ template @@ -859,7 +869,7 @@ namespace smt { } /** - \brief Normalize row as a quasi base row, it does not contain quasi-base + \brief Normalize row as a quasi base row, it does not contain quasi-base variables different from r.m_base_var. */ template @@ -905,7 +915,7 @@ namespace smt { // For example, consider the following scenario: // // 1) s is a quasi-base var, s depends on x, and value of x is v0 - // + // // 2) x is updated to v1, but the update does not affect s (s is a quasi-base var). // // 3) quasi_base_row2base_row is executed, and we compute the value of s using @@ -915,7 +925,7 @@ namespace smt { // // 5) if this branch is deleted, the row owned by s will not satisfy // valid_row_assignment. - // + // m_value[s] = tmp; SASSERT(!m_in_update_trail_stack.contains(s)); save_value(s); @@ -957,8 +967,8 @@ namespace smt { TRACE("mk_bound_axioms", tout << "add bound axioms for v" << v << " " << a1 << "\n";); if (!get_context().is_searching()) { // - // NB. We make an assumption that user push calls propagation - // before internal scopes are pushed. This flushes all newly + // NB. We make an assumption that user push calls propagation + // before internal scopes are pushed. This flushes all newly // asserted atoms into the right context. // m_new_atoms.push_back(a1); @@ -973,7 +983,7 @@ namespace smt { typename atoms::iterator lo_inf = end, lo_sup = end; typename atoms::iterator hi_inf = end, hi_sup = end; for (; it != end; ++it) { - atom * a2 = *it; + atom * a2 = *it; inf_numeral const & k2(a2->get_k()); atom_kind kind2 = a2->get_atom_kind(); TRACE("mk_bound_axioms", display_atom(tout << "compare " << a2 << " ", a2, true); tout << "\n";); @@ -1001,7 +1011,7 @@ namespace smt { else if (hi_sup == end || k2 < (*hi_sup)->get_k()) { hi_sup = it; } - } + } if (lo_inf != end) mk_bound_axiom(a1, *lo_inf); if (lo_sup != end) mk_bound_axiom(a1, *lo_sup); if (hi_inf != end) mk_bound_axiom(a1, *hi_inf); @@ -1012,8 +1022,8 @@ namespace smt { void theory_arith::mk_bound_axiom(atom* a1, atom* a2) { TRACE("mk_bound_axioms", tout << a1 << " " << a2 << "\n";); theory_var v = a1->get_var(); - literal l1(a1->get_bool_var()); - literal l2(a2->get_bool_var()); + literal l1(a1->get_bool_var()); + literal l2(a2->get_bool_var()); inf_numeral const & k1(a1->get_k()); inf_numeral const & k2(a2->get_k()); atom_kind kind1 = a1->get_atom_kind(); @@ -1022,7 +1032,7 @@ namespace smt { SASSERT(v == a2->get_var()); if (k1 == k2 && kind1 == kind2) return; SASSERT(k1 != k2 || kind1 != kind2); - parameter coeffs[3] = { parameter(symbol("farkas")), + parameter coeffs[3] = { parameter(symbol("farkas")), parameter(rational(1)), parameter(rational(1)) }; if (kind1 == A_LOWER) { @@ -1054,7 +1064,7 @@ namespace smt { } else { // k1 < k2, k2 <= x => ~(x <= k1) - mk_clause(~l1, ~l2, 3, coeffs); + mk_clause(~l1, ~l2, 3, coeffs); if (v_is_int && k1 == k2 - inf_numeral(1)) { // x <= k1 or k1+l <= x mk_clause(l1, l2, 3, coeffs); @@ -1072,7 +1082,7 @@ namespace smt { // k1 <= hi_sup , x <= k1 => x <= hi_sup mk_clause(~l1, l2, 3, coeffs); } - } + } } template @@ -1080,7 +1090,7 @@ namespace smt { CTRACE("arith_verbose", !m_new_atoms.empty(), tout << "flush bound axioms\n";); while (!m_new_atoms.empty()) { - ptr_vector atoms; + ptr_vector atoms; atoms.push_back(m_new_atoms.back()); m_new_atoms.pop_back(); theory_var v = atoms.back()->get_var(); @@ -1091,8 +1101,8 @@ namespace smt { m_new_atoms.pop_back(); --i; } - } - CTRACE("arith", atoms.size() > 1, + } + CTRACE("arith", atoms.size() > 1, for (unsigned i = 0; i < atoms.size(); ++i) { atoms[i]->display(*this, tout); tout << "\n"; }); @@ -1119,10 +1129,10 @@ namespace smt { hi_inf1 = next_inf(a1, A_UPPER, hi_inf, end, fhi_inf); lo_sup1 = next_sup(a1, A_LOWER, lo_sup, end, flo_sup); hi_sup1 = next_sup(a1, A_UPPER, hi_sup, end, fhi_sup); - if (lo_inf1 != end) lo_inf = lo_inf1; - if (lo_sup1 != end) lo_sup = lo_sup1; - if (hi_inf1 != end) hi_inf = hi_inf1; - if (hi_sup1 != end) hi_sup = hi_sup1; + if (lo_inf1 != end) lo_inf = lo_inf1; + if (lo_sup1 != end) lo_sup = lo_sup1; + if (hi_inf1 != end) hi_inf = hi_inf1; + if (hi_sup1 != end) hi_sup = hi_sup1; if (!flo_inf) lo_inf = end; if (!fhi_inf) hi_inf = end; if (!flo_sup) lo_sup = end; @@ -1132,15 +1142,15 @@ namespace smt { if (lo_sup1 != end && lo_sup != end && !visited.contains(*lo_sup)) mk_bound_axiom(a1, *lo_sup); if (hi_inf1 != end && hi_inf != end && !visited.contains(*hi_inf)) mk_bound_axiom(a1, *hi_inf); if (hi_sup1 != end && hi_sup != end && !visited.contains(*hi_sup)) mk_bound_axiom(a1, *hi_sup); - } + } } } template - typename theory_arith::atoms::iterator + typename theory_arith::atoms::iterator theory_arith::first( - atom_kind kind, - typename atoms::iterator it, + atom_kind kind, + typename atoms::iterator it, typename atoms::iterator end) { for (; it != end; ++it) { atom* a = *it; @@ -1150,18 +1160,18 @@ namespace smt { } template - typename theory_arith::atoms::iterator + typename theory_arith::atoms::iterator theory_arith::next_inf( - atom* a1, - atom_kind kind, - typename atoms::iterator it, + atom* a1, + atom_kind kind, + typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { inf_numeral const & k1(a1->get_k()); typename atoms::iterator result = end; found_compatible = false; for (; it != end; ++it) { - atom * a2 = *it; + atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; inf_numeral const & k2(a2->get_k()); @@ -1177,17 +1187,17 @@ namespace smt { } template - typename theory_arith::atoms::iterator + typename theory_arith::atoms::iterator theory_arith::next_sup( - atom* a1, - atom_kind kind, - typename atoms::iterator it, + atom* a1, + atom_kind kind, + typename atoms::iterator it, typename atoms::iterator end, bool& found_compatible) { inf_numeral const & k1(a1->get_k()); found_compatible = false; for (; it != end; ++it) { - atom * a2 = *it; + atom * a2 = *it; if (a1 == a2) continue; if (a2->get_atom_kind() != kind) continue; inf_numeral const & k2(a2->get_k()); @@ -1202,7 +1212,7 @@ namespace smt { template bool theory_arith::internalize_atom(app * n, bool gate_ctx) { - TRACE("arith_internalize", tout << "internalising atom:\n" << mk_pp(n, this->get_manager()) << "\n";); + TRACE("arith_internalize", tout << "internalizing atom:\n" << mk_pp(n, this->get_manager()) << "\n";); context & ctx = get_context(); SASSERT(m_util.is_le(n) || m_util.is_ge(n) || m_util.is_is_int(n)); SASSERT(!ctx.b_internalized(n)); @@ -1229,7 +1239,7 @@ namespace smt { app * rhs = to_app(n->get_arg(1)); expr * rhs2; if (m_util.is_to_real(rhs, rhs2) && is_app(rhs2)) { rhs = to_app(rhs2); } - if (!m_util.is_numeral(rhs)) { + if (!m_util.is_numeral(rhs)) { UNREACHABLE(); throw default_exception("malformed atomic constraint"); } @@ -1285,7 +1295,7 @@ namespace smt { enode * n1 = ctx.get_enode(lhs); enode * n2 = ctx.get_enode(rhs); // The expression atom may be a theory axiom. In this case, it may not be in simplified form. - // So, an atom such as (= a a) may occur. The procedure mk_axioms, expects n1 != n2. + // So, an atom such as (= a a) may occur. The procedure mk_axioms, expects n1 != n2. // So, we should check it. It doesn't make sense to create an axiom for (= a a) in the arith_eq_adapter. if (n1->get_th_var(get_id()) != null_theory_var && n2->get_th_var(get_id()) != null_theory_var && @@ -1300,7 +1310,7 @@ namespace smt { void theory_arith::apply_sort_cnstr(enode * n, sort * s) { // do nothing... } - + template void theory_arith::assign_eh(bool_var v, bool is_true) { TRACE("arith_verbose", tout << "p" << v << " := " << (is_true?"true":"false") << "\n";); @@ -1315,13 +1325,13 @@ namespace smt { template void theory_arith::relevant_eh(app * n) { TRACE("arith_relevant_eh", tout << "relevant_eh: " << mk_pp(n, get_manager()) << "\n";); - if (m_util.is_mod(n)) + if (m_util.is_mod(n)) mk_idiv_mod_axioms(n->get_arg(0), n->get_arg(1)); else if (m_util.is_rem(n)) mk_rem_axiom(n->get_arg(0), n->get_arg(1)); - else if (m_util.is_div(n)) + else if (m_util.is_div(n)) mk_div_axiom(n->get_arg(0), n->get_arg(1)); - else if (m_util.is_to_int(n)) + else if (m_util.is_to_int(n)) mk_to_int_axiom(n); else if (m_util.is_is_int(n)) mk_is_int_axiom(n); @@ -1330,16 +1340,16 @@ namespace smt { template void theory_arith::new_eq_eh(theory_var v1, theory_var v2) { TRACE("arith_new_eq_eh", tout << "#" << get_enode(v1)->get_owner_id() << " = #" << get_enode(v2)->get_owner_id() << "\n";); - TRACE("arith_new_eq_eh_detail", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << + TRACE("arith_new_eq_eh_detail", tout << mk_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); enode * n1 = get_enode(v1); - - if (!m_util.is_int(n1->get_owner()) && + + if (!m_util.is_int(n1->get_owner()) && !m_util.is_real(n1->get_owner())) { return; } - if (m_params.m_arith_eq_bounds) { + if (m_params.m_arith_eq_bounds) { enode * n2 = get_enode(v2); SASSERT(n1->get_root() == n2->get_root()); if (m_util.is_numeral(n1->get_owner())) { @@ -1347,8 +1357,8 @@ namespace smt { std::swap(n1, n2); } rational k; - bound * b1 = 0; - bound * b2 = 0; + bound * b1 = nullptr; + bound * b2 = nullptr; if (m_util.is_numeral(n2->get_owner(), k)) { inf_numeral val(k); b1 = alloc(eq_bound, v1, val, B_LOWER, n1, n2); @@ -1386,7 +1396,7 @@ namespace smt { template void theory_arith::new_diseq_eh(theory_var v1, theory_var v2) { - TRACE("arith_new_diseq_eh", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << + TRACE("arith_new_diseq_eh", tout << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager()) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager()) << "\n";); m_stats.m_assert_diseq++; m_arith_eq_adapter.new_diseq_eh(v1, v2); @@ -1451,8 +1461,8 @@ namespace smt { result = FC_GIVEUP; break; case FC_CONTINUE: - TRACE("arith", - tout << "continue arith..." + TRACE("arith", + tout << "continue arith..." << (get_context().inconsistent()?"inconsistent\n":"\n");); return FC_CONTINUE; } @@ -1470,8 +1480,8 @@ namespace smt { TRACE("arith_eq_adapter_info", m_arith_eq_adapter.display_already_processed(tout);); TRACE("arith", display(tout);); - if (!propagate_core()) - return FC_CONTINUE; + if (!propagate_core()) + return FC_CONTINUE; if (delayed_assume_eqs()) return FC_CONTINUE; get_context().push_trail(value_trail(m_final_check_idx)); @@ -1488,12 +1498,12 @@ namespace smt { TRACE("arith", tout << "result: " << result << "\n";); return result; } - + template bool theory_arith::can_propagate() { return process_atoms() && m_asserted_qhead < m_asserted_bounds.size(); } - + template void theory_arith::propagate() { TRACE("arith_propagate", tout << "propagate\n"; display(tout);); @@ -1507,7 +1517,7 @@ namespace smt { CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); CASSERT("arith", valid_row_assignment()); - + flush_bound_axioms(); propagate_linear_monomials(); while (m_asserted_qhead < m_asserted_bounds.size()) { @@ -1515,7 +1525,7 @@ namespace smt { m_asserted_qhead++; if (!assert_bound(b)) { failed(); - return false; + return false; } } if (!make_feasible()) { @@ -1540,15 +1550,15 @@ namespace smt { CASSERT("arith", satisfy_bounds()); return true; } - + template void theory_arith::failed() { restore_assignment(); m_to_patch.reset(); m_to_check.reset(); m_in_to_check.reset(); - } - + } + template void theory_arith::flush_eh() { std::for_each(m_atoms.begin(), m_atoms.end(), delete_proc()); @@ -1556,7 +1566,7 @@ namespace smt { std::for_each(m_bounds_to_delete.begin(), m_bounds_to_delete.end(), delete_proc()); m_bounds_to_delete.reset(); } - + template void theory_arith::reset_eh() { m_stats.reset(); @@ -1661,7 +1671,7 @@ namespace smt { result.neg(); return is_diff; } - + template theory_arith::theory_arith(ast_manager & m, theory_arith_params & params): theory(m.mk_family_id("arith")), @@ -1697,8 +1707,8 @@ namespace smt { } template - theory* theory_arith::mk_fresh(context* new_ctx) { - return alloc(theory_arith, new_ctx->get_manager(), m_params); + theory* theory_arith::mk_fresh(context* new_ctx) { + return alloc(theory_arith, new_ctx->get_manager(), m_params); } template @@ -1712,7 +1722,7 @@ namespace smt { // Add Row // // ----------------------------------- - + /** \brief Set: row1 <- row1 + coeff * row2 */ @@ -1730,8 +1740,8 @@ namespace smt { CASSERT("arith", check_null_var_pos()); r1.save_var_pos(m_var_pos); - - // + + // // loop over variables in row2, // add terms in row2 to row1. // @@ -1780,7 +1790,7 @@ namespace smt { r_entry.m_coeff -= it->m_coeff); } else { - ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff *= coeff, + ADD_ROW(r_entry.m_coeff = it->m_coeff; r_entry.m_coeff *= coeff, r_entry.m_coeff += it->m_coeff * coeff); } @@ -1792,17 +1802,17 @@ namespace smt { theory_var v = r1.get_base_var(); if (is_int(v) && !get_value(v).is_int()) gcd_test(r1); - } + } } /** \brief Set r1 <- r1 + a_xs[0].m_coeff * get_var_row(a_xs[0].m_var) + ... + a_xs[0].m_coeff * get_var_row(a_xs[sz-1].m_var) - + \pre For all i in [0..sz-1]. not is_non_base(a_xs[i]) */ template void theory_arith::add_rows(unsigned r1, unsigned sz, linear_monomial * a_xs) { - if (sz == 0) + if (sz == 0) return; for (unsigned i = 0; i < sz; i++) { linear_monomial & m = a_xs[i]; @@ -1832,7 +1842,7 @@ namespace smt { } m_changed_assignment = true; } - + template void theory_arith::discard_update_trail() { m_in_update_trail_stack.reset(); @@ -1871,15 +1881,15 @@ namespace smt { } /** - \brief m_value[v] += delta, and update dependent (non-base) variables. + \brief m_value[v] += delta, and update dependent (non-base) variables. */ template void theory_arith::update_value(theory_var v, inf_numeral const & delta) { update_value_core(v, delta); - + column & c = m_columns[v]; c.compress_if_needed(m_rows); - + inf_numeral delta2; typename svector::const_iterator it = c.begin_entries(); typename svector::const_iterator end = c.end_entries(); @@ -1924,7 +1934,7 @@ namespace smt { int r_id = get_var_row(x_i); row & r = m_rows[r_id]; - + SASSERT(r.is_coeff_of(x_j, a_ij)); #define DIVIDE_ROW(_ADJUST_COEFF_) \ @@ -1948,14 +1958,14 @@ namespace smt { set_var_row(x_i, -1); set_var_row(x_j, r_id); - + SASSERT(r.m_base_var == x_i); r.m_base_var = x_j; set_var_kind(x_i, NON_BASE); set_var_kind(x_j, BASE); - - eliminate(x_j, apply_gcd_test); + + eliminate(x_j, apply_gcd_test); CASSERT("arith", wf_rows()); CASSERT("arith", wf_columns()); @@ -1974,7 +1984,7 @@ namespace smt { /** \brief Eliminate x_i from the rows different from get_var_row(x_i) - + If Lazy = true, then x_i is only eliminated from base rows. */ template @@ -1993,7 +2003,7 @@ namespace smt { unsigned r1_sz = m_rows[r_id].size(); if (it->m_row_id != static_cast(r_id)) { row & r2 = m_rows[it->m_row_id]; - theory_var s2 = r2.m_base_var; + theory_var s2 = r2.m_base_var; if (s2 != null_theory_var && (!Lazy || is_base(s2))) { a_kj = r2[it->m_row_idx].m_coeff; a_kj.neg(); @@ -2001,12 +2011,12 @@ namespace smt { get_manager().limit().inc((r1_sz + r2.size()) * (a_kj.storage_size())); } } - else { + else { s_pos = i; } } - } - CTRACE("eliminate", !Lazy && c.size() != 1, + } + CTRACE("eliminate", !Lazy && c.size() != 1, tout << "eliminating v" << x_i << ", Lazy: " << Lazy << ", c.size: " << c.size() << "\n"; display(tout);); SASSERT(Lazy || c.size() == 1); @@ -2046,7 +2056,7 @@ namespace smt { pivot(x_i, x_j, a_ij, m_eager_gcd); CASSERT("arith", valid_row_assignment()); } - + /** \brief Return the number of base variables that are non free and are v dependent. The function adds 1 to the result if v is non free. @@ -2072,9 +2082,9 @@ namespace smt { } return result; } - + /** - \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, + \brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. @@ -2085,29 +2095,29 @@ namespace smt { theory_var max = get_num_vars(); theory_var result = max; row const & r = m_rows[get_var_row(x_i)]; - + typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); - for (; it != end; ++it) { - if (!it->is_dead()) { - theory_var x_j = it->m_var; - numeral const & a_ij = it->m_coeff; + for (; it != end; ++it) { + if (!it->is_dead()) { + theory_var x_j = it->m_var; + numeral const & a_ij = it->m_coeff; bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos(); - bool is_pos = !is_neg; + bool is_pos = !is_neg; if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { SASSERT(is_non_base(x_j)); - if (x_j < result) { - result = x_j; - out_a_ij = a_ij; + if (x_j < result) { + result = x_j; + out_a_ij = a_ij; } } } } return result < max ? result : null_theory_var; } - + /** - \brief Select a variable x_j in the row r defining the base var x_i, + \brief Select a variable x_j in the row r defining the base var x_i, s.t. x_j can be used to patch the error in x_i. Return null_theory_var if there is no x_j. Otherwise, return x_j and store its coefficient in out_a_ij. @@ -2130,13 +2140,13 @@ namespace smt { for (; it != end; ++it) { - if (!it->is_dead()) { - theory_var x_j = it->m_var; - numeral const & a_ij = it->m_coeff; - + if (!it->is_dead()) { + theory_var x_j = it->m_var; + numeral const & a_ij = it->m_coeff; + bool is_neg = is_below ? a_ij.is_neg() : a_ij.is_pos(); - bool is_pos = !is_neg; - if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { + bool is_pos = !is_neg; + if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) { int num = get_num_non_free_dep_vars(x_j, best_so_far); int col_sz = m_columns[x_j].size(); if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) { @@ -2152,13 +2162,13 @@ namespace smt { result = x_j; out_a_ij = a_ij; } - } + } } } } return result < max ? result : null_theory_var; } - + /** \brief Wrapper for select_blands_pivot_core and select_pivot_core */ @@ -2179,7 +2189,7 @@ namespace smt { // Make feasible // // ----------------------------------- - + /** \brief Make the given variable feasible. This method assumes that x_i is a base var. Return false if it was not possible to @@ -2187,8 +2197,8 @@ namespace smt { */ template bool theory_arith::make_var_feasible(theory_var x_i) { - CTRACE("arith_bug", !is_base(x_i), - tout << "x_i: " << x_i << ", below_lower(x_i): " << below_lower(x_i) << + CTRACE("arith_bug", !is_base(x_i), + tout << "x_i: " << x_i << ", below_lower(x_i): " << below_lower(x_i) << ", above_upper(x_i): " << above_upper(x_i) << "\n"; display(tout);); SASSERT(is_base(x_i)); @@ -2240,7 +2250,7 @@ namespace smt { continue; SASSERT(curr_error > inf_numeral(0)); if (best == null_theory_var || (!least && curr_error > best_error) || (least && curr_error < best_error)) { - TRACE("select_pivot", tout << "best: " << best << " v" << v + TRACE("select_pivot", tout << "best: " << best << " v" << v << ", best_error: " << best_error << ", curr_error: " << curr_error << "\n";); best = v; best_error = curr_error; @@ -2261,7 +2271,7 @@ namespace smt { m_to_patch.erase(best); return best; } - + template theory_var theory_arith::select_smallest_var() { return m_to_patch.erase_min(); @@ -2272,7 +2282,7 @@ namespace smt { if (m_blands_rule) return select_smallest_var(); switch (m_params.m_arith_pivot_strategy) { - case ARITH_PIVOT_GREATEST_ERROR: + case ARITH_PIVOT_GREATEST_ERROR: return select_greatest_error_var(); case ARITH_PIVOT_LEAST_ERROR: return select_least_error_var(); @@ -2314,12 +2324,12 @@ namespace smt { m_left_basis.insert(v); } } - if (!make_var_feasible(v)) { + if (!make_var_feasible(v)) { TRACE("arith_make_feasible", tout << "make_feasible: unsat\n"; display(tout);); return false; } TRACE("arith_make_feasible_detail", display(tout);); - if (get_context().get_cancel_flag()) { + if (get_context().get_cancel_flag()) { return true; } } @@ -2334,7 +2344,7 @@ namespace smt { /** \brief A row is in a sign inconsistency when it is implying a lower (upper) bound on x_i, which is above (below) its known - upper (lower) bound. + upper (lower) bound. */ template void theory_arith::sign_row_conflict(theory_var x_i, bool is_below) { @@ -2342,13 +2352,13 @@ namespace smt { row const & r = m_rows[get_var_row(x_i)]; int idx = r.get_idx_of(x_i); SASSERT(idx >= 0); - bound * b = 0; + bound * b = nullptr; // Remark: // if x_i is an integer variable, then delta can be negative: // // Example: x_i <= 0 get_value(x_i) = 1/4 - // + // // The value is above the upper bound. // Since x_i is an integer, get_epsilon(x_i) = 1, and delta = -3/4 @@ -2378,7 +2388,7 @@ namespace smt { antecedents ante(*this); explain_bound(r, idx, !is_below, delta, ante); b->push_justification(ante, numeral(1), coeffs_enabled()); - + TRACE("sign_row_conflict", tout << "v" << x_i << " is_below: " << is_below << " delta: " << delta << "\n"; display_var(tout, x_i); tout << "is_below_lower: " << below_lower(x_i) << ", is_above_upper: " << above_upper(x_i) << "\n"; ante.display(tout);); @@ -2392,7 +2402,7 @@ namespace smt { // Assert bound // // ----------------------------------- - + /** \brief Assert x >= k, return false if a conflict is detected. */ @@ -2404,7 +2414,7 @@ namespace smt { bound * u = upper(v); bound * l = lower(v); - + if (u && k > u->get_value()) { sign_bound_conflict(u, b); return false; @@ -2414,7 +2424,7 @@ namespace smt { // redundant return true; } - + switch (get_var_kind(v)) { case QUASI_BASE: quasi_base_row2base_row(get_var_row(v)); @@ -2433,10 +2443,10 @@ namespace smt { push_bound_trail(v, l, false); set_bound(b, false); - + if (propagation_mode() != BP_NONE) mark_rows_for_bound_prop(v); - + return true; } @@ -2452,12 +2462,12 @@ namespace smt { TRACE("arith", display_bound(tout, b); tout << "v" << v << " <= " << k << "\n";); bound * u = upper(v); bound * l = lower(v); - + if (l && k < l->get_value()) { sign_bound_conflict(l, b); return false; } - + if (u && k >= u->get_value()) { // redundant return true; @@ -2469,7 +2479,7 @@ namespace smt { SASSERT(get_var_kind(v) == BASE); case BASE: if (!m_to_patch.contains(v) && get_value(v) > k) { - TRACE("to_patch_bug", tout << "need to be patched (assert upper): "; display_var(tout, v);); + TRACE("to_patch_bug", tout << "need to be patched (assert upper): "; display_var(tout, v);); m_to_patch.insert(v); } break; @@ -2481,12 +2491,12 @@ namespace smt { push_bound_trail(v, u, true); set_bound(b, true); - + if (propagation_mode() != BP_NONE) mark_rows_for_bound_prop(v); return true; - } + } template bool theory_arith::assert_bound(bound * b) { @@ -2502,7 +2512,7 @@ namespace smt { bool result = true; switch (b->get_bound_kind()) { - case B_LOWER: + case B_LOWER: m_stats.m_assert_lower++; result = assert_lower(b); break; @@ -2511,7 +2521,7 @@ namespace smt { result = assert_upper(b); break; } - + TRACE("arith_bound", tout << "result: " << result << "\n"; display(tout);); return result; } @@ -2536,7 +2546,7 @@ namespace smt { // Bound propagation // // ----------------------------------- - + /** \brief Mark the row r1 for bound propagation. */ @@ -2549,7 +2559,7 @@ namespace smt { } /** - \brief Mark all rows that contain v for bound propagation. + \brief Mark all rows that contain v for bound propagation. */ template void theory_arith::mark_rows_for_bound_prop(theory_var v) { @@ -2595,7 +2605,7 @@ namespace smt { - lower_idx >= 0 : row can imply a lower bound for the monomial at 'lower_idx' - lower_idx == -1 : row can imply a lower bound for every monomial in the row. - lower_idx == -2 : row cannot be used to imply a lower bound. - + - upper_idx >= 0 : row can imply a upper bound for the monomial at 'upper_idx' - upper_idx == -1 : row can imply a upper bound for every monomial in the row. - upper_idx == -2 : row cannot be used to imply a upper bound. @@ -2603,7 +2613,7 @@ namespace smt { template void theory_arith::is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const { lower_idx = -1; - upper_idx = -1; + upper_idx = -1; typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); for (int i = 0; it != end; ++it, ++i) { @@ -2616,7 +2626,7 @@ namespace smt { return; } bool is_pos = it->m_coeff.is_pos(); - if (lower(it->m_var) == 0) { + if (lower(it->m_var) == nullptr) { if (is_pos) { UPDATE_IDX(upper_idx); } @@ -2624,7 +2634,7 @@ namespace smt { UPDATE_IDX(lower_idx); } } - if (upper(it->m_var) == 0) { + if (upper(it->m_var) == nullptr) { if (is_pos) { UPDATE_IDX(lower_idx); } @@ -2661,8 +2671,8 @@ namespace smt { if (entry.m_coeff.is_pos() == is_lower) { // implied_k is a lower bound for entry.m_var bound * curr = lower(entry.m_var); - if (curr == 0 || implied_k > curr->get_value()) { - TRACE("arith_imply_bound", + if (curr == nullptr || implied_k > curr->get_value()) { + TRACE("arith_imply_bound", tout << "implying lower bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); @@ -2670,21 +2680,21 @@ namespace smt { } } else { - // implied_k is an upper bound for it->m_var + // implied_k is an upper bound for it->m_var bound * curr = upper(entry.m_var); - if (curr == 0 || implied_k < curr->get_value()) { - TRACE("arith_imply_bound", + if (curr == nullptr || implied_k < curr->get_value()) { + TRACE("arith_imply_bound", tout << "implying upper bound for v" << entry.m_var << " " << implied_k << " using row:\n"; display_row_info(tout, r); display_var(tout, entry.m_var);); mk_implied_bound(r, idx, is_lower, entry.m_var, B_UPPER, implied_k); } } - } + } } /** - \brief Auxiliary method. See is_row_useful_for_bound_prop + \brief Auxiliary method. See is_row_useful_for_bound_prop If is_lower = true (false), then imply a lower (upper) bound for all monomials in the row. The monomial bounds are used to compute bounds @@ -2692,7 +2702,7 @@ namespace smt { */ template void theory_arith::imply_bound_for_all_monomials(row const & r, bool is_lower) { - // Traverse the row once and compute + // Traverse the row once and compute // bb = (Sum_{a_i < 0} -a_i*lower(x_i)) + (Sum_{a_j > 0} -a_j * upper(x_j)) If is_lower = true // bb = (Sum_{a_i > 0} -a_i*lower(x_i)) + (Sum_{a_j < 0} -a_j * upper(x_j)) If is_lower = false inf_numeral bb; @@ -2705,7 +2715,7 @@ namespace smt { bb.submul(it->m_coeff, b); } } - + inf_numeral implied_k; it = r.begin_entries(); for (int idx = 0; it != end; ++it, ++idx) { @@ -2716,13 +2726,13 @@ namespace smt { implied_k.addmul(it->m_coeff, b); // implied_k is a bound for the monomial in position it implied_k /= it->m_coeff; - TRACE("arith_imply_bound", + TRACE("arith_imply_bound", display_var(tout, it->m_var); tout << "implied bound: " << (it->m_coeff.is_pos() ? ">=" : "<=") << implied_k << "\n";); if (it->m_coeff.is_pos() == is_lower) { // implied_k is a lower bound for it->m_var bound * curr = lower(it->m_var); - if (curr == 0 || implied_k > curr->get_value()) { + if (curr == nullptr || implied_k > curr->get_value()) { // improved lower bound TRACE("arith_imply_bound", tout << "implying lower bound for v" << it->m_var << " " << implied_k << " using row:\n"; @@ -2732,9 +2742,9 @@ namespace smt { } } else { - // implied_k is an upper bound for it->m_var + // implied_k is an upper bound for it->m_var bound * curr = upper(it->m_var); - if (curr == 0 || implied_k < curr->get_value()) { + if (curr == nullptr || implied_k < curr->get_value()) { // improved upper bound TRACE("arith_imply_bound", tout << "implying upper bound for v" << it->m_var << " " << implied_k << " using row:\n"; @@ -2749,13 +2759,13 @@ namespace smt { /** \brief Create an explanation for the lower/upper bound of the variable at position idx. - + \remark delta is used for relaxing the explanation. That is, the implied bound can be delta weaker the the computed value. - \remark the is_lower parameter is a little bit counterintuitive. It says if the other monomials + \remark the is_lower parameter is a little bit counterintuitive. It says if the other monomials imply a lower (upper) bound for the monomial at position idx. - + Store the result in 'antecedent' */ template @@ -2765,7 +2775,7 @@ namespace smt { return; context & ctx = get_context(); row_entry const & entry = r[idx]; - numeral coeff = entry.m_coeff; + numeral coeff = entry.m_coeff; if (relax_bounds()) { // if the variable v at position idx can have a delta increase (decrease) of 'delta', then // the monomial (coeff * v) at position idx can have a delta increase (decrease) of '|coeff| * delta' @@ -2806,10 +2816,10 @@ namespace smt { // limit_k1 += delta * coeff; limit_k1.addmul(inv_coeff, delta); } - TRACE("propagate_bounds_bug", tout << "is_b_lower: " << is_b_lower << " k1: " << k_1 << " limit_k1: " + TRACE("propagate_bounds_bug", tout << "is_b_lower: " << is_b_lower << " k1: " << k_1 << " limit_k1: " << limit_k1 << " delta: " << delta << " coeff: " << coeff << "\n";); inf_numeral k_2 = k_1; - atom * new_atom = 0; + atom * new_atom = nullptr; atoms const & as = m_var_occs[it->m_var]; typename atoms::const_iterator it = as.begin(); typename atoms::const_iterator end = as.end(); @@ -2844,7 +2854,7 @@ namespace smt { } SASSERT(!is_b_lower || k_2 <= k_1); SASSERT(is_b_lower || k_2 >= k_1); - if (new_atom == 0) { + if (new_atom == nullptr) { b->push_justification(ante, coeff, coeffs_enabled()); continue; } @@ -2886,7 +2896,7 @@ namespace smt { delta = k; delta -= k2; } - TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n"; + TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); } @@ -2896,13 +2906,13 @@ namespace smt { // example: // k = -1/5*epsilon // k2 = 0 - // Thus, v <= -1/5*epsilon + // Thus, v <= -1/5*epsilon // (not v >= 0) which is equivalent to v <= -epsilon. delta = k2; delta -= k; delta -= epsilon; if (delta.is_nonneg()) { - TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n"; + TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", not v" << v << " >= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); } @@ -2917,18 +2927,18 @@ namespace smt { delta -= k2; delta -= epsilon; if (delta.is_nonneg()) { - TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n"; + TRACE("propagate_bounds", tout << "v" << v << " >= " << k << ", not v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(~l, r, idx, is_lower, delta); } } - // v <= k k <= k2 |- v <= k2 + // v <= k k <= k2 |- v <= k2 if (kind == B_UPPER && k <= k2) { if (relax_bounds()) { delta = k2; delta -= k; } - TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n"; + TRACE("propagate_bounds", tout << "v" << v << " <= " << k << ", v" << v << " <= " << k2 << ", delta: " << delta << "\n"; display_row(tout, r);); assign_bound_literal(l, r, idx, is_lower, delta); } @@ -2942,7 +2952,7 @@ namespace smt { context & ctx = get_context(); if (dump_lemmas()) { TRACE("arith", ante.display(tout) << " --> "; ctx.display_detailed_literal(tout, l); tout << "\n";); - ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), + ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), l); } } @@ -2951,7 +2961,7 @@ namespace smt { void theory_arith::dump_lemmas(literal l, derived_bound const& ante) { context & ctx = get_context(); if (dump_lemmas()) { - ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), + ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), l); } } @@ -2963,10 +2973,10 @@ namespace smt { antecedents ante(*this); explain_bound(r, idx, is_lower, delta, ante); dump_lemmas(l, ante); - - TRACE("propagate_bounds", + + TRACE("propagate_bounds", ante.display(tout) << " --> "; - ctx.display_detailed_literal(tout, l); + ctx.display_detailed_literal(tout, l); tout << "\n";); if (ante.lits().size() < small_lemma_size() && ante.eqs().empty()) { literal_vector & lits = m_tmp_literal_vector2; @@ -2976,19 +2986,19 @@ namespace smt { literal_vector::const_iterator end = ante.lits().end(); for (; it != end; ++it) lits.push_back(~(*it)); - justification * js = 0; + justification * js = nullptr; if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr(), ante.num_params(), ante.params("assign-bounds")); } - 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); } else { region & r = ctx.get_region(); ctx.assign(l, ctx.mk_justification( ext_theory_propagation_justification( - get_id(), r, ante.lits().size(), ante.lits().c_ptr(), - ante.eqs().size(), ante.eqs().c_ptr(), l, + get_id(), r, ante.lits().size(), ante.lits().c_ptr(), + ante.eqs().size(), ante.eqs().c_ptr(), l, ante.num_params(), ante.params("assign-bounds")))); } } @@ -3009,29 +3019,29 @@ namespace smt { int lower_idx; int upper_idx; is_row_useful_for_bound_prop(r, lower_idx, upper_idx); - + if (lower_idx >= 0) { imply_bound_for_monomial(r, lower_idx, true); } else if (lower_idx == -1) { imply_bound_for_all_monomials(r, true); } - + if (upper_idx >= 0) { imply_bound_for_monomial(r, upper_idx, false); } else if (upper_idx == -1) { imply_bound_for_all_monomials(r, false); } - - // sneaking cheap eq detection in this loop + + // sneaking cheap eq detection in this loop propagate_cheap_eq(*it); } - + #if 0 theory_var v = r.get_base_var(); if (!is_int(v) || get_value(v).is_int()) { - // If an integer value is not assigned to an integer value, then + // If an integer value is not assigned to an integer value, then // bound propagation can diverge. m_in_to_check.remove(v); } @@ -3059,15 +3069,15 @@ namespace smt { dump_lemmas(false_literal, ante); set_conflict(ante.lits().size(), ante.lits().c_ptr(), ante.eqs().size(), ante.eqs().c_ptr(), bounds, proof_rule); } - + template - void theory_arith::set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs, + void theory_arith::set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs, antecedents& bounds, char const* proof_rule) { SASSERT(num_literals != 0 || num_eqs != 0); context & ctx = get_context(); m_stats.m_conflicts++; m_num_conflicts++; - TRACE("arith_conflict", + TRACE("arith_conflict", tout << "scope: " << ctx.get_scope_level() << "\n"; for (unsigned i = 0; i < num_literals; i++) { ctx.display_detailed_literal(tout, lits[i]); @@ -3090,13 +3100,13 @@ namespace smt { record_conflict(num_literals, lits, num_eqs, eqs, bounds.num_params(), bounds.params(proof_rule)); ctx.set_conflict( ctx.mk_justification( - ext_theory_conflict_justification(get_id(), ctx.get_region(), num_literals, lits, num_eqs, eqs, + ext_theory_conflict_justification(get_id(), ctx.get_region(), num_literals, lits, num_eqs, eqs, bounds.num_params(), bounds.params(proof_rule)))); } /** \brief Collect the proofs for the fixed variables in the given row. Store - the proofs in result. + the proofs in result. */ template void theory_arith::collect_fixed_var_justifications(row const & r, antecedents& antecedents) const { @@ -3105,7 +3115,7 @@ namespace smt { for (; it != end; ++it) { if (!it->is_dead() && is_fixed(it->m_var)) { lower(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); - upper(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); + upper(it->m_var)->push_justification(antecedents, it->m_coeff, coeffs_enabled()); } } } @@ -3119,33 +3129,33 @@ namespace smt { // // The arithmetic module uses infinitesimals. So, // an inf_numeral (n,k) represents n + k*epsilon - // where epsilon is a very small number. + // where epsilon is a very small number. // In order to generate a model, we need to compute // a value for epsilon in a way all bounds remain // satisfied. // // 1) Handling inequalities: (n1, k1) <= (n2, k2) - // - // The only intersting case is n1 < n2 and k1 > k2. + // + // The only intersting case is n1 < n2 and k1 > k2. // Using the definition of infinitesimal numbers // we have: // n1 + k1 * epsilon <= n2 + k2 - epsilon // Therefore: // epsilon <= (n2 - n1) / (k1 - k2) - // + // // Starting at Z3 V2.0, we split disequalities. // So, we do not need to handle them. If we decide // to support them again in the future: // // 2) Handling disequalities: (n1, k1) /= n2 - // + // // case a) k1 is positive and n1 < n2 // Thus, epsilon < (n2 - n1) / k1 // => epsilon <= (n2 - n1) / 2*k1 // // case b) k1 is negative and n1 > n2 - // Similarly, epsilon <= (n2 - n1) / 2*k1 - // + // Similarly, epsilon <= (n2 - n1) / 2*k1 + // /** \brief Update the value of epsilon using the inequality l <= u @@ -3161,7 +3171,7 @@ namespace smt { } SASSERT(m_epsilon.is_pos()); } - + template void theory_arith::compute_epsilon() { m_epsilon = numeral(1); @@ -3169,9 +3179,9 @@ namespace smt { for (theory_var v = 0; v < num; v++) { bound * l = lower(v); bound * u = upper(v); - if (l != 0) + if (l != nullptr) update_epsilon(l->get_value(), get_value(v)); - if (u != 0) + if (u != nullptr) update_epsilon(get_value(v), u->get_value()); } TRACE("epsilon_bug", tout << "epsilon: " << m_epsilon << "\n";); @@ -3179,21 +3189,21 @@ namespace smt { /** The epsilon computed by compute_epsilon may accidentally make two shared - variables to have the same assignment. This method keeps dividing + variables to have the same assignment. This method keeps dividing epsilon by 2 until this "clash" does not occur. Here is an example of the problem - + Assignment: x -> 9.5 - y -> 10 - epsilon - + y -> 10 - epsilon + x and y have different assignments. However, if compute_epsilon sets epsilon to 0.5, then x and y become 9.5. However, an equality is not propagated to the core since in the assignment above they are assigned to distinct values. - - This bug was reported by Marcello Bersani. + + This bug was reported by Marcello Bersani. Remark: this is not really a soundness bug. The result sat/unsat produced by Z3 was still correct. - However, the model construction was incorrect. Perhaps, this explains why this bug was not + However, the model construction was incorrect. Perhaps, this explains why this bug was not detected before. */ template @@ -3216,7 +3226,7 @@ namespace smt { if (mapping.find(value, v2)) { SASSERT(!is_int_src(v2)); if (get_value(v) != get_value(v2)) { - // v and v2 are not known to be equal. + // v and v2 are not known to be equal. // The choice of m_epsilon is making them equal. TRACE("refine_epsilon", tout << "v" << v << " v" << v2 << " " << get_value(v) << " " << get_value(v2) << " " << value << std::endl; @@ -3269,7 +3279,7 @@ namespace smt { template bool theory_arith::to_expr(inf_numeral const& val, bool is_int, expr_ref & r) { - if (val.get_infinitesimal().is_zero()) { + if (val.get_infinitesimal().is_zero()) { numeral _val = val.get_rational(); r = m_util.mk_numeral(_val.to_rational(), is_int); return true; @@ -3287,25 +3297,25 @@ namespace smt { } template - bool theory_arith::get_lower(enode * n, expr_ref & r) { + bool theory_arith::get_lower(enode * n, expr_ref & r) { theory_var v = n->get_th_var(get_id()); - bound* b = (v == null_theory_var) ? 0 : lower(v); + bound* b = (v == null_theory_var) ? nullptr : lower(v); return b && to_expr(b->get_value(), is_int(v), r); } - + template - bool theory_arith::get_upper(enode * n, expr_ref & r) { + bool theory_arith::get_upper(enode * n, expr_ref & r) { theory_var v = n->get_th_var(get_id()); - bound* b = (v == null_theory_var) ? 0 : upper(v); + bound* b = (v == null_theory_var) ? nullptr : upper(v); return b && to_expr(b->get_value(), is_int(v), r); } - + // ----------------------------------- // // Backtracking // // ----------------------------------- - + template void theory_arith::push_scope_eh() { theory::push_scope_eh(); @@ -3388,7 +3398,7 @@ namespace smt { bound * b = it->get_old_bound(); SASSERT(is_base(v) || is_non_base(v)); restore_bound(v, b, it->is_upper()); - if (lazy_pivoting_lvl() > 2 && b == 0 && is_base(v) && is_free(v)) { + if (lazy_pivoting_lvl() > 2 && b == nullptr && is_base(v) && is_free(v)) { SASSERT(!has_var_kind(get_var_row(v), BASE)); SASSERT(!has_var_kind(get_var_row(v), QUASI_BASE)); eliminate(v, false); @@ -3410,7 +3420,7 @@ namespace smt { --it; m_unassigned_atoms[*it]++; } - + m_unassigned_atoms_trail.shrink(old_trail_size); } @@ -3427,7 +3437,7 @@ namespace smt { SASSERT(m_var_occs[v].back() == a); m_var_occs[v].pop_back(); dealloc(a); - } + } m_atoms.shrink(old_size); } @@ -3439,7 +3449,7 @@ namespace smt { --it; bound * b = *it; dealloc(b); - } + } m_bounds_to_delete.shrink(old_size); } @@ -3456,7 +3466,7 @@ namespace smt { SASSERT(m_columns[v].size() == 1); del_row(get_var_row(v)); TRACE("arith_make_feasible", tout << "del row v" << v << "\n";); - break; + break; case BASE: SASSERT(lazy_pivoting_lvl() != 0 || m_columns[v].size() == 1); if (lazy_pivoting_lvl() > 0) @@ -3514,7 +3524,7 @@ namespace smt { r.reset(); m_dead_rows.push_back(r_id); } - + /** \brief reset and retrieve built-in explanation hints for arithmetic lemmmas. */ @@ -3537,4 +3547,3 @@ namespace smt { }; #endif /* THEORY_ARITH_CORE_H_ */ - diff --git a/src/smt/theory_arith_eq.h b/src/smt/theory_arith_eq.h index a7f7fae59..40879edd9 100644 --- a/src/smt/theory_arith_eq.h +++ b/src/smt/theory_arith_eq.h @@ -117,7 +117,7 @@ namespace smt { for (; it != end; ++it) { if (!it->is_dead()) { theory_var v = it->m_var; - if (lower(v) != 0 && upper(v) != 0) + if (lower(v) != nullptr && upper(v) != nullptr) continue; bad++; if (bad > 2) { diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index 4f15a6156..b5281f218 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -50,15 +50,15 @@ namespace smt { bound * l = lower(v); bound * u = upper(v); const inf_numeral & val = get_value(v); - if (l != 0 && u != 0) { + if (l != nullptr && u != nullptr) { if (val != l->get_value() && val != u->get_value()) set_value(v, l->get_value()); } - else if (l != 0) { + else if (l != nullptr) { if (val != l->get_value()) set_value(v, l->get_value()); } - else if (u != 0) { + else if (u != nullptr) { if (val != u->get_value()) set_value(v, u->get_value()); } @@ -200,10 +200,12 @@ namespace smt { SASSERT(is_int(v)); SASSERT(!get_value(v).is_int()); m_stats.m_branches++; - TRACE("arith_int", tout << "branching v" << v << " = " << get_value(v) << "\n"; - display_var(tout, v);); numeral k = ceil(get_value(v)); rational _k = k.to_rational(); + TRACE("arith_int", tout << "branching v" << v << " = " << get_value(v) << "\n"; + display_var(tout, v); + tout << "k = " << k << ", _k = "<< _k << std::endl; + ); expr_ref bound(get_manager()); expr* e = get_enode(v)->get_owner(); bound = m_util.mk_ge(e, m_util.mk_numeral(_k, m_util.is_int(e))); @@ -245,12 +247,12 @@ namespace smt { numeral const_coeff(0); bound* l = lower(b), *u = upper(b); - if (l != 0 && get_value(b) - inf_numeral(1) < l->get_value()) { + if (l != nullptr && get_value(b) - inf_numeral(1) < l->get_value()) { SASSERT(l->get_value() <= get_value(b)); is_tight = true; const_coeff = l->get_value().get_rational(); } - else if (u != 0 && get_value(b) + inf_numeral(1) > u->get_value()) { + else if (u != nullptr && get_value(b) + inf_numeral(1) > u->get_value()) { SASSERT(get_value(b) <= u->get_value()); is_tight = true; const_coeff = u->get_value().get_rational(); @@ -472,7 +474,7 @@ namespace smt { bounds.num_params(), bounds.params("gomory-cut")) { } // Remark: the assignment must be propagated back to arith - virtual theory_id get_from_theory() const { return null_theory_id; } + theory_id get_from_theory() const override { return null_theory_id; } }; /** @@ -1074,7 +1076,7 @@ namespace smt { derived_bound * new_bound = alloc(derived_bound, v, inf_numeral(k), lower ? B_LOWER : B_UPPER); t.m_tmp_lit_set.reset(); t.m_tmp_eq_set.reset(); - if (old_bound != 0) { + if (old_bound != nullptr) { t.accumulate_justification(*old_bound, *new_bound, numeral(0) /* refine for proof gen */, t.m_tmp_lit_set, t.m_tmp_eq_set); } unsigned_vector::const_iterator it = js.begin(); @@ -1174,8 +1176,8 @@ namespace smt { c2 = rational(c); TRACE("euclidean_solver_new", tout << "new fixed: " << c2 << "\n";); propagated = true; - mk_lower(v, c2, 0, m_js); - mk_upper(v, c2, 0, m_js); + mk_lower(v, c2, nullptr, m_js); + mk_upper(v, c2, nullptr, m_js); } else { TRACE("euclidean_solver", tout << "inequality can be tightned, since all coefficients are multiple of: " << g << "\n";); @@ -1187,7 +1189,7 @@ namespace smt { bound * l = t.lower(v); bound * u = t.upper(v); c2 = rational(c); - if (l != 0) { + if (l != nullptr) { rational l_old = l->get_value().get_rational().to_rational(); rational l_new = g*ceil((l_old - c2)/g) + c2; TRACE("euclidean_solver_new", tout << "new lower: " << l_new << " old: " << l_old << "\n"; @@ -1197,7 +1199,7 @@ namespace smt { mk_lower(v, l_new, l, m_js); } } - if (u != 0) { + if (u != nullptr) { rational u_old = u->get_value().get_rational().to_rational(); rational u_new = g*floor((u_old - c2)/g) + c2; TRACE("euclidean_solver_new", tout << "new upper: " << u_new << " old: " << u_old << "\n";); @@ -1223,7 +1225,7 @@ namespace smt { continue; // skip equations... if (!t.is_int(v)) continue; // skip non integer definitions... - if (t.lower(v) == 0 && t.upper(v) == 0) + if (t.lower(v) == nullptr && t.upper(v) == nullptr) continue; // there is nothing to be tightned if (tight_bounds(v)) propagated = true; diff --git a/src/smt/theory_arith_nl.h b/src/smt/theory_arith_nl.h index 87b811e8f..ba6648dac 100644 --- a/src/smt/theory_arith_nl.h +++ b/src/smt/theory_arith_nl.h @@ -141,8 +141,8 @@ namespace smt { \brief Return the number of variables that do not have bounds associated with it. The result is 0, 1, or 2. The value 2 means "2 or more". - The second value is the idx of the a variable that does not - have bounds associated with it. It is only usefull when the first value is 1. + The second value is the idx of the variable that does not + have bounds associated with it. It is only useful when the first value is 1. The second value is -1 if such variable does not exist, that is, the first value is 0. @@ -161,14 +161,14 @@ namespace smt { template std::pair theory_arith::analyze_monomial(expr * m) const { SASSERT(is_pure_monomial(m)); - expr * var = 0; + expr * var = nullptr; unsigned power = 0; unsigned c = 0; int free_var_idx = -1; int idx = 0; for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * arg = to_app(m)->get_arg(i); - if (var == 0) { + if (var == nullptr) { var = arg; power = 1; } @@ -227,7 +227,7 @@ namespace smt { SASSERT(!m_util.is_numeral(m)); if (m_util.is_mul(m)) { unsigned num_vars = 0; - expr * var = 0; + expr * var = nullptr; for (unsigned i = 0; i < to_app(m)->get_num_args(); i++) { expr * curr = to_app(m)->get_arg(i); if (var != curr) { @@ -252,12 +252,12 @@ namespace smt { m = get_monomial_body(m); if (m_util.is_mul(m)) { unsigned curr_idx = 0; - expr * var = 0; + expr * var = nullptr; unsigned power = 0; unsigned j; for (j = 0; j < to_app(m)->get_num_args(); j++) { expr * arg = to_app(m)->get_arg(j); - if (var == 0) { + if (var == nullptr) { var = arg; power = 1; } @@ -471,7 +471,7 @@ namespace smt { } } bound * old_lower = lower(v); - if (old_lower == 0 || new_lower > old_lower->get_value()) { + if (old_lower == nullptr || new_lower > old_lower->get_value()) { TRACE("non_linear", tout << "NEW lower bound for v" << v << " " << new_lower << "\n"; display_interval(tout, i); tout << "\n";); mk_derived_nl_bound(v, new_lower, B_LOWER, i.get_lower_dependencies()); @@ -494,7 +494,7 @@ namespace smt { } } bound * old_upper = upper(v); - if (old_upper == 0 || new_upper < old_upper->get_value()) { + if (old_upper == nullptr || new_upper < old_upper->get_value()) { TRACE("non_linear", tout << "NEW upper bound for v" << v << " " << new_upper << "\n"; display_interval(tout, i); tout << "\n";); mk_derived_nl_bound(v, new_upper, B_UPPER, i.get_upper_dependencies()); @@ -788,7 +788,7 @@ namespace smt { TRACE("non_linear", tout << "BRANCHING on v" << v << "\n";); m_stats.m_nl_branching++; SASSERT(is_int(v)); - expr * bound = 0; + expr * bound = nullptr; if (lower(v)) bound = m_util.mk_le(var2expr(v), m_util.mk_numeral(lower_bound(v).get_rational().to_rational(), true)); else if (upper(v)) @@ -857,7 +857,7 @@ namespace smt { if (!is_fixed(_var)) return arg; } - return 0; + return nullptr; } /** @@ -882,12 +882,12 @@ namespace smt { numeral k = get_monomial_fixed_var_product(m); TRACE("non_linear", tout << "new linear monomial... k: " << k << "\n";); - expr * x_n = k.is_zero() ? 0 : get_monomial_non_fixed_var(m); + expr * x_n = k.is_zero() ? nullptr : get_monomial_non_fixed_var(m); TRACE("non_linear_bug", if (x_n != 0) { tout << "x_n: " << mk_bounded_pp(x_n, get_manager()) << "\nx_n: #" << x_n->get_id() << "\n"; }); context & ctx = get_context(); - derived_bound * new_lower = 0; - derived_bound * new_upper = 0; - if (x_n != 0) { + derived_bound * new_lower = nullptr; + derived_bound * new_upper = nullptr; + if (x_n != nullptr) { // All but one of the x_i variables are assigned. // Let x_n be the unassigned variable. // Then, we know that x_1*...*x_n = k*x_n, where k is the product of beta(x_1)*...*beta(x_{n-1}) @@ -1267,7 +1267,7 @@ namespace smt { expr * var = ce.second; if (!c.is_one()) { rational c2; - expr * m = 0; + expr * m = nullptr; if (m_util.is_numeral(var, c2)) m = m_util.mk_numeral(c*c2, m_util.is_int(var) && c.is_int() && c2.is_int()); else @@ -1492,7 +1492,7 @@ namespace smt { r.push_back(coeff_expr(kv.first, f)); } } - expr * s = cross_nested(e, 0); + expr * s = cross_nested(e, nullptr); if (!r.empty()) { expr * q = horner(r, var); // TODO: improve here @@ -1519,7 +1519,7 @@ namespace smt { template expr * theory_arith::cross_nested(sbuffer & p, expr * var) { TRACE("non_linear", tout << "p.size: " << p.size() << "\n";); - if (var == 0) { + if (var == nullptr) { sbuffer varinfo; get_polynomial_info(p, varinfo); if (varinfo.empty()) @@ -1556,7 +1556,7 @@ namespace smt { tout << "b: " << b << "\n"; tout << "nm: " << nm << "\n";); SASSERT(n != nm); - expr * new_expr = 0; + expr * new_expr = nullptr; if (nm < n) { std::swap(n, nm); std::swap(a, b); @@ -1576,7 +1576,7 @@ namespace smt { expr * xm_a2b = m_util.mk_add(m_util.mk_numeral(a2b, m_util.is_int(var)), xm); expr * xm_a2b2 = m_util.mk_mul(xm_a2b, xm_a2b); expr * rhs = m_util.mk_add(xm_a2b2, m_util.mk_numeral(ma2b2, m_util.is_int(var))); - expr * rhs2 = 0; + expr * rhs2 = nullptr; if (n > m) rhs2 = m_util.mk_mul(power(var, n - m), rhs); else @@ -1593,7 +1593,7 @@ namespace smt { if (rest.empty()) return new_expr; TRACE("non_linear", tout << "rest size: " << rest.size() << ", i1: " << i1 << ", i2: " << i2 << "\n";); - expr * h = cross_nested(rest, 0); + expr * h = cross_nested(rest, nullptr); expr * r = m_util.mk_add(new_expr, h); m_nl_new_exprs.push_back(r); return r; @@ -1631,7 +1631,7 @@ namespace smt { tout << "c:\n" << mk_ismt2_pp(cn, get_manager()) << "\n";); interval i = evaluate_as_interval(cn); TRACE("cross_nested", tout << "interval: " << i << "\n";); - v_dependency * d = 0; + v_dependency * d = nullptr; if (!i.minus_infinity() && (i.get_lower_value().is_pos() || (i.get_lower_value().is_zero() && i.is_lower_open()))) d = i.get_lower_dependencies(); else if (!i.plus_infinity() && (i.get_upper_value().is_neg() || (i.get_upper_value().is_zero() && i.is_upper_open()))) @@ -1823,7 +1823,7 @@ namespace smt { if (!coeff.is_zero()) return gb.mk_monomial(coeff, vars.size(), vars.c_ptr()); else - return 0; + return nullptr; } /** @@ -1834,7 +1834,7 @@ namespace smt { void theory_arith::add_row_to_gb(row const & r, grobner & gb) { TRACE("non_linear", tout << "adding row to gb\n"; display_row(tout, r);); ptr_buffer monomials; - v_dependency * dep = 0; + v_dependency * dep = nullptr; m_tmp_var_set.reset(); typename vector::const_iterator it = r.begin_entries(); typename vector::const_iterator end = r.end_entries(); @@ -1860,7 +1860,7 @@ namespace smt { template void theory_arith::add_monomial_def_to_gb(theory_var v, grobner & gb) { ptr_buffer monomials; - v_dependency * dep = 0; + v_dependency * dep = nullptr; m_tmp_var_set.reset(); expr * m = var2expr(v); SASSERT(is_pure_monomial(m)); @@ -1872,7 +1872,7 @@ namespace smt { dep = m_dep_manager.mk_join(dep, m_dep_manager.mk_join(m_dep_manager.mk_leaf(lower(v)), m_dep_manager.mk_leaf(upper(v)))); coeff *= lower_bound(v).get_rational().to_rational(); if (!coeff.is_zero()) - monomials.push_back(gb.mk_monomial(coeff, 0, 0)); + monomials.push_back(gb.mk_monomial(coeff, 0, nullptr)); } else { monomials.push_back(gb.mk_monomial(coeff, 1, &m)); @@ -1904,12 +1904,12 @@ namespace smt { template interval theory_arith::mk_interval_for(grobner::monomial const * m) { interval r(m_dep_manager, rational(m->get_coeff())); - expr * var = 0; + expr * var = nullptr; unsigned power = 0; unsigned num_vars = m->get_degree(); for (unsigned i = 0; i < num_vars; i++) { expr * curr = m->get_var(i); - if (var == 0) { + if (var == nullptr) { var = curr; power = 1; } @@ -1922,7 +1922,7 @@ namespace smt { power = 1; } } - if (var != 0) + if (var != nullptr) mul_bound_of(var, power, r); return r; } @@ -1957,7 +1957,7 @@ namespace smt { return false; } TRACE("non_linear_bug", tout << "is_inconsistent, r: " << r << "\n";); - v_dependency * interval_deps = 0; + v_dependency * interval_deps = nullptr; bool conflict = false; if (!r.minus_infinity() && (r.get_lower_value().is_pos() || (r.get_lower_value().is_zero() && r.is_lower_open()))) { interval_deps = r.get_lower_dependencies(); @@ -2002,11 +2002,11 @@ namespace smt { return false; if (!m->get_coeff().is_perfect_square(r)) return false; - expr * var = 0; + expr * var = nullptr; unsigned power = 0; for (unsigned i = 0; i < num_vars; i++) { expr * curr = m->get_var(i); - if (var == 0) { + if (var == nullptr) { var = curr; power = 1; } @@ -2050,18 +2050,18 @@ namespace smt { unsigned i1, i2, i12; i1 = i2 = i12 = 0; while (true) { - expr * v1 = 0; - expr * v2 = 0; - expr * v12 = 0; + expr * v1 = nullptr; + expr * v2 = nullptr; + expr * v12 = nullptr; if (i1 < num1) v1 = m1_sq->get_var(i1); if (i2 < num2) v2 = m2_sq->get_var(i2); if (i12 < num12) v12 = m1m2->get_var(i12); - if (v1 == 0 && v2 == 0 && v12 == 0) + if (v1 == nullptr && v2 == nullptr && v12 == nullptr) return true; - if (v12 == 0) + if (v12 == nullptr) return false; if (v1 == v12) { SASSERT(m1_sq->get_var(i1+1) == v1); @@ -2162,7 +2162,7 @@ namespace smt { } if (monomials.size() == num) return false; // didn't find any perfect square. - interval ge_zero(m_dep_manager, rational(0), false, true, 0); + interval ge_zero(m_dep_manager, rational(0), false, true, nullptr); if (is_inconsistent(ge_zero, monomials.size(), monomials.c_ptr(), eq->get_dependency())) { TRACE("non_linear", tout << "found conflict\n"; gb.display_equation(tout, *eq);); return true; diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index 997698411..a218445f5 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -504,7 +504,7 @@ namespace smt { pp.add_assumption(eq); } else { - if (lower(v) != 0) { + if (lower(v) != nullptr) { inf_numeral k_inf = lower_bound(v); rational k = k_inf.get_rational().to_rational(); expr_ref ineq(m); @@ -514,7 +514,7 @@ namespace smt { ineq = m_util.mk_lt(m_util.mk_numeral(k, is_int(v)), n); pp.add_assumption(ineq); } - if (upper(v) != 0) { + if (upper(v) != nullptr) { inf_numeral k_inf = upper_bound(v); rational k = k_inf.get_rational().to_rational(); expr_ref ineq(m); diff --git a/src/smt/theory_array.h b/src/smt/theory_array.h index 3a013cf73..26d59c7bf 100644 --- a/src/smt/theory_array.h +++ b/src/smt/theory_array.h @@ -55,19 +55,19 @@ namespace smt { th_trail_stack m_trail_stack; unsigned m_final_check_idx; - virtual void init(context * ctx); - virtual theory_var mk_var(enode * n); - virtual bool internalize_atom(app * atom, bool gate_ctx); - virtual bool internalize_term(app * term); - virtual void apply_sort_cnstr(enode * n, sort * s); - virtual void new_eq_eh(theory_var v1, theory_var v2); - virtual void new_diseq_eh(theory_var v1, theory_var v2); - virtual void relevant_eh(app * n); - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); - virtual final_check_status final_check_eh(); - virtual void reset_eh(); - virtual void init_search_eh() { m_final_check_idx = 0; } + void init(context * ctx) override; + theory_var mk_var(enode * n) override; + bool internalize_atom(app * atom, bool gate_ctx) override; + bool internalize_term(app * term) override; + void apply_sort_cnstr(enode * n, sort * s) override; + void new_eq_eh(theory_var v1, theory_var v2) override; + void new_diseq_eh(theory_var v1, theory_var v2) override; + void relevant_eh(app * n) override; + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; + final_check_status final_check_eh() override; + void reset_eh() override; + void init_search_eh() override { m_final_check_idx = 0; } virtual void set_prop_upward(theory_var v); virtual void set_prop_upward(enode* n); @@ -96,15 +96,15 @@ namespace smt { static void display_ids(std::ostream & out, unsigned n, enode * const * v); public: theory_array(ast_manager & m, theory_array_params & params); - virtual ~theory_array(); + ~theory_array() override; - virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_array, new_ctx->get_manager(), new_ctx->get_fparams()); } + theory * mk_fresh(context * new_ctx) override { return alloc(theory_array, new_ctx->get_manager(), new_ctx->get_fparams()); } - virtual char const * get_name() const { return "array"; } + char const * get_name() const override { return "array"; } virtual void display_var(std::ostream & out, theory_var v) const; - virtual void display(std::ostream & out) const; - virtual void collect_statistics(::statistics & st) const; + void display(std::ostream & out) const override; + void collect_statistics(::statistics & st) const override; th_trail_stack & get_trail_stack() { return m_trail_stack; } virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var); static void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {} diff --git a/src/smt/theory_array_base.cpp b/src/smt/theory_array_base.cpp index 472053fdc..a0e9fad2b 100644 --- a/src/smt/theory_array_base.cpp +++ b/src/smt/theory_array_base.cpp @@ -41,7 +41,7 @@ namespace smt { } app * theory_array_base::mk_select(unsigned num_args, expr * const * args) { - app * r = get_manager().mk_app(get_family_id(), OP_SELECT, 0, 0, num_args, args); + app * r = get_manager().mk_app(get_family_id(), OP_SELECT, 0, nullptr, num_args, args); TRACE("mk_var_bug", tout << "mk_select: " << r->get_id() << " num_args: " << num_args; for (unsigned i = 0; i < num_args; i++) tout << " " << args[i]->get_id(); tout << "\n";); @@ -49,7 +49,7 @@ namespace smt { } app * theory_array_base::mk_store(unsigned num_args, expr * const * args) { - return get_manager().mk_app(get_family_id(), OP_STORE, 0, 0, num_args, args); + return get_manager().mk_app(get_family_id(), OP_STORE, 0, nullptr, num_args, args); } app * theory_array_base::mk_default(expr * a) { @@ -152,7 +152,7 @@ namespace smt { expr_ref sel1(m), sel2(m); bool init = false; literal conseq = null_literal; - expr * conseq_expr = 0; + expr * conseq_expr = nullptr; for (unsigned i = 0; i < num_args; i++) { enode * idx1 = js[i]; @@ -212,7 +212,7 @@ namespace smt { func_decl_ref_vector * theory_array_base::register_sort(sort * s_array) { unsigned dimension = get_dimension(s_array); - func_decl_ref_vector * ext_skolems = 0; + func_decl_ref_vector * ext_skolems = nullptr; if (!m_sort2skolem.find(s_array, ext_skolems)) { array_util util(get_manager()); ast_manager & m = get_manager(); @@ -305,7 +305,7 @@ namespace smt { context & ctx = get_context(); ast_manager & m = get_manager(); - func_decl_ref_vector * funcs = 0; + func_decl_ref_vector * funcs = nullptr; sort * s = m.get_sort(e1); VERIFY(m_sort2skolem.find(s, funcs)); @@ -495,7 +495,7 @@ namespace smt { void theory_array_base::restore_sorts(unsigned old_size) { while (m_sorts_trail.size() > old_size) { sort * s = m_sorts_trail.back(); - func_decl_ref_vector * funcs = 0; + func_decl_ref_vector * funcs = nullptr; if (m_sort2skolem.find(s, funcs)) { m_sort2skolem.remove(s); dealloc(funcs); @@ -672,9 +672,9 @@ namespace smt { theory_array_base::select_set * theory_array_base::get_select_set(enode * n) { enode * r = n->get_root(); - select_set * set = 0; + select_set * set = nullptr; m_selects.find(r, set); - if (set == 0) { + if (set == nullptr) { set = alloc(select_set); m_selects.insert(r, set); m_selects_domain.push_back(r); @@ -795,7 +795,7 @@ namespace smt { m_sort(s), m_num_entries(0), m_dim(0), - m_else(0), + m_else(nullptr), m_unspecified_else(false) { m_dependencies.push_back(model_value_dependency(v)); } @@ -814,7 +814,7 @@ namespace smt { m_sort(s), m_num_entries(0), m_dim(0), - m_else(0), + m_else(nullptr), m_unspecified_else(false) { m_dependencies.push_back(model_value_dependency(else_value)); } @@ -824,11 +824,11 @@ namespace smt { m_sort(s), m_num_entries(0), m_dim(0), - m_else(0), + m_else(nullptr), m_unspecified_else(true) { } - virtual ~array_value_proc() {} + ~array_value_proc() override {} void add_entry(unsigned num_args, enode * const * args, enode * value) { SASSERT(num_args > 0); @@ -840,11 +840,11 @@ namespace smt { m_dependencies.push_back(model_value_dependency(value)); } - virtual void get_dependencies(buffer & result) { + void get_dependencies(buffer & result) override { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } - virtual app * mk_value(model_generator & mg, ptr_vector & values) { + app * mk_value(model_generator & mg, ptr_vector & values) override { // values must have size = m_num_entries * (m_dim + 1) + ((m_else || m_unspecified_else) ? 0 : 1) // an array value is a lookup table + else_value // each entry has m_dim indexes that map to a value. @@ -888,14 +888,14 @@ namespace smt { SASSERT(v != null_theory_var); sort * s = get_manager().get_sort(n->get_owner()); enode * else_val_n = get_default(v); - array_value_proc * result = 0; + array_value_proc * result = nullptr; if (m_use_unspecified_default) { SASSERT(else_val_n == 0); result = alloc(array_value_proc, get_id(), s); } else { - if (else_val_n != 0) { + if (else_val_n != nullptr) { SASSERT(get_context().is_relevant(else_val_n)); result = alloc(array_value_proc, get_id(), s, else_val_n); } @@ -905,7 +905,7 @@ namespace smt { // DISABLED. It seems wrong, since different nodes can share the same // else_val according to the mg class. // SASSERT(else_val == 0 || get_context().is_relevant(UNTAG(app*, else_val))); - if (else_val == 0) { + if (else_val == nullptr) { sort * range = to_sort(s->get_parameter(s->get_num_parameters() - 1).get_ast()); // IMPORTANT: // The implementation should not assume a fresh value is created for @@ -925,9 +925,9 @@ namespace smt { } } SASSERT(result != 0); - select_set * sel_set = 0; + select_set * sel_set = nullptr; m_selects.find(n->get_root(), sel_set); - if (sel_set != 0) { + if (sel_set != nullptr) { ptr_buffer args; select_set::iterator it = sel_set->begin(); select_set::iterator end = sel_set->end(); diff --git a/src/smt/theory_array_base.h b/src/smt/theory_array_base.h index 197ad9d2f..730c02f94 100644 --- a/src/smt/theory_array_base.h +++ b/src/smt/theory_array_base.h @@ -123,15 +123,15 @@ namespace smt { // // // -------------------------------------------------- - virtual bool is_shared(theory_var v) const; + bool is_shared(theory_var v) const override; void collect_shared_vars(sbuffer & result); unsigned mk_interface_eqs(); - virtual bool can_propagate(); - virtual void propagate(); - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); - virtual void reset_eh(); + bool can_propagate() override; + void propagate() override; + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; + void reset_eh() override; void reset_queues(); // ----------------------------------- @@ -177,7 +177,7 @@ namespace smt { void set_default(theory_var v, enode* n); enode* get_default(theory_var v); - virtual void init_model(model_generator & m); + void init_model(model_generator & m) override; bool is_unspecified_default_ok() const; void collect_defaults(); void collect_selects(); @@ -185,12 +185,12 @@ namespace smt { void propagate_selects_to_store_parents(enode * r, enode_pair_vector & todo); void propagate_selects(); select_set * get_select_set(enode * n); - virtual void finalize_model(model_generator & m); - virtual model_value_proc * mk_value(enode * n, model_generator & m); + void finalize_model(model_generator & m) override; + model_value_proc * mk_value(enode * n, model_generator & m) override; public: theory_array_base(ast_manager & m); - virtual ~theory_array_base() { restore_sorts(0); } + ~theory_array_base() override { restore_sorts(0); } }; }; diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 274f89e8b..048e3f581 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -746,7 +746,7 @@ namespace smt { } app* theory_array_full::mk_epsilon(sort* s) { - app* eps = 0; + app* eps = nullptr; if (m_sort2epsilon.find(s, eps)) { return eps; } diff --git a/src/smt/theory_array_full.h b/src/smt/theory_array_full.h index 2cad3acbd..3216b4286 100644 --- a/src/smt/theory_array_full.h +++ b/src/smt/theory_array_full.h @@ -42,29 +42,29 @@ namespace smt { protected: //virtual final_check_status final_check_eh(); - virtual void reset_eh(); + void reset_eh() override; - virtual void set_prop_upward(theory_var v); - virtual void set_prop_upward(enode* n); - virtual void set_prop_upward(theory_var v, var_data* d); - virtual unsigned get_lambda_equiv_size(theory_var v, var_data* d); + void set_prop_upward(theory_var v) override; + void set_prop_upward(enode* n) override; + void set_prop_upward(theory_var v, var_data* d) override; + unsigned get_lambda_equiv_size(theory_var v, var_data* d) override; - virtual bool internalize_term(app * term); - virtual bool internalize_atom(app * atom, bool gate_ctx); - virtual void pop_scope_eh(unsigned num_scopes); - virtual theory_var mk_var(enode * n); - virtual void relevant_eh(app * n); + bool internalize_term(app * term) override; + bool internalize_atom(app * atom, bool gate_ctx) override; + void pop_scope_eh(unsigned num_scopes) override; + theory_var mk_var(enode * n) override; + void relevant_eh(app * n) override; void add_const(theory_var v, enode* c); void add_map(theory_var v, enode* s); void add_parent_map(theory_var v, enode* s); void add_as_array(theory_var v, enode* arr); - virtual void add_parent_select(theory_var v, enode * s); + void add_parent_select(theory_var v, enode * s) override; void add_parent_default(theory_var v); - virtual final_check_status assert_delayed_axioms(); + final_check_status assert_delayed_axioms() override; bool instantiate_default_const_axiom(enode* cnst); bool instantiate_default_store_axiom(enode* store); @@ -87,14 +87,14 @@ namespace smt { public: theory_array_full(ast_manager & m, theory_array_params & params); - virtual ~theory_array_full(); + ~theory_array_full() override; - virtual theory * mk_fresh(context * new_ctx); + theory * mk_fresh(context * new_ctx) override; - virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var); - virtual void display_var(std::ostream & out, theory_var v) const; - virtual void collect_statistics(::statistics & st) const; - virtual void init(context* ctx) { + void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) override; + void display_var(std::ostream & out, theory_var v) const override; + void collect_statistics(::statistics & st) const override; + void init(context* ctx) override { // the parent class is theory_array. // theory::init(ctx); theory_array::init(ctx); diff --git a/src/smt/theory_bv.cpp b/src/smt/theory_bv.cpp index ec3913415..873d1692d 100644 --- a/src/smt/theory_bv.cpp +++ b/src/smt/theory_bv.cpp @@ -65,7 +65,7 @@ namespace smt { bool_var m_var; public: mk_atom_trail(bool_var v):m_var(v) {} - virtual void undo(theory_bv & th) { + void undo(theory_bv & th) override { theory_bv::atom * a = th.get_bv2a(m_var); a->~atom(); th.erase_bv2a(m_var); @@ -213,7 +213,7 @@ namespace smt { theory_bv::bit_atom * m_atom; public: add_var_pos_trail(theory_bv::bit_atom * a):m_atom(a) {} - virtual void undo(theory_bv & th) { + void undo(theory_bv & th) override { SASSERT(m_atom->m_occs); m_atom->m_occs = m_atom->m_occs->m_next; } @@ -373,7 +373,7 @@ namespace smt { void get_proof(conflict_resolution & cr, literal l, ptr_buffer & prs, bool & visited) { if (l.var() == true_bool_var) return; - proof * pr = 0; + proof * pr = nullptr; if (cr.get_context().get_assignment(l) == l_true) pr = cr.get_proof(l); else @@ -389,12 +389,12 @@ namespace smt { m_th(th), m_var1(v1), m_var2(v2) { } - virtual void get_antecedents(conflict_resolution & cr) { + void get_antecedents(conflict_resolution & cr) override { mark_bits(cr, m_th.m_bits[m_var1]); mark_bits(cr, m_th.m_bits[m_var2]); } - virtual proof * mk_proof(conflict_resolution & cr) { + proof * mk_proof(conflict_resolution & cr) override { ptr_buffer prs; context & ctx = cr.get_context(); bool visited = true; @@ -408,17 +408,17 @@ namespace smt { get_proof(cr, *it2, prs, visited); } if (!visited) - return 0; + return nullptr; expr * fact = ctx.mk_eq_atom(m_th.get_enode(m_var1)->get_owner(), m_th.get_enode(m_var2)->get_owner()); ast_manager & m = ctx.get_manager(); return m.mk_th_lemma(get_from_theory(), fact, prs.size(), prs.c_ptr()); } - virtual theory_id get_from_theory() const { + theory_id get_from_theory() const override { return m_th.get_id(); } - virtual char const * get_name() const { return "bv-fixed-eq"; } + char const * get_name() const override { return "bv-fixed-eq"; } }; @@ -1510,13 +1510,13 @@ namespace smt { bit_eq_justification(theory_id th_id, enode * v1, enode * v2, literal c, literal a): m_v1(v1), m_v2(v2), m_th_id(th_id), m_consequent(c), m_antecedent(a) {} - virtual void get_antecedents(conflict_resolution & cr) { + void get_antecedents(conflict_resolution & cr) override { cr.mark_eq(m_v1, m_v2); if (m_antecedent.var() != true_bool_var) cr.mark_literal(m_antecedent); } - virtual proof * mk_proof(conflict_resolution & cr) { + proof * mk_proof(conflict_resolution & cr) override { bool visited = true; ptr_buffer prs; proof * pr = cr.get_proof(m_v1, m_v2); @@ -1532,7 +1532,7 @@ namespace smt { visited = false; } if (!visited) - return 0; + return nullptr; context & ctx = cr.get_context(); ast_manager & m = cr.get_manager(); expr_ref fact(m); @@ -1540,11 +1540,11 @@ namespace smt { return m.mk_th_lemma(get_from_theory(), fact, prs.size(), prs.c_ptr()); } - virtual theory_id get_from_theory() const { + theory_id get_from_theory() const override { return m_th_id; } - virtual char const * get_name() const { return "bv-bit-eq"; } + char const * get_name() const override { return "bv-bit-eq"; } }; inline justification * theory_bv::mk_bit_eq_justification(theory_var v1, theory_var v2, literal consequent, literal antecedent) { diff --git a/src/smt/theory_bv.h b/src/smt/theory_bv.h index c35078ace..f2e0f5bed 100644 --- a/src/smt/theory_bv.h +++ b/src/smt/theory_bv.h @@ -52,22 +52,22 @@ namespace smt { theory_var m_var; unsigned m_idx; var_pos_occ * m_next; - var_pos_occ(theory_var v = null_theory_var, unsigned idx = 0, var_pos_occ * next = 0):m_var(v), m_idx(idx), m_next(next) {} + var_pos_occ(theory_var v = null_theory_var, unsigned idx = 0, var_pos_occ * next = nullptr):m_var(v), m_idx(idx), m_next(next) {} }; struct bit_atom : public atom { var_pos_occ * m_occs; - bit_atom():m_occs(0) {} - virtual ~bit_atom() {} - virtual bool is_bit() const { return true; } + bit_atom():m_occs(nullptr) {} + ~bit_atom() override {} + bool is_bit() const override { return true; } }; struct le_atom : public atom { literal m_var; literal m_def; le_atom(literal v, literal d):m_var(v), m_def(d) {} - virtual ~le_atom() {} - virtual bool is_bit() const { return false; } + ~le_atom() override {} + bool is_bit() const override { return false; } }; /** @@ -216,21 +216,21 @@ namespace smt { void assert_bv2int_axiom(app* n); protected: - virtual void init(context * ctx); - virtual theory_var mk_var(enode * n); - virtual bool internalize_atom(app * atom, bool gate_ctx); - virtual bool internalize_term(app * term); - virtual void apply_sort_cnstr(enode * n, sort * s); - virtual void new_eq_eh(theory_var v1, theory_var v2); - virtual void new_diseq_eh(theory_var v1, theory_var v2); + void init(context * ctx) override; + theory_var mk_var(enode * n) override; + bool internalize_atom(app * atom, bool gate_ctx) override; + bool internalize_term(app * term) override; + void apply_sort_cnstr(enode * n, sort * s) override; + void new_eq_eh(theory_var v1, theory_var v2) override; + void new_diseq_eh(theory_var v1, theory_var v2) override; virtual void expand_diseq(theory_var v1, theory_var v2); - virtual void assign_eh(bool_var v, bool is_true); - virtual void relevant_eh(app * n); - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); - virtual final_check_status final_check_eh(); - virtual void reset_eh(); - virtual bool include_func_interp(func_decl* f); + void assign_eh(bool_var v, bool is_true) override; + void relevant_eh(app * n) override; + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; + final_check_status final_check_eh() override; + void reset_eh() override; + bool include_func_interp(func_decl* f) override; svector m_merge_aux[2]; //!< auxiliary vector used in merge_zero_one_bits bool merge_zero_one_bits(theory_var r1, theory_var r2); @@ -240,16 +240,16 @@ namespace smt { // // ----------------------------------- bv_factory * m_factory; - virtual void init_model(model_generator & m); - virtual model_value_proc * mk_value(enode * n, model_generator & mg); + void init_model(model_generator & m) override; + model_value_proc * mk_value(enode * n, model_generator & mg) override; public: theory_bv(ast_manager & m, theory_bv_params const & params, bit_blaster_params const & bb_params); - virtual ~theory_bv(); + ~theory_bv() override; - virtual theory * mk_fresh(context * new_ctx); + theory * mk_fresh(context * new_ctx) override; - virtual char const * get_name() const { return "bit-vector"; } + char const * get_name() const override { return "bit-vector"; } th_trail_stack & get_trail_stack() { return m_trail_stack; } void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2); @@ -259,8 +259,8 @@ namespace smt { void display_var(std::ostream & out, theory_var v) const; void display_bit_atom(std::ostream & out, bool_var v, bit_atom const * a) const; void display_atoms(std::ostream & out) const; - virtual void display(std::ostream & out) const; - virtual void collect_statistics(::statistics & st) const; + void display(std::ostream & out) const override; + void collect_statistics(::statistics & st) const override; bool get_fixed_value(app* x, numeral & result) const; diff --git a/src/smt/theory_datatype.cpp b/src/smt/theory_datatype.cpp index bbed6840d..049555297 100644 --- a/src/smt/theory_datatype.cpp +++ b/src/smt/theory_datatype.cpp @@ -30,12 +30,47 @@ namespace smt { class dt_eq_justification : public ext_theory_eq_propagation_justification { public: dt_eq_justification(family_id fid, region & r, literal antecedent, enode * lhs, enode * rhs): - ext_theory_eq_propagation_justification(fid, r, 1, &antecedent, 0, 0, lhs, rhs) { + ext_theory_eq_propagation_justification(fid, r, 1, &antecedent, 0, nullptr, lhs, rhs) { } // Remark: the assignment must be propagated back to the datatype theory. - virtual theory_id get_from_theory() const { return null_theory_id; } + theory_id get_from_theory() const override { return null_theory_id; } }; + theory_datatype::final_check_st::final_check_st(theory_datatype * th) : th(th) { + SASSERT(th->m_to_unmark.empty()); + SASSERT(th->m_to_unmark2.empty()); + th->m_used_eqs.reset(); + th->m_stack.reset(); + th->m_parent.reset(); + } + + theory_datatype::final_check_st::~final_check_st() { + unmark_enodes(th->m_to_unmark.size(), th->m_to_unmark.c_ptr()); + unmark_enodes2(th->m_to_unmark2.size(), th->m_to_unmark2.c_ptr()); + th->m_to_unmark.reset(); + th->m_to_unmark2.reset(); + th->m_used_eqs.reset(); + th->m_stack.reset(); + th->m_parent.reset(); + } + + void theory_datatype::oc_mark_on_stack(enode * n) { + n = n->get_root(); + n->set_mark(); + m_to_unmark.push_back(n); + } + + void theory_datatype::oc_mark_cycle_free(enode * n) { + n = n->get_root(); + n->set_mark2(); + m_to_unmark2.push_back(n); + } + + void theory_datatype::oc_push_stack(enode * n) { + m_stack.push_back(std::make_pair(EXIT, n)); + m_stack.push_back(std::make_pair(ENTER, n)); + } + theory* theory_datatype::mk_fresh(context* new_ctx) { return alloc(theory_datatype, new_ctx->get_manager(), m_params); @@ -167,7 +202,7 @@ namespace smt { func_decl * upd = n->get_decl(); func_decl * acc = to_func_decl(upd->get_parameter(0).get_ast()); func_decl * con = m_util.get_accessor_constructor(acc); - func_decl * rec = m_util.get_constructor_recognizer(con); + func_decl * rec = m_util.get_constructor_is(con); ptr_vector const & accessors = *m_util.get_constructor_accessors(con); app_ref rec_app(m.mk_app(rec, arg1), m); ctx.internalize(rec_app, false); @@ -340,12 +375,12 @@ namespace smt { func_decl * c = m_util.get_recognizer_constructor(r); if (is_true) { SASSERT(tv != null_theory_var); - if (d->m_constructor != 0 && d->m_constructor->get_decl() == c) + if (d->m_constructor != nullptr && d->m_constructor->get_decl() == c) return; // do nothing assert_is_constructor_axiom(arg, c, literal(v)); } else { - if (d->m_constructor != 0) { + if (d->m_constructor != nullptr) { if (d->m_constructor->get_decl() == c) { // conflict sign_recognizer_conflict(d->m_constructor, n); @@ -389,10 +424,11 @@ namespace smt { final_check_status theory_datatype::final_check_eh() { int num_vars = get_num_vars(); final_check_status r = FC_DONE; + final_check_st _guard(this); // RAII for managing state for (int v = 0; v < num_vars; v++) { if (v == static_cast(m_find.find(v))) { enode * node = get_enode(v); - if (occurs_check(node)) { + if (!oc_cycle_free(node) && occurs_check(node)) { // conflict was detected... // return... return FC_CONTINUE; @@ -400,7 +436,7 @@ namespace smt { if (m_params.m_dt_lazy_splits > 0) { // using lazy case splits... var_data * d = m_var_data[v]; - if (d->m_constructor == 0) { + if (d->m_constructor == nullptr) { mk_split(v); r = FC_CONTINUE; } @@ -410,6 +446,73 @@ namespace smt { return r; } + // Assuming `app` is equal to a constructor term, return the constructor enode + inline enode * theory_datatype::oc_get_cstor(enode * app) { + theory_var v = app->get_root()->get_th_var(get_id()); + SASSERT(v != null_theory_var); + v = m_find.find(v); + var_data * d = m_var_data[v]; + SASSERT(d->m_constructor); + return d->m_constructor; + } + + // explain the cycle root -> ... -> app -> root + void theory_datatype::occurs_check_explain(enode * app, enode * root) { + TRACE("datatype", tout << "occurs_check_explain " << mk_bounded_pp(app->get_owner(), get_manager()) << " <-> " << mk_bounded_pp(root->get_owner(), get_manager()) << "\n";); + enode* app_parent = nullptr; + + // first: explain that root=v, given that app=cstor(...,v,...) + for (enode * arg : enode::args(oc_get_cstor(app))) { + // found an argument which is equal to root + if (arg->get_root() == root->get_root()) { + if (arg != root) + m_used_eqs.push_back(enode_pair(arg, root)); + break; + } + } + + // now explain app=cstor(..,v,..) where v=root, and recurse with parent of app + while (app->get_root() != root->get_root()) { + enode * app_cstor = oc_get_cstor(app); + if (app != app_cstor) + m_used_eqs.push_back(enode_pair(app, app_cstor)); + app_parent = m_parent[app->get_root()]; + app = app_parent; + } + + SASSERT(app->get_root() == root->get_root()); + if (app != root) + m_used_eqs.push_back(enode_pair(app, root)); + } + + // start exploring subgraph below `app` + bool theory_datatype::occurs_check_enter(enode * app) { + oc_mark_on_stack(app); + theory_var v = app->get_root()->get_th_var(get_id()); + if (v != null_theory_var) { + v = m_find.find(v); + var_data * d = m_var_data[v]; + if (d->m_constructor) { + for (enode * arg : enode::args(d->m_constructor)) { + if (oc_cycle_free(arg)) { + continue; + } + if (oc_on_stack(arg)) { + // arg was explored before app, and is still on the stack: cycle + occurs_check_explain(app, arg); + return true; + } + // explore `arg` (with parent `app`) + if (m_util.is_datatype(get_manager().get_sort(arg->get_owner()))) { + m_parent.insert(arg->get_root(), app); + oc_push_stack(arg); + } + } + } + } + return false; + } + /** \brief Check if n can be reached starting from n and following equalities and constructors. For example, occur_check(a1) returns true in the following set of equalities: @@ -418,17 +521,39 @@ namespace smt { a3 = cons(v3, a1) */ bool theory_datatype::occurs_check(enode * n) { - TRACE("datatype", tout << "occurs check: #" << n->get_owner_id() << "\n";); - m_to_unmark.reset(); - m_used_eqs.reset(); - m_main = n; - bool res = occurs_check_core(m_main); - unmark_enodes(m_to_unmark.size(), m_to_unmark.c_ptr()); + TRACE("datatype", tout << "occurs check: #" << n->get_owner_id() << " " << mk_bounded_pp(n->get_owner(), get_manager()) << "\n";); + m_stats.m_occurs_check++; + + bool res = false; + oc_push_stack(n); + + // DFS traversal from `n`. Look at top element and explore it. + while (!res && !m_stack.empty()) { + stack_op op = m_stack.back().first; + enode * app = m_stack.back().second; + m_stack.pop_back(); + + if (oc_cycle_free(app)) continue; + + TRACE("datatype", tout << "occurs check loop: #" << app->get_owner_id() << " " << mk_bounded_pp(app->get_owner(), get_manager()) << (op==ENTER?" enter":" exit")<< "\n";); + + switch (op) { + case ENTER: + res = occurs_check_enter(app); + break; + + case EXIT: + oc_mark_cycle_free(app); + break; + } + } + if (res) { + // m_used_eqs should contain conflict context & ctx = get_context(); region & r = ctx.get_region(); - ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, 0, m_used_eqs.size(), m_used_eqs.c_ptr()))); - TRACE("occurs_check", + ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, nullptr, m_used_eqs.size(), m_used_eqs.c_ptr()))); + TRACE("datatype", tout << "occurs_check: true\n"; for (enode_pair const& p : m_used_eqs) { tout << "eq: #" << p.first->get_owner_id() << " #" << p.second->get_owner_id() << "\n"; @@ -437,48 +562,6 @@ namespace smt { } return res; } - - /** - \brief Auxiliary method for occurs_check. - TODO: improve performance. - */ - bool theory_datatype::occurs_check_core(enode * app) { - if (app->is_marked()) - return false; - - m_stats.m_occurs_check++; - app->set_mark(); - m_to_unmark.push_back(app); - - TRACE("datatype", tout << "occurs check_core: #" << app->get_owner_id() << " #" << m_main->get_owner_id() << "\n";); - - theory_var v = app->get_root()->get_th_var(get_id()); - if (v != null_theory_var) { - v = m_find.find(v); - var_data * d = m_var_data[v]; - if (d->m_constructor) { - if (app != d->m_constructor) - m_used_eqs.push_back(enode_pair(app, d->m_constructor)); - unsigned num_args = d->m_constructor->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - enode * arg = d->m_constructor->get_arg(i); - if (arg->get_root() == m_main->get_root()) { - if (arg != m_main) - m_used_eqs.push_back(enode_pair(arg, m_main)); - return true; - } - if (m_util.is_datatype(get_manager().get_sort(arg->get_owner())) && occurs_check_core(arg)) - return true; - } - if (app != d->m_constructor) { - SASSERT(m_used_eqs.back().first == app); - SASSERT(m_used_eqs.back().second == d->m_constructor); - m_used_eqs.pop_back(); - } - } - } - return false; - } void theory_datatype::reset_eh() { m_trail_stack.reset(); @@ -551,11 +634,11 @@ namespace smt { public: datatype_value_proc(func_decl * d):m_constructor(d) {} void add_dependency(enode * n) { m_dependencies.push_back(model_value_dependency(n)); } - virtual ~datatype_value_proc() {} - virtual void get_dependencies(buffer & result) { + ~datatype_value_proc() override {} + void get_dependencies(buffer & result) override { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } - virtual app * mk_value(model_generator & mg, ptr_vector & values) { + app * mk_value(model_generator & mg, ptr_vector & values) override { SASSERT(values.size() == m_dependencies.size()); return mg.get_manager().mk_app(m_constructor, values.size(), values.c_ptr()); } @@ -581,21 +664,21 @@ namespace smt { SASSERT(v1 == static_cast(m_find.find(v1))); var_data * d1 = m_var_data[v1]; var_data * d2 = m_var_data[v2]; - if (d2->m_constructor != 0) { + if (d2->m_constructor != nullptr) { context & ctx = get_context(); - if (d1->m_constructor != 0 && d1->m_constructor->get_decl() != d2->m_constructor->get_decl()) { + if (d1->m_constructor != nullptr && d1->m_constructor->get_decl() != d2->m_constructor->get_decl()) { region & r = ctx.get_region(); enode_pair p(d1->m_constructor, d2->m_constructor); SASSERT(d1->m_constructor->get_root() == d2->m_constructor->get_root()); - ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, 0, 1, &p))); + ctx.set_conflict(ctx.mk_justification(ext_theory_conflict_justification(get_id(), r, 0, nullptr, 1, &p))); } - if (d1->m_constructor == 0) { + if (d1->m_constructor == nullptr) { m_trail_stack.push(set_ptr_trail(d1->m_constructor)); // check whether there is a recognizer in d1 that conflicts with d2->m_constructor; if (!d1->m_recognizers.empty()) { unsigned c_idx = m_util.get_constructor_idx(d2->m_constructor->get_decl()); enode * recognizer = d1->m_recognizers[c_idx]; - if (recognizer != 0 && ctx.get_assignment(recognizer) == l_false) { + if (recognizer != nullptr && ctx.get_assignment(recognizer) == l_false) { sign_recognizer_conflict(d2->m_constructor, recognizer); return; } @@ -634,7 +717,7 @@ namespace smt { // Otherwise, it will be set when assign_eh is invoked. return; } - if (val == l_false && d->m_constructor != 0) { + if (val == l_false && d->m_constructor != nullptr) { func_decl * c_decl = m_util.get_recognizer_constructor(recognizer->get_decl()); if (d->m_constructor->get_decl() == c_decl) { // conflict @@ -710,7 +793,7 @@ namespace smt { literal consequent; if (!r) { ptr_vector const & constructors = *m_util.get_datatype_constructors(dt); - func_decl * rec = m_util.get_constructor_recognizer(constructors[unassigned_idx]); + func_decl * rec = m_util.get_constructor_is(constructors[unassigned_idx]); app * rec_app = get_manager().mk_app(rec, n->get_owner()); ctx.internalize(rec_app, false); consequent = literal(ctx.get_bool_var(rec_app)); @@ -747,16 +830,16 @@ namespace smt { unsigned non_rec_idx = m_util.get_constructor_idx(non_rec_c); var_data * d = m_var_data[v]; SASSERT(d->m_constructor == 0); - func_decl * r = 0; + func_decl * r = nullptr; m_stats.m_splits++; if (d->m_recognizers.empty()) { - r = m_util.get_constructor_recognizer(non_rec_c); + r = m_util.get_constructor_is(non_rec_c); } else { enode * recognizer = d->m_recognizers[non_rec_idx]; - if (recognizer == 0) { - r = m_util.get_constructor_recognizer(non_rec_c); + if (recognizer == nullptr) { + r = m_util.get_constructor_is(non_rec_c); } else if (!ctx.is_relevant(recognizer)) { ctx.mark_as_relevant(recognizer); @@ -773,10 +856,10 @@ namespace smt { ptr_vector::const_iterator end = d->m_recognizers.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { enode * curr = *it; - if (curr == 0) { + if (curr == nullptr) { ptr_vector const & constructors = *m_util.get_datatype_constructors(s); // found empty slot... - r = m_util.get_constructor_recognizer(constructors[idx]); + r = m_util.get_constructor_is(constructors[idx]); break; } else if (!ctx.is_relevant(curr)) { @@ -787,7 +870,7 @@ namespace smt { return; } } - if (r == 0) + if (r == nullptr) return; // all recognizers are asserted to false... conflict will be detected... } } diff --git a/src/smt/theory_datatype.h b/src/smt/theory_datatype.h index 20fa371fa..010e78cb3 100644 --- a/src/smt/theory_datatype.h +++ b/src/smt/theory_datatype.h @@ -26,7 +26,6 @@ Revision History: #include "smt/proto_model/datatype_factory.h" namespace smt { - class theory_datatype : public theory { typedef trail_stack th_trail_stack; typedef union_find th_union_find; @@ -35,7 +34,7 @@ namespace smt { ptr_vector m_recognizers; //!< recognizers of this equivalence class that are being watched. enode * m_constructor; //!< constructor of this equivalence class, 0 if there is no constructor in the eqc. var_data(): - m_constructor(0) { + m_constructor(nullptr) { } }; @@ -73,46 +72,71 @@ namespace smt { void propagate_recognizer(theory_var v, enode * r); void sign_recognizer_conflict(enode * c, enode * r); - ptr_vector m_to_unmark; - enode_pair_vector m_used_eqs; - enode * m_main; + typedef enum { ENTER, EXIT } stack_op; + typedef map, ptr_eq > parent_tbl; + typedef std::pair stack_entry; + + ptr_vector m_to_unmark; + ptr_vector m_to_unmark2; + enode_pair_vector m_used_eqs; // conflict, if any + parent_tbl m_parent; // parent explanation for occurs_check + svector m_stack; // stack for DFS for occurs_check + + void oc_mark_on_stack(enode * n); + bool oc_on_stack(enode * n) const { return n->get_root()->is_marked(); } + + void oc_mark_cycle_free(enode * n); + bool oc_cycle_free(enode * n) const { return n->get_root()->is_marked2(); } + + void oc_push_stack(enode * n); + + // class for managing state of final_check + class final_check_st { + theory_datatype * th; + public: + final_check_st(theory_datatype * th); + ~final_check_st(); + }; + + enode * oc_get_cstor(enode * n); bool occurs_check(enode * n); - bool occurs_check_core(enode * n); + bool occurs_check_enter(enode * n); + void occurs_check_explain(enode * top, enode * root); void mk_split(theory_var v); void display_var(std::ostream & out, theory_var v) const; protected: - virtual theory_var mk_var(enode * n); - virtual bool internalize_atom(app * atom, bool gate_ctx); - virtual bool internalize_term(app * term); - virtual void apply_sort_cnstr(enode * n, sort * s); - virtual void new_eq_eh(theory_var v1, theory_var v2); - virtual bool use_diseqs() const; - virtual void new_diseq_eh(theory_var v1, theory_var v2); - virtual void assign_eh(bool_var v, bool is_true); - virtual void relevant_eh(app * n); - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); - virtual final_check_status final_check_eh(); - virtual void reset_eh(); - virtual void restart_eh() { m_util.reset(); } - virtual bool is_shared(theory_var v) const; + theory_var mk_var(enode * n) override; + bool internalize_atom(app * atom, bool gate_ctx) override; + bool internalize_term(app * term) override; + void apply_sort_cnstr(enode * n, sort * s) override; + void new_eq_eh(theory_var v1, theory_var v2) override; + bool use_diseqs() const override; + void new_diseq_eh(theory_var v1, theory_var v2) override; + void assign_eh(bool_var v, bool is_true) override; + void relevant_eh(app * n) override; + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; + final_check_status final_check_eh() override; + void reset_eh() override; + void restart_eh() override { m_util.reset(); } + bool is_shared(theory_var v) const override; public: theory_datatype(ast_manager & m, theory_datatype_params & p); - virtual ~theory_datatype(); - virtual theory * mk_fresh(context * new_ctx); - virtual void display(std::ostream & out) const; - virtual void collect_statistics(::statistics & st) const; - virtual void init_model(model_generator & m); - virtual model_value_proc * mk_value(enode * n, model_generator & m); + ~theory_datatype() override; + theory * mk_fresh(context * new_ctx) override; + void display(std::ostream & out) const override; + void collect_statistics(::statistics & st) const override; + void init_model(model_generator & m) override; + model_value_proc * mk_value(enode * n, model_generator & m) override; th_trail_stack & get_trail_stack() { return m_trail_stack; } virtual void merge_eh(theory_var v1, theory_var v2, theory_var, theory_var); static void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {} void unmerge_eh(theory_var v1, theory_var v2); - virtual char const * get_name() const { return "datatype"; } - virtual bool include_func_interp(func_decl* f); + char const * get_name() const override { return "datatype"; } + bool include_func_interp(func_decl* f) override; }; diff --git a/src/smt/theory_dense_diff_logic.h b/src/smt/theory_dense_diff_logic.h index 980830447..b7845a736 100644 --- a/src/smt/theory_dense_diff_logic.h +++ b/src/smt/theory_dense_diff_logic.h @@ -182,7 +182,7 @@ namespace smt { return false; } app * mk_zero_for(expr * n); - theory_var mk_var(enode * n); + theory_var mk_var(enode * n) override; theory_var internalize_term_core(app * n); void found_non_diff_logic_expr(expr * n); bool is_connected(theory_var source, theory_var target) const { return m_matrix[source][target].m_edge_id != null_edge_id; } @@ -214,38 +214,38 @@ namespace smt { // Internalization // // ----------------------------------- - virtual bool internalize_atom(app * n, bool gate_ctx); - virtual bool internalize_term(app * term); - virtual void internalize_eq_eh(app * atom, bool_var v); - virtual void apply_sort_cnstr(enode * n, sort * s); + bool internalize_atom(app * n, bool gate_ctx) override; + bool internalize_term(app * term) override; + void internalize_eq_eh(app * atom, bool_var v) override; + void apply_sort_cnstr(enode * n, sort * s) override; - virtual void assign_eh(bool_var v, bool is_true); - virtual void new_eq_eh(theory_var v1, theory_var v2); - virtual bool use_diseqs() const; - virtual void new_diseq_eh(theory_var v1, theory_var v2); + void assign_eh(bool_var v, bool is_true) override; + void new_eq_eh(theory_var v1, theory_var v2) override; + bool use_diseqs() const override; + void new_diseq_eh(theory_var v1, theory_var v2) override; - virtual void conflict_resolution_eh(app * atom, bool_var v); + void conflict_resolution_eh(app * atom, bool_var v) override; - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; - virtual void restart_eh(); - virtual void init_search_eh(); - virtual final_check_status final_check_eh(); + void restart_eh() override; + void init_search_eh() override; + final_check_status final_check_eh() override; - virtual bool can_propagate(); - virtual void propagate(); + bool can_propagate() override; + void propagate() override; - virtual void flush_eh(); - virtual void reset_eh(); + void flush_eh() override; + void reset_eh() override; bool dump_lemmas() const { return m_params.m_arith_dump_lemmas; } - virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const; + bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override; - virtual void display(std::ostream & out) const; + void display(std::ostream & out) const override; virtual void display_atom(std::ostream & out, atom * a) const; - virtual void collect_statistics(::statistics & st) const; + void collect_statistics(::statistics & st) const override; // ----------------------------------- // @@ -258,8 +258,8 @@ namespace smt { void compute_epsilon(); void fix_zero(); - virtual void init_model(model_generator & m); - virtual model_value_proc * mk_value(enode * n, model_generator & mg); + void init_model(model_generator & m) override; + model_value_proc * mk_value(enode * n, model_generator & mg) override; // ----------------------------------- // @@ -267,11 +267,11 @@ namespace smt { // // ----------------------------------- - virtual inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared); - virtual inf_eps_rational value(theory_var v); - virtual theory_var add_objective(app* term); + inf_eps_rational maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; + inf_eps_rational value(theory_var v) override; + theory_var add_objective(app* term) override; virtual expr_ref mk_gt(theory_var v, inf_eps const& val); - expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val); + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_eps const& val); // ----------------------------------- // @@ -280,16 +280,16 @@ namespace smt { // ----------------------------------- public: theory_dense_diff_logic(ast_manager & m, theory_arith_params & p); - virtual ~theory_dense_diff_logic() { reset_eh(); } + ~theory_dense_diff_logic() override { reset_eh(); } - virtual theory * mk_fresh(context * new_ctx); + theory * mk_fresh(context * new_ctx) override; - virtual char const * get_name() const { return "difference-logic"; } + char const * get_name() const override { return "difference-logic"; } /** \brief See comment in theory::mk_eq_atom */ - virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return m_autil.mk_eq(lhs, rhs); } + app * mk_eq_atom(expr * lhs, expr * rhs) override { return m_autil.mk_eq(lhs, rhs); } }; typedef theory_dense_diff_logic theory_dense_mi; diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index 78fb4d03d..3dfba6b1b 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -867,7 +867,7 @@ namespace smt { } else { context& ctx = get_context(); - enode * e = 0; + enode * e = nullptr; theory_var v = 0; if (ctx.e_internalized(n)) { e = ctx.get_enode(to_app(n)); @@ -1055,7 +1055,7 @@ namespace smt { template expr_ref theory_dense_diff_logic::mk_ge( - filter_model_converter& fm, theory_var v, inf_eps const& val) { + generic_model_converter& fm, theory_var v, inf_eps const& val) { return mk_ineq(v, val, false); } diff --git a/src/smt/theory_diff_logic.h b/src/smt/theory_diff_logic.h index 1ad239e58..6213c36b0 100644 --- a/src/smt/theory_diff_logic.h +++ b/src/smt/theory_diff_logic.h @@ -208,7 +208,7 @@ namespace smt { void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges); // Create a new theory variable. - virtual theory_var mk_var(enode* n); + theory_var mk_var(enode* n) override; virtual theory_var mk_var(app* n); @@ -234,85 +234,85 @@ namespace smt { m_agility(0.5), m_is_lia(true), m_non_diff_logic_exprs(false), - m_factory(0), + m_factory(nullptr), m_nc_functor(*this), m_S(m.limit()), m_num_simplex_edges(0) { } - virtual ~theory_diff_logic() { + ~theory_diff_logic() override { reset_eh(); } - virtual theory * mk_fresh(context * new_ctx); + theory * mk_fresh(context * new_ctx) override; - virtual char const * get_name() const { return "difference-logic"; } + char const * get_name() const override { return "difference-logic"; } /** \brief See comment in theory::mk_eq_atom */ - virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return m_util.mk_eq(lhs, rhs); } + app * mk_eq_atom(expr * lhs, expr * rhs) override { return m_util.mk_eq(lhs, rhs); } - virtual void init(context * ctx); + void init(context * ctx) override; - virtual bool internalize_atom(app * atom, bool gate_ctx); + bool internalize_atom(app * atom, bool gate_ctx) override; - virtual bool internalize_term(app * term); + bool internalize_term(app * term) override; - virtual void internalize_eq_eh(app * atom, bool_var v); + void internalize_eq_eh(app * atom, bool_var v) override; - virtual void assign_eh(bool_var v, bool is_true); + void assign_eh(bool_var v, bool is_true) override; - virtual void new_eq_eh(theory_var v1, theory_var v2); + void new_eq_eh(theory_var v1, theory_var v2) override; - virtual bool use_diseqs() const { return true; } + bool use_diseqs() const override { return true; } - virtual void new_diseq_eh(theory_var v1, theory_var v2); + void new_diseq_eh(theory_var v1, theory_var v2) override; - virtual void push_scope_eh(); + void push_scope_eh() override; - virtual void pop_scope_eh(unsigned num_scopes); + void pop_scope_eh(unsigned num_scopes) override; - virtual void restart_eh() { + void restart_eh() override { m_arith_eq_adapter.restart_eh(); } - virtual void relevant_eh(app* e) {} + void relevant_eh(app* e) override {} - virtual void init_search_eh() { + void init_search_eh() override { m_arith_eq_adapter.init_search_eh(); } - virtual final_check_status final_check_eh(); + final_check_status final_check_eh() override; - virtual bool is_shared(theory_var v) const { + bool is_shared(theory_var v) const override { return false; } - virtual bool can_propagate() { + bool can_propagate() override { return m_asserted_qhead != m_asserted_atoms.size(); } - virtual void propagate(); + void propagate() override; - virtual justification * why_is_diseq(theory_var v1, theory_var v2) { + justification * why_is_diseq(theory_var v1, theory_var v2) override { NOT_IMPLEMENTED_YET(); - return 0; + return nullptr; } // virtual void flush_eh(); - virtual void reset_eh(); + void reset_eh() override; - virtual void init_model(model_generator & m); + void init_model(model_generator & m) override; - virtual model_value_proc * mk_value(enode * n, model_generator & mg); + model_value_proc * mk_value(enode * n, model_generator & mg) override; - virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const; + bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override; - virtual void display(std::ostream & out) const; + void display(std::ostream & out) const override; - virtual void collect_statistics(::statistics & st) const; + void collect_statistics(::statistics & st) const override; // ----------------------------------- @@ -321,10 +321,10 @@ namespace smt { // // ----------------------------------- - virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared); - virtual inf_eps value(theory_var v); - virtual theory_var add_objective(app* term); - expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val); + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_eps const& val); + inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; + inf_eps value(theory_var v) override; + theory_var add_objective(app* term) override; bool internalize_objective(expr * n, rational const& m, rational& r, objective_term & objective); diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index 59f5521c5..48991da8e 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -298,7 +298,7 @@ void theory_diff_logic::internalize_eq_eh(app * atom, bool_var v) { template void theory_diff_logic::assign_eh(bool_var v, bool is_true) { m_stats.m_num_assertions++; - atom * a = 0; + atom * a = nullptr; VERIFY (m_bool_var2atom.find(v, a)); SASSERT(a); SASSERT(get_context().get_assignment(v) != l_undef); @@ -602,7 +602,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges ctx.mark_as_relevant(le.get()); literal lit(ctx.get_literal(le)); bool_var bv = lit.var(); - atom* a = 0; + atom* a = nullptr; m_bool_var2atom.find(bv, a); SASSERT(a); @@ -619,7 +619,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges tout << "\n"; ); - justification * js = 0; + justification * js = nullptr; if (get_manager().proofs_enabled()) { vector params; params.push_back(parameter(symbol("farkas"))); @@ -628,7 +628,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges lits.size(), lits.c_ptr(), params.size(), params.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); if (dump_lemmas()) { symbol logic(m_is_lia ? "QF_LIA" : "QF_LRA"); ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); @@ -692,7 +692,7 @@ void theory_diff_logic::set_neg_cycle_conflict() { ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), - lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); + lits.size(), lits.c_ptr(), 0, nullptr, params.size(), params.c_ptr()))); } @@ -774,7 +774,7 @@ theory_var theory_diff_logic::mk_term(app* n) { template theory_var theory_diff_logic::mk_num(app* n, rational const& r) { theory_var v = null_theory_var; - enode* e = 0; + enode* e = nullptr; context& ctx = get_context(); if (r.is_zero()) { v = get_zero(); @@ -813,7 +813,7 @@ theory_var theory_diff_logic::mk_var(enode* n) { template theory_var theory_diff_logic::mk_var(app* n) { context & ctx = get_context(); - enode* e = 0; + enode* e = nullptr; theory_var v = null_theory_var; if (ctx.e_internalized(n)) { e = ctx.get_enode(n); @@ -1338,7 +1338,7 @@ expr_ref theory_diff_logic::mk_gt(theory_var v, inf_eps const& val) { } template -expr_ref theory_diff_logic::mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val) { +expr_ref theory_diff_logic::mk_ge(generic_model_converter& fm, theory_var v, inf_eps const& val) { return mk_ineq(v, val, false); } diff --git a/src/smt/theory_dl.cpp b/src/smt/theory_dl.cpp index ea02121d9..824bd1d9e 100644 --- a/src/smt/theory_dl.cpp +++ b/src/smt/theory_dl.cpp @@ -51,7 +51,7 @@ namespace smt { m_util(u) {} - virtual app * mk_value_core(unsigned const & val, sort * s) { + app * mk_value_core(unsigned const & val, sort * s) override { return m_util.mk_numeral(val, s); } }; @@ -74,11 +74,11 @@ namespace smt { dl_value_proc(theory_dl& th, smt::enode* n) : m_th(th), m_node(n) {} - virtual void get_dependencies(buffer & result) {} + void get_dependencies(buffer & result) override {} - virtual app * mk_value(smt::model_generator & mg, ptr_vector & ) { + app * mk_value(smt::model_generator & mg, ptr_vector & ) override { smt::context& ctx = m_th.get_context(); - app* result = 0; + app* result = nullptr; expr* n = m_node->get_owner(); sort* s = m_th.m().get_sort(n); func_decl* r, *v; @@ -111,9 +111,9 @@ namespace smt { } - virtual char const * get_name() const { return "datalog"; } + char const * get_name() const override { return "datalog"; } - virtual bool internalize_atom(app * atom, bool gate_ctx) { + bool internalize_atom(app * atom, bool gate_ctx) override { TRACE("theory_dl", tout << mk_pp(atom, m()) << "\n";); context& ctx = get_context(); if (ctx.b_internalized(atom)) { @@ -136,7 +136,7 @@ namespace smt { return false; } - virtual bool internalize_term(app * term) { + bool internalize_term(app * term) override { TRACE("theory_dl", tout << mk_pp(term, m()) << "\n";); if (u().is_finite_sort(term)) { return mk_rep(term); @@ -146,27 +146,27 @@ namespace smt { } } - virtual void new_eq_eh(theory_var v1, theory_var v2) { + void new_eq_eh(theory_var v1, theory_var v2) override { } - virtual void new_diseq_eh(theory_var v1, theory_var v2) { + void new_diseq_eh(theory_var v1, theory_var v2) override { } - virtual theory * mk_fresh(context * new_ctx) { + theory * mk_fresh(context * new_ctx) override { return alloc(theory_dl, new_ctx->get_manager()); } - virtual void init_model(smt::model_generator & m) { + void init_model(smt::model_generator & m) override { m.register_factory(alloc(dl_factory, m_util, m.get_model())); } - virtual smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator&) { + smt::model_value_proc * mk_value(smt::enode * n, smt::model_generator&) override { return alloc(dl_value_proc, *this, n); } - virtual void apply_sort_cnstr(enode * n, sort * s) { + void apply_sort_cnstr(enode * n, sort * s) override { app* term = n->get_owner(); if (u().is_finite_sort(term)) { mk_rep(term); @@ -174,7 +174,7 @@ namespace smt { } - virtual void relevant_eh(app * n) { + void relevant_eh(app * n) override { if (u().is_finite_sort(n)) { sort* s = m().get_sort(n); func_decl* r, *v; @@ -182,7 +182,7 @@ namespace smt { if (n->get_decl() != v) { expr* rep = m().mk_app(r, n); - uint64 vl; + uint64_t vl; if (u().is_numeral_ext(n, vl)) { assert_cnstr(m().mk_eq(rep, mk_bv_constant(vl, s))); } @@ -194,7 +194,7 @@ namespace smt { } } - virtual void display(std::ostream & out) const { + void display(std::ostream & out) const override { } @@ -204,8 +204,8 @@ namespace smt { if(!m_reps.find(s, r) || !m_vals.find(s,v)) { SASSERT(!m_reps.contains(s)); sort* bv = b().mk_sort(64); - r = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_REP, 0, 0, 1, &s, bv); - v = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_ABS, 0, 0, 1, &bv, s); + r = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_REP, 0, nullptr, 1, &s, bv); + v = m().mk_func_decl(m_util.get_family_id(), datalog::OP_DL_ABS, 0, nullptr, 1, &bv, s); m_reps.insert(s, r); m_vals.insert(s, v); add_trail(r); @@ -218,7 +218,7 @@ namespace smt { bool mk_rep(app* n) { context & ctx = get_context(); unsigned num_args = n->get_num_args(); - enode * e = 0; + enode * e = nullptr; for (unsigned i = 0; i < num_args; i++) { ctx.internalize(n->get_arg(i), false); } @@ -237,12 +237,12 @@ namespace smt { return true; } - app* mk_bv_constant(uint64 val, sort* s) { + app* mk_bv_constant(uint64_t val, sort* s) { return b().mk_numeral(rational(val, rational::ui64()), 64); } app* max_value(sort* s) { - uint64 sz; + uint64_t sz; VERIFY(u().try_get_size(s, sz)); SASSERT(sz > 0); return mk_bv_constant(sz-1, s); diff --git a/src/smt/theory_dummy.h b/src/smt/theory_dummy.h index b20d86270..c58695aac 100644 --- a/src/smt/theory_dummy.h +++ b/src/smt/theory_dummy.h @@ -33,25 +33,25 @@ namespace smt { void found_theory_expr(); protected: - virtual bool internalize_atom(app * atom, bool gate_ctx); - virtual bool internalize_term(app * term); - virtual void new_eq_eh(theory_var v1, theory_var v2); - virtual bool use_diseqs() const; - virtual void new_diseq_eh(theory_var v1, theory_var v2); - virtual void reset_eh(); - virtual final_check_status final_check_eh(); - virtual bool build_models() const { + bool internalize_atom(app * atom, bool gate_ctx) override; + bool internalize_term(app * term) override; + void new_eq_eh(theory_var v1, theory_var v2) override; + bool use_diseqs() const override; + void new_diseq_eh(theory_var v1, theory_var v2) override; + void reset_eh() override; + final_check_status final_check_eh() override; + bool build_models() const override { return false; } - virtual void display(std::ostream& out) const {} + void display(std::ostream& out) const override {} public: theory_dummy(family_id fid, char const * name); - virtual ~theory_dummy() {} + ~theory_dummy() override {} - virtual theory * mk_fresh(context * new_ctx) { return alloc(theory_dummy, get_family_id(), m_name); } + theory * mk_fresh(context * new_ctx) override { return alloc(theory_dummy, get_family_id(), m_name); } - virtual char const * get_name() const; + char const * get_name() const override; }; }; diff --git a/src/smt/theory_fpa.cpp b/src/smt/theory_fpa.cpp index cc9b0017d..b7aaff68a 100644 --- a/src/smt/theory_fpa.cpp +++ b/src/smt/theory_fpa.cpp @@ -32,13 +32,13 @@ namespace smt { public: fpa2bv_conversion_trail_elem(ast_manager & m, obj_map & map, expr * e) : m(m), m_map(map), key(e, m) { } - virtual ~fpa2bv_conversion_trail_elem() { } - virtual void undo(theory_fpa & th) { + ~fpa2bv_conversion_trail_elem() override { } + void undo(theory_fpa & th) override { expr * val = m_map.find(key); m_map.remove(key); m.dec_ref(key); m.dec_ref(val); - key = 0; + key = nullptr; } }; @@ -208,7 +208,7 @@ namespace smt { for (unsigned i = 0; i < values.size(); i++) tout << "value[" << i << "] = " << mk_ismt2_pp(values[i], m) << std::endl;); - app * result = 0; + app * result = nullptr; unsigned bv_sz; rational val(0); @@ -256,7 +256,7 @@ namespace smt { } func_decl_ref wrap_fd(m); - wrap_fd = m.mk_func_decl(get_family_id(), OP_FPA_BVWRAP, 0, 0, 1, &es, bv_srt); + wrap_fd = m.mk_func_decl(get_family_id(), OP_FPA_BVWRAP, 0, nullptr, 1, &es, bv_srt); res = m.mk_app(wrap_fd, e); } @@ -705,7 +705,7 @@ namespace smt { m_trail_stack.pop_scope(m_trail_stack.get_num_scopes()); if (m_factory) { dealloc(m_factory); - m_factory = 0; + m_factory = nullptr; } ast_manager & m = get_manager(); dec_ref_map_key_values(m, m_conversions); @@ -743,7 +743,7 @@ namespace smt { return alloc(expr_wrapper_proc, owner); } - model_value_proc * res = 0; + model_value_proc * res = nullptr; app_ref wrapped(m); wrapped = wrap(owner); diff --git a/src/smt/theory_fpa.h b/src/smt/theory_fpa.h index 9e9801ee0..75bcec13c 100644 --- a/src/smt/theory_fpa.h +++ b/src/smt/theory_fpa.h @@ -43,16 +43,16 @@ namespace smt { value_factory(m, fid), m_util(m) {} - virtual ~fpa_value_factory() {} + ~fpa_value_factory() override {} - virtual expr * get_some_value(sort * s) { + expr * get_some_value(sort * s) override { mpf_manager & mpfm = m_util.fm(); scoped_mpf q(mpfm); mpfm.set(q, m_util.get_ebits(s), m_util.get_sbits(s), 0); return m_util.mk_value(q); } - virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { + bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override { mpf_manager & mpfm = m_util.fm(); scoped_mpf q(mpfm); mpfm.set(q, m_util.get_ebits(s), m_util.get_sbits(s), 0); @@ -62,8 +62,8 @@ namespace smt { return true; } - virtual expr * get_fresh_value(sort * s) { NOT_IMPLEMENTED_YET(); } - virtual void register_value(expr * n) { /* Ignore */ } + expr * get_fresh_value(sort * s) override { return get_some_value(s); } + void register_value(expr * n) override { /* Ignore */ } app * mk_value(mpf const & x) { return m_util.mk_value(x); @@ -81,8 +81,8 @@ namespace smt { fpa2bv_converter(m), m_th(*th) {} virtual ~fpa2bv_converter_wrapped() {} - virtual void mk_const(func_decl * f, expr_ref & result); - virtual void mk_rm_const(func_decl * f, expr_ref & result); + void mk_const(func_decl * f, expr_ref & result) override; + void mk_rm_const(func_decl * f, expr_ref & result) override; }; class fpa_value_proc : public model_value_proc { @@ -100,15 +100,15 @@ namespace smt { m_th(*th), m(th->get_manager()), m_fu(th->m_fpa_util), m_bu(th->m_bv_util), m_ebits(ebits), m_sbits(sbits) {} - virtual ~fpa_value_proc() {} + ~fpa_value_proc() override {} void add_dependency(enode * e) { m_deps.push_back(model_value_dependency(e)); } - virtual void get_dependencies(buffer & result) { + void get_dependencies(buffer & result) override { result.append(m_deps); } - virtual app * mk_value(model_generator & mg, ptr_vector & values); + app * mk_value(model_generator & mg, ptr_vector & values) override; }; class fpa_rm_value_proc : public model_value_proc { @@ -124,12 +124,12 @@ namespace smt { void add_dependency(enode * e) { m_deps.push_back(model_value_dependency(e)); } - virtual void get_dependencies(buffer & result) { + void get_dependencies(buffer & result) override { result.append(m_deps); } - virtual ~fpa_rm_value_proc() {} - virtual app * mk_value(model_generator & mg, ptr_vector & values); + ~fpa_rm_value_proc() override {} + app * mk_value(model_generator & mg, ptr_vector & values) override; }; protected: @@ -145,32 +145,32 @@ namespace smt { bool m_is_initialized; obj_hashtable m_is_added_to_model; - virtual final_check_status final_check_eh(); - virtual bool internalize_atom(app * atom, bool gate_ctx); - virtual bool internalize_term(app * term); - virtual void apply_sort_cnstr(enode * n, sort * s); - virtual void new_eq_eh(theory_var, theory_var); - virtual void new_diseq_eh(theory_var, theory_var); - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); - virtual void reset_eh(); - virtual theory* mk_fresh(context* new_ctx); - virtual char const * get_name() const { return "fpa"; } + final_check_status final_check_eh() override; + bool internalize_atom(app * atom, bool gate_ctx) override; + bool internalize_term(app * term) override; + void apply_sort_cnstr(enode * n, sort * s) override; + void new_eq_eh(theory_var, theory_var) override; + void new_diseq_eh(theory_var, theory_var) override; + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; + void reset_eh() override; + theory* mk_fresh(context* new_ctx) override; + char const * get_name() const override { return "fpa"; } - virtual model_value_proc * mk_value(enode * n, model_generator & mg); + model_value_proc * mk_value(enode * n, model_generator & mg) override; - void assign_eh(bool_var v, bool is_true); - virtual void relevant_eh(app * n); - virtual void init_model(model_generator & m); - virtual void finalize_model(model_generator & mg); + void assign_eh(bool_var v, bool is_true) override; + void relevant_eh(app * n) override; + void init_model(model_generator & m) override; + void finalize_model(model_generator & mg) override; public: theory_fpa(ast_manager & m); - virtual ~theory_fpa(); + ~theory_fpa() override; - virtual void init(context * ctx); + void init(context * ctx) override; - virtual void display(std::ostream & out) const; + void display(std::ostream & out) const override; protected: expr_ref mk_side_conditions(); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 292d2ab0d..de9b41f64 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -36,7 +36,7 @@ Revision History: #include "smt/smt_model_generator.h" #include "smt/arith_eq_adapter.h" #include "util/nat_set.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace lra_lp { enum bound_kind { lower_t, upper_t }; @@ -137,7 +137,7 @@ namespace smt { 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(); } }; @@ -658,12 +658,12 @@ namespace smt { m_internalize_head(0), m_delay_constraints(false), m_delayed_terms(m), - m_not_handled(0), + m_not_handled(nullptr), m_asserted_qhead(0), m_assume_eq_head(0), m_num_conflicts(0), m_model_eqs(DEFAULT_HASHTABLE_INITIAL_CAPACITY, var_value_hash(*this), var_value_eq(*this)), - m_solver(0), + m_solver(nullptr), m_resource_limit(*this) { } @@ -689,7 +689,7 @@ namespace smt { SASSERT(!ctx().b_internalized(atom)); bool_var bv = ctx().mk_bool_var(atom); ctx().set_var_theory(bv, get_id()); - expr* n1 = 0, *n2 = 0; + expr* n1 = nullptr, *n2 = nullptr; rational r; lra_lp::bound_kind k; theory_var v = null_theory_var; @@ -721,7 +721,7 @@ namespace smt { SASSERT(!ctx().b_internalized(atom)); bool_var bv = ctx().mk_bool_var(atom); ctx().set_var_theory(bv, get_id()); - expr* n1 = 0, *n2 = 0; + expr* n1 = nullptr, *n2 = nullptr; rational r; lra_lp::bound_kind k; theory_var v = null_theory_var; @@ -771,7 +771,7 @@ namespace smt { } 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); @@ -862,7 +862,7 @@ namespace smt { void relevant_eh(app* n) { TRACE("arith", tout << mk_pp(n, m) << "\n";); - expr* n1 = 0, *n2 = 0; + expr* n1 = nullptr, *n2 = nullptr; if (a.is_mod(n, n1, n2)) mk_idiv_mod_axioms(n1, n2); else if (a.is_rem(n, n1, n2)) @@ -898,7 +898,7 @@ namespace smt { // 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)); @@ -914,7 +914,7 @@ namespace smt { // 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); @@ -1180,7 +1180,7 @@ namespace smt { if (assume_eqs()) { return FC_CONTINUE; } - if (m_not_handled != 0) { + if (m_not_handled != nullptr) { return FC_GIVEUP; } return FC_DONE; @@ -1358,11 +1358,11 @@ namespace smt { imp & m_imp; local_bound_propagator(imp& i) : lp_bound_propagator(*i.m_solver), m_imp(i) {} - bool bound_is_interesting(unsigned j, lp::lconstraint_kind kind, const rational & v) { + bool bound_is_interesting(unsigned j, lp::lconstraint_kind kind, const rational & v) override { return m_imp.bound_is_interesting(j, kind, v); } - virtual void consume(rational const& v, unsigned j) { + void consume(rational const& v, unsigned j) override { m_imp.set_evidence(j); } }; @@ -1439,12 +1439,12 @@ namespace smt { m_core2.push_back(~m_core[i]); } 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( @@ -1499,7 +1499,7 @@ namespace smt { rational const& k1 = b.get_value(); lp_bounds & bounds = m_bounds[v]; - lra_lp::bound* end = 0; + lra_lp::bound* end = nullptr; lra_lp::bound* lo_inf = end, *lo_sup = end; lra_lp::bound* hi_inf = end, *hi_sup = end; @@ -1764,7 +1764,7 @@ namespace smt { bool find_glb = (is_true == (k == lra_lp::lower_t)); if (find_glb) { rational glb; - lra_lp::bound* lb = 0; + lra_lp::bound* lb = nullptr; for (unsigned i = 0; i < bounds.size(); ++i) { lra_lp::bound* b2 = bounds[i]; if (b2 == &b) continue; @@ -1780,7 +1780,7 @@ namespace smt { } else { rational lub; - lra_lp::bound* ub = 0; + lra_lp::bound* ub = nullptr; for (unsigned i = 0; i < bounds.size(); ++i) { lra_lp::bound* b2 = bounds[i]; if (b2 == &b) continue; @@ -1916,7 +1916,7 @@ namespace smt { lp::var_index vi = m_theory_var2var_index[v]; SASSERT(m_solver->is_term(vi)); lp::lar_term const& term = m_solver->get_term(vi); - for (auto const coeff : term.m_coeffs) { + for (auto const& coeff : term.m_coeffs) { lp::var_index wi = coeff.first; lp::constraint_index ci; rational value; @@ -2111,7 +2111,7 @@ namespace smt { 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) { @@ -2236,12 +2236,12 @@ namespace smt { } justification * why_is_diseq(theory_var v1, theory_var v2) { - return 0; + return nullptr; } void reset_eh() { m_arith_eq_adapter.reset_eh(); - m_solver = 0; + m_solver = nullptr; m_not_handled = nullptr; del_bounds(0); m_unassigned_bounds.reset(); @@ -2417,7 +2417,7 @@ namespace smt { } } - expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val) { + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val) { rational r = val.get_rational(); bool is_strict = val.get_infinitesimal().is_pos(); app_ref b(m); @@ -2429,7 +2429,7 @@ namespace smt { b = a.mk_ge(mk_obj(v), a.mk_numeral(r, is_int)); } if (!ctx().b_internalized(b)) { - fm.insert(b->get_decl()); + fm.hide(b); bool_var bv = ctx().mk_bool_var(b); ctx().set_var_theory(bv, get_id()); // ctx().set_enode_flag(bv, true); @@ -2620,7 +2620,7 @@ namespace smt { theory_var theory_lra::add_objective(app* term) { return m_imp->add_objective(term); } - expr_ref theory_lra::mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val) { + expr_ref theory_lra::mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val) { return m_imp->mk_ge(fm, v, val); } diff --git a/src/smt/theory_lra.h b/src/smt/theory_lra.h index 774ec15ad..4b1f67b79 100644 --- a/src/smt/theory_lra.h +++ b/src/smt/theory_lra.h @@ -29,69 +29,67 @@ namespace smt { public: theory_lra(ast_manager& m, theory_arith_params& ap); - virtual ~theory_lra(); - virtual theory* mk_fresh(context* new_ctx); - virtual char const* get_name() const { return "lra"; } + ~theory_lra() override; + theory* mk_fresh(context* new_ctx) override; + char const* get_name() const override { return "lra"; } - virtual void init(context * ctx); + void init(context * ctx) override; - virtual bool internalize_atom(app * atom, bool gate_ctx); + bool internalize_atom(app * atom, bool gate_ctx) override; - virtual bool internalize_term(app * term); + bool internalize_term(app * term) override; - virtual void internalize_eq_eh(app * atom, bool_var v); + void internalize_eq_eh(app * atom, bool_var v) override; - virtual void assign_eh(bool_var v, bool is_true); + void assign_eh(bool_var v, bool is_true) override; - virtual void new_eq_eh(theory_var v1, theory_var v2); + void new_eq_eh(theory_var v1, theory_var v2) override; - virtual bool use_diseqs() const; + bool use_diseqs() const override; - virtual void new_diseq_eh(theory_var v1, theory_var v2); + void new_diseq_eh(theory_var v1, theory_var v2) override; - virtual void push_scope_eh(); + void push_scope_eh() override; - virtual void pop_scope_eh(unsigned num_scopes); + void pop_scope_eh(unsigned num_scopes) override; - virtual void restart_eh(); + void restart_eh() override; - virtual void relevant_eh(app* e); + void relevant_eh(app* e) override; - virtual void init_search_eh(); + void init_search_eh() override; - virtual final_check_status final_check_eh(); + final_check_status final_check_eh() override; - virtual bool is_shared(theory_var v) const; + bool is_shared(theory_var v) const override; - virtual bool can_propagate(); + bool can_propagate() override; - virtual void propagate(); + void propagate() override; - virtual justification * why_is_diseq(theory_var v1, theory_var v2); + justification * why_is_diseq(theory_var v1, theory_var v2) override; // virtual void flush_eh(); - virtual void reset_eh(); + void reset_eh() override; - virtual void init_model(model_generator & m); + void init_model(model_generator & m) override; - virtual model_value_proc * mk_value(enode * n, model_generator & mg); + model_value_proc * mk_value(enode * n, model_generator & mg) override; - virtual bool get_value(enode* n, expr_ref& r); + bool get_value(enode* n, expr_ref& r) override; - virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const; + bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override; - virtual void display(std::ostream & out) const; + void display(std::ostream & out) const override; - virtual void collect_statistics(::statistics & st) const; + void collect_statistics(::statistics & st) const override; // optimization - virtual inf_eps value(theory_var); - virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared); - virtual theory_var add_objective(app* term); - virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val); - - + expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_rational const& val); + inf_eps value(theory_var) override; + inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) override; + theory_var add_objective(app* term) override; }; } diff --git a/src/smt/theory_opt.h b/src/smt/theory_opt.h index 49f436ea5..2947d86c1 100644 --- a/src/smt/theory_opt.h +++ b/src/smt/theory_opt.h @@ -25,7 +25,7 @@ Notes: #ifndef THEORY_OPT_H_ #define THEORY_OPT_H_ -class filter_model_converter; +class generic_model_converter; namespace smt { class theory_opt { public: diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 9d2059f55..953ecea2c 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -21,6 +21,7 @@ Notes: #include #include "smt/theory_pb.h" #include "smt/smt_context.h" +#include "smt/smt_kernel.h" #include "ast/ast_pp.h" #include "util/sorting_network.h" #include "util/uint_set.h" @@ -65,9 +66,6 @@ namespace smt { }; }; - const unsigned theory_pb::null_index = UINT_MAX; - - unsigned theory_pb::arg_t::get_hash() const { return get_composite_hash(*this, size()); } @@ -227,18 +225,234 @@ namespace smt { SASSERT(sum >= k()); return true; } + + // ----------------------------- + // cardinality constraints + + void theory_pb::card::negate() { + m_lit.neg(); + unsigned sz = size(); + for (unsigned i = 0; i < sz; ++i) { + m_args[i].neg(); + } + m_bound = sz - m_bound + 1; + SASSERT(sz >= m_bound && m_bound > 0); + } + + app_ref theory_pb::card::to_expr(theory_pb& th) { + ast_manager& m = th.get_manager(); + expr_ref_vector args(m); + for (unsigned i = 0; i < size(); ++i) { + args.push_back(th.literal2expr(m_args[i])); + } + return app_ref(th.pb.mk_at_least_k(args.size(), args.c_ptr(), k()), m); + } + + lbool theory_pb::card::assign(theory_pb& th, literal alit) { + // literal is assigned to false. + context& ctx = th.get_context(); + unsigned sz = size(); + unsigned bound = k(); + TRACE("pb", tout << "assign: " << m_lit << " " << ~alit << " " << bound << "\n";); + + SASSERT(0 < bound && bound < sz); + SASSERT(ctx.get_assignment(alit) == l_false); + SASSERT(ctx.get_assignment(m_lit) == l_true); + unsigned index = 0; + for (index = 0; index <= bound; ++index) { + if (lit(index) == alit) { + break; + } + } + if (index == bound + 1) { + // literal is no longer watched. + return l_undef; + } + SASSERT(index <= bound); + SASSERT(lit(index) == alit); + + // find a literal to swap with: + for (unsigned i = bound + 1; i < sz; ++i) { + literal lit2 = lit(i); + if (ctx.get_assignment(lit2) != l_false) { + TRACE("pb", tout << "swap " << lit2 << "\n";); + std::swap(m_args[index], m_args[i]); + th.watch_literal(lit2, this); + return l_undef; + } + } + + // conflict + if (bound != index && ctx.get_assignment(lit(bound)) == l_false) { + TRACE("pb", tout << "conflict " << lit(bound) << " " << alit << "\n";); + set_conflict(th, alit); + return l_false; + } + + TRACE("pb", tout << "no swap " << index << " " << alit << "\n";); + // there are no literals to swap with, + // prepare for unit propagation by swapping the false literal into + // position bound. Then literals in positions 0..bound-1 have to be + // assigned l_true. + if (index != bound) { + std::swap(m_args[index], m_args[bound]); + } + SASSERT(th.validate_unit_propagation(*this)); + + for (unsigned i = 0; i < bound && !ctx.inconsistent(); ++i) { + th.add_assign(*this, lit(i)); + } + + return ctx.inconsistent() ? l_false : l_true; + } + + /** + \brief The conflict clause position for cardinality constraint have the following properties: + 0. The position for the literal corresponding to the cardinality constraint. + 1. The literal at position 0 of the cardinality constraint. + 2. The asserting literal. + 3. .. the remaining false literals. + */ + void theory_pb::card::set_conflict(theory_pb& th, literal l) { + SASSERT(validate_conflict(th)); + context& ctx = th.get_context(); + (void)ctx; + literal_vector& lits = th.get_literals(); + SASSERT(ctx.get_assignment(l) == l_false); + SASSERT(ctx.get_assignment(lit()) == l_true); + lits.push_back(~lit()); + lits.push_back(l); + unsigned sz = size(); + for (unsigned i = m_bound; i < sz; ++i) { + SASSERT(ctx.get_assignment(m_args[i]) == l_false); + lits.push_back(m_args[i]); + } + th.add_clause(*this, lits); + } + + bool theory_pb::card::validate_conflict(theory_pb& th) { + context& ctx = th.get_context(); + unsigned num_false = 0; + for (unsigned i = 0; i < size(); ++i) { + if (ctx.get_assignment(m_args[i]) == l_false) { + ++num_false; + } + } + return size() - num_false < m_bound; + } + + bool theory_pb::card::validate_assign(theory_pb& th, literal_vector const& lits, literal l) { + context& ctx = th.get_context(); + VERIFY(ctx.get_assignment(l) == l_undef); + for (unsigned i = 0; i < lits.size(); ++i) { + SASSERT(ctx.get_assignment(lits[i]) == l_true); + } + return size() - lits.size() <= m_bound; + } + + void theory_pb::card::init_watch(theory_pb& th, bool is_true) { + context& ctx = th.get_context(); + th.clear_watch(*this); + if (lit().sign() == is_true) { + negate(); + } + SASSERT(ctx.get_assignment(lit()) == l_true); + unsigned j = 0, sz = size(), bound = k(); + if (bound == sz) { + for (unsigned i = 0; i < sz && !ctx.inconsistent(); ++i) { + th.add_assign(*this, lit(i)); + } + return; + } + // put the non-false literals into the head. + for (unsigned i = 0; i < sz; ++i) { + if (ctx.get_assignment(lit(i)) != l_false) { + if (j != i) { + std::swap(m_args[i], m_args[j]); + } + ++j; + } + } + DEBUG_CODE( + bool is_false = false; + for (unsigned k = 0; k < sz; ++k) { + SASSERT(!is_false || ctx.get_assignment(lit(k)) == l_false); + is_false = ctx.get_assignment(lit(k)) == l_false; + }); + + // j is the number of non-false, sz - j the number of false. + if (j < bound) { + SASSERT(0 < bound && bound < sz); + literal alit = lit(j); + + // + // we need the assignment level of the asserting literal to be maximal. + // such that conflict resolution can use the asserting literal as a starting + // point. + // + + for (unsigned i = bound; i < sz; ++i) { + if (ctx.get_assign_level(alit) < ctx.get_assign_level(lit(i))) { + std::swap(m_args[j], m_args[i]); + alit = lit(j); + } + } + set_conflict(th, alit); + } + else if (j == bound) { + for (unsigned i = 0; i < bound && !ctx.inconsistent(); ++i) { + th.add_assign(*this, lit(i)); + } + } + else { + for (unsigned i = 0; i <= bound; ++i) { + th.watch_literal(lit(i), this); + } + } + } + + + void theory_pb::card::add_arg(literal lit) { + if (lit == false_literal) { + return; + } + else if (lit == true_literal) { + if (m_bound > 0) { + --m_bound; + } + } + else { + m_args.push_back(lit); + } + + } + + void theory_pb::card::inc_propagations(theory_pb& th) { + ++m_num_propagations; + if (m_compiled == l_false && m_num_propagations >= m_compilation_threshold) { + // m_compiled = l_undef; + // th.m_to_compile.push_back(&c); + } + } + + // ------------------------ + // theory_pb + theory_pb::theory_pb(ast_manager& m, theory_pb_params& p): theory(m.mk_family_id("pb")), m_params(p), - m_simplex(m.limit()), - m_util(m), - m_max_compiled_coeff(rational(8)) + pb(m), + m_max_compiled_coeff(rational(8)), + m_cardinality_lemma(false), + m_restart_lim(3), + m_restart_inc(0), + m_antecedent_exprs(m), + m_cardinality_exprs(m) { m_learn_complements = p.m_pb_learn_complements; m_conflict_frequency = p.m_pb_conflict_frequency; m_enable_compilation = p.m_pb_enable_compilation; - m_enable_simplex = p.m_pb_enable_simplex; } theory_pb::~theory_pb() { @@ -249,195 +463,36 @@ namespace smt { return alloc(theory_pb, new_ctx->get_manager(), m_params); } - class theory_pb::remove_var : public trail { - theory_pb& pb; - unsigned v; - public: - remove_var(theory_pb& pb, unsigned v): pb(pb), v(v) {} - virtual void undo(context& ctx) { - pb.m_vars.remove(v); - pb.m_simplex.unset_lower(v); - pb.m_simplex.unset_upper(v); - } - }; - - class theory_pb::undo_bound : public trail { - theory_pb& pb; - unsigned m_v; - bool m_is_lower; - scoped_eps_numeral m_last_bound; - bool m_last_bound_valid; - literal m_last_explain; - - public: - undo_bound(theory_pb& pb, unsigned v, - bool is_lower, - scoped_eps_numeral& last_bound, - bool last_bound_valid, - literal last_explain): - pb(pb), - m_v(v), - m_is_lower(is_lower), - m_last_bound(last_bound), - m_last_bound_valid(last_bound_valid), - m_last_explain(last_explain) {} - - virtual void undo(context& ctx) { - if (m_is_lower) { - if (m_last_bound_valid) { - pb.m_simplex.set_lower(m_v, m_last_bound); - } - else { - pb.m_simplex.unset_lower(m_v); - } - pb.set_explain(pb.m_explain_lower, m_v, m_last_explain); - } - else { - if (m_last_bound_valid) { - pb.m_simplex.set_upper(m_v, m_last_bound); - } - else { - pb.m_simplex.unset_upper(m_v); - } - pb.set_explain(pb.m_explain_upper, m_v, m_last_explain); - } - m_last_bound.reset(); - } - }; - - literal theory_pb::set_explain(literal_vector& explains, unsigned var, literal expl) { - if (var >= explains.size()) { - explains.resize(var+1, null_literal); - } - literal last_explain = explains[var]; - explains[var] = expl; - return last_explain; - } - - bool theory_pb::update_bound(bool_var v, literal explain, bool is_lower, mpq_inf const& bound) { - if (is_lower) { - if (m_simplex.above_lower(v, bound)) { - scoped_eps_numeral last_bound(m_mpq_inf_mgr); - if (m_simplex.upper_valid(v)) { - m_simplex.get_upper(v, last_bound); - if (m_mpq_inf_mgr.gt(bound, last_bound)) { - literal lit = m_explain_upper.get(v, null_literal); - TRACE("pb", tout << ~lit << " " << ~explain << "\n";); - get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); - return false; - } - } - bool last_bound_valid = m_simplex.lower_valid(v); - if (last_bound_valid) { - m_simplex.get_lower(v, last_bound); - } - m_simplex.set_lower(v, bound); - literal last_explain = set_explain(m_explain_lower, v, explain); - get_context().push_trail(undo_bound(*this, v, true, last_bound, last_bound_valid, last_explain)); - } - } - else { - if (m_simplex.below_upper(v, bound)) { - scoped_eps_numeral last_bound(m_mpq_inf_mgr); - if (m_simplex.lower_valid(v)) { - m_simplex.get_lower(v, last_bound); - if (m_mpq_inf_mgr.gt(last_bound, bound)) { - literal lit = m_explain_lower.get(v, null_literal); - TRACE("pb", tout << ~lit << " " << ~explain << "\n";); - get_context().mk_clause(~lit, ~explain, justify(~lit, ~explain)); - return false; - } - } - bool last_bound_valid = m_simplex.upper_valid(v); - if (last_bound_valid) { - m_simplex.get_upper(v, last_bound); - } - m_simplex.set_upper(v, bound); - literal last_explain = set_explain(m_explain_upper, v, explain); - get_context().push_trail(undo_bound(*this, v, false, last_bound, last_bound_valid, last_explain)); - } - } - return true; - }; - - bool theory_pb::check_feasible() { - context& ctx = get_context(); - lbool is_sat = m_simplex.make_feasible(); - if (l_false != is_sat) { - return true; - } - - row r = m_simplex.get_infeasible_row(); - // m_simplex.display_row(std::cout, r, true); - mpz const& coeff = m_simplex.get_base_coeff(r); - bool_var base_var = m_simplex.get_base_var(r); - SASSERT(m_simplex.below_lower(base_var) || m_simplex.above_upper(base_var)); - bool cant_increase = m_simplex.below_lower(base_var)?m_mpz_mgr.is_pos(coeff):m_mpz_mgr.is_neg(coeff); - - literal_vector explains; - row_iterator it = m_simplex.row_begin(r), end = m_simplex.row_end(r); - for (; it != end; ++it) { - bool_var v = it->m_var; - if (v == base_var) { - if (m_simplex.below_lower(base_var)) { - explains.push_back(m_explain_lower.get(v, null_literal)); - } - else { - explains.push_back(m_explain_upper.get(v, null_literal)); - } - } - else if (cant_increase == m_mpz_mgr.is_pos(it->m_coeff)) { - explains.push_back(m_explain_lower.get(v, null_literal)); - } - else { - explains.push_back(m_explain_upper.get(v, null_literal)); - } - } - - literal_vector lits; - for (unsigned i = 0; i < explains.size(); ++i) { - literal lit(explains[i]); - if (lit != null_literal) { - lits.push_back(~lit); - } - } - - m_stats.m_num_conflicts++; - justification* js = 0; - if (proofs_enabled()) { - js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); - } - TRACE("pb", tout << lits << "\n";); - ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); - - return false; - } - bool theory_pb::internalize_atom(app * atom, bool gate_ctx) { context& ctx = get_context(); + TRACE("pb", tout << mk_pp(atom, get_manager()) << "\n";); if (ctx.b_internalized(atom)) { - return false; + return true; } SASSERT(!ctx.b_internalized(atom)); m_stats.m_num_predicates++; - if (m_util.is_aux_bool(atom)) { + if (pb.is_aux_bool(atom)) { bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); return true; } - SASSERT(m_util.is_at_most_k(atom) || m_util.is_le(atom) || - m_util.is_ge(atom) || m_util.is_at_least_k(atom) || - m_util.is_eq(atom)); + if (internalize_card(atom, gate_ctx)) { + return true; + } + + SASSERT(pb.is_at_most_k(atom) || pb.is_le(atom) || + pb.is_ge(atom) || pb.is_at_least_k(atom) || + pb.is_eq(atom)); unsigned num_args = atom->get_num_args(); bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); - ineq* c = alloc(ineq, m_mpz_mgr, literal(abv), m_util.is_eq(atom)); - c->m_args[0].m_k = m_util.get_k(atom); + ineq* c = alloc(ineq, m_mpz_mgr, literal(abv), pb.is_eq(atom)); + c->m_args[0].m_k = pb.get_k(atom); numeral& k = c->m_args[0].m_k; arg_t& args = c->m_args[0]; @@ -445,10 +500,19 @@ namespace smt { for (unsigned i = 0; i < num_args; ++i) { expr* arg = atom->get_arg(i); literal l = compile_arg(arg); - numeral c = m_util.get_coeff(atom, i); - args.push_back(std::make_pair(l, c)); + numeral c = pb.get_coeff(atom, i); + switch (ctx.get_assignment(l)) { + case l_true: + k -= c; + break; + case l_false: + break; + default: + args.push_back(std::make_pair(l, c)); + break; + } } - if (m_util.is_at_most_k(atom) || m_util.is_le(atom)) { + if (pb.is_at_most_k(atom) || pb.is_le(atom)) { // turn W <= k into -W >= -k for (unsigned i = 0; i < args.size(); ++i) { args[i].second = -args[i].second; @@ -456,9 +520,9 @@ namespace smt { k = -k; } else { - SASSERT(m_util.is_at_least_k(atom) || m_util.is_ge(atom) || m_util.is_eq(atom)); + SASSERT(pb.is_at_least_k(atom) || pb.is_ge(atom) || pb.is_eq(atom)); } - TRACE("pb", display(tout, *c);); + TRACE("pb", display(tout, *c, true);); //app_ref fml1(m), fml2(m); //fml1 = c->to_expr(ctx, m); c->unique(); @@ -485,8 +549,8 @@ namespace smt { break; } - if (c->k().is_one() && c->is_ge() && !m_enable_simplex) { - literal_vector& lits = get_lits(); + if (c->k().is_one() && c->is_ge()) { + literal_vector& lits = get_literals(); lits.push_back(~lit); for (unsigned i = 0; i < c->size(); ++i) { lits.push_back(c->lit(i)); @@ -531,64 +595,6 @@ namespace smt { m_var_infos[abv].m_ineq = c; m_ineqs_trail.push_back(abv); - if (m_enable_simplex) { - // - // TBD: using abv as slack identity doesn't quite - // work if psuedo-Booleans are used - // in a nested way. So assume - // - - arg_t rep(c->args()); - rep.remove_negations(); // normalize representative - numeral k = rep.k(); - theory_var slack; - bool_var abv2; - TRACE("pb", display(tout << abv <<"\n", rep);); - if (m_ineq_rep.find(rep, abv2)) { - slack = abv2; - TRACE("pb", - tout << "Old row: " << abv << " |-> " << slack << " "; - tout << m_ineq_row_info.find(abv2).m_bound << " vs. " << k << "\n"; - display(tout, rep);); - } - else { - m_ineq_rep.insert(rep, abv); - svector vars; - scoped_mpz_vector coeffs(m_mpz_mgr); - for (unsigned i = 0; i < rep.size(); ++i) { - unsigned v = rep.lit(i).var(); - m_simplex.ensure_var(v); - vars.push_back(v); - if (!m_vars.contains(v)) { - mpq_inf zero(mpq(0),mpq(0)), one(mpq(1),mpq(0)); - switch(ctx.get_assignment(rep.lit(i))) { - case l_true: - VERIFY(update_bound(v, literal(v), true, one)); - m_simplex.set_lower(v, one); - break; - case l_false: - VERIFY(update_bound(v, ~literal(v), false, zero)); - m_simplex.set_upper(v, zero); - break; - default: - m_simplex.set_lower(v, zero); - m_simplex.set_upper(v, one); - break; - } - m_vars.insert(v); - ctx.push_trail(remove_var(*this, v)); - } - coeffs.push_back(rep.coeff(i).to_mpq().numerator()); - } - slack = abv; - m_simplex.ensure_var(slack); - vars.push_back(slack); - coeffs.push_back(mpz(-1)); - m_simplex.add_row(slack, vars.size(), vars.c_ptr(), coeffs.c_ptr()); - TRACE("pb", tout << "New row: " << abv << " " << k << "\n"; display(tout, rep);); - } - m_ineq_row_info.insert(abv, row_info(slack, k, rep)); - } TRACE("pb", display(tout, *c);); @@ -629,7 +635,6 @@ namespace smt { // is available. if (!has_bv) { app_ref tmp(m), fml(m); - pb_util pb(m); tmp = pb.mk_fresh_bool(); fml = m.mk_iff(tmp, arg); TRACE("pb", tout << "create proxy " << fml << "\n";); @@ -644,7 +649,7 @@ namespace smt { return negate?~literal(bv):literal(bv); } - void theory_pb::del_watch(watch_list& watch, unsigned index, ineq& c, unsigned ineq_index) { + void theory_pb::del_watch(ineq_watch& watch, unsigned index, ineq& c, unsigned ineq_index) { SASSERT(c.is_ge()); if (index < watch.size()) { std::swap(watch[index], watch[watch.size()-1]); @@ -694,20 +699,22 @@ namespace smt { } } + void theory_pb::watch_literal(literal lit, ineq* c) { init_watch(lit.var()); ptr_vector* ineqs = m_var_infos[lit.var()].m_lit_watch[lit.sign()]; - if (ineqs == 0) { + if (ineqs == nullptr) { ineqs = alloc(ptr_vector); m_var_infos[lit.var()].m_lit_watch[lit.sign()] = ineqs; } ineqs->push_back(c); } + void theory_pb::watch_var(bool_var v, ineq* c) { init_watch(v); ptr_vector* ineqs = m_var_infos[v].m_var_watch; - if (ineqs == 0) { + if (ineqs == nullptr) { ineqs = alloc(ptr_vector); m_var_infos[v].m_var_watch = ineqs; } @@ -738,6 +745,232 @@ namespace smt { } } + // ---------------------------- + // cardinality constraints + + + class theory_pb::card_justification : public justification { + card& m_card; + family_id m_fid; + public: + card_justification(card& c, family_id fid) + : justification(true), m_card(c), m_fid(fid) {} + + card& get_card() { return m_card; } + + virtual void get_antecedents(conflict_resolution& cr) { + 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 { + return m_fid; + } + + virtual proof* mk_proof(smt::conflict_resolution& cr) { return 0; } + + + }; + + + bool theory_pb::is_cardinality_constraint(app * atom) { + if (pb.is_ge(atom) && pb.has_unit_coefficients(atom)) { + return true; + } + if (pb.is_at_least_k(atom)) { + return true; + } + return false; + } + + bool theory_pb::internalize_card(app * atom, bool gate_ctx) { + context& ctx = get_context(); + if (ctx.b_internalized(atom)) { + return true; + } + if (!is_cardinality_constraint(atom)) { + return false; + } + unsigned num_args = atom->get_num_args(); + bool_var abv = ctx.mk_bool_var(atom); + ctx.set_var_theory(abv, get_id()); + unsigned bound = pb.get_k(atom).get_unsigned(); + literal lit(abv); + + if (bound == 0) { + ctx.mk_th_axiom(get_id(), 1, &lit); + return true; + } + if (bound > num_args) { + lit.neg(); + ctx.mk_th_axiom(get_id(), 1, &lit); + return true; + } + + // hack to differentiate constraints that come from input vs. lemmas. + bool aux = pb.is_at_least_k(atom); + + card* c = alloc(card, lit, bound, aux); + + for (expr* arg : *atom) { + c->add_arg(compile_arg(arg)); + } + + if (bound == c->size() || bound == 1) { + // + } + + if (bound == c->size()) { + card2conjunction(*c); + dealloc(c); + } + else if (1 == c->size()) { + card2disjunction(*c); + dealloc(c); + } + else { + SASSERT(0 < c->k() && c->k() < c->size()); + // initialize compilation thresholds, TBD + init_watch(abv); + m_var_infos[abv].m_card = c; + m_card_trail.push_back(abv); + } + return true; + } + + // \brief define cardinality constraint as conjunction. + // + void theory_pb::card2conjunction(card const& c) { + context& ctx = get_context(); + literal lit = c.lit(); + literal_vector& lits = get_literals(); + for (unsigned i = 0; i < c.size(); ++i) { + lits.push_back(~c.lit(i)); + } + lits.push_back(lit); + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + for (unsigned i = 0; i < c.size(); ++i) { + literal lits2[2] = { ~lit, c.lit(i) }; + ctx.mk_th_axiom(get_id(), 2, lits2); + } + } + + void theory_pb::card2disjunction(card const& c) { + context& ctx = get_context(); + literal lit = c.lit(); + literal_vector& lits = get_literals(); + for (unsigned i = 0; i < c.size(); ++i) { + lits.push_back(c.lit(i)); + } + lits.push_back(~lit); + ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); + for (unsigned i = 0; i < c.size(); ++i) { + literal lits2[2] = { lit, ~c.lit(i) }; + ctx.mk_th_axiom(get_id(), 2, lits2); + } + } + + 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) { + cards = alloc(ptr_vector); + m_var_infos[lit.var()].m_lit_cwatch[lit.sign()] = cards; + } + cards->push_back(c); + } + + + void theory_pb::unwatch_literal(literal lit, card* c) { + if (m_var_infos.size() <= static_cast(lit.var())) { + return; + } + ptr_vector* cards = m_var_infos[lit.var()].m_lit_cwatch[lit.sign()]; + if (cards) { + remove(*cards, c); + } + } + + void theory_pb::remove(ptr_vector& cards, card* c) { + for (unsigned j = 0; j < cards.size(); ++j) { + if (cards[j] == c) { + std::swap(cards[j], cards[cards.size()-1]); + cards.pop_back(); + break; + } + } + } + + std::ostream& theory_pb::display(std::ostream& out, card const& c, bool values) const { + context& ctx = get_context(); + out << c.lit(); + if (c.lit() != null_literal) { + if (values) { + out << "@(" << ctx.get_assignment(c.lit()); + if (ctx.get_assignment(c.lit()) != l_undef) { + out << ":" << ctx.get_assign_level(c.lit()); + } + out << ")"; + } + ctx.display_literal_verbose(out, c.lit()); out << "\n"; + } + else { + out << " "; + } + for (unsigned i = 0; i < c.size(); ++i) { + literal l = c.lit(i); + out << l; + if (values) { + out << "@(" << ctx.get_assignment(l); + if (ctx.get_assignment(l) != l_undef) { + out << ":" << ctx.get_assign_level(l); + } + out << ") "; + } + } + out << " >= " << c.k() << "\n"; + if (c.all_propagations()) out << "propagations: " << c.all_propagations() << "\n"; + return out; + } + + + void theory_pb::add_clause(card& c, literal_vector const& lits) { + m_stats.m_num_conflicts++; + context& ctx = get_context(); + justification* js = 0; + if (proofs_enabled()) { + js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); + } + c.inc_propagations(*this); + if (!resolve_conflict(c, lits)) { + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); + } + SASSERT(ctx.inconsistent()); + } + + void theory_pb::add_assign(card& c, literal l) { + context& ctx = get_context(); + if (ctx.get_assignment(l) == l_true) { + return; + } + c.inc_propagations(*this); + m_stats.m_num_propagations++; + TRACE("pb", tout << "#prop: " << c.num_propagations() << " - " << c.lit() << " => " << l << "\n";); + SASSERT(validate_unit_propagation(c)); + ctx.assign(l, ctx.mk_justification(card_justification(c, get_id()))); + } + + void theory_pb::clear_watch(card& c) { + unsigned sz = std::min(c.k() + 1, c.size()); + for (unsigned i = 0; i < sz; ++i) { + unwatch_literal(c.lit(i), &c); + } + } + + // + void theory_pb::collect_statistics(::statistics& st) const { st.update("pb conflicts", m_stats.m_num_conflicts); st.update("pb propagations", m_stats.m_num_propagations); @@ -745,7 +978,6 @@ namespace smt { st.update("pb compilations", m_stats.m_num_compiles); st.update("pb compiled clauses", m_stats.m_num_compiled_clauses); st.update("pb compiled vars", m_stats.m_num_compiled_vars); - m_simplex.collect_statistics(st); } void theory_pb::reset_eh() { @@ -755,8 +987,11 @@ namespace smt { } m_ineqs_trail.reset(); m_ineqs_lim.reset(); + m_card_trail.reset(); + m_card_lim.reset(); m_stats.reset(); m_to_compile.reset(); + m_cardinality_lemma = false; } void theory_pb::new_eq_eh(theory_var v1, theory_var v2) { @@ -771,22 +1006,12 @@ namespace smt { void theory_pb::assign_eh(bool_var v, bool is_true) { ptr_vector* ineqs = 0; + context& ctx = get_context(); literal nlit(v, is_true); init_watch(v); TRACE("pb", tout << "assign: " << ~nlit << "\n";); ineqs = m_var_infos[v].m_lit_watch[nlit.sign()]; - if (ineqs != 0) { - if (m_enable_simplex) { - mpq_inf num(mpq(is_true?1:0),mpq(0)); - if (!update_bound(v, ~nlit, is_true, num)) { - return; - } - - if (!check_feasible()) { - return; - } - } - + if (ineqs != nullptr) { for (unsigned i = 0; i < ineqs->size(); ++i) { SASSERT((*ineqs)[i]->is_ge()); if (assign_watch_ge(v, is_true, *ineqs, i)) { @@ -796,35 +1021,14 @@ namespace smt { } } ineqs = m_var_infos[v].m_var_watch; - if (ineqs != 0) { + if (ineqs != nullptr) { for (unsigned i = 0; i < ineqs->size(); ++i) { ineq* c = (*ineqs)[i]; assign_watch(v, is_true, *c); } } ineq* c = m_var_infos[v].m_ineq; - if (c != 0) { - if (m_enable_simplex) { - row_info const& info = m_ineq_row_info.find(v); - unsynch_mpq_manager mgr; - scoped_eps_numeral coeff(m_mpq_inf_mgr); - coeff = std::make_pair(mgr.dup(info.m_bound.to_mpq()), mpq(0)); - unsigned slack = info.m_slack; - if (is_true) { - update_bound(slack, literal(v), true, coeff); - if (c->is_eq()) { - update_bound(slack, literal(v), false, coeff); - } - } - else if (c->is_ge()) { - m_mpq_inf_mgr.sub(coeff, std::make_pair(mpq(1),mpq(0)), coeff); - update_bound(slack, ~literal(v), false, coeff); - } - - if (!check_feasible()) { - return; - } - } + if (c != nullptr) { if (c->is_ge()) { assign_ineq(*c, is_true); } @@ -832,11 +1036,45 @@ namespace smt { assign_eq(*c, is_true); } } + + ptr_vector* cards = m_var_infos[v].m_lit_cwatch[nlit.sign()]; + if (cards != 0 && !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) { + continue; + } + switch ((*it)->assign(*this, nlit)) { + case l_false: // conflict + for (; it != end; ++it, ++it2) { + *it2 = *it; + } + SASSERT(ctx.inconsistent()); + cards->set_end(it2); + return; + case l_undef: // watch literal was swapped + break; + case l_true: // unit propagation, keep watching the literal + if (it2 != it) { + *it2 = *it; + } + ++it2; + break; + } + } + cards->set_end(it2); + } + + card* crd = m_var_infos[v].m_card; + if (crd != 0 && !ctx.inconsistent()) { + crd->init_watch(*this, is_true); + } + } literal_vector& theory_pb::get_all_literals(ineq& c, bool negate) { context& ctx = get_context(); - literal_vector& lits = get_lits(); + literal_vector& lits = get_literals(); for (unsigned i = 0; i < c.size(); ++i) { literal l = c.lit(i); switch(ctx.get_assignment(l)) { @@ -851,14 +1089,13 @@ namespace smt { } } return lits; - } literal_vector& theory_pb::get_helpful_literals(ineq& c, bool negate) { scoped_mpz sum(m_mpz_mgr); mpz const& k = c.mpz_k(); context& ctx = get_context(); - literal_vector& lits = get_lits(); + literal_vector& lits = get_literals(); for (unsigned i = 0; sum < k && i < c.size(); ++i) { literal l = c.lit(i); if (ctx.get_assignment(l) == l_true) { @@ -873,7 +1110,7 @@ namespace smt { literal_vector& theory_pb::get_unhelpful_literals(ineq& c, bool negate) { context& ctx = get_context(); - literal_vector& lits = get_lits(); + literal_vector& lits = get_literals(); for (unsigned i = 0; i < c.size(); ++i) { literal l = c.lit(i); if (ctx.get_assignment(l) == l_false) { @@ -889,18 +1126,19 @@ namespace smt { ineq& c; public: rewatch_vars(theory_pb& p, ineq& c): pb(p), c(c) {} - virtual void undo(context& ctx) { + void undo(context& ctx) override { for (unsigned i = 0; i < c.size(); ++i) { pb.watch_var(c.lit(i).var(), &c); } } }; + class theory_pb::negate_ineq : public trail { ineq& c; public: negate_ineq(ineq& c): c(c) {} - virtual void undo(context& ctx) { + void undo(context& ctx) override { c.negate(); } }; @@ -918,7 +1156,6 @@ namespace smt { ctx.push_trail(value_trail(c.m_nfixed)); ctx.push_trail(rewatch_vars(*this, c)); - clear_watch(c); SASSERT(c.is_ge()); unsigned sz = c.size(); if (c.lit().sign() == is_true) { @@ -957,10 +1194,8 @@ namespace smt { literal_vector& lits = get_unhelpful_literals(c, true); lits.push_back(c.lit()); for (unsigned i = 0; i < sz; ++i) { - if (ctx.get_assignment(c.lit(i)) == l_undef) { - DEBUG_CODE(validate_assign(c, lits, c.lit(i));); - add_assign(c, lits, c.lit(i)); - } + DEBUG_CODE(validate_assign(c, lits, c.lit(i));); + add_assign(c, lits, c.lit(i)); } } } @@ -1077,11 +1312,10 @@ namespace smt { inequalities are unit literals and formulas in negation normal form (inequalities are closed under negation). */ - bool theory_pb::assign_watch_ge(bool_var v, bool is_true, watch_list& watch, unsigned watch_index) { + bool theory_pb::assign_watch_ge(bool_var v, bool is_true, ineq_watch& watch, unsigned watch_index) { bool removed = false; context& ctx = get_context(); ineq& c = *watch[watch_index]; - //display(std::cout << v << " ", c, true); unsigned w = c.find_lit(v, 0, c.watch_size()); SASSERT(ctx.get_assignment(c.lit()) == l_true); SASSERT(is_true == c.lit(w).sign()); @@ -1158,8 +1392,8 @@ namespace smt { ast_manager& m; theory_pb& th; pb_util pb; - typedef smt::literal literal; - typedef smt::literal_vector literal_vector; + typedef smt::literal pliteral; + typedef smt::literal_vector pliteral_vector; psort_expr(context& c, theory_pb& th): ctx(c), @@ -1167,7 +1401,7 @@ namespace smt { th(th), pb(m) {} - literal fresh() { + literal fresh(char const* ) { app_ref y(m); y = pb.mk_fresh_bool(); return literal(ctx.mk_bool_var(y)); @@ -1197,7 +1431,7 @@ namespace smt { void mk_clause(unsigned n, literal const* ls) { literal_vector tmp(n, ls); - ctx.mk_clause(n, tmp.c_ptr(), th.justify(tmp), CLS_AUX, 0); + ctx.mk_clause(n, tmp.c_ptr(), th.justify(tmp), CLS_AUX, nullptr); } literal mk_false() { return false_literal; } @@ -1230,6 +1464,81 @@ namespace smt { compile_ineq(*m_to_compile[i]); } m_to_compile.reset(); + + return; + + if (m_restart_lim <= m_restart_inc) { + m_restart_inc = 0; + if (gc()) { + m_restart_lim = 3; + } + else { + m_restart_lim *= 4; + m_restart_lim /= 3; + } + } + ++m_restart_inc; + } + + bool theory_pb::gc() { + + context& ctx = get_context(); + + unsigned z = 0, nz = 0; + m_occs.reset(); + for (unsigned i = 0; i < m_card_trail.size(); ++i) { + bool_var v = m_card_trail[i]; + if (v == null_bool_var) continue; + card* c = m_var_infos[v].m_card; + if (c) { + c->reset_propagations(); + literal lit = c->lit(); + if (c->is_aux() && ctx.get_assign_level(lit) > ctx.get_search_level()) { + double activity = ctx.get_activity(v); + if (activity <= 0) { + nz++; + } + else { + z++; + clear_watch(*c); + m_var_infos[v].m_card = 0; + dealloc(c); + m_card_trail[i] = null_bool_var; + ctx.remove_watch(v); + // TBD: maybe v was used in a clause for propagation. + m_occs.insert(v); + } + } + } + } + clause_vector const& lemmas = ctx.get_lemmas(); + for (unsigned i = 0; i < lemmas.size(); ++i) { + clause* cl = lemmas[i]; + if (!cl->deleted()) { + unsigned sz = cl->get_num_literals(); + for (unsigned j = 0; j < sz; ++j) { + literal lit = cl->get_literal(j); + if (m_occs.contains(lit.var())) { + //std::cout << "deleting clause " << lit << " " << sz << "\n"; + //ctx.mark_as_deleted(cl); + break; + } + } + } + } + + std::cout << "zs: " << z << " nzs: " << nz << " lemmas: " << ctx.get_lemmas().size() << " trail: " << m_card_trail.size() << "\n"; + return z*10 >= nz; + + m_occs.reset(); + for (unsigned i = 0; i < lemmas.size(); ++i) { + clause* cl = lemmas[i]; + unsigned sz = cl->get_num_literals(); + for (unsigned j = 0; j < sz; ++j) { + unsigned idx = cl->get_literal(j).index(); + m_occs.insert(idx); + } + } } @@ -1308,6 +1617,7 @@ namespace smt { void theory_pb::push_scope_eh() { m_ineqs_lim.push_back(m_ineqs_trail.size()); + m_card_lim.push_back(m_card_trail.size()); } void theory_pb::pop_scope_eh(unsigned num_scopes) { @@ -1319,22 +1629,28 @@ namespace smt { bool_var v = m_ineqs_trail.back(); ineq* c = m_var_infos[v].m_ineq; clear_watch(*c); - m_var_infos[v].m_ineq = 0; + m_var_infos[v].m_ineq = nullptr; m_ineqs_trail.pop_back(); - if (m_enable_simplex) { - row_info r_info; - VERIFY(m_ineq_row_info.find(v, r_info)); - m_ineq_row_info.erase(v); - bool_var v2 = m_ineq_rep.find(r_info.m_rep); - if (v == v2) { - m_simplex.del_row(r_info.m_slack); - m_ineq_rep.erase(r_info.m_rep); - } - } m_to_compile.erase(c); dealloc(c); } m_ineqs_lim.resize(new_lim); + + + new_lim = m_card_lim.size() - num_scopes; + sz = m_card_lim[new_lim]; + while (m_card_trail.size() > sz) { + bool_var v = m_card_trail.back(); + m_card_trail.pop_back(); + if (v != null_bool_var) { + card* c = m_var_infos[v].m_card; + clear_watch(*c); + m_var_infos[v].m_card = 0; + dealloc(c); + } + } + + m_card_lim.resize(new_lim); } void theory_pb::clear_watch(ineq& c) { @@ -1357,7 +1673,7 @@ namespace smt { public: unwatch_ge(theory_pb& p, ineq& c): pb(p), c(c) {} - virtual void undo(context& ctx) { + void undo(context& ctx) override { for (unsigned i = 0; i < c.watch_size(); ++i) { pb.unwatch_literal(c.lit(i), &c); } @@ -1399,11 +1715,6 @@ namespace smt { } } - literal_vector& theory_pb::get_lits() { - m_literals.reset(); - return m_literals; - } - class theory_pb::pb_justification : public theory_propagation_justification { ineq& m_ineq; public: @@ -1419,364 +1730,665 @@ namespace smt { inc_propagations(c); m_stats.m_num_propagations++; context& ctx = get_context(); - TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - "; - for (unsigned i = 0; i < lits.size(); ++i) { - tout << lits[i] << " "; - } - tout << "=> " << l << "\n"; + TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - " << lits; + tout << " => " << l << "\n"; display(tout, c, true);); + SASSERT(validate_antecedents(lits)); ctx.assign(l, ctx.mk_justification( pb_justification( c, get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), l))); } - + void theory_pb::add_clause(ineq& c, literal_vector const& lits) { inc_propagations(c); m_stats.m_num_conflicts++; context& ctx = get_context(); -#if 0 - if (m_stats.m_num_conflicts == 1000) { - display(std::cout); - } -#endif - TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - "; - for (unsigned i = 0; i < lits.size(); ++i) { - tout << lits[i] << " "; - } - tout << "\n"; - display(tout, c, true);); - + TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - " << lits << "\n"; + display(tout, c, true);); justification* js = 0; - - if (m_conflict_frequency == 0 || (m_conflict_frequency -1 == (c.m_num_propagations % m_conflict_frequency))) { - resolve_conflict(c); - } if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); } - TRACE("pb", tout << lits << "\n";); ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); } - void theory_pb::set_mark(bool_var v, unsigned idx) { - SASSERT(v != null_bool_var); - if (v >= static_cast(m_conseq_index.size())) { - m_conseq_index.resize(v+1, null_index); + int theory_pb::get_coeff(bool_var v) const { + return m_coeffs.get(v, 0); + } + + int theory_pb::get_abs_coeff(bool_var v) const { + int coeff = get_coeff(v); + if (coeff < 0) coeff = -coeff; + return coeff; + } + + void theory_pb::reset_coeffs() { + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + m_coeffs[m_active_vars[i]] = 0; } - SASSERT(!is_marked(v) || m_conseq_index[v] == idx); - m_marked.push_back(v); - m_conseq_index[v] = idx; + m_active_vars.reset(); } - bool theory_pb::is_marked(bool_var v) const { - return - (v < static_cast(m_conseq_index.size())) && - (m_conseq_index[v] != null_index); - } - - void theory_pb::unset_mark(bool_var v) { - SASSERT(v != null_bool_var); - if (v < static_cast(m_conseq_index.size())) { - m_conseq_index[v] = null_index; - } - } - - void theory_pb::unset_marks() { - for (unsigned i = 0; i < m_marked.size(); ++i) { - unset_mark(m_marked[i]); - } - m_marked.reset(); - } - - void theory_pb::process_antecedent(literal l, numeral coeff) { + void theory_pb::process_antecedent(literal l, int offset) { context& ctx = get_context(); + SASSERT(ctx.get_assignment(l) == l_false); bool_var v = l.var(); unsigned lvl = ctx.get_assign_level(v); - if (ctx.get_assignment(l) != l_false) { - m_lemma.m_k -= coeff; - if (m_learn_complements && is_marked(v)) { - SASSERT(ctx.get_assignment(l) == l_true); - numeral& lcoeff = m_lemma[m_conseq_index[v]].second; - lcoeff -= coeff; - if (!lcoeff.is_pos()) { - // perhaps let lemma simplification change coefficient - // when negative? - remove_from_lemma(m_conseq_index[v]); - } - } + if (lvl > ctx.get_base_level() && !ctx.is_marked(v) && lvl == m_conflict_lvl) { + ctx.set_mark(v); + ++m_num_marks; } - else if (lvl > ctx.get_base_level()) { - if (is_marked(v)) { - m_lemma[m_conseq_index[v]].second += coeff; - SASSERT(m_lemma[m_conseq_index[v]].second.is_pos()); - } - else { - if (lvl == m_conflict_lvl) { - TRACE("pb", tout << "add mark: " << l << " " << coeff << "\n";); - ++m_num_marks; - } - set_mark(v, m_lemma.size()); - m_lemma.push_back(std::make_pair(l, coeff)); - } - TRACE("pb_verbose", tout - << "ante: " << m_lemma.lit(m_conseq_index[v]) << "*" - << m_lemma.coeff(m_conseq_index[v]) << " " << lvl << "\n";); + inc_coeff(l, offset); + } + + void theory_pb::process_card(card& c, int offset) { + context& ctx = get_context(); + SASSERT(c.k() <= c.size()); + SASSERT(ctx.get_assignment(c.lit()) == l_true); + for (unsigned i = c.k(); i < c.size(); ++i) { + process_antecedent(c.lit(i), offset); + } + for (unsigned i = 0; i < c.k(); ++i) { + inc_coeff(c.lit(i), offset); + } + if (ctx.get_assign_level(c.lit()) > ctx.get_base_level()) { + m_antecedents.push_back(c.lit()); } } - void theory_pb::process_ineq(ineq& c, literal conseq, numeral coeff1) { - - // - // Create CUT. - // - - // - // . find coeff2 - // . find lcm of coefficients to conseq. - // . multiply m_lemma by lcm/coeff coefficient to align. - // . create lcm/coeff_2 to multiply on this side. - // . cut resolve constraints. - // - + bool theory_pb::validate_lemma() { + int value = -m_bound; context& ctx = get_context(); - numeral coeff2 = (conseq==null_literal)?numeral::one():numeral::zero(); - for (unsigned i = 0; i < c.size(); ++i) { - if (c.lit(i) == conseq) { - coeff2 = c.coeff(i); + normalize_active_coeffs(); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int coeff = get_coeff(v); + SASSERT(coeff != 0); + if (coeff < 0 && ctx.get_assignment(v) != l_true) { + value -= coeff; + } + else if (coeff > 0 && ctx.get_assignment(v) != l_false) { + value += coeff; + } + } + // std::cout << "bound: " << m_bound << " value " << value << " coeffs: " << m_active_vars.size() << " lemma is " << (value >= 0 ? "sat" : "unsat") << "\n"; + return value < 0; + } + + bool theory_pb::validate_implies(app_ref& A, app_ref& B) { + static bool validating = true; // false; + if (validating) return true; + validating = true; + ast_manager& m = get_manager(); + smt_params fp; + kernel k(m, fp); + expr_ref notB(m.mk_not(B), m); + k.assert_expr(A); + k.assert_expr(notB); + lbool is_sat = k.check(); + validating = false; + std::cout << is_sat << "\n"; + if (is_sat == l_true) { + std::cout << A << "\n"; + std::cout << B << "\n"; + } + SASSERT(is_sat != l_true); + return true; + } + + app_ref theory_pb::justification2expr(b_justification& js, literal conseq) { + ast_manager& m = get_manager(); + app_ref result(m.mk_true(), m); + expr_ref_vector args(m); + vector coeffs; + switch(js.get_kind()) { + + case b_justification::CLAUSE: { + clause& cls = *js.get_clause(); + justification* cjs = cls.get_justification(); + if (cjs && !is_proof_justification(*cjs)) { + break; + } + for (unsigned i = 0; i < cls.get_num_literals(); ++i) { + literal lit = cls.get_literal(i); + args.push_back(literal2expr(lit)); + } + result = m.mk_or(args.size(), args.c_ptr()); + break; + } + case b_justification::BIN_CLAUSE: + result = m.mk_or(literal2expr(conseq), literal2expr(~js.get_literal())); + break; + case b_justification::AXIOM: + break; + case b_justification::JUSTIFICATION: { + justification* j = js.get_justification(); + card_justification* pbj = 0; + if (j->get_from_theory() == get_id()) { + pbj = dynamic_cast(j); + } + if (pbj != 0) { + card& c2 = pbj->get_card(); + result = card2expr(c2); + } + break; + } + default: + break; + } + return result; + } + + int theory_pb::arg_max(int& max_coeff) { + max_coeff = 0; + int arg_max = -1; + while (!m_active_coeffs.empty()) { + max_coeff = m_active_coeffs.back(); + if (m_coeff2args[max_coeff].empty()) { + m_active_coeffs.pop_back(); + } + else { + arg_max = m_coeff2args[max_coeff].back(); + m_coeff2args[max_coeff].pop_back(); break; } } - SASSERT(coeff2.is_pos()); - numeral lc = lcm(coeff1, coeff2); - numeral g = lc/coeff1; - SASSERT(g.is_int()); - if (g > numeral::one()) { - for (unsigned i = 0; i < m_lemma.size(); ++i) { - m_lemma[i].second *= g; + return arg_max; + } + + literal theory_pb::get_asserting_literal(literal p) { + if (get_abs_coeff(p.var()) != 0) { + return p; + } + context& ctx = get_context(); + unsigned lvl = 0; + + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + if (ctx.get_assignment(lit) == l_false && ctx.get_assign_level(lit) > lvl) { + p = lit; } - m_lemma.m_k *= g; - } - g = lc/coeff2; - SASSERT(g.is_int()); - m_lemma.m_k += g*c.k(); - - for (unsigned i = 0; i < c.size(); ++i) { - process_antecedent(c.lit(i), g*c.coeff(i)); } - SASSERT(ctx.get_assignment(c.lit()) == l_true); - if (ctx.get_assign_level(c.lit()) > ctx.get_base_level()) { - m_ineq_literals.push_back(c.lit()); + return p; + } + + void theory_pb::reset_arg_max() { + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + int coeff = get_abs_coeff(m_active_vars[i]); + if (static_cast(m_coeff2args.size()) > coeff) { + m_coeff2args[coeff].reset(); + } } } - - // - // modeled after sat_solver/smt_context - // - bool theory_pb::resolve_conflict(ineq& c) { - - if (!c.is_ge()) { + + bool theory_pb::init_arg_max() { + if (m_coeff2args.size() < (1 << 10)) { + m_coeff2args.resize(1 << 10); + } + m_active_coeffs.reset(); + if (m_active_vars.empty()) { return false; } - TRACE("pb", display(tout, c, true);); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int coeff = get_abs_coeff(v); + if (coeff >= static_cast(m_coeff2args.size())) { + reset_arg_max(); + return false; + } + if (m_coeff2args[coeff].empty()) { + m_active_coeffs.push_back(coeff); + } + m_coeff2args[coeff].push_back(v); + } + std::sort(m_active_coeffs.begin(), m_active_coeffs.end()); + return true; + } - bool_var v; - literal conseq; + void theory_pb::add_cardinality_lemma() { context& ctx = get_context(); - unsigned& lvl = m_conflict_lvl = 0; - for (unsigned i = 0; i < c.size(); ++i) { - if (ctx.get_assignment(c.lit(i)) == l_false) { - lvl = std::max(lvl, ctx.get_assign_level(c.lit(i))); + normalize_active_coeffs(); + int s = 0; + int new_bound = 0; + if (!init_arg_max()) { + return; + } + // TBD: can be optimized + while (s < m_bound) { + int coeff; + int arg = arg_max(coeff); + if (arg == -1) break; + s += coeff; + ++new_bound; + } + int slack = m_active_coeffs.empty() ? m_bound : (std::min(m_bound, static_cast(m_active_coeffs[0]) - 1)); + reset_arg_max(); + + while (slack > 0) { + bool found = false; + int v = 0; + int coeff = 0; + for (unsigned i = 0; !found && i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + coeff = get_abs_coeff(v); + if (0 < coeff && coeff < slack) { + found = true; + } + } + if (!found) { + break; + } + slack -= coeff; + m_coeffs[v] = 0; // deactivate coefficient. + } + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int coeff = get_coeff(v); + if (coeff < 0) { + m_coeffs[v] = -1; + } + else if (coeff > 0) { + m_coeffs[v] = 1; + } + } + + m_bound = new_bound; + if (!validate_lemma()) { + return; + } + SASSERT(m_bound > 0); + if (m_bound > static_cast(m_active_vars.size())) { + return; + } + if (m_bound == static_cast(m_active_vars.size())) { + return; + } + + m_antecedent_exprs.reset(); + m_antecedent_signs.reset(); + m_cardinality_exprs.reset(); + m_cardinality_signs.reset(); + for (unsigned i = 0; i < m_antecedents.size(); ++i) { + literal lit = m_antecedents[i]; + m_antecedent_exprs.push_back(ctx.bool_var2expr(lit.var())); + m_antecedent_signs.push_back(lit.sign()); + } + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + m_cardinality_exprs.push_back(ctx.bool_var2expr(v)); + m_cardinality_signs.push_back(get_coeff(v) < 0); + } + m_cardinality_lemma = true; + } + + void theory_pb::normalize_active_coeffs() { + while (!m_active_var_set.empty()) m_active_var_set.erase(); + unsigned i = 0, j = 0, sz = m_active_vars.size(); + for (; i < sz; ++i) { + bool_var v = m_active_vars[i]; + if (!m_active_var_set.contains(v) && get_coeff(v) != 0) { + m_active_var_set.insert(v); + if (j != i) { + m_active_vars[j] = m_active_vars[i]; + } + ++j; } } - if (lvl < ctx.get_assign_level(c.lit()) || lvl == ctx.get_base_level()) { + sz = j; + m_active_vars.shrink(sz); + } + + void theory_pb::inc_coeff(literal l, int offset) { + SASSERT(offset > 0); + bool_var v = l.var(); + SASSERT(v != null_bool_var); + if (static_cast(m_coeffs.size()) <= v) { + m_coeffs.resize(v + 1, 0); + } + int coeff0 = m_coeffs[v]; + if (coeff0 == 0) { + m_active_vars.push_back(v); + } + + int inc = l.sign() ? -offset : offset; + int coeff1 = inc + coeff0; + m_coeffs[v] = coeff1; + + if (coeff0 > 0 && inc < 0) { + m_bound -= coeff0 - std::max(0, coeff1); + } + else if (coeff0 < 0 && inc > 0) { + m_bound -= std::min(0, coeff1) - coeff0; + } + } + + /** + \brief attempt a cut and simplification of constraints. + */ + void theory_pb::cut() { + unsigned g = 0; + for (unsigned i = 0; g != 1 && i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + int coeff = get_abs_coeff(v); + if (coeff == 0) { + continue; + } + if (m_bound < coeff) { + if (get_coeff(v) > 0) { + m_coeffs[v] = m_bound; + } + else { + m_coeffs[v] = -m_bound; + } + coeff = m_bound; + } + SASSERT(0 < coeff && coeff <= m_bound); + if (g == 0) { + g = static_cast(coeff); + } + else { + g = u_gcd(g, static_cast(coeff)); + } + } + if (g >= 2) { + normalize_active_coeffs(); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + m_coeffs[m_active_vars[i]] /= g; + } + m_bound = (m_bound + g - 1) / g; + std::cout << "CUT " << g << "\n"; + TRACE("pb", display_resolved_lemma(tout << "cut\n");); + } + } + + bool theory_pb::can_propagate() { return m_cardinality_lemma; } + + void theory_pb::propagate() { + context& ctx = get_context(); + ast_manager& m = get_manager(); + if (!m_cardinality_lemma) { + return; + } + m_cardinality_lemma = false; + if (ctx.inconsistent()) { + return; + } + m_antecedents.reset(); + + for (unsigned i = 0; i < m_antecedent_exprs.size(); ++i) { + expr* a = m_antecedent_exprs[i].get(); + if (!ctx.b_internalized(a)) { + std::cout << "not internalized " << mk_pp(a, m) << "\n"; + return; + } + m_antecedents.push_back(~literal(ctx.get_bool_var(a), m_antecedent_signs[i])); + } + for (unsigned i = 0; i < m_cardinality_exprs.size(); ++i) { + expr* a = m_cardinality_exprs[i].get(); + if (!ctx.b_internalized(a)) { + std::cout << "not internalized " << mk_pp(a, m) << "\n"; + return; + } + if (m_cardinality_signs[i]) { + m_cardinality_exprs[i] = m.mk_not(a); + } + } + app_ref atl(pb.mk_at_least_k(m_cardinality_exprs.size(), m_cardinality_exprs.c_ptr(), m_bound), m); + VERIFY(internalize_card(atl, false)); + bool_var abv = ctx.get_bool_var(atl); + m_antecedents.push_back(literal(abv)); + justification* js = 0; + if (proofs_enabled()) { + js = 0; // + } + ctx.mk_clause(m_antecedents.size(), m_antecedents.c_ptr(), js, CLS_AUX_LEMMA, 0); + } + + bool theory_pb::resolve_conflict(card& c, literal_vector const& confl) { + + TRACE("pb", display(tout, c, true); ); + + bool_var v; + context& ctx = get_context(); + ast_manager& m = get_manager(); + m_conflict_lvl = 0; + m_cardinality_lemma = false; + for (unsigned i = 0; i < confl.size(); ++i) { + literal lit = confl[i]; + SASSERT(ctx.get_assignment(lit) == l_false); + m_conflict_lvl = std::max(m_conflict_lvl, ctx.get_assign_level(lit)); + } + if (m_conflict_lvl < ctx.get_assign_level(c.lit()) || m_conflict_lvl == ctx.get_base_level()) { return false; } - unset_marks(); - m_num_marks = 0; - m_lemma.reset(); - m_lemma.m_k.reset(); - m_ineq_literals.reset(); - process_ineq(c, null_literal, numeral::one()); // add consequent to lemma. + // std::cout << c.lit() << "\n"; + reset_coeffs(); + m_num_marks = 0; + m_bound = c.k(); + m_antecedents.reset(); + m_resolved.reset(); + literal_vector ante; + + process_card(c, 1); + + app_ref A(m), B(m), C(m); + DEBUG_CODE(A = c.to_expr(*this);); + // point into stack of assigned literals literal_vector const& lits = ctx.assigned_literals(); SASSERT(!lits.empty()); unsigned idx = lits.size()-1; - + b_justification js; + literal conseq = ~confl[2]; + int bound = 1; + while (m_num_marks > 0) { - TRACE("pb_verbose", display(tout << "lemma ", m_lemma);); + v = conseq.var(); - lbool is_sat = m_lemma.normalize(false); - if (is_sat == l_false) { - break; + int offset = get_abs_coeff(v); + + if (offset == 0) { + goto process_next_resolvent; } - if (is_sat == l_true) { - IF_VERBOSE(0, verbose_stream() << "lemma already evaluated\n";); - TRACE("pb", tout << "lemma already evaluated\n";); + SASSERT(validate_lemma()); + if (offset > 1000) { + while (m_num_marks > 0 && idx > 0) { + v = lits[idx].var(); + if (ctx.is_marked(v)) { + ctx.unset_mark(v); + } + --idx; + } return false; } - TRACE("pb", display(tout, m_lemma);); - SASSERT(m_lemma.well_formed()); - // - // find the next marked variable in the assignment stack - // - do { - conseq = lits[idx]; - v = conseq.var(); - --idx; - } - while (!is_marked(v) && idx > 0); - if (idx == 0 && !is_marked(v)) { - // - // Yes, this can (currently) happen because - // the decisions for performing unit propagation - // are made asynchronously. - // In other words, PB unit propagation does not follow the - // same order as the assignment stack. - // It is not a correctness bug but causes to miss lemmas. - // - IF_VERBOSE(12, display_resolved_lemma(verbose_stream());); - TRACE("pb", display_resolved_lemma(tout);); - return false; - } + SASSERT(offset > 0); + + js = ctx.get_justification(v); + + TRACE("pb", + display_resolved_lemma(tout << conseq << "\n"); + ctx.display(tout, js);); + + m_resolved.push_back(conseq); + - unsigned conseq_index = m_conseq_index[v]; - numeral conseq_coeff = m_lemma.coeff(conseq_index); - - TRACE("pb", display(tout, m_lemma, true); - tout << "conseq: " << conseq << " at index: " << conseq_index << "\n";); - - SASSERT(~conseq == m_lemma.lit(conseq_index)); - - remove_from_lemma(conseq_index); - - b_justification js = ctx.get_justification(v); - // // Resolve selected conseq with antecedents. // + + bound = 1; switch(js.get_kind()) { case b_justification::CLAUSE: { + inc_coeff(conseq, offset); clause& cls = *js.get_clause(); justification* cjs = cls.get_justification(); if (cjs && !is_proof_justification(*cjs)) { TRACE("pb", tout << "skipping justification for clause over: " << conseq << " " << typeid(*cjs).name() << "\n";); - m_ineq_literals.push_back(conseq); break; } unsigned num_lits = cls.get_num_literals(); if (cls.get_literal(0) == conseq) { - process_antecedent(cls.get_literal(1), conseq_coeff); + process_antecedent(cls.get_literal(1), offset); } else { SASSERT(cls.get_literal(1) == conseq); - process_antecedent(cls.get_literal(0), conseq_coeff); + process_antecedent(cls.get_literal(0), offset); } for (unsigned i = 2; i < num_lits; ++i) { - process_antecedent(cls.get_literal(i), conseq_coeff); + process_antecedent(cls.get_literal(i), offset); } - TRACE("pb", for (unsigned i = 0; i < num_lits; ++i) tout << cls.get_literal(i) << " "; tout << "\n";); + TRACE("pb", tout << literal_vector(cls.get_num_literals(), cls.begin()) << "\n";); break; } case b_justification::BIN_CLAUSE: - process_antecedent(~js.get_literal(), conseq_coeff); - TRACE("pb", tout << "binary: " << js.get_literal() << "\n";); + inc_coeff(conseq, offset); + process_antecedent(~js.get_literal(), offset); break; case b_justification::AXIOM: - if (ctx.get_assign_level(v) > ctx.get_base_level()) { - m_ineq_literals.push_back(conseq); - } - TRACE("pb", tout << "axiom " << conseq << "\n";); break; case b_justification::JUSTIFICATION: { justification* j = js.get_justification(); - pb_justification* pbj = 0; + card_justification* pbj = nullptr; - if (!conseq.sign() && j->get_from_theory() == get_id()) { - pbj = dynamic_cast(j); + if (j->get_from_theory() == get_id()) { + pbj = dynamic_cast(j); } - if (pbj && pbj->get_ineq().is_eq()) { - // only resolve >= that are positive consequences. - pbj = 0; - } - if (pbj && pbj->get_ineq().lit() == conseq) { - // can't resolve against literal representing inequality. - pbj = 0; - } - if (pbj) { - // weaken the lemma and resolve. - TRACE("pb", display(tout << "resolve with inequality", pbj->get_ineq(), true);); - process_ineq(pbj->get_ineq(), conseq, conseq_coeff); + if (pbj == nullptr) { + TRACE("pb", tout << "skip justification for " << conseq << "\n";); + inc_coeff(conseq, offset); } else { - TRACE("pb", tout << "skipping justification for " << conseq - << " from theory " << j->get_from_theory() << " " - << typeid(*j).name() << "\n";); - m_ineq_literals.push_back(conseq); + card& c2 = pbj->get_card(); + process_card(c2, offset); + bound = c2.k(); } + + // std::cout << " offset: " << offset << " bound: " << bound << "\n"; break; } default: UNREACHABLE(); } - } + m_bound += offset * bound; - TRACE("pb", - for (unsigned i = 0; i < m_ineq_literals.size(); ++i) { - tout << m_ineq_literals[i] << " "; - } - display(tout << "=> ", m_lemma);); + DEBUG_CODE( + B = justification2expr(js, conseq); + C = active2expr(); + B = m.mk_and(A, B); + validate_implies(B, C); + A = C;); - // 3x + 3y + z + u >= 4 - // ~x /\ ~y => z + u >= + cut(); - IF_VERBOSE(14, display(verbose_stream() << "lemma1: ", m_lemma);); - hoist_maximal_values(); - lbool is_true = m_lemma.normalize(false); - m_lemma.prune(false); + process_next_resolvent: - IF_VERBOSE(14, display(verbose_stream() << "lemma2: ", m_lemma);); - //unsigned l_size = m_ineq_literals.size() + ((is_true==l_false)?0:m_lemma.size()); - //if (s_min_l_size >= l_size) { - // verbose_stream() << "(pb.conflict min size: " << l_size << ")\n"; - // s_min_l_size = l_size; - //} - IF_VERBOSE(1, verbose_stream() << "(pb.conflict " << m_ineq_literals.size() << " " << m_lemma.size() << ")\n";); - switch(is_true) { - case l_true: - UNREACHABLE(); - return false; - case l_false: - inc_propagations(c); - m_stats.m_num_conflicts++; - for (unsigned i = 0; i < m_ineq_literals.size(); ++i) { - m_ineq_literals[i].neg(); + // find the next marked variable in the assignment stack + // + while (true) { + conseq = lits[idx]; + v = conseq.var(); + if (ctx.is_marked(v)) break; + SASSERT(idx > 0); + --idx; } - TRACE("pb", tout << m_ineq_literals << "\n";); - ctx.mk_clause(m_ineq_literals.size(), m_ineq_literals.c_ptr(), justify(m_ineq_literals), CLS_AUX_LEMMA, 0); - break; - default: { - app_ref tmp = m_lemma.to_expr(false, ctx, get_manager()); - internalize_atom(tmp, false); - ctx.mark_as_relevant(tmp.get()); - literal l(ctx.get_bool_var(tmp)); - add_assign(c, m_ineq_literals, l); - break; + + SASSERT(ctx.get_assign_level(v) == m_conflict_lvl); + ctx.unset_mark(v); + --idx; + --m_num_marks; } + SASSERT(validate_lemma()); + + TRACE("pb", display_resolved_lemma(tout << "done\n");); + + + normalize_active_coeffs(); + + if (m_bound > 0 && m_active_vars.empty()) { + return false; } - return true; + + int slack = -m_bound; + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + slack += get_abs_coeff(v); + } + +#if 1 + //std::cout << slack << " " << m_bound << "\n"; + unsigned i = 0; + literal_vector const& alits = ctx.assigned_literals(); + + literal alit = get_asserting_literal(~conseq); + slack -= get_abs_coeff(alit.var()); + + for (i = alits.size(); 0 <= slack && i > 0; ) { + --i; + literal lit = alits[i]; + bool_var v = lit.var(); + // -3*x >= k + if (m_active_var_set.contains(v) && v != alit.var()) { + int coeff = get_coeff(v); + //std::cout << coeff << " " << lit << "\n"; + if (coeff < 0 && !lit.sign()) { + slack += coeff; + m_antecedents.push_back(lit); + //std::cout << "ante: " << lit << "\n"; + } + else if (coeff > 0 && lit.sign()) { + slack -= coeff; + m_antecedents.push_back(lit); + //std::cout << "ante: " << lit << "\n"; + } + } + } + SASSERT(slack < 0); + +#else + + literal alit = get_asserting_literal(~conseq); + slack -= get_abs_coeff(alit.var()); + + for (unsigned i = 0; 0 <= slack; ++i) { + SASSERT(i < m_active_vars.size()); + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + if (v != alit.var() && ctx.get_assignment(lit) == l_false) { + m_antecedents.push_back(~lit); + slack -= get_abs_coeff(v); + } + if (slack < 0) { + std::cout << i << " " << m_active_vars.size() << "\n"; + } + } +#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))); + + DEBUG_CODE( + m_antecedents.push_back(~alit); + expr_ref_vector args(m); + for (unsigned i = 0; i < m_antecedents.size(); ++i) { + args.push_back(literal2expr(m_antecedents[i])); + } + B = m.mk_not(m.mk_and(args.size(), args.c_ptr())); + validate_implies(A, B); ); + // add_cardinality_lemma(); + return true; } bool theory_pb::is_proof_justification(justification const& j) const { @@ -1785,7 +2397,7 @@ namespace smt { justification* theory_pb::justify(literal l1, literal l2) { literal lits[2] = { l1, l2 }; - justification* js = 0; + justification* js = nullptr; if (proofs_enabled()) { js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), 2, lits)); } @@ -1793,37 +2405,13 @@ namespace smt { } justification* theory_pb::justify(literal_vector const& lits) { - justification* js = 0; + justification* js = nullptr; if (proofs_enabled()) { js = get_context().mk_justification(theory_axiom_justification(get_id(), get_context().get_region(), lits.size(), lits.c_ptr())); } return js; } - void theory_pb::hoist_maximal_values() { - for (unsigned i = 0; i < m_lemma.size(); ++i) { - if (m_lemma.coeff(i) >= m_lemma.k()) { - m_ineq_literals.push_back(~m_lemma.lit(i)); - std::swap(m_lemma[i], m_lemma[m_lemma.size()-1]); - m_lemma.pop_back(); - --i; - } - } - } - - void theory_pb::remove_from_lemma(unsigned idx) { - // Remove conseq from lemma: - literal lit = m_lemma.lit(idx); - unsigned last = m_lemma.size()-1; - if (idx != last) { - m_lemma[idx] = m_lemma[last]; - m_conseq_index[m_lemma.lit(idx).var()] = idx; - } - m_lemma.pop_back(); - unset_mark(lit.var()); - --m_num_marks; - } - // debug methods void theory_pb::validate_watch(ineq const& c) const { @@ -1841,9 +2429,9 @@ namespace smt { void theory_pb::validate_assign(ineq const& c, literal_vector const& lits, literal l) const { uint_set nlits; - for (unsigned i = 0; i < lits.size(); ++i) { - SASSERT(get_context().get_assignment(lits[i]) == l_true); - nlits.insert((~lits[i]).index()); + for (literal lit : lits) { + SASSERT(get_context().get_assignment(lit) == l_true); + nlits.insert((~lit).index()); } SASSERT(get_context().get_assignment(l) == l_undef); SASSERT(get_context().get_assignment(c.lit()) == l_true); @@ -1857,9 +2445,7 @@ namespace smt { } CTRACE("pb", (sum >= c.k()), display(tout << "invalid assign" , c, true); - for (unsigned i = 0; i < lits.size(); ++i) { - tout << lits[i] << " "; - } + for (literal lit : lits) tout << lit << " "; tout << " => " << l << "\n";); SASSERT(sum < c.k()); } @@ -1905,41 +2491,93 @@ namespace smt { SASSERT(!c.is_eq() || (sum == c.k()) == (ctx.get_assignment(c.lit()) == l_true)); } + bool theory_pb::validate_antecedents(literal_vector const& lits) { + context& ctx = get_context(); + for (unsigned i = 0; i < lits.size(); ++i) { + if (ctx.get_assignment(lits[i]) != l_true) { + return false; + } + } + return true; + } + + bool theory_pb::validate_unit_propagation(card const& c) { + context& ctx = get_context(); + for (unsigned i = c.k(); i < c.size(); ++i) { + VERIFY(ctx.get_assignment(c.lit(i)) == l_false); + } + return true; + } + + app_ref theory_pb::literal2expr(literal lit) { + ast_manager& m = get_manager(); + app_ref arg(m.mk_const(symbol(lit.var()), m.mk_bool_sort()), m); + return app_ref(lit.sign() ? m.mk_not(arg) : arg, m); + } + + app_ref theory_pb::active2expr() { + ast_manager& m = get_manager(); + expr_ref_vector args(m); + vector coeffs; + normalize_active_coeffs(); + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + literal lit(v, get_coeff(v) < 0); + args.push_back(literal2expr(lit)); + coeffs.push_back(rational(get_abs_coeff(v))); + } + rational k(m_bound); + return app_ref(pb.mk_ge(args.size(), coeffs.c_ptr(), args.c_ptr(), k), m); + } + // display methods void theory_pb::display_resolved_lemma(std::ostream& out) const { context& ctx = get_context(); - literal_vector const& lits = ctx.assigned_literals(); bool_var v; unsigned lvl; out << "num marks: " << m_num_marks << "\n"; out << "conflict level: " << m_conflict_lvl << "\n"; - for (unsigned i = 0; i < lits.size(); ++i) { - v = lits[i].var(); + for (unsigned i = 0; i < m_resolved.size(); ++i) { + v = m_resolved[i].var(); lvl = ctx.get_assign_level(v); - out << lits[i] - << "@ " << lvl - << " " << (is_marked(v)?"m":"u") - << "\n"; - - if (lvl == m_conflict_lvl && is_marked(v)) { - out << "skipped: " << lits[i] << ":"<< i << "\n"; - } + out << lvl << ": " << m_resolved[i] << " "; + ctx.display(out, ctx.get_justification(v)); } - display(out, m_lemma, true); - unsigned nc = 0; - for (unsigned i = 0; i < m_lemma.size(); ++i) { - v = m_lemma.lit(i).var(); - lvl = ctx.get_assign_level(v); - if (lvl == m_conflict_lvl) ++nc; - out << m_lemma.lit(i) - << "@" << lvl - << " " << (is_marked(v)?"m":"u") - << " " << ctx.get_assignment(m_lemma.lit(i)) - << "\n"; + if (!m_antecedents.empty()) { + out << m_antecedents << " ==> "; } - out << "num conflicts: " << nc << "\n"; + uint_set seen; + bool first = true; + for (unsigned i = 0; i < m_active_vars.size(); ++i) { + bool_var v = m_active_vars[i]; + if (seen.contains(v)) { + continue; + } + seen.insert(v); + int coeff = get_coeff(v); + if (coeff == 0) { + continue; + } + if (!first) { + out << " + "; + } + if (coeff == 1) { + out << literal(v); + } + else if (coeff == -1) { + out << literal(v, true); + } + else if (coeff > 0) { + out << coeff << " * " << literal(v); + } + else { + out << (-coeff) << " * " << literal(v, true); + } + first = false; + } + out << " >= " << m_bound << "\n"; } std::ostream& theory_pb::display(std::ostream& out, arg_t const& c, bool values) const { @@ -2008,11 +2646,11 @@ namespace smt { m_dependencies.push_back(model_value_dependency(n)); } - virtual void get_dependencies(buffer & result) { + void get_dependencies(buffer & result) override { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } - virtual app * mk_value(model_generator & mg, ptr_vector & values) { + app * mk_value(model_generator & mg, ptr_vector & values) override { ast_manager& m = mg.get_manager(); SASSERT(values.size() == m_dependencies.size()); SASSERT(values.size() == m_app->get_num_args()); @@ -2038,9 +2676,9 @@ namespace smt { return (sum >= k)?m.mk_true():m.mk_false(); default: UNREACHABLE(); - return 0; + return nullptr; } - return 0; + return nullptr; } }; @@ -2049,18 +2687,18 @@ namespace smt { pb_factory(ast_manager& m, family_id fid): value_factory(m, fid) {} - virtual expr * get_some_value(sort * s) { + expr * get_some_value(sort * s) override { return m_manager.mk_true(); } - virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { + bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override { v1 = m_manager.mk_true(); v2 = m_manager.mk_false(); return true; } - virtual expr * get_fresh_value(sort * s) { - return 0; + expr * get_fresh_value(sort * s) override { + return nullptr; } - virtual void register_value(expr * n) { } + void register_value(expr * n) override { } }; void theory_pb::init_model(model_generator & m) { @@ -2078,9 +2716,9 @@ namespace smt { } void theory_pb::display_watch(std::ostream& out, bool_var v, bool sign) const { - watch_list const* w = m_var_infos[v].m_lit_watch[sign]; + ineq_watch const* w = m_var_infos[v].m_lit_watch[sign]; if (!w) return; - watch_list const& wl = *w; + ineq_watch const& wl = *w; out << "watch: " << literal(v, sign) << " |-> "; for (unsigned i = 0; i < wl.size(); ++i) { out << wl[i]->lit() << " "; @@ -2094,10 +2732,10 @@ namespace smt { display_watch(out, vi, true); } for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { - watch_list const* w = m_var_infos[vi].m_var_watch; + ineq_watch const* w = m_var_infos[vi].m_var_watch; if (!w) continue; out << "watch (v): " << literal(vi) << " |-> "; - watch_list const& wl = *w; + ineq_watch const& wl = *w; for (unsigned i = 0; i < wl.size(); ++i) { out << wl[i]->lit() << " "; } @@ -2109,6 +2747,14 @@ namespace smt { display(out, *c, true); } } + + for (unsigned vi = 0; vi < m_var_infos.size(); ++vi) { + card* c = m_var_infos[vi].m_card; + if (c) { + display(out, *c, true); + } + } + } diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h index 662378bdf..7e9c55a12 100644 --- a/src/smt/theory_pb.h +++ b/src/smt/theory_pb.h @@ -23,6 +23,7 @@ Notes: #include "smt/smt_theory.h" #include "ast/pb_decl_plugin.h" #include "smt/smt_clause.h" +#include "smt/smt_b_justification.h" #include "smt/params/theory_pb_params.h" #include "math/simplex/simplex.h" @@ -37,10 +38,10 @@ namespace smt { class negate_ineq; class remove_var; class undo_bound; + + class card_justification; + typedef rational numeral; - typedef simplex::simplex simplex; - typedef simplex::row row; - typedef simplex::row_iterator row_iterator; typedef unsynch_mpq_inf_manager eps_manager; typedef _scoped_numeral scoped_eps_numeral; @@ -181,27 +182,88 @@ namespace smt { }; - struct row_info { - unsigned m_slack; // slack variable in simplex tableau - numeral m_bound; // bound - arg_t m_rep; // representative - row_info(theory_var slack, numeral const& b, arg_t const& r): - m_slack(slack), m_bound(b), m_rep(r) {} - row_info(): m_slack(0) {} + // cardinality constraint args >= bound + // unit propagation on cardinality constraints is valid if the literals + // from k() up to size are false. + // In this case the literals 0..k()-1 need to be true. + // The literals in position 0..k() are watched. + // whenever they are assigned to false, then find a literal among + // k() + 1.. sz() to swap with. + // If none are available, then perform unit propagation. + // + class card { + literal m_lit; // literal repesenting predicate + literal_vector m_args; + unsigned m_bound; + unsigned m_num_propagations; + unsigned m_all_propagations; + unsigned m_compilation_threshold; + lbool m_compiled; + bool m_aux; + + public: + card(literal l, unsigned bound, bool is_aux): + m_lit(l), + m_bound(bound), + m_num_propagations(0), + m_all_propagations(0), + m_compilation_threshold(0), + m_compiled(l_false), + m_aux(is_aux) + { + SASSERT(bound > 0); + } + + literal lit() const { return m_lit; } + literal lit(unsigned i) const { return m_args[i]; } + unsigned k() const { return m_bound; } + unsigned size() const { return m_args.size(); } + unsigned all_propagations() const { return m_all_propagations; } + unsigned num_propagations() const { return m_num_propagations; } + void add_arg(literal l); + + void init_watch(theory_pb& th, bool is_true); + + lbool assign(theory_pb& th, literal lit); + + void negate(); + + app_ref to_expr(theory_pb& th); + + void inc_propagations(theory_pb& th); + + void reset_propagations() { m_all_propagations += m_num_propagations; m_num_propagations = 0; } + + bool is_aux() const { return m_aux; } + + private: + + bool validate_conflict(theory_pb& th); + + bool validate_assign(theory_pb& th, literal_vector const& lits, literal l); + + void set_conflict(theory_pb& th, literal l); }; - typedef ptr_vector watch_list; + typedef ptr_vector card_watch; + typedef ptr_vector ineq_watch; typedef map arg_map; + struct var_info { - watch_list* m_lit_watch[2]; - watch_list* m_var_watch; - ineq* m_ineq; + ineq_watch* m_lit_watch[2]; + ineq_watch* m_var_watch; + ineq* m_ineq; + + card_watch* m_lit_cwatch[2]; + card* m_card; - var_info(): m_var_watch(0), m_ineq(0) + var_info(): m_var_watch(0), m_ineq(0), m_card(0) { - m_lit_watch[0] = 0; - m_lit_watch[1] = 0; + m_lit_watch[0] = nullptr; + m_lit_watch[1] = nullptr; + m_lit_cwatch[0] = nullptr; + m_lit_cwatch[1] = nullptr; } void reset() { @@ -209,38 +271,39 @@ namespace smt { dealloc(m_lit_watch[1]); dealloc(m_var_watch); dealloc(m_ineq); + dealloc(m_lit_cwatch[0]); + dealloc(m_lit_cwatch[1]); + dealloc(m_card); } }; - theory_pb_params m_params; svector m_var_infos; - arg_map m_ineq_rep; // Simplex: representative inequality - u_map m_ineq_row_info; // Simplex: row information per variable - uint_set m_vars; // Simplex: 0-1 variables. - simplex m_simplex; // Simplex: tableau - literal_vector m_explain_lower; // Simplex: explanations for lower bounds - literal_vector m_explain_upper; // Simplex: explanations for upper bounds - unsynch_mpq_inf_manager m_mpq_inf_mgr; // Simplex: manage inf_mpq numerals mutable unsynch_mpz_manager m_mpz_mgr; // Simplex: manager mpz numerals unsigned_vector m_ineqs_trail; unsigned_vector m_ineqs_lim; literal_vector m_literals; // temporary vector - pb_util m_util; + pb_util pb; stats m_stats; ptr_vector m_to_compile; // inequalities to compile. unsigned m_conflict_frequency; bool m_learn_complements; bool m_enable_compilation; - bool m_enable_simplex; rational m_max_compiled_coeff; + bool m_cardinality_lemma; + unsigned m_restart_lim; + unsigned m_restart_inc; + uint_set m_occs; + // internalize_atom: literal compile_arg(expr* arg); - void add_watch(ineq& c, unsigned index); - void del_watch(watch_list& watch, unsigned index, ineq& c, unsigned ineq_index); void init_watch(bool_var v); + + // general purpose pb constraints + void add_watch(ineq& c, unsigned index); + void del_watch(ineq_watch& watch, unsigned index, ineq& c, unsigned ineq_index); void init_watch_literal(ineq& c); void init_watch_var(ineq& c); void clear_watch(ineq& c); @@ -249,19 +312,40 @@ namespace smt { void unwatch_literal(literal w, ineq* c); void unwatch_var(bool_var v, ineq* c); void remove(ptr_vector& ineqs, ineq* c); - bool assign_watch_ge(bool_var v, bool is_true, watch_list& watch, unsigned index); + + bool assign_watch_ge(bool_var v, bool is_true, ineq_watch& watch, unsigned index); void assign_watch(bool_var v, bool is_true, ineq& c); void assign_ineq(ineq& c, bool is_true); void assign_eq(ineq& c, bool is_true); + // cardinality constraints + // these are cheaper to handle than general purpose PB constraints + // and in the common case PB constraints with small coefficients can + // be handled using cardinality constraints. + + unsigned_vector m_card_trail; + unsigned_vector m_card_lim; + bool is_cardinality_constraint(app * atom); + bool internalize_card(app * atom, bool gate_ctx); + void card2conjunction(card const& c); + void card2disjunction(card const& c); + + void watch_literal(literal lit, card* c); + void unwatch_literal(literal w, card* c); + void add_clause(card& c, literal_vector const& lits); + void add_assign(card& c, literal l); + void remove(ptr_vector& cards, card* c); + void clear_watch(card& c); + bool gc(); + std::ostream& display(std::ostream& out, card const& c, bool values = false) const; + + // simplex: - literal set_explain(literal_vector& explains, unsigned var, literal expl); - bool update_bound(bool_var v, literal explain, bool is_lower, mpq_inf const& bound); bool check_feasible(); std::ostream& display(std::ostream& out, ineq const& c, bool values = false) const; std::ostream& display(std::ostream& out, arg_t const& c, bool values = false) const; - virtual void display(std::ostream& out) const; + void display(std::ostream& out) const override; void display_watch(std::ostream& out, bool_var v, bool sign) const; void display_resolved_lemma(std::ostream& out) const; @@ -284,31 +368,58 @@ namespace smt { // Conflict resolution, cutting plane derivation. // unsigned m_num_marks; + literal_vector m_resolved; unsigned m_conflict_lvl; - arg_t m_lemma; - literal_vector m_ineq_literals; - svector m_marked; - // bool_var |-> index into m_lemma - unsigned_vector m_conseq_index; - static const unsigned null_index; - bool is_marked(bool_var v) const; - void set_mark(bool_var v, unsigned idx); - void unset_mark(bool_var v); - void unset_marks(); + // Conflict PB constraints + svector m_coeffs; + svector m_active_vars; + int m_bound; + literal_vector m_antecedents; + tracked_uint_set m_active_var_set; + expr_ref_vector m_antecedent_exprs; + svector m_antecedent_signs; + expr_ref_vector m_cardinality_exprs; + svector m_cardinality_signs; - bool resolve_conflict(ineq& c); - void process_antecedent(literal l, numeral coeff); - void process_ineq(ineq& c, literal conseq, numeral coeff); - void remove_from_lemma(unsigned idx); + void normalize_active_coeffs(); + void inc_coeff(literal l, int offset); + int get_coeff(bool_var v) const; + int get_abs_coeff(bool_var v) const; + int arg_max(int& coeff); + + literal_vector& get_literals() { m_literals.reset(); return m_literals; } + + vector > m_coeff2args; + unsigned_vector m_active_coeffs; + bool init_arg_max(); + void reset_arg_max(); + + void reset_coeffs(); + void add_cardinality_lemma(); + literal get_asserting_literal(literal conseq); + + bool resolve_conflict(card& c, literal_vector const& conflict_clause); + void process_antecedent(literal l, int offset); + void process_card(card& c, int offset); + void cut(); bool is_proof_justification(justification const& j) const; + void hoist_maximal_values(); + bool validate_lemma(); void validate_final_check(); void validate_final_check(ineq& c); void validate_assign(ineq const& c, literal_vector const& lits, literal l) const; void validate_watch(ineq const& c) const; + bool validate_unit_propagation(card const& c); + bool validate_antecedents(literal_vector const& lits); + bool validate_implies(app_ref& A, app_ref& B); + app_ref active2expr(); + app_ref literal2expr(literal lit); + app_ref card2expr(card& c) { return c.to_expr(*this); } + app_ref justification2expr(b_justification& js, literal conseq); bool proofs_enabled() const { return get_manager().proofs_enabled(); } justification* justify(literal l1, literal l2); @@ -317,27 +428,28 @@ namespace smt { public: theory_pb(ast_manager& m, theory_pb_params& p); - virtual ~theory_pb(); - - virtual theory * mk_fresh(context * new_ctx); - virtual bool internalize_atom(app * atom, bool gate_ctx); - virtual bool internalize_term(app * term) { UNREACHABLE(); return false; } - virtual void new_eq_eh(theory_var v1, theory_var v2); - virtual void new_diseq_eh(theory_var v1, theory_var v2) { } - virtual bool use_diseqs() const { return false; } - virtual bool build_models() const { return false; } - virtual final_check_status final_check_eh(); - virtual void reset_eh(); - virtual void assign_eh(bool_var v, bool is_true); - virtual void init_search_eh(); - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); - virtual void restart_eh(); - virtual void collect_statistics(::statistics & st) const; - virtual model_value_proc * mk_value(enode * n, model_generator & mg); - virtual void init_model(model_generator & m); - virtual bool include_func_interp(func_decl* f) { return false; } + ~theory_pb() override; + theory * mk_fresh(context * new_ctx) override; + bool internalize_atom(app * atom, bool gate_ctx) override; + bool internalize_term(app * term) override { UNREACHABLE(); return false; } + void new_eq_eh(theory_var v1, theory_var v2) override; + void new_diseq_eh(theory_var v1, theory_var v2) override { } + bool use_diseqs() const override { return false; } + bool build_models() const override { return false; } + final_check_status final_check_eh() override; + void reset_eh() override; + void assign_eh(bool_var v, bool is_true) override; + void init_search_eh() override; + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; + void restart_eh() override; + void collect_statistics(::statistics & st) const override; + model_value_proc * mk_value(enode * n, model_generator & mg) override; + void init_model(model_generator & m) override; + bool include_func_interp(func_decl* f) override { return false; } + bool can_propagate() override; + void propagate() override; static literal assert_ge(context& ctx, unsigned k, unsigned n, literal const* xs); }; }; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index a775b268d..26ab92665 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -45,7 +45,7 @@ public: 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(); @@ -87,7 +87,7 @@ bool theory_seq::solution_map::is_root(expr* e) const { // e1 -> ... x, e2 -> ... x void theory_seq::solution_map::find_rec(expr* e, svector >& finds) { - dependency* d = 0; + dependency* d = nullptr; std::pair value(e, d); do { e = value.first; @@ -111,7 +111,7 @@ bool theory_seq::solution_map::find1(expr* e, expr*& r, dependency*& d) { expr* theory_seq::solution_map::find(expr* e, dependency*& d) { std::pair value; - d = 0; + d = nullptr; expr* result = e; while (m_map.find(result, value)) { d = m_dm.mk_join(d, value.second); @@ -134,8 +134,7 @@ void theory_seq::solution_map::pop_scope(unsigned num_scopes) { if (num_scopes == 0) return; m_cache.reset(); unsigned start = m_limit[m_limit.size() - num_scopes]; - for (unsigned i = m_updates.size(); i > start; ) { - --i; + for (unsigned i = m_updates.size(); i-- > start; ) { if (m_updates[i] == INS) { m_map.remove(m_lhs[i].get()); } @@ -199,12 +198,12 @@ theory_seq::theory_seq(ast_manager& m): m_reset_cache(false), m_eq_id(0), m_find(*this), - m_factory(0), + m_factory(nullptr), m_exclude(m), m_axioms(m), m_axioms_head(0), m_int_string(m), - m_mg(0), + m_mg(nullptr), m_rewrite(m), m_seq_rewrite(m), m_util(m), @@ -243,8 +242,7 @@ theory_seq::~theory_seq() { } void theory_seq::init(context* ctx) { - theory::init(ctx); - m_mk_aut.set_solver(alloc(seq_expr_solver, m, get_context().get_fparams())); + theory::init(ctx); } final_check_status theory_seq::final_check_eh() { @@ -400,7 +398,7 @@ bool theory_seq::branch_binary_variable(eq const& e) { // |x| - |y| = |ys| - |xs| expr_ref a(mk_sub(m_util.str.mk_length(x), m_util.str.mk_length(y)), m); expr_ref b(m_autil.mk_int(ys.size()-xs.size()), m); - propagate_lit(e.dep(), 0, 0, mk_eq(a, b, false)); + propagate_lit(e.dep(), 0, nullptr, mk_eq(a, b, false)); return true; } if (lenX <= rational(ys.size())) { @@ -475,7 +473,7 @@ void theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector if (lenX > rational(units.size())) { expr_ref le(m_autil.mk_le(m_util.str.mk_length(X), m_autil.mk_int(units.size())), m); TRACE("seq", tout << "propagate length on " << mk_pp(X, m) << "\n";); - propagate_lit(dep, 0, 0, mk_literal(le)); + propagate_lit(dep, 0, nullptr, mk_literal(le)); return; } SASSERT(lenX.is_unsigned()); @@ -1601,12 +1599,30 @@ unsigned theory_seq::find_branch_start(unsigned k) { return 0; } -bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_ref_vector const& ls, expr_ref_vector const& rs) { +expr_ref_vector theory_seq::expand_strings(expr_ref_vector const& es) { + expr_ref_vector ls(m); + for (expr* e : es) { + zstring s; + if (m_util.str.is_string(e, s)) { + for (unsigned i = 0; i < s.length(); ++i) { + ls.push_back(m_util.str.mk_unit(m_util.str.mk_char(s, i))); + } + } + else { + ls.push_back(e); + } + } + return ls; +} + +bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_ref_vector const& _ls, expr_ref_vector const& _rs) { + expr_ref_vector ls = expand_strings(_ls); + expr_ref_vector rs = expand_strings(_rs); if (ls.empty()) { return false; } - expr* l = ls[0]; + expr* l = ls.get(0); if (!is_var(l)) { return false; @@ -1624,9 +1640,9 @@ bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_re } for (; start < rs.size(); ++start) { unsigned j = start; - SASSERT(!m_util.str.is_concat(rs[j])); - SASSERT(!m_util.str.is_string(rs[j])); - if (l == rs[j]) { + SASSERT(!m_util.str.is_concat(rs.get(j))); + SASSERT(!m_util.str.is_string(rs.get(j))); + if (l == rs.get(j)) { return false; } if (!can_be_equal(ls.size() - 1, ls.c_ptr() + 1, rs.size() - j - 1, rs.c_ptr() + j + 1)) { @@ -1641,8 +1657,11 @@ bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_re } bool all_units = true; - for (unsigned j = 0; all_units && j < rs.size(); ++j) { - all_units &= m_util.str.is_unit(rs[j]); + for (expr* r : rs) { + if (!m_util.str.is_unit(r)) { + all_units = false; + break; + } } if (all_units) { context& ctx = get_context(); @@ -1654,20 +1673,20 @@ bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_re lits.push_back(~mk_eq(l, v0, false)); } } - for (unsigned i = 0; i < lits.size(); ++i) { - switch (ctx.get_assignment(lits[i])) { + for (literal lit : lits) { + switch (ctx.get_assignment(lit)) { case l_true: break; case l_false: start = 0; return true; - case l_undef: ctx.force_phase(~lits[i]); start = 0; return true; + case l_undef: ctx.force_phase(~lit); start = 0; return true; } } set_conflict(dep, lits); TRACE("seq", tout << "start: " << start << "\n"; - for (unsigned i = 0; i < lits.size(); ++i) { - ctx.display_literal_verbose(tout << lits[i] << ": ", lits[i]); + for (literal lit : lits) { + ctx.display_literal_verbose(tout << lit << ": ", lit); tout << "\n"; - ctx.display(tout, ctx.get_justification(lits[i].var())); + ctx.display(tout, ctx.get_justification(lit.var())); tout << "\n"; }); return true; @@ -1890,7 +1909,7 @@ bool theory_seq::fixed_length(expr* e, bool is_zero) { */ void theory_seq::propagate_non_empty(literal lit, expr* s) { SASSERT(get_context().get_assignment(lit) == l_true); - propagate_lit(0, 1, &lit, ~mk_eq_empty(s)); + propagate_lit(nullptr, 1, &lit, ~mk_eq_empty(s)); } bool theory_seq::propagate_is_conc(expr* e, expr* conc) { @@ -1898,7 +1917,7 @@ bool theory_seq::propagate_is_conc(expr* e, expr* conc) { context& ctx = get_context(); literal lit = ~mk_eq_empty(e); if (ctx.get_assignment(lit) == l_true) { - propagate_lit(0, 1, &lit, mk_eq(e, conc, false)); + propagate_lit(nullptr, 1, &lit, mk_eq(e, conc, false)); expr_ref e1(e, m), e2(conc, m); new_eq_eh(m_dm.mk_leaf(assumption(lit)), ctx.get_enode(e1), ctx.get_enode(e2)); return true; @@ -1952,13 +1971,13 @@ bool theory_seq::is_post(expr* e, expr*& s, expr*& i) { expr_ref theory_seq::mk_nth(expr* s, expr* idx) { - sort* char_sort = 0; + sort* char_sort = nullptr; VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); - return mk_skolem(m_nth, s, idx, 0, 0, char_sort); + return mk_skolem(m_nth, s, idx, nullptr, nullptr, char_sort); } expr_ref theory_seq::mk_sk_ite(expr* c, expr* t, expr* e) { - return mk_skolem(symbol("seq.if"), c, t, e, 0, m.get_sort(t)); + return mk_skolem(symbol("seq.if"), c, t, e, nullptr, m.get_sort(t)); } expr_ref theory_seq::mk_last(expr* s) { @@ -1966,9 +1985,9 @@ expr_ref theory_seq::mk_last(expr* s) { if (m_util.str.is_string(s, str) && str.length() > 0) { return expr_ref(m_util.str.mk_char(str, str.length()-1), m); } - sort* char_sort = 0; + sort* char_sort = nullptr; VERIFY(m_util.is_seq(m.get_sort(s), char_sort)); - return mk_skolem(m_seq_last, s, 0, 0, 0, char_sort); + return mk_skolem(m_seq_last, s, nullptr, nullptr, nullptr, char_sort); } expr_ref theory_seq::mk_first(expr* s) { @@ -1981,7 +2000,7 @@ expr_ref theory_seq::mk_first(expr* s) { void theory_seq::mk_decompose(expr* e, expr_ref& head, expr_ref& tail) { - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; zstring s; if (m_util.str.is_empty(e)) { head = m_util.str.mk_unit(mk_nth(e, m_autil.mk_int(0))); @@ -2028,7 +2047,7 @@ bool theory_seq::check_extensionality() { continue; } if (!seqs.empty() && ctx.is_relevant(n1) && m_util.is_seq(o1) && ctx.is_shared(n1)) { - dependency* dep = 0; + dependency* dep = nullptr; expr_ref e1 = canonize(o1, dep); for (unsigned i = 0; i < seqs.size(); ++i) { enode* n2 = get_enode(seqs[i]); @@ -2129,7 +2148,7 @@ bool theory_seq::linearize(dependency* dep, enode_pair_vector& eqs, literal_vect lits.push_back(a.lit); asserted &= ctx.get_assignment(a.lit) == l_true; } - if (a.n1 != 0) { + if (a.n1 != nullptr) { eqs.push_back(enode_pair(a.n1, a.n2)); } } @@ -2178,7 +2197,7 @@ void theory_seq::set_conflict(dependency* dep, literal_vector const& _lits) { ctx.set_conflict( ctx.mk_justification( ext_theory_conflict_justification( - get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), 0, 0))); + get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), 0, nullptr))); } void theory_seq::propagate_eq(dependency* dep, enode* n1, enode* n2) { @@ -2338,7 +2357,7 @@ bool theory_seq::occurs(expr* a, expr* b) { // true if a occurs under an interpreted function or under left/right selector. SASSERT(is_var(a)); SASSERT(m_todo.empty()); - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; m_todo.push_back(b); while (!m_todo.empty()) { b = m_todo.back(); @@ -2373,7 +2392,7 @@ bool theory_seq::add_solution(expr* l, expr* r, dependency* deps) { if (l == r) { return false; } - TRACE("seq", tout << mk_pp(l, m) << " ==> " << mk_pp(r, m) << "\n";); + TRACE("seq", tout << mk_pp(l, m) << " ==> " << mk_pp(r, m) << "\n"; display_deps(tout, deps);); m_new_solution = true; m_rep.update(l, r, deps); enode* n1 = ensure_enode(l); @@ -2408,12 +2427,14 @@ bool theory_seq::solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, de expr_ref_vector& ls = m_ls; expr_ref_vector& rs = m_rs; rs.reset(); ls.reset(); - dependency* dep2 = 0; + dependency* dep2 = nullptr; bool change = canonize(l, ls, dep2); change = canonize(r, rs, dep2) || change; deps = m_dm.mk_join(dep2, deps); TRACE("seq", tout << l << " = " << r << " ==> "; - tout << ls << " = " << rs << "\n";); + tout << ls << " = " << rs << "\n"; + display_deps(tout, deps); + ); if (!ctx.inconsistent() && simplify_eq(ls, rs, deps)) { return true; } @@ -2463,7 +2484,7 @@ bool theory_seq::propagate_max_length(expr* l, expr* r, dependency* deps) { } rational hi; if (is_tail(l, s, idx) && has_length(s) && m_util.str.is_empty(r) && !upper_bound(s, hi)) { - propagate_lit(deps, 0, 0, mk_literal(m_autil.mk_le(m_util.str.mk_length(s), m_autil.mk_int(idx+1)))); + propagate_lit(deps, 0, nullptr, mk_literal(m_autil.mk_le(m_util.str.mk_length(s), m_autil.mk_int(idx+1)))); return true; } return false; @@ -2775,7 +2796,7 @@ bool theory_seq::solve_binary_eq(expr_ref_vector const& ls, expr_ref_vector cons } ctx.mark_as_relevant(eq); if (sz == 1) { - propagate_lit(dep, 0, 0, eq); + propagate_lit(dep, 0, nullptr, eq); return true; } m_new_propagation = true; @@ -2923,7 +2944,7 @@ bool theory_seq::solve_ne(unsigned idx) { expr_ref_vector& lhs = m_lhs; expr_ref_vector& rhs = m_rhs; ls.reset(); rs.reset(); lhs.reset(); rhs.reset(); - dependency* deps = 0; + dependency* deps = nullptr; bool change = false; change = canonize(n.ls(i), ls, deps) || change; change = canonize(n.rs(i), rs, deps) || change; @@ -3029,7 +3050,7 @@ bool theory_seq::solve_ne(unsigned idx) { if (num_undef_lits == 0 && new_ls.empty()) { TRACE("seq", tout << "conflict\n";); - dependency* deps1 = 0; + dependency* deps1 = nullptr; if (explain_eq(n.l(), n.r(), deps1)) { literal diseq = mk_eq(n.l(), n.r(), false); if (ctx.get_assignment(diseq) == l_false) { @@ -3072,10 +3093,10 @@ bool theory_seq::solve_nc(unsigned idx) { return true; } - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; if (m.is_eq(c, e1, e2)) { literal eq = mk_eq(e1, e2, false); - propagate_lit(deps, 0, 0, ~eq); + propagate_lit(deps, 0, nullptr, ~eq); return true; } @@ -3097,7 +3118,7 @@ theory_seq::cell* theory_seq::mk_cell(cell* p, expr* e, dependency* d) { } void theory_seq::unfold(cell* c, ptr_vector& cons) { - dependency* dep = 0; + dependency* dep = nullptr; expr* a, *e1, *e2; if (m_rep.find1(c->m_expr, a, dep)) { cell* c1 = mk_cell(c, a, m_dm.mk_join(dep, c->m_dep)); @@ -3105,7 +3126,7 @@ void theory_seq::unfold(cell* c, ptr_vector& cons) { } else if (m_util.str.is_concat(c->m_expr, e1, e2)) { cell* c1 = mk_cell(c, e1, c->m_dep); - cell* c2 = mk_cell(0, e2, 0); + cell* c2 = mk_cell(nullptr, e2, nullptr); unfold(c1, cons); unfold(c2, cons); } @@ -3123,7 +3144,7 @@ void theory_seq::unfold(cell* c, ptr_vector& cons) { void theory_seq::display_explain(std::ostream& out, unsigned indent, expr* e) { expr* e1, *e2, *a; - dependency* dep = 0; + dependency* dep = nullptr; smt2_pp_environment_dbg env(m); params_ref p; for (unsigned i = 0; i < indent; ++i) out << " "; @@ -3147,8 +3168,8 @@ bool theory_seq::explain_eq(expr* e1, expr* e2, dependency*& dep) { expr* a1, *a2; ptr_vector v1, v2; unsigned cells_sz = m_all_cells.size(); - cell* c1 = mk_cell(0, e1, 0); - cell* c2 = mk_cell(0, e2, 0); + cell* c1 = mk_cell(nullptr, e1, nullptr); + cell* c2 = mk_cell(nullptr, e2, nullptr); unfold(c1, v1); unfold(c2, v2); unsigned i = 0, j = 0; @@ -3262,62 +3283,7 @@ void theory_seq::internalize_eq_eh(app * atom, bool_var v) { } bool theory_seq::internalize_atom(app* a, bool) { -#if 1 return internalize_term(a); -#else - if (is_skolem(m_eq, a)) { - return internalize_term(a); - } - context & ctx = get_context(); - bool_var bv = ctx.mk_bool_var(a); - ctx.set_var_theory(bv, get_id()); - ctx.mark_as_relevant(bv); - - expr* e1, *e2; - if (m_util.str.is_in_re(a, e1, e2)) { - return internalize_term(to_app(e1)) && internalize_re(e2); - } - if (m_util.str.is_contains(a, e1, e2) || - m_util.str.is_prefix(a, e1, e2) || - m_util.str.is_suffix(a, e1, e2)) { - return internalize_term(to_app(e1)) && internalize_term(to_app(e2)); - } - if (is_accept(a) || is_reject(a) || is_step(a) || is_skolem(symbol("seq.is_digit"), a)) { - return true; - } - UNREACHABLE(); - return internalize_term(a); -#endif -} - -bool theory_seq::internalize_re(expr* e) { - expr* e1, *e2; - unsigned lc, uc; - if (m_util.re.is_to_re(e, e1)) { - return internalize_term(to_app(e1)); - } - if (m_util.re.is_star(e, e1) || - m_util.re.is_plus(e, e1) || - m_util.re.is_opt(e, e1) || - m_util.re.is_loop(e, e1, lc) || - m_util.re.is_loop(e, e1, lc, uc) || - m_util.re.is_complement(e, e1)) { - return internalize_re(e1); - } - if (m_util.re.is_union(e, e1, e2) || - m_util.re.is_intersection(e, e1, e2) || - m_util.re.is_concat(e, e1, e2)) { - return internalize_re(e1) && internalize_re(e2); - } - if (m_util.re.is_full(e) || - m_util.re.is_empty(e)) { - return true; - } - if (m_util.re.is_range(e, e1, e2)) { - return internalize_term(to_app(e1)) && internalize_term(to_app(e2)); - } - UNREACHABLE(); - return internalize_term(to_app(e)); } bool theory_seq::internalize_term(app* term) { @@ -3337,7 +3303,7 @@ bool theory_seq::internalize_term(app* term) { ctx.mark_as_relevant(bv); } - enode* e = 0; + enode* e = nullptr; if (ctx.e_internalized(term)) { e = ctx.get_enode(term); } @@ -3345,7 +3311,6 @@ bool theory_seq::internalize_term(app* term) { e = ctx.mk_enode(term, false, m.is_bool(term), true); } mk_var(e); - return true; } @@ -3381,30 +3346,45 @@ void theory_seq::add_int_string(expr* e) { bool theory_seq::check_int_string() { bool change = false; - for (unsigned i = 0; i < m_int_string.size(); ++i) { - expr* e = m_int_string[i].get(), *n; - if (m_util.str.is_itos(e) && add_itos_axiom(e)) { + for (expr * e : m_int_string) { + expr* n = nullptr; + if (m_util.str.is_itos(e) && add_itos_val_axiom(e)) { change = true; } - else if (m_util.str.is_stoi(e, n) && add_stoi_axiom(e)) { + else if (m_util.str.is_stoi(e, n) && add_stoi_val_axiom(e)) { change = true; } } return change; } -bool theory_seq::add_stoi_axiom(expr* e) { +void theory_seq::add_stoi_axiom(expr* e) { + TRACE("seq", tout << mk_pp(e, m) << "\n";); + expr* s = nullptr; + VERIFY (m_util.str.is_stoi(e, s)); + + // stoi(s) >= -1 + literal l = mk_simplified_literal(m_autil.mk_ge(e, m_autil.mk_int(-1))); + add_axiom(l); + + // stoi(s) >= 0 <=> s in (0-9)+ + expr_ref num_re(m); + num_re = m_util.re.mk_range(m_util.str.mk_string(symbol("0")), m_util.str.mk_string(symbol("9"))); + num_re = m_util.re.mk_plus(num_re); + app_ref in_re(m_util.re.mk_in_re(s, num_re), m); + literal ge0 = mk_simplified_literal(m_autil.mk_ge(e, m_autil.mk_int(0))); + add_axiom(~ge0, mk_literal(in_re)); + add_axiom(ge0, ~mk_literal(in_re)); +} + +bool theory_seq::add_stoi_val_axiom(expr* e) { context& ctx = get_context(); - expr* n = 0; + expr* n = nullptr; rational val; TRACE("seq", tout << mk_pp(e, m) << "\n";); VERIFY(m_util.str.is_stoi(e, n)); if (!get_num_value(e, val)) { - literal l = mk_simplified_literal(m_autil.mk_ge(e, arith_util(m).mk_int(-1))); - add_axiom(l); - TRACE("seq", tout << l << " " << ctx.get_assignment(l) << "\n"; - ctx.display(tout);); - return true; + return false; } if (!m_stoi_axioms.contains(val)) { m_stoi_axioms.insert(val); @@ -3438,9 +3418,9 @@ bool theory_seq::add_stoi_axiom(expr* e) { lits.push_back(~is_digit(ith_char)); nums.push_back(digit2int(ith_char)); } - for (unsigned i = sz-1, c = 1; i > 0; c *= 10) { - --i; - coeff = m_autil.mk_int(c); + rational c(1); + for (unsigned i = sz; i-- > 0; c *= rational(10)) { + coeff = m_autil.mk_numeral(c, true); nums[i] = m_autil.mk_mul(coeff, nums[i].get()); } num = m_autil.mk_add(nums.size(), nums.c_ptr()); @@ -3448,9 +3428,10 @@ bool theory_seq::add_stoi_axiom(expr* e) { lits.push_back(mk_eq(e, num, false)); ++m_stats.m_add_axiom; m_new_propagation = true; - for (unsigned i = 0; i < lits.size(); ++i) { - ctx.mark_as_relevant(lits[i]); + for (literal lit : lits) { + ctx.mark_as_relevant(lit); } + TRACE("seq", ctx.display_literals_verbose(tout, lits); tout << "\n";); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); m_stoi_axioms.insert(val); m_trail_stack.push(insert_map(m_stoi_axioms, val)); @@ -3463,7 +3444,7 @@ bool theory_seq::add_stoi_axiom(expr* e) { literal theory_seq::is_digit(expr* ch) { bv_util bv(m); - literal isd = mk_literal(mk_skolem(symbol("seq.is_digit"), ch, 0, 0, 0, m.mk_bool_sort())); + literal isd = mk_literal(mk_skolem(symbol("seq.is_digit"), ch, nullptr, nullptr, nullptr, m.mk_bool_sort())); expr_ref d2i = digit2int(ch); expr_ref _lo(bv.mk_ule(bv.mk_numeral(rational('0'), bv.mk_sort(8)), ch), m); expr_ref _hi(bv.mk_ule(ch, bv.mk_numeral(rational('9'), bv.mk_sort(8))), m); @@ -3479,57 +3460,63 @@ literal theory_seq::is_digit(expr* ch) { } expr_ref theory_seq::digit2int(expr* ch) { - return expr_ref(mk_skolem(symbol("seq.digit2int"), ch, 0, 0, 0, m_autil.mk_int()), m); + return expr_ref(mk_skolem(symbol("seq.digit2int"), ch, nullptr, nullptr, nullptr, m_autil.mk_int()), m); } -bool theory_seq::add_itos_axiom(expr* e) { - context& ctx = get_context(); +void theory_seq::add_itos_axiom(expr* e) { rational val; - expr* n = 0; + expr* n = nullptr; TRACE("seq", tout << mk_pp(e, m) << "\n";); VERIFY(m_util.str.is_itos(e, n)); - if (get_num_value(n, val)) { - if (!m_itos_axioms.contains(val)) { - m_itos_axioms.insert(val); - app_ref e1(m_util.str.mk_string(symbol(val.to_string().c_str())), m); - expr_ref n1(arith_util(m).mk_numeral(val, true), m); - // itos(n) = "25" <=> n = 25 - literal eq1 = mk_eq(n1, n , false); - literal eq2 = mk_eq(e, e1, false); - add_axiom(~eq1, eq2); - add_axiom(~eq2, eq1); - ctx.force_phase(eq1); - ctx.force_phase(eq2); + // itos(n) = "" <=> n < 0 + app_ref e1(m_util.str.mk_empty(m.get_sort(e)), m); + expr_ref zero(arith_util(m).mk_int(0), m); + literal eq1 = mk_eq(e1, e, false); + literal ge0 = mk_literal(m_autil.mk_ge(n, zero)); + // n >= 0 => itos(n) != "" + // itos(n) = "" or n >= 0 + add_axiom(~eq1, ~ge0); + add_axiom(eq1, ge0); + + // n >= 0 => stoi(itos(n)) = n + app_ref stoi(m_util.str.mk_stoi(e), m); + add_axiom(~ge0, mk_eq(stoi, n, false)); - m_trail_stack.push(insert_map(m_itos_axioms, val)); - m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); - return true; - } + // n >= 0 => itos(n) in (0-9)+ + expr_ref num_re(m); + num_re = m_util.re.mk_range(m_util.str.mk_string(symbol("0")), m_util.str.mk_string(symbol("9"))); + num_re = m_util.re.mk_plus(num_re); + app_ref in_re(m_util.re.mk_in_re(e, num_re), m); + add_axiom(~ge0, mk_literal(in_re)); +} + +bool theory_seq::add_itos_val_axiom(expr* e) { + context& ctx = get_context(); + rational val; + expr* n = nullptr; + TRACE("seq", tout << mk_pp(e, m) << "\n";); + VERIFY(m_util.str.is_itos(e, n)); + bool change = false; + + if (get_num_value(n, val) && !val.is_neg() && !m_itos_axioms.contains(val)) { + m_itos_axioms.insert(val); + app_ref e1(m_util.str.mk_string(symbol(val.to_string().c_str())), m); + expr_ref n1(arith_util(m).mk_numeral(val, true), m); + + // itos(n) = "25" <=> n = 25 + literal eq1 = mk_eq(n1, n , false); + literal eq2 = mk_eq(e, e1, false); + add_axiom(~eq1, eq2); + add_axiom(~eq2, eq1); + ctx.force_phase(eq1); + ctx.force_phase(eq2); + + m_trail_stack.push(insert_map(m_itos_axioms, val)); + m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); + change = true; } - else { - // stoi(itos(n)) = n - app_ref e2(m_util.str.mk_stoi(e), m); - if (ctx.e_internalized(e2) && ctx.get_enode(e2)->get_root() == ctx.get_enode(n)->get_root()) { - return false; - } - add_axiom(mk_eq(e2, n, false)); - -#if 1 - expr_ref num_re(m), opt_re(m); - num_re = m_util.re.mk_range(m_util.str.mk_string(symbol("0")), m_util.str.mk_string(symbol("9"))); - num_re = m_util.re.mk_plus(num_re); - opt_re = m_util.re.mk_opt(m_util.re.mk_to_re(m_util.str.mk_string(symbol("-")))); - num_re = m_util.re.mk_concat(opt_re, num_re); - app_ref in_re(m_util.re.mk_in_re(e, num_re), m); - internalize_term(in_re); - propagate_in_re(in_re, true); -#endif - m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); - return true; - } - - return false; + return change; } void theory_seq::apply_sort_cnstr(enode* n, sort* s) { @@ -3693,7 +3680,7 @@ void theory_seq::init_search_eh() { void theory_seq::init_model(expr_ref_vector const& es) { expr_ref new_s(m); for (auto e : es) { - dependency* eqs = 0; + dependency* eqs = nullptr; expr_ref s = canonize(e, eqs); if (is_var(s)) { new_s = m_factory->get_fresh_value(m.get_sort(s)); @@ -3702,7 +3689,12 @@ void theory_seq::init_model(expr_ref_vector const& es) { } } +void theory_seq::finalize_model(model_generator& mg) { + m_rep.pop_scope(1); +} + void theory_seq::init_model(model_generator & mg) { + m_rep.push_scope(); m_factory = alloc(seq_factory, get_manager(), get_family_id(), mg.get_model()); mg.register_factory(m_factory); for (ne const& n : m_nqs) { @@ -3728,7 +3720,7 @@ class theory_seq::seq_value_proc : public model_value_proc { public: seq_value_proc(theory_seq& th, sort* s): th(th), m_sort(s) { } - virtual ~seq_value_proc() {} + ~seq_value_proc() override {} void add_unit(enode* n) { m_dependencies.push_back(model_value_dependency(n)); m_source.push_back(unit_source); @@ -3741,7 +3733,7 @@ public: m_strings.push_back(n); m_source.push_back(string_source); } - virtual void get_dependencies(buffer & result) { + void get_dependencies(buffer & result) override { result.append(m_dependencies.size(), m_dependencies.c_ptr()); } @@ -3751,27 +3743,26 @@ public: } } - virtual app * mk_value(model_generator & mg, ptr_vector & values) { + app * mk_value(model_generator & mg, ptr_vector & values) override { SASSERT(values.size() == m_dependencies.size()); expr_ref_vector args(th.m); unsigned j = 0, k = 0; bool is_string = th.m_util.is_string(m_sort); expr_ref result(th.m); if (is_string) { - svector sbuffer; + unsigned_vector sbuffer; bv_util bv(th.m); rational val; unsigned sz; - - for (unsigned i = 0; i < m_source.size(); ++i) { - switch (m_source[i]) { + for (source_t src : m_source) { + switch (src) { case unit_source: { VERIFY(bv.is_numeral(values[j++], val, sz)); sbuffer.push_back(val.get_unsigned()); break; } case string_source: { - dependency* deps = 0; + dependency* deps = nullptr; expr_ref tmp = th.canonize(m_strings[k], deps); zstring zs; if (th.m_util.str.is_string(tmp, zs)) { @@ -3794,12 +3785,13 @@ public: break; } } + // TRACE("seq", tout << src << " " << sbuffer << "\n";); } result = th.m_util.str.mk_string(zstring(sbuffer.size(), sbuffer.c_ptr())); } else { - for (unsigned i = 0; i < m_source.size(); ++i) { - switch (m_source[i]) { + for (source_t src : m_source) { + switch (src) { case unit_source: args.push_back(th.m_util.str.mk_unit(values[j++])); break; @@ -3841,8 +3833,8 @@ model_value_proc * theory_seq::mk_value(enode * n, model_generator & mg) { seq_value_proc* sv = alloc(seq_value_proc, *this, srt); TRACE("seq", tout << mk_pp(e, m) << "\n";); - for (unsigned i = 0; i < concats.size(); ++i) { - expr* c = concats[i], *c1; + for (expr* c : concats) { + expr *c1; TRACE("seq", tout << mk_pp(c, m) << "\n";); if (m_util.str.is_unit(c, c1)) { if (ctx.e_internalized(c1)) { @@ -3885,7 +3877,7 @@ app* theory_seq::mk_value(app* e) { } m_factory->add_trail(result); TRACE("seq", tout << mk_pp(e, m) << " -> " << result << "\n";); - m_rep.update(e, result, 0); + m_rep.update(e, result, nullptr); return to_app(result); } @@ -3972,6 +3964,9 @@ expr_ref theory_seq::try_expand(expr* e, dependency*& eqs){ } result = ed.first; } + else if (false && m_util.str.is_string(e)) { + result = add_elim_string_axiom(e); + } else { m_expand_todo.push_back(e); } @@ -3981,7 +3976,7 @@ expr_ref theory_seq::expand1(expr* e0, dependency*& eqs) { expr_ref result(m); result = try_expand(e0, eqs); if (result) return result; - dependency* deps = 0; + dependency* deps = nullptr; expr* e = m_rep.find(e0, deps); expr* e1, *e2, *e3; expr_ref arg1(m), arg2(m); @@ -3994,7 +3989,7 @@ expr_ref theory_seq::expand1(expr* e0, dependency*& eqs) { } else if (m_util.str.is_empty(e) || m_util.str.is_string(e)) { result = e; - } + } else if (m_util.str.is_prefix(e, e1, e2)) { arg1 = try_expand(e1, deps); arg2 = try_expand(e2, deps); @@ -4085,8 +4080,11 @@ expr_ref theory_seq::expand1(expr* e0, dependency*& eqs) { enode* n2 = ctx.get_enode(e1); res = m_util.str.mk_string(symbol(val.to_string().c_str())); #if 1 + if (val.is_neg()) { + result = e; + } // TBD remove this: using roots is unsound for propagation. - if (n1->get_root() == n2->get_root()) { + else if (n1->get_root() == n2->get_root()) { result = res; deps = m_dm.mk_join(deps, m_dm.mk_leaf(assumption(n1, n2))); } @@ -4111,7 +4109,7 @@ expr_ref theory_seq::expand1(expr* e0, dependency*& eqs) { result = e; } if (result == e0) { - deps = 0; + deps = nullptr; } expr_dep edr(result, deps); m_rep.add_cache(e0, edr); @@ -4150,6 +4148,7 @@ void theory_seq::propagate() { void theory_seq::enque_axiom(expr* e) { if (!m_axiom_set.contains(e)) { + TRACE("seq", tout << "add axiom " << mk_pp(e, m) << "\n";); m_axioms.push_back(e); m_axiom_set.insert(e); m_trail_stack.push(push_back_vector(m_axioms)); @@ -4184,6 +4183,9 @@ void theory_seq::deque_axiom(expr* n) { else if (m_util.str.is_itos(n)) { add_itos_axiom(n); } + else if (m_util.str.is_stoi(n)) { + add_stoi_axiom(n); + } } @@ -4241,7 +4243,7 @@ void theory_seq::tightest_prefix(expr* s, expr* x) { (len(s) <= len(t) -> i <= len(t)-len(s)) */ void theory_seq::add_indexof_axiom(expr* i) { - expr* s = 0, *t = 0, *offset = 0; + expr* s = nullptr, *t = nullptr, *offset = nullptr; rational r; VERIFY(m_util.str.is_index(i, t, s) || m_util.str.is_index(i, t, s, offset)); @@ -4256,9 +4258,10 @@ void theory_seq::add_indexof_axiom(expr* i) { literal t_eq_empty = mk_eq_empty(t); // |t| = 0 => |s| = 0 or indexof(t,s,offset) = -1 - // ~contains(t,s) => indexof(t,s,offset) = -1 + // ~contains(t,s) <=> indexof(t,s,offset) = -1 add_axiom(cnt, i_eq_m1); +// add_axiom(~cnt, ~i_eq_m1); add_axiom(~t_eq_empty, s_eq_empty, i_eq_m1); if (!offset || (m_autil.is_numeral(offset, r) && r.is_zero())) { @@ -4271,6 +4274,7 @@ void theory_seq::add_indexof_axiom(expr* i) { add_axiom(~s_eq_empty, i_eq_0); add_axiom(~cnt, s_eq_empty, mk_seq_eq(t, xsy)); add_axiom(~cnt, s_eq_empty, mk_eq(i, lenx, false)); + add_axiom(~cnt, mk_literal(m_autil.mk_ge(i, zero))); tightest_prefix(s, x); } else { @@ -4324,7 +4328,7 @@ void theory_seq::add_indexof_axiom(expr* i) { */ void theory_seq::add_replace_axiom(expr* r) { context& ctx = get_context(); - expr* a = 0, *s = 0, *t = 0; + expr* a = nullptr, *s = nullptr, *t = nullptr; VERIFY(m_util.str.is_replace(r, a, s, t)); expr_ref x = mk_skolem(m_indexof_left, a, s); expr_ref y = mk_skolem(m_indexof_right, a, s); @@ -4342,20 +4346,21 @@ void theory_seq::add_replace_axiom(expr* r) { tightest_prefix(s, x); } -void theory_seq::add_elim_string_axiom(expr* n) { +expr_ref theory_seq::add_elim_string_axiom(expr* n) { zstring s; + TRACE("seq", tout << mk_pp(n, m) << "\n";); VERIFY(m_util.str.is_string(n, s)); if (s.length() == 0) { - return; + return expr_ref(n, m); } expr_ref result(m_util.str.mk_unit(m_util.str.mk_char(s, s.length()-1)), m); - for (unsigned i = s.length()-1; i > 0; ) { - --i; + for (unsigned i = s.length()-1; i-- > 0; ) { result = mk_concat(m_util.str.mk_unit(m_util.str.mk_char(s, i)), result); } add_axiom(mk_eq(n, result, false)); - m_rep.update(n, result, 0); + m_rep.update(n, result, nullptr); m_new_solution = true; + return result; } @@ -4370,7 +4375,7 @@ void theory_seq::add_elim_string_axiom(expr* n) { */ void theory_seq::add_length_axiom(expr* n) { context& ctx = get_context(); - expr* x = 0; + expr* x = nullptr; VERIFY(m_util.str.is_length(n, x)); if (m_util.str.is_concat(x) || m_util.str.is_unit(x) || @@ -4393,7 +4398,7 @@ void theory_seq::add_length_axiom(expr* n) { } void theory_seq::add_itos_length_axiom(expr* len) { - expr* x = 0, *n = 0; + expr* x = nullptr, *n = nullptr; VERIFY(m_util.str.is_length(len, x)); VERIFY(m_util.str.is_itos(x, n)); @@ -4401,9 +4406,9 @@ void theory_seq::add_itos_length_axiom(expr* len) { rational len1, len2; rational ten(10); if (get_num_value(n, len1)) { - bool neg = len1.is_neg(); - if (neg) len1.neg(); - num_char1 = neg?2:1; + if (len1.is_neg()) { + return; + } // 0 <= x < 10 // 10 <= x < 100 // 100 <= x < 1000 @@ -4422,13 +4427,12 @@ void theory_seq::add_itos_length_axiom(expr* len) { literal len_le(mk_literal(m_autil.mk_le(len, m_autil.mk_int(num_char)))); literal len_ge(mk_literal(m_autil.mk_ge(len, m_autil.mk_int(num_char)))); + literal n_ge_0(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0)))); + add_axiom(~n_ge_0, mk_literal(m_autil.mk_ge(len, m_autil.mk_int(1)))); if (num_char == 1) { - add_axiom(len_ge); - literal n_ge_0(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(0)))); literal n_ge_10(mk_literal(m_autil.mk_ge(n, m_autil.mk_int(10)))); add_axiom(~n_ge_0, n_ge_10, len_le); - add_axiom(~len_le, n_ge_0); add_axiom(~len_le, ~n_ge_10); return; } @@ -4436,30 +4440,21 @@ void theory_seq::add_itos_length_axiom(expr* len) { for (unsigned i = 2; i < num_char; ++i) { hi *= ten; } - // n <= -hi or n >= hi*10 <=> len >= num_chars - // -10*hi < n < 100*hi <=> len <= num_chars - literal n_le_hi = mk_literal(m_autil.mk_le(n, m_autil.mk_numeral(-hi, true))); + // n >= hi*10 <=> len >= num_chars + // n < 100*hi <=> len <= num_chars literal n_ge_10hi = mk_literal(m_autil.mk_ge(n, m_autil.mk_numeral(ten*hi, true))); - literal n_le_m10hi = mk_literal(m_autil.mk_le(n, m_autil.mk_numeral(-ten*hi, true))); literal n_ge_100hi = mk_literal(m_autil.mk_ge(n, m_autil.mk_numeral(ten*ten*hi, true))); - add_axiom(~n_le_hi, len_ge); add_axiom(~n_ge_10hi, len_ge); - add_axiom(n_le_hi, n_ge_10hi, ~len_ge); - - add_axiom(n_le_m10hi, n_ge_100hi, len_le); - add_axiom(~n_le_m10hi, ~len_le); add_axiom(~n_ge_100hi, ~len_le); - - add_axiom(mk_literal(m_autil.mk_ge(len, m_autil.mk_int(1)))); } void theory_seq::propagate_in_re(expr* n, bool is_true) { TRACE("seq", tout << mk_pp(n, m) << " <- " << (is_true?"true":"false") << "\n";); - expr* e1 = 0, *e2 = 0; - VERIFY(m_util.str.is_in_re(n, e1, e2)); + expr* s = nullptr, *re = nullptr; + VERIFY(m_util.str.is_in_re(n, s, re)); expr_ref tmp(n, m); m_rewrite(tmp); @@ -4467,7 +4462,7 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { if (!is_true) { literal_vector lits; lits.push_back(mk_literal(n)); - set_conflict(0, lits); + set_conflict(nullptr, lits); } return; } @@ -4475,26 +4470,26 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { if (is_true) { literal_vector lits; lits.push_back(~mk_literal(n)); - set_conflict(0, lits); + set_conflict(nullptr, lits); } return; } - expr_ref e3(e2, m); + expr_ref e3(re, m); context& ctx = get_context(); literal lit = ctx.get_literal(n); if (!is_true) { - e3 = m_util.re.mk_complement(e2); + e3 = m_util.re.mk_complement(re); lit.neg(); } eautomaton* a = get_automaton(e3); if (!a) return; - expr_ref len(m_util.str.mk_length(e1), m); + expr_ref len(m_util.str.mk_length(s), m); for (unsigned i = 0; i < a->num_states(); ++i) { - literal acc = mk_accept(e1, len, e3, i); - literal rej = mk_reject(e1, len, e3, i); + literal acc = mk_accept(s, len, e3, i); + literal rej = mk_reject(s, len, e3, i); add_axiom(a->is_final_state(i)?acc:~acc); add_axiom(a->is_final_state(i)?~rej:rej); } @@ -4505,11 +4500,11 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { literal_vector lits; lits.push_back(~lit); - for (unsigned i = 0; i < states.size(); ++i) { - lits.push_back(mk_accept(e1, zero, e3, states[i])); + for (unsigned st : states) { + lits.push_back(mk_accept(s, zero, e3, st)); } if (lits.size() == 2) { - propagate_lit(0, 1, &lit, lits[1]); + propagate_lit(nullptr, 1, &lit, lits[1]); } else { TRACE("seq", ctx.display_literals_verbose(tout, lits); tout << "\n";); @@ -4547,7 +4542,7 @@ static T* get_th_arith(context& ctx, theory_id afid, expr* e) { return dynamic_cast(th); } else { - return 0; + return nullptr; } } @@ -4563,8 +4558,8 @@ static bool get_arith_value(context& ctx, theory_id afid, expr* e, expr_ref& v) bool theory_seq::get_num_value(expr* e, rational& val) const { context& ctx = get_context(); expr_ref _val(m); - if (!ctx.e_internalized(e)) - return false; + if (!ctx.e_internalized(e)) + return false; enode* next = ctx.get_enode(e), *n = next; do { if (get_arith_value(ctx, m_autil.get_family_id(), next->get_owner(), _val) && m_autil.is_numeral(_val, val) && val.is_int()) { @@ -4646,7 +4641,7 @@ bool theory_seq::get_length(expr* e, rational& val) const { context& ctx = get_context(); rational val1; expr_ref len(m), len_val(m); - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; ptr_vector todo; todo.push_back(e); val.reset(); @@ -4718,7 +4713,7 @@ this translates to: void theory_seq::add_extract_axiom(expr* e) { TRACE("seq", tout << mk_pp(e, m) << "\n";); - expr* s = 0, *i = 0, *l = 0; + expr* s = nullptr, *i = nullptr, *l = nullptr; VERIFY(m_util.str.is_extract(e, s, i, l)); if (is_tail(s, i, l)) { add_tail_axiom(e, s); @@ -4809,6 +4804,7 @@ bool theory_seq::is_extract_suffix(expr* s, expr* i, expr* l) { /* 0 <= l <= len(s) => s = ey & l = len(e) + len(s) < l => s = e */ void theory_seq::add_extract_prefix_axiom(expr* e, expr* s, expr* l) { TRACE("seq", tout << mk_pp(e, m) << " " << mk_pp(s, m) << " " << mk_pp(l, m) << "\n";); @@ -4823,6 +4819,7 @@ void theory_seq::add_extract_prefix_axiom(expr* e, expr* s, expr* l) { add_axiom(~l_ge_0, ~l_le_s, mk_seq_eq(s, ey)); add_axiom(~l_ge_0, ~l_le_s, mk_eq(l, le, false)); add_axiom(~l_ge_0, ~l_le_s, mk_eq(ls_minus_l, m_util.str.mk_length(y), false)); + add_axiom(l_le_s, mk_eq(e, s, false)); } /* @@ -4849,7 +4846,7 @@ void theory_seq::add_extract_suffix_axiom(expr* e, expr* s, expr* i) { */ void theory_seq::add_at_axiom(expr* e) { - expr* s = 0, *i = 0; + expr* s = nullptr, *i = nullptr; VERIFY(m_util.str.is_at(e, s, i)); expr_ref len_e(m_util.str.mk_length(e), m); expr_ref len_s(m_util.str.mk_length(s), m); @@ -4878,17 +4875,17 @@ void theory_seq::add_at_axiom(expr* e) { */ void theory_seq::propagate_step(literal lit, expr* step) { SASSERT(get_context().get_assignment(lit) == l_true); - expr* re = 0, *acc = 0, *s = 0, *idx = 0, *i = 0, *j = 0; + expr* re = nullptr, *acc = nullptr, *s = nullptr, *idx = nullptr, *i = nullptr, *j = nullptr; VERIFY(is_step(step, s, idx, re, i, j, acc)); TRACE("seq", tout << mk_pp(step, m) << " -> " << mk_pp(acc, m) << "\n";); - propagate_lit(0, 1, &lit, mk_simplified_literal(acc)); + propagate_lit(nullptr, 1, &lit, mk_simplified_literal(acc)); rational lo; rational _idx; if (lower_bound(s, lo) && lo.is_unsigned() && m_autil.is_numeral(idx, _idx) && lo >= _idx) { // skip } else { - propagate_lit(0, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), idx))); + propagate_lit(nullptr, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), idx))); } ensure_nth(lit, s, idx); } @@ -4934,7 +4931,7 @@ literal theory_seq::mk_literal(expr* _e) { literal theory_seq::mk_seq_eq(expr* a, expr* b) { SASSERT(m_util.is_seq(a)); - return mk_literal(mk_skolem(m_eq, a, b, 0, 0, m.mk_bool_sort())); + return mk_literal(mk_skolem(m_eq, a, b, nullptr, nullptr, m.mk_bool_sort())); } literal theory_seq::mk_eq_empty(expr* _e, bool phase) { @@ -5055,8 +5052,8 @@ theory_seq::dependency* theory_seq::mk_join(dependency* deps, literal lit) { } theory_seq::dependency* theory_seq::mk_join(dependency* deps, literal_vector const& lits) { - for (unsigned i = 0; i < lits.size(); ++i) { - deps = mk_join(deps, lits[i]); + for (literal l : lits) { + deps = mk_join(deps, l); } return deps; } @@ -5064,7 +5061,7 @@ theory_seq::dependency* theory_seq::mk_join(dependency* deps, literal_vector con void theory_seq::propagate_eq(literal lit, expr* e1, expr* e2, bool add_to_eqs) { literal_vector lits; lits.push_back(lit); - propagate_eq(0, lits, e1, e2, add_to_eqs); + propagate_eq(nullptr, lits, e1, e2, add_to_eqs); } void theory_seq::propagate_eq(dependency* deps, literal_vector const& _lits, expr* e1, expr* e2, bool add_to_eqs) { @@ -5103,7 +5100,7 @@ void theory_seq::propagate_eq(dependency* deps, literal_vector const& _lits, exp void theory_seq::assign_eh(bool_var v, bool is_true) { context & ctx = get_context(); expr* e = ctx.bool_var2expr(v); - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; expr_ref f(m); bool change = false; literal lit(v, !is_true); @@ -5261,53 +5258,15 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { TRACE("seq", tout << "new disequality " << get_context().get_scope_level() << ": " << eq << "\n";); m_rewrite(eq); if (!m.is_false(eq)) { - literal lit = mk_eq(e1, e2, false); - if (m_util.str.is_empty(e2)) { std::swap(e1, e2); } - if (false && m_util.str.is_empty(e1)) { - expr_ref head(m), tail(m), conc(m); - mk_decompose(e2, head, tail); - conc = mk_concat(head, tail); - propagate_eq(~lit, e2, conc, true); - } -#if 0 - - // (e1 = "" & e2 = xdz) or (e2 = "" & e1 = xcy) or (e1 = xcy & e2 = xdz & c != d) or (e1 = x & e2 = xdz) or (e2 = x & e1 = xcy) - // e1 = "" or e1 = xcy or e1 = x - // e2 = "" or e2 = xdz or e2 = x - // e1 = xcy or e2 = xdz - // c != d - - sort* char_sort = 0; - expr_ref emp(m); - VERIFY(m_util.is_seq(m.get_sort(e1), char_sort)); - emp = m_util.str.mk_empty(m.get_sort(e1)); - - expr_ref x = mk_skolem(symbol("seq.ne.x"), e1, e2); - expr_ref y = mk_skolem(symbol("seq.ne.y"), e1, e2); - expr_ref z = mk_skolem(symbol("seq.ne.z"), e1, e2); - expr_ref c = mk_skolem(symbol("seq.ne.c"), e1, e2, 0, char_sort); - expr_ref d = mk_skolem(symbol("seq.ne.d"), e1, e2, 0, char_sort); - literal e1_is_emp = mk_seq_eq(e1, emp); - literal e2_is_emp = mk_seq_eq(e2, emp); - literal e1_is_xcy = mk_seq_eq(e1, mk_concat(x, m_util.str.mk_unit(c), y)); - literal e2_is_xdz = mk_seq_eq(e2, mk_concat(x, m_util.str.mk_unit(d), z)); - add_axiom(lit, e1_is_emp, e1_is_xcy, mk_seq_eq(e1, x)); - add_axiom(lit, e2_is_emp, e2_is_xdz, mk_seq_eq(e2, x)); - add_axiom(lit, e1_is_xcy, e2_is_xdz); - add_axiom(lit, ~mk_eq(c, d, false)); -#else - else { - dependency* dep = m_dm.mk_leaf(assumption(~lit)); - m_nqs.push_back(ne(e1, e2, dep)); - solve_nqs(m_nqs.size() - 1); - } -#endif + dependency* dep = m_dm.mk_leaf(assumption(~lit)); + m_nqs.push_back(ne(e1, e2, dep)); + solve_nqs(m_nqs.size() - 1); } } @@ -5355,7 +5314,9 @@ void theory_seq::relevant_eh(app* n) { m_util.str.is_extract(n) || m_util.str.is_at(n) || m_util.str.is_empty(n) || - m_util.str.is_string(n)) { + m_util.str.is_string(n) || + m_util.str.is_itos(n) || + m_util.str.is_stoi(n)) { enque_axiom(n); } @@ -5372,10 +5333,13 @@ void theory_seq::relevant_eh(app* n) { eautomaton* theory_seq::get_automaton(expr* re) { - eautomaton* result = 0; + eautomaton* result = nullptr; if (m_re2aut.find(re, result)) { return result; } + if (!m_mk_aut.has_solver()) { + m_mk_aut.set_solver(alloc(seq_expr_solver, m, get_context().get_fparams())); + } result = m_mk_aut(re); if (result) { display_expr disp(m); @@ -5453,9 +5417,9 @@ expr_ref theory_seq::mk_step(expr* s, expr* idx, expr* re, unsigned i, unsigned rej(s, idx, re, i) -> len(s) > idx if i is final */ void theory_seq::propagate_acc_rej_length(literal lit, expr* e) { - expr *s = 0, *idx = 0, *re = 0; + expr *s = nullptr, *idx = nullptr, *re = nullptr; unsigned src; - eautomaton* aut = 0; + eautomaton* aut = nullptr; bool is_acc; is_acc = is_accept(e, s, idx, re, src, aut); if (!is_acc) { @@ -5465,15 +5429,15 @@ void theory_seq::propagate_acc_rej_length(literal lit, expr* e) { SASSERT(m_autil.is_numeral(idx)); SASSERT(get_context().get_assignment(lit) == l_true); if (aut->is_sink_state(src)) { - propagate_lit(0, 1, &lit, false_literal); + propagate_lit(nullptr, 1, &lit, false_literal); return; } bool is_final = aut->is_final_state(src); if (is_final == is_acc) { - propagate_lit(0, 1, &lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), idx))); + propagate_lit(nullptr, 1, &lit, mk_literal(m_autil.mk_ge(m_util.str.mk_length(s), idx))); } else { - propagate_lit(0, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), idx))); + propagate_lit(nullptr, 1, &lit, ~mk_literal(m_autil.mk_le(m_util.str.mk_length(s), idx))); } } @@ -5486,10 +5450,10 @@ bool theory_seq::add_accept2step(expr* acc, bool& change) { TRACE("seq", tout << mk_pp(acc, m) << "\n";); SASSERT(ctx.get_assignment(acc) == l_true); - expr *e = 0, *idx = 0, *re = 0; + expr *e = nullptr, *idx = nullptr, *re = nullptr; expr_ref step(m); unsigned src; - eautomaton* aut = 0; + eautomaton* aut = nullptr; VERIFY(is_accept(acc, e, idx, re, src, aut)); if (!aut || m_util.str.is_length(idx)) { return false; @@ -5542,7 +5506,7 @@ bool theory_seq::add_accept2step(expr* acc, bool& change) { for (unsigned i = 0; i < lits.size(); ++i) { lits[i].neg(); } - propagate_lit(0, lits.size(), lits.c_ptr(), lit); + propagate_lit(nullptr, lits.size(), lits.c_ptr(), lit); return false; } if (has_undef) { @@ -5553,7 +5517,7 @@ bool theory_seq::add_accept2step(expr* acc, bool& change) { SASSERT(ctx.get_assignment(lits[i]) == l_false); lits[i].neg(); } - set_conflict(0, lits); + set_conflict(nullptr, lits); return false; } @@ -5565,7 +5529,7 @@ bool theory_seq::add_accept2step(expr* acc, bool& change) { bool theory_seq::add_step2accept(expr* step, bool& change) { context& ctx = get_context(); SASSERT(ctx.get_assignment(step) == l_true); - expr* re = 0, *_acc = 0, *s = 0, *idx = 0, *i = 0, *j = 0; + expr* re = nullptr, *_acc = nullptr, *s = nullptr, *idx = nullptr, *i = nullptr, *j = nullptr; VERIFY(is_step(step, s, idx, re, i, j, _acc)); literal acc1 = mk_accept(s, idx, re, i); switch (ctx.get_assignment(acc1)) { @@ -5586,12 +5550,12 @@ bool theory_seq::add_step2accept(expr* step, bool& change) { lits.push_back(~acc2); switch (ctx.get_assignment(acc2)) { case l_undef: - propagate_lit(0, 2, lits.c_ptr(), acc2); + propagate_lit(nullptr, 2, lits.c_ptr(), acc2); break; case l_true: break; case l_false: - set_conflict(0, lits); + set_conflict(nullptr, lits); break; } break; @@ -5614,10 +5578,10 @@ Recall we also have: bool theory_seq::add_reject2reject(expr* rej, bool& change) { context& ctx = get_context(); SASSERT(ctx.get_assignment(rej) == l_true); - expr* s = 0, *idx = 0, *re = 0; + expr* s = nullptr, *idx = nullptr, *re = nullptr; unsigned src; rational r; - eautomaton* aut = 0; + eautomaton* aut = nullptr; VERIFY(is_reject(rej, s, idx, re, src, aut)); if (!aut || m_util.str.is_length(idx)) return false; VERIFY(m_autil.is_numeral(idx, r) && r.is_unsigned()); @@ -5640,8 +5604,7 @@ bool theory_seq::add_reject2reject(expr* rej, bool& change) { ensure_nth(~len_le_idx, s, idx); literal_vector eqs; bool has_undef = false; - for (unsigned i = 0; i < mvs.size(); ++i) { - eautomaton::move const& mv = mvs[i]; + for (eautomaton::move const& mv : mvs) { literal eq = mk_literal(mv.t()->accept(nth)); switch (ctx.get_assignment(eq)) { case l_false: @@ -5676,7 +5639,7 @@ bool theory_seq::add_reject2reject(expr* rej, bool& change) { void theory_seq::propagate_not_prefix(expr* e) { context& ctx = get_context(); - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_prefix(e, e1, e2)); literal lit = ctx.get_literal(e); SASSERT(ctx.get_assignment(lit) == l_false); @@ -5686,13 +5649,13 @@ void theory_seq::propagate_not_prefix(expr* e) { propagate_non_empty(~lit, e1); expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); literal e2_is_emp = mk_seq_eq(e2, emp); - sort* char_sort = 0; + sort* char_sort = nullptr; VERIFY(m_util.is_seq(m.get_sort(e1), char_sort)); expr_ref x = mk_skolem(symbol("seq.prefix.x"), e1, e2); expr_ref y = mk_skolem(symbol("seq.prefix.y"), e1, e2); expr_ref z = mk_skolem(symbol("seq.prefix.z"), e1, e2); - expr_ref c = mk_skolem(symbol("seq.prefix.c"), e1, e2, 0, 0, char_sort); - expr_ref d = mk_skolem(symbol("seq.prefix.d"), e1, e2, 0, 0, char_sort); + expr_ref c = mk_skolem(symbol("seq.prefix.c"), e1, e2, nullptr, nullptr, char_sort); + expr_ref d = mk_skolem(symbol("seq.prefix.d"), e1, e2, nullptr, nullptr, char_sort); add_axiom(lit, e2_is_emp, mk_seq_eq(e1, mk_concat(x, m_util.str.mk_unit(c), y))); add_axiom(lit, e2_is_emp, mk_seq_eq(e2, mk_concat(x, m_util.str.mk_unit(d), z)), mk_seq_eq(e2, x)); add_axiom(lit, e2_is_emp, ~mk_eq(c, d, false), mk_seq_eq(e2, x)); @@ -5705,7 +5668,7 @@ void theory_seq::propagate_not_prefix(expr* e) { void theory_seq::propagate_not_prefix2(expr* e) { context& ctx = get_context(); - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_prefix(e, e1, e2)); literal lit = ctx.get_literal(e); SASSERT(ctx.get_assignment(lit) == l_false); @@ -5732,7 +5695,7 @@ void theory_seq::propagate_not_prefix2(expr* e) { void theory_seq::propagate_not_suffix(expr* e) { context& ctx = get_context(); - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_suffix(e, e1, e2)); literal lit = ctx.get_literal(e); SASSERT(ctx.get_assignment(lit) == l_false); @@ -5743,13 +5706,13 @@ void theory_seq::propagate_not_suffix(expr* e) { expr_ref emp(m_util.str.mk_empty(m.get_sort(e1)), m); literal e2_is_emp = mk_seq_eq(e2, emp); - sort* char_sort = 0; + sort* char_sort = nullptr; VERIFY(m_util.is_seq(m.get_sort(e1), char_sort)); expr_ref x = mk_skolem(symbol("seq.suffix.x"), e1, e2); expr_ref y = mk_skolem(symbol("seq.suffix.y"), e1, e2); expr_ref z = mk_skolem(symbol("seq.suffix.z"), e1, e2); - expr_ref c = mk_skolem(symbol("seq.suffix.c"), e1, e2, 0, 0, char_sort); - expr_ref d = mk_skolem(symbol("seq.suffix.d"), e1, e2, 0, 0, char_sort); + expr_ref c = mk_skolem(symbol("seq.suffix.c"), e1, e2, nullptr, nullptr, char_sort); + expr_ref d = mk_skolem(symbol("seq.suffix.d"), e1, e2, nullptr, nullptr, char_sort); add_axiom(lit, e2_is_emp, mk_seq_eq(e1, mk_concat(y, m_util.str.mk_unit(c), x))); add_axiom(lit, e2_is_emp, mk_seq_eq(e2, mk_concat(z, m_util.str.mk_unit(d), x)), mk_seq_eq(e2, x)); add_axiom(lit, e2_is_emp, ~mk_eq(c, d, false), mk_seq_eq(e2, x)); @@ -5761,7 +5724,7 @@ void theory_seq::propagate_not_suffix(expr* e) { */ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { context& ctx = get_context(); - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_prefix(e, e1, e2)); SASSERT(ctx.get_assignment(e) == l_false); if (canonizes(false, e)) { @@ -5823,7 +5786,7 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { lits.push_back(~ctx.get_literal(e)); lits.push_back(~e2_is_emp); lits.push_back(lit); - propagate_lit(0, lits.size(), lits.c_ptr(), ~mk_literal(m_util.str.mk_prefix(tail1, tail2))); + propagate_lit(nullptr, lits.size(), lits.c_ptr(), ~mk_literal(m_util.str.mk_prefix(tail1, tail2))); TRACE("seq", tout << mk_pp(e, m) << " saturate: " << tail1 << " = " << tail2 << "\n";); return false; } @@ -5833,7 +5796,7 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { */ bool theory_seq::add_suffix2suffix(expr* e, bool& change) { context& ctx = get_context(); - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_suffix(e, e1, e2)); SASSERT(ctx.get_assignment(e) == l_false); if (canonizes(false, e)) { @@ -5887,21 +5850,21 @@ bool theory_seq::add_suffix2suffix(expr* e, bool& change) { lits.push_back(~ctx.get_literal(e)); lits.push_back(~e2_is_emp); lits.push_back(last_eq); - propagate_lit(0, lits.size(), lits.c_ptr(), ~mk_literal(m_util.str.mk_suffix(first1, first2))); + propagate_lit(nullptr, lits.size(), lits.c_ptr(), ~mk_literal(m_util.str.mk_suffix(first1, first2))); TRACE("seq", tout << mk_pp(e, m) << " saturate\n";); return false; } bool theory_seq::canonizes(bool sign, expr* e) { context& ctx = get_context(); - dependency* deps = 0; + dependency* deps = nullptr; expr_ref cont = canonize(e, deps); TRACE("seq", tout << mk_pp(e, m) << " -> " << cont << "\n"; if (deps) display_deps(tout, deps);); if ((m.is_true(cont) && !sign) || (m.is_false(cont) && sign)) { TRACE("seq", display(tout); tout << ctx.get_assignment(ctx.get_literal(e)) << "\n";); - propagate_lit(deps, 0, 0, ctx.get_literal(e)); + propagate_lit(deps, 0, nullptr, ctx.get_literal(e)); return true; } if ((m.is_false(cont) && !sign) || @@ -5919,7 +5882,7 @@ bool theory_seq::canonizes(bool sign, expr* e) { bool theory_seq::add_contains2contains(expr* e, bool& change) { context& ctx = get_context(); - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; VERIFY(m_util.str.is_contains(e, e1, e2)); SASSERT(ctx.get_assignment(e) == l_false); if (canonizes(false, e)) { @@ -5944,7 +5907,7 @@ bool theory_seq::add_contains2contains(expr* e, bool& change) { propagate_eq(~e1_is_emp, e1, conc, true); literal lits[2] = { ~ctx.get_literal(e), ~e1_is_emp }; - propagate_lit(0, 2, lits, ~mk_literal(m_util.str.mk_contains(tail, e2))); + propagate_lit(nullptr, 2, lits, ~mk_literal(m_util.str.mk_contains(tail, e2))); return false; } @@ -5989,7 +5952,7 @@ bool theory_seq::propagate_automata() { } void theory_seq::get_concat(expr* e, ptr_vector& concats) { - expr* e1 = 0, *e2 = 0; + expr* e1 = nullptr, *e2 = nullptr; while (true) { e = m_rep.find(e); if (m_util.str.is_concat(e, e1, e2)) { diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 577454621..b5ce30236 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -38,7 +38,7 @@ namespace smt { enode* n1, *n2; literal lit; assumption(enode* n1, enode* n2): n1(n1), n2(n2), lit(null_literal) {} - assumption(literal lit): n1(0), n2(0), lit(lit) {} + assumption(literal lit): n1(nullptr), n2(nullptr), lit(lit) {} }; typedef scoped_dependency_manager dependency_manager; typedef dependency_manager::dependency dependency; @@ -65,14 +65,14 @@ namespace smt { // + a cache for normalization. class solution_map { enum map_update { INS, DEL }; - ast_manager& m; + ast_manager& m; dependency_manager& m_dm; - eqdep_map_t m_map; - eval_cache m_cache; - expr_ref_vector m_lhs, m_rhs; + eqdep_map_t m_map; + eval_cache m_cache; + expr_ref_vector m_lhs, m_rhs; ptr_vector m_deps; - svector m_updates; - unsigned_vector m_limit; + svector m_updates; + unsigned_vector m_limit; void add_trail(map_update op, expr* l, expr* r, dependency* d); public: @@ -225,8 +225,8 @@ namespace smt { expr_ref m_e; public: replay_length_coherence(ast_manager& m, expr* e) : m_e(e, m) {} - virtual ~replay_length_coherence() {} - virtual void operator()(theory_seq& th) { + ~replay_length_coherence() override {} + void operator()(theory_seq& th) override { th.check_length_coherence(m_e); m_e.reset(); } @@ -236,8 +236,8 @@ namespace smt { expr_ref m_e; public: replay_fixed_length(ast_manager& m, expr* e) : m_e(e, m) {} - virtual ~replay_fixed_length() {} - virtual void operator()(theory_seq& th) { + ~replay_fixed_length() override {} + void operator()(theory_seq& th) override { th.fixed_length(m_e); m_e.reset(); } @@ -247,8 +247,8 @@ namespace smt { expr_ref m_e; public: replay_axiom(ast_manager& m, expr* e) : m_e(e, m) {} - virtual ~replay_axiom() {} - virtual void operator()(theory_seq& th) { + ~replay_axiom() override {} + void operator()(theory_seq& th) override { th.enque_axiom(m_e); m_e.reset(); } @@ -258,7 +258,7 @@ namespace smt { apply* m_apply; public: push_replay(apply* app): m_apply(app) {} - virtual void undo(theory_seq& th) { + void undo(theory_seq& th) override { th.m_replay.push_back(m_apply); } }; @@ -267,7 +267,7 @@ namespace smt { unsigned k; public: pop_branch(unsigned k): k(k) {} - virtual void undo(theory_seq& th) { + void undo(theory_seq& th) override { th.m_branch_start.erase(k); } }; @@ -347,29 +347,30 @@ namespace smt { obj_hashtable m_fixed; // string variables that are fixed length. - virtual void init(context* ctx); - virtual final_check_status final_check_eh(); - virtual bool internalize_atom(app* atom, bool); - virtual bool internalize_term(app*); - virtual void internalize_eq_eh(app * atom, bool_var v); - virtual void new_eq_eh(theory_var, theory_var); - virtual void new_diseq_eh(theory_var, theory_var); - virtual void assign_eh(bool_var v, bool is_true); - virtual bool can_propagate(); - virtual void propagate(); - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); - virtual void restart_eh(); - virtual void relevant_eh(app* n); - virtual theory* mk_fresh(context* new_ctx) { return alloc(theory_seq, new_ctx->get_manager()); } - virtual char const * get_name() const { return "seq"; } - virtual theory_var mk_var(enode* n); - virtual void apply_sort_cnstr(enode* n, sort* s); - virtual void display(std::ostream & out) const; - virtual void collect_statistics(::statistics & st) const; - virtual model_value_proc * mk_value(enode * n, model_generator & mg); - virtual void init_model(model_generator & mg); - virtual void init_search_eh(); + void init(context* ctx) override; + final_check_status final_check_eh() override; + bool internalize_atom(app* atom, bool) override; + bool internalize_term(app*) override; + void internalize_eq_eh(app * atom, bool_var v) override; + void new_eq_eh(theory_var, theory_var) override; + void new_diseq_eh(theory_var, theory_var) override; + void assign_eh(bool_var v, bool is_true) override; + bool can_propagate() override; + void propagate() override; + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; + void restart_eh() override; + void relevant_eh(app* n) override; + theory* mk_fresh(context* new_ctx) override { return alloc(theory_seq, new_ctx->get_manager()); } + char const * get_name() const override { return "seq"; } + theory_var mk_var(enode* n) override; + void apply_sort_cnstr(enode* n, sort* s) override; + void display(std::ostream & out) const override; + void collect_statistics(::statistics & st) const override; + model_value_proc * mk_value(enode * n, model_generator & mg) override; + void init_model(model_generator & mg) override; + void finalize_model(model_generator & mg) override; + void init_search_eh() override; void init_model(expr_ref_vector const& es); @@ -417,7 +418,6 @@ namespace smt { vector const& ll, vector const& rl); bool set_empty(expr* x); bool is_complex(eq const& e); - bool internalize_re(expr* e); bool check_extensionality(); bool check_contains(); @@ -465,7 +465,7 @@ namespace smt { // asserting consequences bool linearize(dependency* dep, enode_pair_vector& eqs, literal_vector& lits) const; - void propagate_lit(dependency* dep, literal lit) { propagate_lit(dep, 0, 0, lit); } + void propagate_lit(dependency* dep, literal lit) { propagate_lit(dep, 0, nullptr, lit); } void propagate_lit(dependency* dep, unsigned n, literal const* lits, literal lit); void propagate_eq(dependency* dep, enode* n1, enode* n2); void propagate_eq(literal lit, expr* e1, expr* e2, bool add_to_eqs); @@ -478,6 +478,7 @@ namespace smt { void insert_branch_start(unsigned k, unsigned s); unsigned find_branch_start(unsigned k); bool find_branch_candidate(unsigned& start, dependency* dep, expr_ref_vector const& ls, expr_ref_vector const& rs); + expr_ref_vector expand_strings(expr_ref_vector const& es); bool can_be_equal(unsigned szl, expr* const* ls, unsigned szr, expr* const* rs) const; lbool assume_equality(expr* l, expr* r); @@ -536,11 +537,13 @@ namespace smt { void add_int_string(expr* e); bool check_int_string(); - void add_elim_string_axiom(expr* n); + expr_ref add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); void add_in_re_axiom(expr* n); - bool add_stoi_axiom(expr* n); - bool add_itos_axiom(expr* n); + void add_itos_axiom(expr* n); + void add_stoi_axiom(expr* n); + bool add_stoi_val_axiom(expr* n); + bool add_itos_val_axiom(expr* n); literal is_digit(expr* ch); expr_ref digit2int(expr* ch); void add_itos_length_axiom(expr* n); @@ -566,7 +569,7 @@ namespace smt { void mk_decompose(expr* e, expr_ref& head, expr_ref& tail); expr* coalesce_chars(expr* const& str); - expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = 0, expr* e3 = 0, expr* e4 = 0, sort* range = 0); + expr_ref mk_skolem(symbol const& s, expr* e1, expr* e2 = nullptr, expr* e3 = nullptr, expr* e4 = nullptr, sort* range = nullptr); bool is_skolem(symbol const& s, expr* e) const; void set_incomplete(app* term); @@ -619,7 +622,7 @@ namespace smt { void display_nc(std::ostream& out, nc const& nc) const; public: theory_seq(ast_manager& m); - virtual ~theory_seq(); + ~theory_seq() override; // model building app* mk_value(app* a); diff --git a/src/smt/theory_seq_empty.h b/src/smt/theory_seq_empty.h index 85408f7e5..93b4be173 100644 --- a/src/smt/theory_seq_empty.h +++ b/src/smt/theory_seq_empty.h @@ -63,18 +63,18 @@ namespace smt { m_unique_sequences.insert(m.get_sort(uniq), uniq); } - virtual expr* get_some_value(sort* s) { + expr* get_some_value(sort* s) override { if (u.is_seq(s)) { return u.str.mk_empty(s); } - sort* seq = 0; + sort* seq = nullptr; if (u.is_re(s, seq)) { return u.re.mk_to_re(u.str.mk_empty(seq)); } UNREACHABLE(); - return 0; + return nullptr; } - virtual bool get_some_values(sort* s, expr_ref& v1, expr_ref& v2) { + bool get_some_values(sort* s, expr_ref& v1, expr_ref& v2) override { if (u.is_string(s)) { v1 = u.str.mk_string(symbol("a")); v2 = u.str.mk_string(symbol("b")); @@ -94,7 +94,7 @@ namespace smt { NOT_IMPLEMENTED_YET(); return false; } - virtual expr* get_fresh_value(sort* s) { + expr* get_fresh_value(sort* s) override { if (u.is_string(s)) { while (true) { std::ostringstream strm; @@ -105,7 +105,7 @@ namespace smt { return u.str.mk_string(sym); } } - sort* seq = 0, *ch = 0; + sort* seq = nullptr, *ch = nullptr; if (u.is_re(s, seq)) { expr* v0 = get_fresh_value(seq); return u.re.mk_to_re(v0); @@ -120,9 +120,9 @@ namespace smt { return u.str.mk_unit(v); } UNREACHABLE(); - return 0; + return nullptr; } - virtual void register_value(expr* n) { + void register_value(expr* n) override { symbol sym; if (u.str.is_string(n, sym)) { m_strings.insert(sym); @@ -148,17 +148,17 @@ namespace smt { class theory_seq_empty : public theory { bool m_used; - virtual final_check_status final_check_eh() { return m_used?FC_GIVEUP:FC_DONE; } - virtual bool internalize_atom(app*, bool) { if (!m_used) { get_context().push_trail(value_trail(m_used)); m_used = true; } return false; } - virtual bool internalize_term(app*) { return internalize_atom(0,false); } - virtual void new_eq_eh(theory_var, theory_var) { } - virtual void new_diseq_eh(theory_var, theory_var) {} - virtual theory* mk_fresh(context* new_ctx) { return alloc(theory_seq_empty, new_ctx->get_manager()); } - virtual char const * get_name() const { return "seq-empty"; } - virtual void display(std::ostream& out) const {} + final_check_status final_check_eh() override { return m_used?FC_GIVEUP:FC_DONE; } + bool internalize_atom(app*, bool) override { if (!m_used) { get_context().push_trail(value_trail(m_used)); m_used = true; } return false; } + bool internalize_term(app*) override { return internalize_atom(nullptr,false); } + void new_eq_eh(theory_var, theory_var) override { } + void new_diseq_eh(theory_var, theory_var) override {} + theory* mk_fresh(context* new_ctx) override { return alloc(theory_seq_empty, new_ctx->get_manager()); } + char const * get_name() const override { return "seq-empty"; } + void display(std::ostream& out) const override {} public: theory_seq_empty(ast_manager& m):theory(m.mk_family_id("seq")), m_used(false) {} - virtual void init_model(model_generator & mg) { + void init_model(model_generator & mg) override { mg.register_factory(alloc(seq_factory, get_manager(), get_family_id(), mg.get_model())); } diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 022a2ad73..f2432b4fb 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -25,6 +25,8 @@ #include "smt/theory_seq_empty.h" #include "smt/theory_arith.h" #include "ast/ast_util.h" +#include "ast/rewriter/seq_rewriter.h" +#include "smt_kernel.h" namespace smt { @@ -33,7 +35,7 @@ namespace smt { m_params(params), /* Options */ opt_EagerStringConstantLengthAssertions(true), - opt_VerifyFinalCheckProgress(true), + opt_VerifyFinalCheckProgress(false), opt_LCMUnrollStep(2), opt_NoQuickReturn_IntegerTheory(false), opt_DisableIntegerTheoryIntegration(false), @@ -48,8 +50,12 @@ namespace smt { finalCheckProgressIndicator(false), m_trail(m), m_factory(nullptr), + m_mk_aut(m), m_unused_id(0), m_delayed_axiom_setup_terms(m), + m_delayed_assertions_todo(m), + m_persisted_axioms(m), + m_persisted_axiom_todo(m), tmpStringVarCount(0), tmpXorVarCount(0), tmpLenTestVarCount(0), @@ -71,6 +77,10 @@ namespace smt { theory_str::~theory_str() { m_trail_stack.reset(); + for (eautomaton * aut : regex_automata) { + dealloc(aut); + } + regex_automata.clear(); } expr * theory_str::mk_string(zstring const& str) { @@ -95,6 +105,26 @@ namespace smt { return u.str.mk_string(sym); } + class seq_expr_solver : public expr_solver { + kernel m_kernel; + public: + seq_expr_solver(ast_manager& m, smt_params& fp): + m_kernel(m, fp) {} + virtual lbool check_sat(expr* e) { + m_kernel.push(); + m_kernel.assert_expr(e); + lbool r = m_kernel.check(); + m_kernel.pop(1); + return r; + } + }; + + void theory_str::init(context * ctx) { + theory::init(ctx); + m_mk_aut.set_solver(alloc(seq_expr_solver, get_manager(), + get_context().get_fparams())); + } + void theory_str::initialize_charset() { bool defaultCharset = true; if (defaultCharset) { @@ -169,6 +199,8 @@ namespace smt { } void theory_str::assert_axiom(expr * _e) { + if (_e == nullptr) + return; if (opt_VerifyFinalCheckProgress) { finalCheckProgressIndicator = true; } @@ -263,10 +295,13 @@ namespace smt { } void theory_str::refresh_theory_var(expr * e) { + ast_manager & m = get_manager(); enode * en = ensure_enode(e); theory_var v = mk_var(en); (void)v; TRACE("str", tout << "refresh " << mk_pp(e, get_manager()) << ": v#" << v << std::endl;); - m_basicstr_axiom_todo.push_back(en); + if (m.get_sort(e) == u.str.mk_string_sort()) { + m_basicstr_axiom_todo.push_back(en); + } } theory_var theory_str::mk_var(enode* n) { @@ -288,10 +323,9 @@ namespace smt { } } - static void cut_vars_map_copy(std::map & dest, std::map & src) { - std::map::iterator itor = src.begin(); - for (; itor != src.end(); itor++) { - dest[itor->first] = 1; + static void cut_vars_map_copy(obj_map & dest, obj_map & src) { + for (auto const& kv : src) { + dest.insert(kv.m_key, 1); } } @@ -306,9 +340,8 @@ namespace smt { return false; } - std::map::iterator itor = cut_var_map[n1].top()->vars.begin(); - for (; itor != cut_var_map[n1].top()->vars.end(); ++itor) { - if (cut_var_map[n2].top()->vars.find(itor->first) != cut_var_map[n2].top()->vars.end()) { + for (auto const& kv : cut_var_map[n1].top()->vars) { + if (cut_var_map[n2].top()->vars.contains(kv.m_key)) { return true; } } @@ -323,7 +356,7 @@ namespace smt { T_cut * varInfo = alloc(T_cut); m_cut_allocs.push_back(varInfo); varInfo->level = slevel; - varInfo->vars[node] = 1; + varInfo->vars.insert(node, 1); cut_var_map.insert(baseNode, std::stack()); cut_var_map[baseNode].push(varInfo); TRACE("str", tout << "add var info for baseNode=" << mk_pp(baseNode, get_manager()) << ", node=" << mk_pp(node, get_manager()) << " [" << slevel << "]" << std::endl;); @@ -332,7 +365,7 @@ namespace smt { T_cut * varInfo = alloc(T_cut); m_cut_allocs.push_back(varInfo); varInfo->level = slevel; - varInfo->vars[node] = 1; + varInfo->vars.insert(node, 1); cut_var_map[baseNode].push(varInfo); TRACE("str", tout << "add var info for baseNode=" << mk_pp(baseNode, get_manager()) << ", node=" << mk_pp(node, get_manager()) << " [" << slevel << "]" << std::endl;); } else { @@ -341,11 +374,11 @@ namespace smt { m_cut_allocs.push_back(varInfo); varInfo->level = slevel; cut_vars_map_copy(varInfo->vars, cut_var_map[baseNode].top()->vars); - varInfo->vars[node] = 1; + varInfo->vars.insert(node, 1); cut_var_map[baseNode].push(varInfo); TRACE("str", tout << "add var info for baseNode=" << mk_pp(baseNode, get_manager()) << ", node=" << mk_pp(node, get_manager()) << " [" << slevel << "]" << std::endl;); } else if (cut_var_map[baseNode].top()->level == slevel) { - cut_var_map[baseNode].top()->vars[node] = 1; + cut_var_map[baseNode].top()->vars.insert(node, 1); TRACE("str", tout << "add var info for baseNode=" << mk_pp(baseNode, get_manager()) << ", node=" << mk_pp(node, get_manager()) << " [" << slevel << "]" << std::endl;); } else { get_manager().raise_exception("entered illegal state during add_cut_info_one_node()"); @@ -443,7 +476,7 @@ namespace smt { void theory_str::track_variable_scope(expr * var) { if (internal_variable_scope_levels.find(sLevel) == internal_variable_scope_levels.end()) { - internal_variable_scope_levels[sLevel] = std::set(); + internal_variable_scope_levels[sLevel] = obj_hashtable(); } internal_variable_scope_levels[sLevel].insert(var); } @@ -458,7 +491,7 @@ namespace smt { buffer << "!tmp"; buffer << m_fresh_id; m_fresh_id++; - return u.mk_skolem(symbol(buffer.c_str()), 0, 0, s); + return u.mk_skolem(symbol(buffer.c_str()), 0, nullptr, s); } @@ -617,7 +650,7 @@ namespace smt { ast_manager & m = get_manager(); expr * args[2] = {n, bound}; - app * unrollFunc = get_manager().mk_app(get_id(), _OP_RE_UNROLL, 0, 0, 2, args); + app * unrollFunc = get_manager().mk_app(get_id(), _OP_RE_UNROLL, 0, nullptr, 2, args); m_trail.push_back(unrollFunc); expr_ref_vector items(m); @@ -640,8 +673,8 @@ namespace smt { return contains; } + // note, this invokes "special-case" handling for the start index being 0 app * theory_str::mk_indexof(expr * haystack, expr * needle) { - // TODO check meaning of the third argument here app * indexof = u.str.mk_index(haystack, needle, mk_int(0)); m_trail.push_back(indexof); // immediately force internalization so that axiom setup does not fail @@ -659,7 +692,7 @@ namespace smt { } else { if (false) { // use cache - app * lenTerm = NULL; + app * lenTerm = nullptr; if (!length_ast_map.find(e, lenTerm)) { lenTerm = u.str.mk_length(e); length_ast_map.insert(e, lenTerm); @@ -685,6 +718,12 @@ namespace smt { bool n2HasEqcValue = false; expr * v1 = get_eqc_value(n1, n1HasEqcValue); expr * v2 = get_eqc_value(n2, n2HasEqcValue); + if (u.str.is_string(v1)) { + n1HasEqcValue = true; + } + if (u.str.is_string(v2)) { + n2HasEqcValue = true; + } if (n1HasEqcValue && n2HasEqcValue) { zstring n1_str; u.str.is_string(v1, n1_str); @@ -705,14 +744,14 @@ namespace smt { return n1; } } - return NULL; + return nullptr; } expr * theory_str::mk_concat(expr * n1, expr * n2) { context & ctx = get_context(); ast_manager & m = get_manager(); - ENSURE(n1 != NULL); - ENSURE(n2 != NULL); + ENSURE(n1 != nullptr); + ENSURE(n2 != nullptr); bool n1HasEqcValue = false; bool n2HasEqcValue = false; n1 = get_eqc_value(n1, n1HasEqcValue); @@ -770,7 +809,7 @@ namespace smt { // Z3 treats (ast1) and (ast2) as two different nodes. //------------------------------------------------------- - expr * concatAst = NULL; + expr * concatAst = nullptr; if (!concat_astNode_map.find(n1, n2, concatAst)) { concatAst = u.str.mk_concat(n1, n2); @@ -782,8 +821,8 @@ namespace smt { ptr_vector childrenVector; get_nodes_in_concat(concatAst, childrenVector); expr_ref_vector items(m); - for (unsigned int i = 0; i < childrenVector.size(); i++) { - items.push_back(mk_strlen(childrenVector.get(i))); + for (auto el : childrenVector) { + items.push_back(mk_strlen(el)); } expr_ref lenAssert(ctx.mk_eq_atom(concat_length, m_autil.mk_add(items.size(), items.c_ptr())), m); assert_axiom(lenAssert); @@ -795,7 +834,9 @@ namespace smt { return !m_basicstr_axiom_todo.empty() || !m_str_eq_todo.empty() || !m_concat_axiom_todo.empty() || !m_concat_eval_todo.empty() || !m_library_aware_axiom_todo.empty() - || !m_delayed_axiom_setup_terms.empty(); + || !m_delayed_axiom_setup_terms.empty() + || !m_persisted_axiom_todo.empty() + || (search_started && !m_delayed_assertions_todo.empty()) ; } @@ -803,73 +844,106 @@ namespace smt { context & ctx = get_context(); while (can_propagate()) { TRACE("str", tout << "propagating..." << std::endl;); - for (unsigned i = 0; i < m_basicstr_axiom_todo.size(); ++i) { - instantiate_basic_string_axioms(m_basicstr_axiom_todo[i]); + while(true) { + // this can potentially recursively activate itself + unsigned start_count = m_basicstr_axiom_todo.size(); + ptr_vector axioms_tmp(m_basicstr_axiom_todo); + for (auto const& el : axioms_tmp) { + instantiate_basic_string_axioms(el); + } + unsigned end_count = m_basicstr_axiom_todo.size(); + if (end_count > start_count) { + TRACE("str", tout << "new basic string axiom terms added -- checking again" << std::endl;); + continue; + } else { + break; + } } m_basicstr_axiom_todo.reset(); TRACE("str", tout << "reset m_basicstr_axiom_todo" << std::endl;); - for (unsigned i = 0; i < m_str_eq_todo.size(); ++i) { - std::pair pair = m_str_eq_todo[i]; + for (auto const& pair : m_str_eq_todo) { enode * lhs = pair.first; enode * rhs = pair.second; handle_equality(lhs->get_owner(), rhs->get_owner()); } m_str_eq_todo.reset(); - for (unsigned i = 0; i < m_concat_axiom_todo.size(); ++i) { - instantiate_concat_axiom(m_concat_axiom_todo[i]); + for (auto const& el : m_concat_axiom_todo) { + instantiate_concat_axiom(el); } m_concat_axiom_todo.reset(); - for (unsigned i = 0; i < m_concat_eval_todo.size(); ++i) { - try_eval_concat(m_concat_eval_todo[i]); + for (auto const& el : m_concat_eval_todo) { + try_eval_concat(el); } m_concat_eval_todo.reset(); - for (unsigned i = 0; i < m_library_aware_axiom_todo.size(); ++i) { - enode * e = m_library_aware_axiom_todo[i]; - app * a = e->get_owner(); - if (u.str.is_stoi(a)) { - instantiate_axiom_str_to_int(e); - } else if (u.str.is_itos(a)) { - instantiate_axiom_int_to_str(e); - } else if (u.str.is_at(a)) { - instantiate_axiom_CharAt(e); - } else if (u.str.is_prefix(a)) { - instantiate_axiom_prefixof(e); - } else if (u.str.is_suffix(a)) { - instantiate_axiom_suffixof(e); - } else if (u.str.is_contains(a)) { - instantiate_axiom_Contains(e); - } else if (u.str.is_index(a)) { - instantiate_axiom_Indexof(e); - /* TODO NEXT: Indexof2/Lastindexof rewrite? - } else if (is_Indexof2(e)) { - instantiate_axiom_Indexof2(e); - } else if (is_LastIndexof(e)) { - instantiate_axiom_LastIndexof(e); - */ - } else if (u.str.is_extract(a)) { - // TODO check semantics of substr vs. extract - instantiate_axiom_Substr(e); - } else if (u.str.is_replace(a)) { - instantiate_axiom_Replace(e); - } else if (u.str.is_in_re(a)) { - instantiate_axiom_RegexIn(e); + while(true) { + // Special handling: terms can recursively set up other terms + // (e.g. indexof can instantiate other indexof terms). + // - Copy the list so it can potentially be modified during setup. + // - Don't clear this list if new ones are added in the process; + // instead, set up all the new terms before proceeding. + // TODO see if any other propagate() worklists need this kind of handling + // TODO we really only need to check the new ones on each pass + unsigned start_count = m_library_aware_axiom_todo.size(); + ptr_vector axioms_tmp(m_library_aware_axiom_todo); + for (auto const& e : axioms_tmp) { + app * a = e->get_owner(); + if (u.str.is_stoi(a)) { + instantiate_axiom_str_to_int(e); + } else if (u.str.is_itos(a)) { + instantiate_axiom_int_to_str(e); + } else if (u.str.is_at(a)) { + instantiate_axiom_CharAt(e); + } else if (u.str.is_prefix(a)) { + instantiate_axiom_prefixof(e); + } else if (u.str.is_suffix(a)) { + instantiate_axiom_suffixof(e); + } else if (u.str.is_contains(a)) { + instantiate_axiom_Contains(e); + } else if (u.str.is_index(a)) { + instantiate_axiom_Indexof(e); + } else if (u.str.is_extract(a)) { + instantiate_axiom_Substr(e); + } else if (u.str.is_replace(a)) { + instantiate_axiom_Replace(e); + } else if (u.str.is_in_re(a)) { + instantiate_axiom_RegexIn(e); + } else { + TRACE("str", tout << "BUG: unhandled library-aware term " << mk_pp(e->get_owner(), get_manager()) << std::endl;); + NOT_IMPLEMENTED_YET(); + } + } + unsigned end_count = m_library_aware_axiom_todo.size(); + if (end_count > start_count) { + TRACE("str", tout << "new library-aware terms added during axiom setup -- checking again" << std::endl;); + continue; } else { - TRACE("str", tout << "BUG: unhandled library-aware term " << mk_pp(e->get_owner(), get_manager()) << std::endl;); - NOT_IMPLEMENTED_YET(); + break; } } m_library_aware_axiom_todo.reset(); - for (unsigned i = 0; i < m_delayed_axiom_setup_terms.size(); ++i) { + for (auto el : m_delayed_axiom_setup_terms) { // I think this is okay - ctx.internalize(m_delayed_axiom_setup_terms[i].get(), false); - set_up_axioms(m_delayed_axiom_setup_terms[i].get()); + ctx.internalize(el, false); + set_up_axioms(el); } m_delayed_axiom_setup_terms.reset(); + + for (expr * a : m_persisted_axiom_todo) { + assert_axiom(a); + } + m_persisted_axiom_todo.reset(); + + if (search_started) { + for (auto const& el : m_delayed_assertions_todo) { + assert_axiom(el); + } + m_delayed_assertions_todo.reset(); + } } } @@ -979,6 +1053,15 @@ namespace smt { TRACE("str", tout << "set up basic string axioms on " << mk_pp(str->get_owner(), m) << std::endl;); + { + sort * a_sort = m.get_sort(str->get_owner()); + sort * str_sort = u.str.mk_string_sort(); + if (a_sort != str_sort) { + TRACE("str", tout << "WARNING: not setting up string axioms on non-string term " << mk_pp(str->get_owner(), m) << std::endl;); + return; + } + } + // TESTING: attempt to avoid a crash here when a variable goes out of scope if (str->get_iscope_lvl() > ctx.get_scope_level()) { TRACE("str", tout << "WARNING: skipping axiom setup on out-of-scope string term" << std::endl;); @@ -987,6 +1070,7 @@ namespace smt { // generate a stronger axiom for constant strings app * a_str = str->get_owner(); + if (u.str.is_string(a_str)) { expr_ref len_str(m); len_str = mk_strlen(a_str); @@ -1214,6 +1298,12 @@ namespace smt { contains_map.push_back(ex); std::pair key = std::pair(str, substr); contain_pair_bool_map.insert(str, substr, ex); + if (!contain_pair_idx_map.contains(str)) { + contain_pair_idx_map.insert(str, std::set>()); + } + if (!contain_pair_idx_map.contains(substr)) { + contain_pair_idx_map.insert(substr, std::set>()); + } contain_pair_idx_map[str].insert(key); contain_pair_idx_map[substr].insert(key); } @@ -1232,27 +1322,37 @@ namespace smt { context & ctx = get_context(); ast_manager & m = get_manager(); - app * expr = e->get_owner(); - if (axiomatized_terms.contains(expr)) { - TRACE("str", tout << "already set up Indexof axiom for " << mk_pp(expr, m) << std::endl;); + app * ex = e->get_owner(); + if (axiomatized_terms.contains(ex)) { + TRACE("str", tout << "already set up str.indexof axiom for " << mk_pp(ex, m) << std::endl;); return; } - axiomatized_terms.insert(expr); + SASSERT(ex->get_num_args() == 3); + // if the third argument is exactly the integer 0, we can use this "simple" indexof; + // otherwise, we call the "extended" version + expr * startingPosition = ex->get_arg(2); + rational startingInteger; + if (!m_autil.is_numeral(startingPosition, startingInteger) || !startingInteger.is_zero()) { + // "extended" indexof term with prefix + instantiate_axiom_Indexof_extended(e); + return; + } + axiomatized_terms.insert(ex); - TRACE("str", tout << "instantiate Indexof axiom for " << mk_pp(expr, m) << std::endl;); + TRACE("str", tout << "instantiate str.indexof axiom for " << mk_pp(ex, m) << std::endl;); expr_ref x1(mk_str_var("x1"), m); expr_ref x2(mk_str_var("x2"), m); expr_ref indexAst(mk_int_var("index"), m); - expr_ref condAst(mk_contains(expr->get_arg(0), expr->get_arg(1)), m); + expr_ref condAst(mk_contains(ex->get_arg(0), ex->get_arg(1)), m); SASSERT(condAst); // ----------------------- // true branch expr_ref_vector thenItems(m); // args[0] = x1 . args[1] . x2 - thenItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(x1, mk_concat(expr->get_arg(1), x2)))); + thenItems.push_back(ctx.mk_eq_atom(ex->get_arg(0), mk_concat(x1, mk_concat(ex->get_arg(1), x2)))); // indexAst = |x1| thenItems.push_back(ctx.mk_eq_atom(indexAst, mk_strlen(x1))); // args[0] = x3 . x4 @@ -1260,11 +1360,11 @@ namespace smt { // /\ ! contains(x3, args[1]) expr_ref x3(mk_str_var("x3"), m); expr_ref x4(mk_str_var("x4"), m); - expr_ref tmpLen(m_autil.mk_add(indexAst, mk_strlen(expr->get_arg(1)), mk_int(-1)), m); + expr_ref tmpLen(m_autil.mk_add(indexAst, mk_strlen(ex->get_arg(1)), mk_int(-1)), m); SASSERT(tmpLen); - thenItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(x3, x4))); + thenItems.push_back(ctx.mk_eq_atom(ex->get_arg(0), mk_concat(x3, x4))); thenItems.push_back(ctx.mk_eq_atom(mk_strlen(x3), tmpLen)); - thenItems.push_back(mk_not(m, mk_contains(x3, expr->get_arg(1)))); + thenItems.push_back(mk_not(m, mk_contains(x3, ex->get_arg(1)))); expr_ref thenBranch(m.mk_and(thenItems.size(), thenItems.c_ptr()), m); SASSERT(thenBranch); @@ -1276,80 +1376,121 @@ namespace smt { expr_ref breakdownAssert(m.mk_ite(condAst, thenBranch, elseBranch), m); SASSERT(breakdownAssert); - expr_ref reduceToIndex(ctx.mk_eq_atom(expr, indexAst), m); + expr_ref reduceToIndex(ctx.mk_eq_atom(ex, indexAst), m); SASSERT(reduceToIndex); expr_ref finalAxiom(m.mk_and(breakdownAssert, reduceToIndex), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); + + { + // heuristic: integrate with str.contains information + // (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 + expr_ref premise(u.str.mk_contains(haystack, needle), m); + ctx.internalize(premise, false); + expr_ref conclusion(m_autil.mk_ge(ex, zeroAst), m); + expr_ref containsAxiom(ctx.mk_eq_atom(premise, conclusion), m); + SASSERT(containsAxiom); + + // we can't assert this during init_search as it breaks an invariant if the instance becomes inconsistent + //m_delayed_axiom_setup_terms.push_back(containsAxiom); + } } - void theory_str::instantiate_axiom_Indexof2(enode * e) { + void theory_str::instantiate_axiom_Indexof_extended(enode * _e) { context & ctx = get_context(); ast_manager & m = get_manager(); - app * expr = e->get_owner(); - if (axiomatized_terms.contains(expr)) { - TRACE("str", tout << "already set up Indexof2 axiom for " << mk_pp(expr, m) << std::endl;); + app * e = _e->get_owner(); + if (axiomatized_terms.contains(e)) { + TRACE("str", tout << "already set up extended str.indexof axiom for " << mk_pp(e, m) << std::endl;); return; } - axiomatized_terms.insert(expr); + SASSERT(e->get_num_args() == 3); + axiomatized_terms.insert(e); - TRACE("str", tout << "instantiate Indexof2 axiom for " << mk_pp(expr, m) << std::endl;); + TRACE("str", tout << "instantiate extended str.indexof axiom for " << mk_pp(e, m) << std::endl;); - // ------------------------------------------------------------------------------- - // if (arg[2] >= length(arg[0])) // ite2 - // resAst = -1 - // else - // args[0] = prefix . suffix - // /\ indexAst = indexof(suffix, arg[1]) - // /\ args[2] = len(prefix) - // /\ if (indexAst == -1) resAst = indexAst // ite3 - // else resAst = args[2] + indexAst - // ------------------------------------------------------------------------------- + // str.indexof(H, N, i): + // i < 0 --> -1 + // i == 0 --> str.indexof(H, N, 0) + // i >= len(H) --> -1 + // 0 < i < len(H) --> + // H = hd ++ tl + // len(hd) = i + // str.indexof(tl, N, 0) - expr_ref resAst(mk_int_var("res"), m); - expr_ref indexAst(mk_int_var("index"), m); - expr_ref prefix(mk_str_var("prefix"), m); - expr_ref suffix(mk_str_var("suffix"), m); - expr_ref prefixLen(mk_strlen(prefix), m); - expr_ref zeroAst(mk_int(0), m); - expr_ref negOneAst(mk_int(-1), m); + expr * H = nullptr; // "haystack" + expr * N = nullptr; // "needle" + expr * i = nullptr; // start index + u.str.is_index(e, H, N, i); - expr_ref ite3(m.mk_ite( - ctx.mk_eq_atom(indexAst, negOneAst), - ctx.mk_eq_atom(resAst, negOneAst), - ctx.mk_eq_atom(resAst, m_autil.mk_add(expr->get_arg(2), indexAst)) - ),m); + expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m); + expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m); - expr_ref_vector ite2ElseItems(m); - ite2ElseItems.push_back(ctx.mk_eq_atom(expr->get_arg(0), mk_concat(prefix, suffix))); - ite2ElseItems.push_back(ctx.mk_eq_atom(indexAst, mk_indexof(suffix, expr->get_arg(1)))); - ite2ElseItems.push_back(ctx.mk_eq_atom(expr->get_arg(2), prefixLen)); - ite2ElseItems.push_back(ite3); - expr_ref ite2Else(m.mk_and(ite2ElseItems.size(), ite2ElseItems.c_ptr()), m); - SASSERT(ite2Else); + // case split - expr_ref ite2(m.mk_ite( - //m_autil.mk_ge(expr->get_arg(2), mk_strlen(expr->get_arg(0))), - m_autil.mk_ge(m_autil.mk_add(expr->get_arg(2), m_autil.mk_mul(mk_int(-1), mk_strlen(expr->get_arg(0)))), zeroAst), - ctx.mk_eq_atom(resAst, negOneAst), - ite2Else - ), m); - SASSERT(ite2); + // case 1: i < 0 + { + expr_ref premise(m_autil.mk_le(i, minus_one), m); + expr_ref conclusion(ctx.mk_eq_atom(e, minus_one), m); + assert_implication(premise, conclusion); + } - expr_ref ite1(m.mk_ite( - //m_autil.mk_lt(expr->get_arg(2), zeroAst), - mk_not(m, m_autil.mk_ge(expr->get_arg(2), zeroAst)), - ctx.mk_eq_atom(resAst, mk_indexof(expr->get_arg(0), expr->get_arg(1))), - ite2 - ), m); - SASSERT(ite1); - assert_axiom(ite1); + // case 2: i = 0 + { + expr_ref premise(ctx.mk_eq_atom(i, zero), m); + // reduction to simpler case + expr_ref conclusion(ctx.mk_eq_atom(e, mk_indexof(H, N)), m); + assert_implication(premise, conclusion); + } + // case 3: i >= len(H) + { + //expr_ref _premise(m_autil.mk_ge(i, mk_strlen(H)), m); + //expr_ref premise(_premise); + //th_rewriter rw(m); + //rw(premise); + expr_ref premise(m_autil.mk_ge(m_autil.mk_add(i, m_autil.mk_mul(minus_one, mk_strlen(H))), zero), m); + expr_ref conclusion(ctx.mk_eq_atom(e, minus_one), m); + assert_implication(premise, conclusion); + } + // case 4: 0 < i < len(H) + { + expr_ref premise1(m_autil.mk_gt(i, zero), m); + //expr_ref premise2(m_autil.mk_lt(i, mk_strlen(H)), m); + expr_ref premise2(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 _premise(m.mk_and(premise1, premise2), m); + expr_ref premise(_premise); + th_rewriter rw(m); + rw(premise); - expr_ref reduceTerm(ctx.mk_eq_atom(expr, resAst), m); - SASSERT(reduceTerm); - assert_axiom(reduceTerm); + expr_ref hd(mk_str_var("hd"), m); + expr_ref tl(mk_str_var("tl"), m); + + expr_ref_vector conclusion_terms(m); + conclusion_terms.push_back(ctx.mk_eq_atom(H, mk_concat(hd, tl))); + conclusion_terms.push_back(ctx.mk_eq_atom(mk_strlen(hd), i)); + conclusion_terms.push_back(ctx.mk_eq_atom(e, mk_indexof(tl, N))); + + expr_ref conclusion(mk_and(conclusion_terms), m); + assert_implication(premise, conclusion); + } + + { + // 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 + 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); + // 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); + } } void theory_str::instantiate_axiom_LastIndexof(enode * e) { @@ -1424,9 +1565,9 @@ namespace smt { void theory_str::instantiate_axiom_Substr(enode * e) { context & ctx = get_context(); ast_manager & m = get_manager(); - expr* substrBase = 0; - expr* substrPos = 0; - expr* substrLen = 0; + expr* substrBase = nullptr; + expr* substrPos = nullptr; + expr* substrLen = nullptr; app * expr = e->get_owner(); if (axiomatized_terms.contains(expr)) { @@ -1598,12 +1739,11 @@ namespace smt { { expr_ref premise(m_autil.mk_ge(ex, m_autil.mk_numeral(rational::one(), true)), m); - expr_ref hd(mk_str_var("hd"), m); - expr_ref tl(mk_str_var("tl"), m); - expr_ref conclusion1(ctx.mk_eq_atom(S, mk_concat(hd, tl)), m); - expr_ref conclusion2(ctx.mk_eq_atom(mk_strlen(hd), m_autil.mk_numeral(rational::one(), true)), m); - expr_ref conclusion3(mk_not(m, ctx.mk_eq_atom(hd, mk_string("0"))), m); - expr_ref conclusion(m.mk_and(conclusion1, conclusion2, conclusion3), m); + // S >= 1 --> S in [1-9][0-9]* + expr_ref re_positiveInteger(u.re.mk_concat( + u.re.mk_range(mk_string("1"), mk_string("9")), + u.re.mk_star(u.re.mk_range(mk_string("0"), mk_string("9")))), m); + expr_ref conclusion(mk_RegexIn(S, re_positiveInteger), m); SASSERT(premise); SASSERT(conclusion); assert_implication(premise, conclusion); @@ -1689,8 +1829,10 @@ 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_full(a_regex)) { + } else if (u.re.is_full_seq(a_regex)) { return zstring("(.*)"); + } else if (u.re.is_full_char(a_regex)) { + return zstring("str.allchar"); } else { TRACE("str", tout << "BUG: unrecognized regex term " << mk_pp(regex, get_manager()) << std::endl;); UNREACHABLE(); return zstring(""); @@ -1715,12 +1857,25 @@ namespace smt { std::pair key1(ex->get_arg(0), regexStr); // skip Z3str's map check, because we already check if we set up axioms on this term regex_in_bool_map[key1] = ex; + if (!regex_in_var_reg_str_map.contains(ex->get_arg(0))) { + regex_in_var_reg_str_map.insert(ex->get_arg(0), std::set()); + } regex_in_var_reg_str_map[ex->get_arg(0)].insert(regexStr); } expr_ref str(ex->get_arg(0), m); app * regex = to_app(ex->get_arg(1)); + if (m_params.m_RegexAutomata) { + regex_terms.insert(ex); + if (!regex_terms_by_string.contains(str)) { + regex_terms_by_string.insert(str, ptr_vector()); + } + regex_terms_by_string[str].push_back(ex); + // stop setting up axioms here, we do this differently + return; + } + // quick reference for the following code: // - ex: top-level regex membership term // - str: string term appearing in ex @@ -1806,9 +1961,15 @@ namespace smt { expr_ref finalAxiom(m.mk_iff(ex, rhs), m); SASSERT(finalAxiom); assert_axiom(finalAxiom); - } else if (u.re.is_full(regex)) { + } else if (u.re.is_full_seq(regex)) { // trivially true for any string! assert_axiom(ex); + } else if (u.re.is_full_char(regex)) { + // any char = any string of length 1 + expr_ref rhs(ctx.mk_eq_atom(mk_strlen(str), mk_int(1)), m); + expr_ref finalAxiom(m.mk_iff(ex, rhs), m); + SASSERT(finalAxiom); + assert_axiom(finalAxiom); } else { TRACE("str", tout << "ERROR: unknown regex expression " << mk_pp(regex, m) << "!" << std::endl;); NOT_IMPLEMENTED_YET(); @@ -1887,7 +2048,7 @@ namespace smt { check_contain_in_new_eq(lhs, rhs); } - if (!regex_in_bool_map.empty()) { + if (!regex_in_bool_map.empty() && !m_params.m_RegexAutomata) { TRACE("str", tout << "checking regex consistency" << std::endl;); check_regex_in(lhs, rhs); } @@ -1996,7 +2157,7 @@ namespace smt { } } // give up - return NULL; + return nullptr; } // trace code helper @@ -2086,7 +2247,7 @@ namespace smt { // (Concat n_eqNode arg1) /\ arg1 has eq const expr * concatResult = eval_concat(eq_str, arg1); - if (concatResult != NULL) { + if (concatResult != nullptr) { bool arg1HasEqcValue = false; expr * arg1Value = get_eqc_value(arg1, arg1HasEqcValue); expr_ref implyL(m); @@ -2157,7 +2318,7 @@ namespace smt { // (Concat arg0 n_eqNode) /\ arg0 has eq const expr * concatResult = eval_concat(arg0, eq_str); - if (concatResult != NULL) { + if (concatResult != nullptr) { bool arg0HasEqcValue = false; expr * arg0Value = get_eqc_value(arg0, arg0HasEqcValue); expr_ref implyL(m); @@ -2340,9 +2501,8 @@ namespace smt { } else { expr_ref_vector items(m); int pos = 0; - std::map::iterator itor = resolvedMap.begin(); - for (; itor != resolvedMap.end(); ++itor) { - items.push_back(ctx.mk_eq_atom(itor->first, itor->second)); + for (auto itor : resolvedMap) { + items.push_back(ctx.mk_eq_atom(itor.first, itor.second)); pos += 1; } expr_ref premise(mk_and(items), m); @@ -2518,8 +2678,7 @@ namespace smt { context & ctx = get_context(); // pull each literal out of the arrangement disjunction literal_vector ls; - for (unsigned i = 0; i < terms.size(); ++i) { - expr * e = terms.get(i); + for (expr * e : terms) { literal l = ctx.get_literal(e); ls.push_back(l); } @@ -2532,9 +2691,8 @@ namespace smt { if (cut_var_map.contains(node)) { if (!cut_var_map[node].empty()) { xout << "[" << cut_var_map[node].top()->level << "] "; - std::map::iterator itor = cut_var_map[node].top()->vars.begin(); - for (; itor != cut_var_map[node].top()->vars.end(); ++itor) { - xout << mk_pp(itor->first, m) << ", "; + for (auto const& kv : cut_var_map[node].top()->vars) { + xout << mk_pp(kv.m_key, m) << ", "; } xout << std::endl; } @@ -2841,8 +2999,8 @@ namespace smt { //************************************************************* if (is_concat_eq_type2(new_nn1, new_nn2)) { - expr * y = NULL; - expr * m = NULL; + expr * y = nullptr; + expr * m = nullptr; expr * v1_arg0 = to_app(new_nn1)->get_arg(0); expr * v1_arg1 = to_app(new_nn1)->get_arg(1); expr * v2_arg0 = to_app(new_nn2)->get_arg(0); @@ -2873,8 +3031,8 @@ namespace smt { expr * v2_arg0 = to_app(new_nn2)->get_arg(0); expr * v2_arg1 = to_app(new_nn2)->get_arg(1); - expr * x = NULL; - expr * n = NULL; + expr * x = nullptr; + expr * n = nullptr; if (u.str.is_string(v1_arg0) && !u.str.is_string(v2_arg0)) { n = v1_arg1; @@ -2915,8 +3073,8 @@ namespace smt { expr * v2_arg0 = to_app(new_nn2)->get_arg(0); expr * v2_arg1 = to_app(new_nn2)->get_arg(1); - expr * y = NULL; - expr * m = NULL; + expr * y = nullptr; + expr * m = nullptr; if (u.str.is_string(v1_arg0)) { y = v1_arg1; @@ -3015,9 +3173,9 @@ namespace smt { << "split type " << splitType << std::endl; ); - expr * t1 = NULL; - expr * t2 = NULL; - expr * xorFlag = NULL; + expr * t1 = nullptr; + expr * t2 = nullptr; + expr * xorFlag = nullptr; std::pair key1(concatAst1, concatAst2); std::pair key2(concatAst2, concatAst1); @@ -3385,10 +3543,10 @@ namespace smt { return; } - expr * x = NULL; - expr * y = NULL; - expr * strAst = NULL; - expr * m = NULL; + expr * x = nullptr; + expr * y = nullptr; + expr * strAst = nullptr; + expr * m = nullptr; expr * v1_arg0 = to_app(concatAst1)->get_arg(0); expr * v1_arg1 = to_app(concatAst1)->get_arg(1); @@ -3419,8 +3577,8 @@ namespace smt { // setup - expr * xorFlag = NULL; - expr * temp1 = NULL; + expr * xorFlag = nullptr; + expr * temp1 = nullptr; std::pair key1(concatAst1, concatAst2); std::pair key2(concatAst2, concatAst1); @@ -3753,10 +3911,10 @@ namespace smt { expr * v2_arg0 = to_app(concatAst2)->get_arg(0); expr * v2_arg1 = to_app(concatAst2)->get_arg(1); - expr * x = NULL; - expr * y = NULL; - expr * strAst = NULL; - expr * n = NULL; + expr * x = nullptr; + expr * y = nullptr; + expr * strAst = nullptr; + expr * n = nullptr; if (u.str.is_string(v1_arg0) && !u.str.is_string(v2_arg0)) { strAst = v1_arg0; @@ -4313,10 +4471,10 @@ namespace smt { expr * v2_arg1 = to_app(concatAst2)->get_arg(1); - expr * str1Ast = NULL; - expr * y = NULL; - expr * m = NULL; - expr * str2Ast = NULL; + expr * str1Ast = nullptr; + expr * y = nullptr; + expr * m = nullptr; + expr * str2Ast = nullptr; if (u.str.is_string(v1_arg0)) { str1Ast = v1_arg0; @@ -4357,8 +4515,8 @@ namespace smt { } //---------------------------------------------------------------- - expr * commonVar = NULL; - expr * xorFlag = NULL; + expr * commonVar = nullptr; + expr * xorFlag = nullptr; std::pair key1(concatAst1, concatAst2); std::pair key2(concatAst2, concatAst1); @@ -4458,8 +4616,7 @@ namespace smt { } } - for (std::list::iterator itor = overlapLen.begin(); itor != overlapLen.end(); itor++) { - unsigned int overLen = *itor; + for (unsigned int overLen : overlapLen) { zstring prefix = str1Value.extract(0, str1Len - overLen); zstring suffix = str2Value.extract(overLen, str2Len - overLen); @@ -4540,10 +4697,10 @@ namespace smt { TRACE("str", tout << "concat = " << mk_pp(concat, mgr) << ", unroll = " << mk_pp(unroll, mgr) << std::endl;); - std::pair key = std::make_pair(concat, unroll); expr_ref toAssert(mgr); + expr * _toAssert; - if (concat_eq_unroll_ast_map.find(key) == concat_eq_unroll_ast_map.end()) { + if (!concat_eq_unroll_ast_map.find(concat, unroll, _toAssert)) { expr_ref arg1(to_app(concat)->get_arg(0), mgr); expr_ref arg2(to_app(concat)->get_arg(1), mgr); expr_ref r1(to_app(unroll)->get_arg(0), mgr); @@ -4594,9 +4751,9 @@ namespace smt { toAssert = mgr.mk_and(opAnd1, opAnd2); m_trail.push_back(toAssert); - concat_eq_unroll_ast_map[key] = toAssert; + concat_eq_unroll_ast_map.insert(concat, unroll, toAssert); } else { - toAssert = concat_eq_unroll_ast_map[key]; + toAssert = _toAssert; } assert_axiom(toAssert); @@ -4669,7 +4826,7 @@ namespace smt { return dynamic_cast(th); } else { - return 0; + return nullptr; } } @@ -4841,7 +4998,7 @@ namespace smt { } expr * theory_str::collect_eq_nodes(expr * n, expr_ref_vector & eqcSet) { - expr * constStrNode = NULL; + expr * constStrNode = nullptr; expr * ex = n; do { @@ -4880,13 +5037,12 @@ namespace smt { expr_ref_vector litems(m); - if (contain_pair_idx_map.find(varNode) != contain_pair_idx_map.end()) { - std::set >::iterator itor1 = contain_pair_idx_map[varNode].begin(); - for (; itor1 != contain_pair_idx_map[varNode].end(); ++itor1) { - expr * strAst = itor1->first; - expr * substrAst = itor1->second; + if (contain_pair_idx_map.contains(varNode)) { + for (auto entry : contain_pair_idx_map[varNode]) { + expr * strAst = entry.first; + expr * substrAst = entry.second; - expr * boolVar = NULL; + expr * boolVar = nullptr; if (!contain_pair_bool_map.find(strAst, substrAst, boolVar)) { TRACE("str", tout << "warning: no entry for boolVar in contain_pair_bool_map" << std::endl;); } @@ -4942,23 +5098,18 @@ namespace smt { // collect eqc concat std::set eqcConcats; get_concats_in_eqc(substrAst, eqcConcats); - for (std::set::iterator concatItor = eqcConcats.begin(); - concatItor != eqcConcats.end(); concatItor++) { + for (expr * aConcat : eqcConcats) { expr_ref_vector constList(m); bool counterEgFound = false; - // get constant strings in concat - expr * aConcat = *concatItor; get_const_str_asts_in_node(aConcat, constList); - for (expr_ref_vector::iterator cstItor = constList.begin(); - cstItor != constList.end(); cstItor++) { + for (auto const& cst : constList) { zstring pieceStr; - u.str.is_string(*cstItor, pieceStr); + u.str.is_string(cst, pieceStr); if (!strConst.contains(pieceStr)) { counterEgFound = true; if (aConcat != substrAst) { litems.push_back(ctx.mk_eq_atom(substrAst, aConcat)); } - //implyR = Z3_mk_eq(ctx, boolVar, Z3_mk_false(ctx)); implyR = mk_not(m, boolVar); break; } @@ -5017,13 +5168,12 @@ namespace smt { ast_manager & m = get_manager(); expr_ref_vector litems(m); - if (contain_pair_idx_map.find(varNode) != contain_pair_idx_map.end()) { - std::set >::iterator itor1 = contain_pair_idx_map[varNode].begin(); - for (; itor1 != contain_pair_idx_map[varNode].end(); ++itor1) { - expr * strAst = itor1->first; - expr * substrAst = itor1->second; + if (contain_pair_idx_map.contains(varNode)) { + for (auto entry : contain_pair_idx_map[varNode]) { + expr * strAst = entry.first; + expr * substrAst = entry.second; - expr * boolVar = NULL; + expr * boolVar = nullptr; if (!contain_pair_bool_map.find(strAst, substrAst, boolVar)) { TRACE("str", tout << "warning: no entry for boolVar in contain_pair_bool_map" << std::endl;); } @@ -5050,17 +5200,16 @@ namespace smt { zstring strConst; u.str.is_string(strValue, strConst); // iterate eqc (also eqc-to-be) of substr - for (expr_ref_vector::iterator itAst = willEqClass.begin(); itAst != willEqClass.end(); itAst++) { + for (auto itAst : willEqClass) { bool counterEgFound = false; - if (u.str.is_concat(to_app(*itAst))) { + if (u.str.is_concat(to_app(itAst))) { expr_ref_vector constList(m); // get constant strings in concat - app * aConcat = to_app(*itAst); + app * aConcat = to_app(itAst); get_const_str_asts_in_node(aConcat, constList); - for (expr_ref_vector::iterator cstItor = constList.begin(); - cstItor != constList.end(); cstItor++) { + for (auto cst : constList) { zstring pieceStr; - u.str.is_string(*cstItor, pieceStr); + u.str.is_string(cst, pieceStr); if (!strConst.contains(pieceStr)) { TRACE("str", tout << "Inconsistency found!" << std::endl;); counterEgFound = true; @@ -5085,7 +5234,7 @@ namespace smt { } bool theory_str::in_contain_idx_map(expr * n) { - return contain_pair_idx_map.find(n) != contain_pair_idx_map.end(); + return contain_pair_idx_map.contains(n); } void theory_str::check_contain_by_eq_nodes(expr * n1, expr * n2) { @@ -5093,12 +5242,9 @@ namespace smt { ast_manager & m = get_manager(); if (in_contain_idx_map(n1) && in_contain_idx_map(n2)) { - std::set >::iterator keysItor1 = contain_pair_idx_map[n1].begin(); - std::set >::iterator keysItor2; - - for (; keysItor1 != contain_pair_idx_map[n1].end(); keysItor1++) { + for (auto const& key1 : contain_pair_idx_map[n1]) { // keysItor1 is on set {<.., n1>, ..., , ...} - std::pair key1 = *keysItor1; + //std::pair key1 = *keysItor1; if (key1.first == n1 && key1.second == n2) { expr_ref implyL(m); expr_ref implyR(contain_pair_bool_map[key1], m); @@ -5110,10 +5256,10 @@ namespace smt { } } - for (keysItor2 = contain_pair_idx_map[n2].begin(); - keysItor2 != contain_pair_idx_map[n2].end(); keysItor2++) { + //for (keysItor2 = contain_pair_idx_map[n2].begin(); keysItor2 != contain_pair_idx_map[n2].end(); keysItor2++) { + for (auto const& key2 : contain_pair_idx_map[n2]) { // keysItor2 is on set {<.., n2>, ..., , ...} - std::pair key2 = *keysItor2; + //std::pair key2 = *keysItor2; // skip if the pair is eq if (key1 == key2) { continue; @@ -5207,10 +5353,12 @@ namespace smt { // * key1.first = key2.first // check eqc(key1.second) and eqc(key2.second) // ----------------------------------------------------------- - expr_ref_vector::iterator eqItorSub1 = subAst1Eqc.begin(); - for (; eqItorSub1 != subAst1Eqc.end(); eqItorSub1++) { - expr_ref_vector::iterator eqItorSub2 = subAst2Eqc.begin(); - for (; eqItorSub2 != subAst2Eqc.end(); eqItorSub2++) { + //expr_ref_vector::iterator eqItorSub1 = subAst1Eqc.begin(); + //for (; eqItorSub1 != subAst1Eqc.end(); eqItorSub1++) { + for (auto eqSubVar1 : subAst1Eqc) { + //expr_ref_vector::iterator eqItorSub2 = subAst2Eqc.begin(); + //for (; eqItorSub2 != subAst2Eqc.end(); eqItorSub2++) { + for (auto eqSubVar2 : subAst2Eqc) { // ------------ // key1.first = key2.first /\ containPairBoolMap[] // ==> (containPairBoolMap[key1] --> containPairBoolMap[key2]) @@ -5220,11 +5368,11 @@ namespace smt { if (n1 != n2) { litems3.push_back(ctx.mk_eq_atom(n1, n2)); } - expr * eqSubVar1 = *eqItorSub1; + if (eqSubVar1 != subAst1) { litems3.push_back(ctx.mk_eq_atom(subAst1, eqSubVar1)); } - expr * eqSubVar2 = *eqItorSub2; + if (eqSubVar2 != subAst2) { litems3.push_back(ctx.mk_eq_atom(subAst2, eqSubVar2)); } @@ -5245,11 +5393,11 @@ namespace smt { if (n1 != n2) { litems4.push_back(ctx.mk_eq_atom(n1, n2)); } - expr * eqSubVar1 = *eqItorSub1; + if (eqSubVar1 != subAst1) { litems4.push_back(ctx.mk_eq_atom(subAst1, eqSubVar1)); } - expr * eqSubVar2 = *eqItorSub2; + if (eqSubVar2 != subAst2) { litems4.push_back(ctx.mk_eq_atom(subAst2, eqSubVar2)); } @@ -5357,20 +5505,18 @@ namespace smt { // * key1.second = key2.second // check eqc(key1.first) and eqc(key2.first) // ----------------------------------------------------------- - expr_ref_vector::iterator eqItorStr1 = str1Eqc.begin(); - for (; eqItorStr1 != str1Eqc.end(); eqItorStr1++) { - expr_ref_vector::iterator eqItorStr2 = str2Eqc.begin(); - for (; eqItorStr2 != str2Eqc.end(); eqItorStr2++) { + for (auto const& eqStrVar1 : str1Eqc) { + for (auto const& eqStrVar2 : str2Eqc) { { expr_ref_vector litems3(m); if (n1 != n2) { litems3.push_back(ctx.mk_eq_atom(n1, n2)); } - expr * eqStrVar1 = *eqItorStr1; + if (eqStrVar1 != str1) { litems3.push_back(ctx.mk_eq_atom(str1, eqStrVar1)); } - expr * eqStrVar2 = *eqItorStr2; + if (eqStrVar2 != str2) { litems3.push_back(ctx.mk_eq_atom(str2, eqStrVar2)); } @@ -5393,11 +5539,9 @@ namespace smt { if (n1 != n2) { litems4.push_back(ctx.mk_eq_atom(n1, n2)); } - expr * eqStrVar1 = *eqItorStr1; if (eqStrVar1 != str1) { litems4.push_back(ctx.mk_eq_atom(str1, eqStrVar1)); } - expr *eqStrVar2 = *eqItorStr2; if (eqStrVar2 != str2) { litems4.push_back(ctx.mk_eq_atom(str2, eqStrVar2)); } @@ -5440,11 +5584,10 @@ namespace smt { expr_ref_vector willEqClass(m); expr * constStrAst_1 = collect_eq_nodes(n1, willEqClass); expr * constStrAst_2 = collect_eq_nodes(n2, willEqClass); - expr * constStrAst = (constStrAst_1 != NULL) ? constStrAst_1 : constStrAst_2; + expr * constStrAst = (constStrAst_1 != nullptr) ? constStrAst_1 : constStrAst_2; TRACE("str", tout << "eqc of n1 is {"; - for (expr_ref_vector::iterator it = willEqClass.begin(); it != willEqClass.end(); ++it) { - expr * el = *it; + for (expr * el : willEqClass) { tout << " " << mk_pp(el, m); } tout << std::endl; @@ -5456,13 +5599,12 @@ namespace smt { ); // step 1: we may have constant values for Contains checks now - if (constStrAst != NULL) { - expr_ref_vector::iterator itAst = willEqClass.begin(); - for (; itAst != willEqClass.end(); itAst++) { - if (*itAst == constStrAst) { + if (constStrAst != nullptr) { + for (auto a : willEqClass) { + if (a == constStrAst) { continue; } - check_contain_by_eqc_val(*itAst, constStrAst); + check_contain_by_eqc_val(a, constStrAst); } } else { // no concrete value to be put in eqc, solely based on context @@ -5474,9 +5616,8 @@ namespace smt { // * "EQC(M) U EQC(concat(..., "jio", ...))" as substr and // * If strAst registered has an eqc constant in the context // ------------------------------------------------------------- - expr_ref_vector::iterator itAst = willEqClass.begin(); - for (; itAst != willEqClass.end(); ++itAst) { - check_contain_by_substr(*itAst, willEqClass); + for (auto a : willEqClass) { + check_contain_by_substr(a, willEqClass); } } @@ -5493,12 +5634,8 @@ namespace smt { // (9) containPairBoolMap[] /\ m = n ==> (b1 -> b2) // ------------------------------------------ - expr_ref_vector::iterator varItor1 = willEqClass.begin(); - for (; varItor1 != willEqClass.end(); ++varItor1) { - expr * varAst1 = *varItor1; - expr_ref_vector::iterator varItor2 = varItor1; - for (; varItor2 != willEqClass.end(); ++varItor2) { - expr * varAst2 = *varItor2; + for (auto varAst1 : willEqClass) { + for (auto varAst2 : willEqClass) { check_contain_by_eq_nodes(varAst1, varAst2); } } @@ -5513,7 +5650,8 @@ namespace smt { return node; } - void theory_str::get_grounded_concats(expr* node, std::map & varAliasMap, + void theory_str::get_grounded_concats(unsigned depth, + expr* node, std::map & varAliasMap, std::map & concatAliasMap, std::map & varConstMap, std::map & concatConstMap, std::map > & varEqConcatMap, std::map, std::set > > & groundedMap) { @@ -5528,6 +5666,9 @@ namespace smt { if (groundedMap.find(node) != groundedMap.end()) { return; } + IF_VERBOSE(100, verbose_stream() << "concats " << depth << "\n"; + if (depth > 100) verbose_stream() << mk_pp(node, get_manager()) << "\n"; + ); // haven't computed grounded concats for "node" (de-aliased) // --------------------------------------------------------- @@ -5557,8 +5698,8 @@ namespace smt { expr * arg1 = to_app(node)->get_arg(1); expr * arg0DeAlias = dealias_node(arg0, varAliasMap, concatAliasMap); expr * arg1DeAlias = dealias_node(arg1, varAliasMap, concatAliasMap); - get_grounded_concats(arg0DeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); - get_grounded_concats(arg1DeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); + get_grounded_concats(depth + 1, arg0DeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); + get_grounded_concats(depth + 1, arg1DeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); std::map, std::set >::iterator arg0_grdItor = groundedMap[arg0DeAlias].begin(); std::map, std::set >::iterator arg1_grdItor; @@ -5608,7 +5749,7 @@ namespace smt { else if (varEqConcatMap.find(node) != varEqConcatMap.end()) { expr * eqConcat = varEqConcatMap[node].begin()->first; expr * deAliasedEqConcat = dealias_node(eqConcat, varAliasMap, concatAliasMap); - get_grounded_concats(deAliasedEqConcat, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); + get_grounded_concats(depth + 1, deAliasedEqConcat, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); std::map, std::set >::iterator grdItor = groundedMap[deAliasedEqConcat].begin(); for (; grdItor != groundedMap[deAliasedEqConcat].end(); grdItor++) { @@ -5808,17 +5949,16 @@ namespace smt { std::map & concatAliasMap, std::map & varConstMap, std::map & concatConstMap, std::map > & varEqConcatMap) { std::map, std::set > > groundedMap; - theory_str_contain_pair_bool_map_t::iterator containItor = contain_pair_bool_map.begin(); - for (; containItor != contain_pair_bool_map.end(); containItor++) { - expr* containBoolVar = containItor->get_value(); - expr* str = containItor->get_key1(); - expr* subStr = containItor->get_key2(); + for (auto const& kv : contain_pair_bool_map) { + expr* containBoolVar = kv.get_value(); + expr* str = kv.get_key1(); + expr* subStr = kv.get_key2(); expr* strDeAlias = dealias_node(str, varAliasMap, concatAliasMap); expr* subStrDeAlias = dealias_node(subStr, varAliasMap, concatAliasMap); - get_grounded_concats(strDeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); - get_grounded_concats(subStrDeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); + get_grounded_concats(0, strDeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); + get_grounded_concats(0, subStrDeAlias, varAliasMap, concatAliasMap, varConstMap, concatConstMap, varEqConcatMap, groundedMap); // debugging print_grounded_concat(strDeAlias, groundedMap); @@ -6309,7 +6449,7 @@ namespace smt { } TRACE("str", tout << "range NFA: start = " << start << ", end = " << end << std::endl;); - } else if (u.re.is_full(e)) { + } else if (u.re.is_full_seq(e)) { // effectively the same as .* where . can be any single character // start --e--> tmp // tmp --e--> end @@ -6322,6 +6462,13 @@ namespace smt { make_transition(tmp, ch, tmp); } TRACE("str", tout << "re.all NFA: start = " << start << ", end = " << end << std::endl;); + } else if (u.re.is_full_char(e)) { + // effectively . (match any one character) + for (unsigned int i = 0; i < 256; ++i) { + char ch = (char)i; + make_transition(start, ch, end); + } + TRACE("str", tout << "re.allchar NFA: start = " << start << ", end = " << end << std::endl;); } else { TRACE("str", tout << "invalid regular expression" << std::endl;); m_valid = false; @@ -6400,14 +6547,14 @@ namespace smt { expr * constStr_1 = collect_eq_nodes(nn1, eqNodeSet); expr * constStr_2 = collect_eq_nodes(nn2, eqNodeSet); - expr * constStr = (constStr_1 != NULL) ? constStr_1 : constStr_2; + expr * constStr = (constStr_1 != nullptr) ? constStr_1 : constStr_2; - if (constStr == NULL) { + if (constStr == nullptr) { return; } else { expr_ref_vector::iterator itor = eqNodeSet.begin(); for (; itor != eqNodeSet.end(); itor++) { - if (regex_in_var_reg_str_map.find(*itor) != regex_in_var_reg_str_map.end()) { + if (regex_in_var_reg_str_map.contains(*itor)) { std::set::iterator strItor = regex_in_var_reg_str_map[*itor].begin(); for (; strItor != regex_in_var_reg_str_map[*itor].end(); strItor++) { zstring regStr = *strItor; @@ -6420,9 +6567,9 @@ namespace smt { expr * regexTerm = a_regexIn->get_arg(1); // TODO figure out regex NFA stuff - if (regex_nfa_cache.find(regexTerm) == regex_nfa_cache.end()) { + if (!regex_nfa_cache.contains(regexTerm)) { TRACE("str", tout << "regex_nfa_cache: cache miss" << std::endl;); - regex_nfa_cache[regexTerm] = nfa(u, regexTerm); + regex_nfa_cache.insert(regexTerm, nfa(u, regexTerm)); } else { TRACE("str", tout << "regex_nfa_cache: cache hit" << std::endl;); } @@ -6446,6 +6593,841 @@ namespace smt { } } + void theory_str::regex_inc_counter(obj_map & counter_map, expr * key) { + unsigned old_v; + if (counter_map.find(key, old_v)) { + unsigned new_v = old_v += 1; + counter_map.insert(key, new_v); + } else { + counter_map.insert(key, 1); + } + } + + unsigned theory_str::regex_get_counter(obj_map & counter_map, expr * key) { + unsigned v; + if (counter_map.find(key, v)) { + return v; + } else { + counter_map.insert(key, 0); + return 0; + } + } + + // saturating unsigned addition + unsigned inline _qadd(unsigned a, unsigned b) { + if (a == UINT_MAX || b == UINT_MAX) { + return UINT_MAX; + } + unsigned result = a + b; + if (result < a || result < b) { + return UINT_MAX; + } + return result; + } + + // saturating unsigned multiply + unsigned inline _qmul(unsigned a, unsigned b) { + if (a == UINT_MAX || b == UINT_MAX) { + return UINT_MAX; + } + if (a == 0 || b == 0) { + return 0; + } + unsigned result = a * b; + if (result < a || result < b) { + return UINT_MAX; + } + return result; + } + + unsigned theory_str::estimate_regex_complexity(expr * re) { + ENSURE(u.is_re(re)); + expr * sub1; + expr * sub2; + if (u.re.is_to_re(re, sub1)) { + if (!u.str.is_string(sub1)) + throw default_exception("regular expressions must be built from string literals"); + zstring str; + u.str.is_string(sub1, str); + return str.length(); + } else if (u.re.is_complement(re, sub1)) { + return estimate_regex_complexity_under_complement(sub1); + } else if (u.re.is_concat(re, sub1, sub2)) { + unsigned cx1 = estimate_regex_complexity(sub1); + unsigned cx2 = estimate_regex_complexity(sub2); + return _qadd(cx1, cx2); + } else if (u.re.is_union(re, sub1, sub2)) { + unsigned cx1 = estimate_regex_complexity(sub1); + unsigned cx2 = estimate_regex_complexity(sub2); + return _qadd(cx1, cx2); + } 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_range(re, sub1, sub2)) { + SASSERT(u.str.is_string(sub1)); + SASSERT(u.str.is_string(sub2)); + zstring str1, str2; + u.str.is_string(sub1, str1); + u.str.is_string(sub2, str2); + SASSERT(str1.length() == 1); + SASSERT(str2.length() == 1); + return 1 + str2[0] - str1[0]; + } else if (u.re.is_full_char(re) || u.re.is_full_seq(re)) { + return 1; + } else { + TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); + return 1; + } + } + + unsigned theory_str::estimate_regex_complexity_under_complement(expr * re) { + ENSURE(u.is_re(re)); + expr * sub1; + expr * sub2; + if (u.re.is_to_re(re, sub1)) { + SASSERT(u.str.is_string(sub1)); + zstring str; + u.str.is_string(sub1, str); + return str.length(); + } else if (u.re.is_complement(re, sub1)) { + // Why don't we return the regular complexity here? + // We could, but this might be called from under another complemented subexpression. + // It's better to give a worst-case complexity. + return estimate_regex_complexity_under_complement(sub1); + } else if (u.re.is_concat(re, sub1, sub2)) { + unsigned cx1 = estimate_regex_complexity_under_complement(sub1); + unsigned cx2 = estimate_regex_complexity_under_complement(sub2); + return _qadd(_qmul(2, cx1), cx2); + } else if (u.re.is_union(re, sub1, sub2)) { + 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)) { + unsigned cx = estimate_regex_complexity_under_complement(sub1); + return _qmul(2, cx); + } else if (u.re.is_range(re, sub1, sub2)) { + SASSERT(u.str.is_string(sub1)); + SASSERT(u.str.is_string(sub2)); + zstring str1, str2; + u.str.is_string(sub1, str1); + u.str.is_string(sub2, str2); + SASSERT(str1.length() == 1); + SASSERT(str2.length() == 1); + return 1 + str2[0] - str1[0]; + } else if (u.re.is_full_char(re) || u.re.is_full_seq(re)) { + return 1; + } else { + TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); + return 1; + } + } + + unsigned theory_str::estimate_automata_intersection_difficulty(eautomaton * aut1, eautomaton * aut2) { + ENSURE(aut1 != NULL); + ENSURE(aut2 != NULL); + return _qmul(aut1->num_states(), aut2->num_states()); + } + + // Check whether a regex translates well to a linear set of length constraints. + bool theory_str::check_regex_length_linearity(expr * re) { + return check_regex_length_linearity_helper(re, false); + } + + bool theory_str::check_regex_length_linearity_helper(expr * re, bool already_star) { + expr * sub1; + expr * sub2; + if (u.re.is_to_re(re)) { + return true; + } else if (u.re.is_concat(re, sub1, sub2)) { + return check_regex_length_linearity_helper(sub1, already_star) && check_regex_length_linearity_helper(sub2, already_star); + } else if (u.re.is_union(re, sub1, sub2)) { + return check_regex_length_linearity_helper(sub1, already_star) && check_regex_length_linearity_helper(sub2, already_star); + } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1)) { + if (already_star) { + return false; + } else { + return check_regex_length_linearity_helper(sub1, true); + } + } else if (u.re.is_range(re)) { + return true; + } else if (u.re.is_full_char(re)) { + return true; + } else if (u.re.is_full_seq(re)) { + return true; + } else if (u.re.is_complement(re)) { + // TODO can we do better? + return false; + } else { + TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); + UNREACHABLE(); return false; + } + } + + // note: returns an empty set `lens` if something went wrong + void theory_str::check_subterm_lengths(expr * re, integer_set & lens) { + expr * sub1; + expr * sub2; + if (u.re.is_to_re(re, sub1)) { + SASSERT(u.str.is_string(sub1)); + zstring str; + u.str.is_string(sub1, str); + lens.insert(str.length()); + } else if (u.re.is_concat(re, sub1, sub2)) { + integer_set lens_1, lens_2; + check_subterm_lengths(sub1, lens_1); + check_subterm_lengths(sub2, lens_2); + if (lens_1.empty() || lens_2.empty()) { + lens.reset(); + } else { + // take all pairwise lengths + for (integer_set::iterator it1 = lens_1.begin(); it1 != lens_1.end(); ++it1) { + for(integer_set::iterator it2 = lens_2.begin(); it2 != lens_2.end(); ++it2) { + int l1 = *it1; + int l2 = *it2; + lens.insert(l1 + l2); + } + } + } + } else if (u.re.is_union(re, sub1, sub2)) { + integer_set lens_1, lens_2; + check_subterm_lengths(sub1, lens_1); + check_subterm_lengths(sub2, lens_2); + if (lens_1.empty() || lens_2.empty()) { + lens.reset(); + } else { + // take all possibilities from either side + for (integer_set::iterator it1 = lens_1.begin(); it1 != lens_1.end(); ++it1) { + lens.insert(*it1); + } + for (integer_set::iterator it2 = lens_2.begin(); it2 != lens_2.end(); ++it2) { + lens.insert(*it2); + } + } + } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1)) { + // this is bad -- term generation requires this not to appear + lens.reset(); + } else if (u.re.is_range(re, sub1, sub2)) { + SASSERT(u.str.is_string(sub1)); + SASSERT(u.str.is_string(sub2)); + zstring str1, str2; + u.str.is_string(sub1, str1); + u.str.is_string(sub2, str2); + SASSERT(str1.length() == 1); + SASSERT(str2.length() == 1); + lens.insert(1); + } else if (u.re.is_full_char(re)) { + lens.insert(1); + } else if (u.re.is_full_seq(re)) { + lens.reset(); + } else if (u.re.is_complement(re)) { + lens.reset(); + } else { + TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); + lens.reset(); + } + } + + /* + * Infer all length constraints implied by the given regular expression `re` + * in order to constrain `lenVar` (which must be of sort Int). + * This assumes that `re` appears in a positive context. + * Returns a Boolean formula expressing the appropriate constraints over `lenVar`. + * In some cases, the returned formula requires one or more free integer variables to be created. + * These variables are returned in the reference parameter `freeVariables`. + * Extra assertions should be made for these free variables constraining them to be non-negative. + */ + expr_ref theory_str::infer_all_regex_lengths(expr * lenVar, expr * re, expr_ref_vector & freeVariables) { + ENSURE(u.is_re(re)); + context & ctx = get_context(); + ast_manager & m = get_manager(); + expr * sub1; + expr * sub2; + if (u.re.is_to_re(re, sub1)) { + if (!u.str.is_string(sub1)) + throw default_exception("regular expressions must be built from string literals"); + zstring str; + u.str.is_string(sub1, str); + rational strlen(str.length()); + expr_ref retval(ctx.mk_eq_atom(lenVar, m_autil.mk_numeral(strlen, true)), m); + return retval; + } else if (u.re.is_union(re, sub1, sub2)) { + expr_ref r1 = infer_all_regex_lengths(lenVar, sub1, freeVariables); + expr_ref r2 = infer_all_regex_lengths(lenVar, sub2, freeVariables); + expr_ref retval(m.mk_or(r1, r2), m); + return retval; + } else if (u.re.is_concat(re, sub1, sub2)) { + expr * v1 = mk_int_var("rlen1"); + expr * v2 = mk_int_var("rlen2"); + freeVariables.push_back(v1); + freeVariables.push_back(v2); + expr_ref r1 = infer_all_regex_lengths(v1, sub1, freeVariables); + expr_ref r2 = infer_all_regex_lengths(v2, sub2, freeVariables); + expr_ref_vector finalResult(m); + finalResult.push_back(ctx.mk_eq_atom(lenVar, m_autil.mk_add(v1, v2))); + finalResult.push_back(r1); + finalResult.push_back(r2); + expr_ref retval(mk_and(finalResult), m); + return retval; + } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1)) { + // stars are generated as a linear combination of all possible subterm lengths; + // this requires that there are no stars under this one + /* + expr * v = mk_int_var("rlen"); + expr * n = mk_int_var("rstar"); + freeVariables.push_back(v); + freeVariables.push_back(n); + expr_ref rsub = infer_all_regex_lengths(v, sub1, freeVariables); + expr_ref_vector finalResult(m); + finalResult.push_back(rsub); + finalResult.push_back(ctx.mk_eq_atom(lenVar, m_autil.mk_mul(v, n))); + expr_ref retval(mk_and(finalResult), m); + return retval; + */ + integer_set subterm_lens; + check_subterm_lengths(sub1, subterm_lens); + if (subterm_lens.empty()) { + // somehow generation was impossible + expr_ref retval(m_autil.mk_ge(lenVar, m_autil.mk_numeral(rational::zero(), true)), m); + return retval; + } else { + TRACE("str", tout << "subterm lengths:"; + for(integer_set::iterator it = subterm_lens.begin(); it != subterm_lens.end(); ++it) { + tout << " " << *it; + } + tout << std::endl;); + expr_ref_vector sum_terms(m); + for (integer_set::iterator it = subterm_lens.begin(); it != subterm_lens.end(); ++it) { + rational lenOption(*it); + expr * n = mk_int_var("rstar"); + freeVariables.push_back(n); + expr_ref term(m_autil.mk_mul(m_autil.mk_numeral(lenOption, true), n), m); + expr_ref term2(term, m); + if (u.re.is_plus(re)) { + // n effectively starts at 1 + term2 = m_autil.mk_add(m_autil.mk_numeral(lenOption, true), term); + } + sum_terms.push_back(term2); + } + expr_ref retval(ctx.mk_eq_atom(lenVar, m_autil.mk_add_simplify(sum_terms)), m); + return retval; + } + } else if (u.re.is_range(re, sub1, sub2)) { + SASSERT(u.str.is_string(sub1)); + SASSERT(u.str.is_string(sub2)); + zstring str1, str2; + u.str.is_string(sub1, str1); + u.str.is_string(sub2, str2); + SASSERT(str1.length() == 1); + SASSERT(str2.length() == 1); + expr_ref retval(ctx.mk_eq_atom(lenVar, m_autil.mk_numeral(rational::one(), true)), m); + return retval; + } else if (u.re.is_full_char(re)) { + expr_ref retval(ctx.mk_eq_atom(lenVar, m_autil.mk_numeral(rational::one(), true)), m); + return retval; + } else if (u.re.is_full_seq(re)) { + // match any unbounded string + expr_ref retval(m_autil.mk_ge(lenVar, m_autil.mk_numeral(rational::zero(), true)), m); + return retval; + } else if (u.re.is_complement(re)) { + // skip complement for now, in general this is difficult to predict + expr_ref retval(m_autil.mk_ge(lenVar, m_autil.mk_numeral(rational::zero(), true)), m); + return retval; + } else { + TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, m) << std::endl;); + expr_ref retval(m_autil.mk_ge(lenVar, m_autil.mk_numeral(rational::zero(), true)), m); + return retval; + } + } + + /* + * Assert initial lower and upper bounds for the positive constraint (str in re) corresponding + * to the automaton `aut`. + * This asserts a constraint of the form: + * str_in_re --> (len(str) ?= 0 OR len(str) >= lb) AND len(str) <= ub + * where the upper bound clause is omitted if the upper bound doesn't exist + * 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); + context & ctx = get_context(); + ast_manager & m = get_manager(); + + expr_ref_vector rhs(m); + expr * str = nullptr; + expr * re = nullptr; + u.str.is_in_re(str_in_re, str, re); + expr_ref strlen(mk_strlen(str), m); + + // lower bound first + rational nonzero_lower_bound; + bool zero_sol_exists = refine_automaton_lower_bound(aut, rational::zero(), nonzero_lower_bound); + if (zero_sol_exists) { + regex_last_lower_bound.insert(str, rational::zero()); + // solution at 0 + if (!nonzero_lower_bound.is_minus_one()) { + expr_ref rhs1(ctx.mk_eq_atom(strlen, m_autil.mk_numeral(rational::zero(), true)), m); + expr_ref rhs2(m_autil.mk_ge(strlen, m_autil.mk_numeral(nonzero_lower_bound, true)), m); + rhs.push_back(m.mk_or(rhs1, rhs2)); + } else { + // shouldn't happen + UNREACHABLE(); + } + } else { + // no solution at 0 + if (!nonzero_lower_bound.is_minus_one()) { + regex_last_lower_bound.insert(str, nonzero_lower_bound); + expr_ref rhs2(m_autil.mk_ge(strlen, m_autil.mk_numeral(nonzero_lower_bound, true)), m); + rhs.push_back(rhs2); + } else { + // shouldn't happen + UNREACHABLE(); + } + } + // TODO upper bound check + + if (!rhs.empty()) { + expr_ref lhs(str_in_re, m); + expr_ref _rhs(mk_and(rhs), m); + assert_implication(lhs, _rhs); + } + } + + /* + * Refine the lower bound on the length of a solution to a given automaton. + * The method returns TRUE if a solution of length `current_lower_bound` exists, + * and FALSE otherwise. In addition, the reference parameter `refined_lower_bound` + * is assigned the length of the shortest solution longer than `current_lower_bound` + * 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); + + if (aut->final_states().size() < 1) { + // no solutions at all + refined_lower_bound = rational::minus_one(); + return false; + } + + // from here we assume that there is a final state reachable from the initial state + + unsigned_vector search_queue; + // populate search_queue with all states reachable from the epsilon-closure of start state + aut->get_epsilon_closure(aut->init(), search_queue); + + unsigned search_depth = 0; + hashtable> next_states; + unsigned_vector next_search_queue; + + bool found_solution_at_lower_bound = false; + + while (!search_queue.empty()) { + // if we are at the lower bound, check for final states + if (search_depth == current_lower_bound.get_unsigned()) { + for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { + unsigned state = *it; + if (aut->is_final_state(state)) { + found_solution_at_lower_bound = true; + break; + } + } + // end phase 1 + break; + } + next_states.reset(); + next_search_queue.clear(); + // move one step along all states + for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { + unsigned src = *it; + eautomaton::moves next_moves; + aut->get_moves_from(src, next_moves, true); + for (eautomaton::moves::iterator move_it = next_moves.begin(); + move_it != next_moves.end(); ++move_it) { + unsigned dst = move_it->dst(); + if (!next_states.contains(dst)) { + next_states.insert(dst); + next_search_queue.push_back(dst); + } + } + } + search_queue.clear(); + search_queue.append(next_search_queue); + search_depth += 1; + } // !search_queue.empty() + + // if we got here before reaching the lower bound, + // there aren't any solutions at or above it, so stop + if (search_depth < current_lower_bound.get_unsigned()) { + refined_lower_bound = rational::minus_one(); + return false; + } + + // phase 2: continue exploring the automaton above the lower bound + SASSERT(search_depth == current_lower_bound.get_unsigned()); + + while (!search_queue.empty()) { + if (search_depth > current_lower_bound.get_unsigned()) { + // check if we have found a solution above the lower bound + for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { + unsigned state = *it; + if (aut->is_final_state(state)) { + // this is a solution at a depth higher than the lower bound + refined_lower_bound = rational(search_depth); + return found_solution_at_lower_bound; + } + } + } + next_states.reset(); + next_search_queue.clear(); + // move one step along all states + for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { + unsigned src = *it; + eautomaton::moves next_moves; + aut->get_moves_from(src, next_moves, true); + for (eautomaton::moves::iterator move_it = next_moves.begin(); + move_it != next_moves.end(); ++move_it) { + unsigned dst = move_it->dst(); + if (!next_states.contains(dst)) { + next_states.insert(dst); + next_search_queue.push_back(dst); + } + } + } + search_queue.clear(); + search_queue.append(next_search_queue); + search_depth += 1; + } + // if we reached this point, we explored the whole automaton and didn't find any + // solutions above the lower bound + refined_lower_bound = rational::minus_one(); + return found_solution_at_lower_bound; + } + + /* + * Refine the upper bound on the length of a solution to a given automaton. + * The method returns TRUE if a solution of length `current_upper_bound` exists, + * and FALSE otherwise. In addition, the reference parameter `refined_upper_bound` + * is assigned the length of the longest solution shorter than `current_upper_bound`, + * 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); + + if (aut->final_states().empty()) { + // no solutions at all! + refined_upper_bound = rational::minus_one(); + return false; + } + + // from here we assume there is a final state reachable from the initial state + unsigned_vector search_queue; + // populate search queue with all states reachable from the epsilon-closure of the start state + aut->get_epsilon_closure(aut->init(), search_queue); + + rational last_solution_depth = rational::minus_one(); + bool found_solution_at_upper_bound = false; + + unsigned search_depth = 0; + hashtable > next_states; + unsigned_vector next_search_queue; + + while(!search_queue.empty()) { + // see if any of the current states are final + for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { + unsigned src = *it; + if (aut->is_final_state(src)) { + if (search_depth == current_upper_bound.get_unsigned()) { + found_solution_at_upper_bound = true; + } else { + last_solution_depth = rational(search_depth); + } + break; + } + } + + if (search_depth == current_upper_bound.get_unsigned()) { + break; + } + + next_states.reset(); + next_search_queue.clear(); + // move one step along all states + for (unsigned_vector::iterator it = search_queue.begin(); it != search_queue.end(); ++it) { + unsigned src = *it; + eautomaton::moves next_moves; + aut->get_moves_from(src, next_moves, true); + for (eautomaton::moves::iterator moves_it = next_moves.begin(); + moves_it != next_moves.end(); ++moves_it) { + unsigned dst = moves_it->dst(); + if (!next_states.contains(dst)) { + next_states.insert(dst); + next_search_queue.push_back(dst); + } + } + } + search_queue.clear(); + search_queue.append(next_search_queue); + search_depth += 1; + } //!search_queue.empty() + + refined_upper_bound = last_solution_depth; + return found_solution_at_upper_bound; + } + + void theory_str::aut_path_add_next(u_map& next, expr_ref_vector& trail, unsigned idx, expr* cond) { + expr* acc; + if (!get_manager().is_true(cond) && next.find(idx, acc)) { + expr* args[2] = { cond, acc }; + cond = mk_or(get_manager(), 2, args); + } + trail.push_back(cond); + next.insert(idx, cond); + } + + expr_ref theory_str::aut_path_rewrite_constraint(expr * cond, expr * ch_var) { + context & ctx = get_context(); + ast_manager & m = get_manager(); + bv_util bvu(m); + + expr_ref retval(m); + + rational char_val; + unsigned int bv_width; + + expr * lhs; + expr * rhs; + + if (bvu.is_numeral(cond, char_val, bv_width)) { + SASSERT(char_val.is_nonneg() && char_val.get_unsigned() < 256); + TRACE("str", tout << "rewrite character constant " << char_val << std::endl;); + zstring str_const(char_val.get_unsigned()); + retval = u.str.mk_string(str_const); + return retval; + } else if (is_var(cond)) { + TRACE("str", tout << "substitute var" << std::endl;); + retval = ch_var; + return retval; + } else if (m.is_eq(cond, lhs, rhs)) { + // handle this specially because the sort of the equality will change + expr_ref new_lhs(aut_path_rewrite_constraint(lhs, ch_var), m); + SASSERT(new_lhs); + expr_ref new_rhs(aut_path_rewrite_constraint(rhs, ch_var), m); + SASSERT(new_rhs); + retval = ctx.mk_eq_atom(new_lhs, new_rhs); + return retval; + } else if (m.is_bool(cond)) { + TRACE("str", tout << "rewrite boolean term " << mk_pp(cond, m) << std::endl;); + app * a_cond = to_app(cond); + expr_ref_vector rewritten_args(m); + for (unsigned i = 0; i < a_cond->get_num_args(); ++i) { + expr * argI = a_cond->get_arg(i); + expr_ref new_arg(aut_path_rewrite_constraint(argI, ch_var), m); + SASSERT(new_arg); + rewritten_args.push_back(new_arg); + } + retval = m.mk_app(a_cond->get_decl(), rewritten_args.c_ptr()); + TRACE("str", tout << "final rewritten term is " << mk_pp(retval, m) << std::endl;); + return retval; + } else { + TRACE("str", tout << "ERROR: unrecognized automaton path constraint " << mk_pp(cond, m) << ", cannot translate" << std::endl;); + retval = NULL; + return retval; + } + } + + /* + * Create finite path constraints for the string variable `str` with respect to the automaton `aut`. + * The returned expression is the right-hand side of a constraint of the form + * (str in re) AND (|str| = len) AND (any applicable length assumptions on aut) -> (rhs AND character constraints). + * The character constraints, which are (str = c0 . c1 . (...) . cn) and (|c0| = 1, ...), + * are returned in `characterConstraints`. + */ + expr_ref theory_str::generate_regex_path_constraints(expr * stringTerm, eautomaton * aut, rational lenVal, expr_ref & characterConstraints) { + ENSURE(aut != NULL); + context & ctx = get_context(); + ast_manager & m = get_manager(); + + if (lenVal.is_zero()) { + // if any state in the epsilon-closure of the start state is accepting, + // then the empty string is in this language + unsigned_vector states; + bool has_final = false; + aut->get_epsilon_closure(aut->init(), states); + for (unsigned i = 0; i < states.size() && !has_final; ++i) { + has_final = aut->is_final_state(states[i]); + } + if (has_final) { + // empty string is OK, assert axiom + expr_ref rhs(ctx.mk_eq_atom(stringTerm, mk_string("")), m); + SASSERT(rhs); + //regex_automata_assertions.insert(stringTerm, final_axiom); + //m_trail_stack.push(insert_obj_map(regex_automata_assertions, stringTerm) ); + return rhs; + } else { + // negate -- the empty string isn't in the language + //expr_ref conflict(m.mk_not(mk_and(toplevel_lhs)), m); + //assert_axiom(conflict); + expr_ref conflict(m.mk_false(), m); + return conflict; + } + } // lenVal.is_zero() + + expr_ref_vector pathChars(m); + expr_ref_vector pathChars_len_constraints(m); + + // reuse character terms over the same string + if (string_chars.contains(stringTerm)) { + // find out whether we have enough characters already + ptr_vector old_chars; + string_chars.find(stringTerm, old_chars); + if (old_chars.size() < lenVal.get_unsigned()) { + for (unsigned i = old_chars.size(); i < lenVal.get_unsigned(); ++i) { + std::stringstream ss; + ss << "ch" << i; + expr_ref ch(mk_str_var(ss.str()), m); + m_trail.push_back(ch); + old_chars.push_back(ch); + } + } + string_chars.insert(stringTerm, old_chars); + // now we're guaranteed to have at least the right number of characters in old_chars + for (unsigned i = 0; i < lenVal.get_unsigned(); ++i) { + expr_ref ch(old_chars.get(i), m); + refresh_theory_var(ch); + pathChars.push_back(ch); + pathChars_len_constraints.push_back(ctx.mk_eq_atom(mk_strlen(ch), m_autil.mk_numeral(rational::one(), true))); + } + } else { + ptr_vector new_chars; + for (unsigned i = 0; i < lenVal.get_unsigned(); ++i) { + std::stringstream ss; + ss << "ch" << i; + expr_ref ch(mk_str_var(ss.str()), m); + pathChars.push_back(ch); + pathChars_len_constraints.push_back(ctx.mk_eq_atom(mk_strlen(ch), m_autil.mk_numeral(rational::one(), true))); + new_chars.push_back(ch); + } + string_chars.insert(stringTerm, new_chars); + } + + // modification of code in seq_rewriter::mk_str_in_regexp() + expr_ref_vector trail(m); + u_map maps[2]; + bool select_map = false; + expr_ref ch(m), cond(m); + eautomaton::moves mvs; + maps[0].insert(aut->init(), m.mk_true()); + // is_accepted(a, aut) & some state in frontier is final. + for (unsigned i = 0; i < lenVal.get_unsigned(); ++i) { + u_map& frontier = maps[select_map]; + u_map& next = maps[!select_map]; + select_map = !select_map; + ch = pathChars.get(i); + next.reset(); + u_map::iterator it = frontier.begin(), end = frontier.end(); + for (; it != end; ++it) { + mvs.reset(); + unsigned state = it->m_key; + expr* acc = it->m_value; + aut->get_moves_from(state, mvs, false); + for (unsigned j = 0; j < mvs.size(); ++j) { + eautomaton::move const& mv = mvs[j]; + SASSERT(mv.t()); + if (mv.t()->is_char() && m.is_value(mv.t()->get_char())) { + // change this to a string constraint + expr_ref cond_rhs = aut_path_rewrite_constraint(mv.t()->get_char(), ch); + SASSERT(cond_rhs); + cond = ctx.mk_eq_atom(ch, cond_rhs); + SASSERT(cond); + expr * args[2] = {cond, acc}; + cond = mk_and(m, 2, args); + aut_path_add_next(next, trail, mv.dst(), cond); + } else if (mv.t()->is_range()) { + expr_ref range_lo(mv.t()->get_lo(), m); + expr_ref range_hi(mv.t()->get_hi(), m); + bv_util bvu(m); + + rational lo_val, hi_val; + unsigned int bv_width; + + if (bvu.is_numeral(range_lo, lo_val, bv_width) && bvu.is_numeral(range_hi, hi_val, bv_width)) { + TRACE("str", tout << "make range predicate from " << lo_val << " to " << hi_val << std::endl;); + expr_ref cond_rhs(m); + + if (hi_val < lo_val) { + rational tmp = lo_val; + lo_val = hi_val; + hi_val = tmp; + } + + expr_ref_vector cond_rhs_terms(m); + for (unsigned i = lo_val.get_unsigned(); i <= hi_val.get_unsigned(); ++i) { + zstring str_const(i); + expr_ref str_expr(u.str.mk_string(str_const), m); + cond_rhs_terms.push_back(ctx.mk_eq_atom(ch, str_expr)); + } + cond_rhs = mk_or(cond_rhs_terms); + SASSERT(cond_rhs); + expr * args[2] = {cond_rhs, acc}; + cond = mk_and(m, 2, args); + aut_path_add_next(next, trail, mv.dst(), cond); + } else { + TRACE("str", tout << "warning: non-bitvectors in automaton range predicate" << std::endl;); + UNREACHABLE(); + } + } else if (mv.t()->is_pred()) { + // rewrite this constraint over string terms + expr_ref cond_rhs = aut_path_rewrite_constraint(mv.t()->get_pred(), ch); + SASSERT(cond_rhs); + + if (m.is_false(cond_rhs)) { + continue; + } else if (m.is_true(cond_rhs)) { + aut_path_add_next(next, trail, mv.dst(), acc); + continue; + } + expr * args[2] = {cond_rhs, acc}; + cond = mk_and(m, 2, args); + aut_path_add_next(next, trail, mv.dst(), cond); + } + } + } + } + u_map const& frontier = maps[select_map]; + u_map::iterator it = frontier.begin(), end = frontier.end(); + expr_ref_vector ors(m); + for (; it != end; ++it) { + unsigned_vector states; + bool has_final = false; + aut->get_epsilon_closure(it->m_key, states); + for (unsigned i = 0; i < states.size() && !has_final; ++i) { + has_final = aut->is_final_state(states[i]); + } + if (has_final) { + ors.push_back(it->m_value); + } + } + expr_ref result(mk_or(ors)); + TRACE("str", tout << "regex path constraint: " << mk_pp(result, m) << "\n";); + + expr_ref concat_rhs(m); + if (pathChars.size() == 1) { + concat_rhs = ctx.mk_eq_atom(stringTerm, pathChars.get(0)); + } else { + expr_ref acc(pathChars.get(0), m); + for (unsigned i = 1; i < pathChars.size(); ++i) { + acc = mk_concat(acc, pathChars.get(i)); + } + concat_rhs = ctx.mk_eq_atom(stringTerm, acc); + } + + //expr_ref toplevel_rhs(m.mk_and(result, mk_and(pathChars_len_constraints), concat_rhs), m); + characterConstraints = m.mk_and(mk_and(pathChars_len_constraints), concat_rhs); + //expr_ref final_axiom(rewrite_implication(mk_and(toplevel_lhs), toplevel_rhs), m); + //regex_automata_assertions.insert(stringTerm, final_axiom); + //m_trail_stack.push(insert_obj_map(regex_automata_assertions, stringTerm) ); + return result; + } + /* * strArgmt::solve_concat_eq_str() * Solve concatenations of the form: @@ -6610,7 +7592,7 @@ namespace smt { } else { // Case 4: Concat(var, var) == const TRACE("str", tout << "Case 4: Concat(var, var) == const" << std::endl;); - if (eval_concat(arg1, arg2) == NULL) { + if (eval_concat(arg1, arg2) == nullptr) { rational arg1Len, arg2Len; bool arg1Len_exists = get_len_value(arg1, arg1Len); bool arg2Len_exists = get_len_value(arg2, arg2Len); @@ -6855,7 +7837,7 @@ namespace smt { } else { // start binary search as normal expr_ref implLhs(ctx.mk_eq_atom(testvar, str), m); - expr_ref implRhs(binary_search_length_test(v, NULL, ""), m); + expr_ref implRhs(binary_search_length_test(v, nullptr, ""), m); assert_implication(implLhs, implRhs); } } else { @@ -6866,12 +7848,12 @@ namespace smt { if (!map_effectively_empty) { map_effectively_empty = true; - ptr_vector indicator_set = fvar_lenTester_map[v]; - for (ptr_vector::iterator it = indicator_set.begin(); it != indicator_set.end(); ++it) { - expr * indicator = *it; - if (internal_variable_set.find(indicator) != internal_variable_set.end()) { - map_effectively_empty = false; - break; + if (fvar_lenTester_map.contains(v)) { + for (expr * indicator : fvar_lenTester_map[v]) { + if (internal_variable_set.contains(indicator)) { + map_effectively_empty = false; + break; + } } } } @@ -6916,16 +7898,19 @@ namespace smt { } // now create a fake length tester over this finite disjunction of lengths - fvar_len_count_map[v] = 1; + fvar_len_count_map.insert(v, 1); unsigned int testNum = fvar_len_count_map[v]; expr_ref indicator(mk_internal_lenTest_var(v, testNum), m); SASSERT(indicator); m_trail.push_back(indicator); + if (!fvar_lenTester_map.contains(v)) { + fvar_lenTester_map.insert(v, ptr_vector()); + } fvar_lenTester_map[v].shrink(0); fvar_lenTester_map[v].push_back(indicator); - lenTester_fvar_map[indicator] = v; + lenTester_fvar_map.insert(indicator, v); expr_ref_vector orList(m); expr_ref_vector andList(m); @@ -6987,14 +7972,19 @@ namespace smt { } expr * valueAssert = gen_free_var_options(fVar, effectiveLenInd, effectiveLenIndiStr, valTester, valTesterValue); TRACE("str", tout << "asserting more value tests for free variable " << mk_ismt2_pp(fVar, m) << std::endl;); - if (valueAssert != NULL) { + if (valueAssert != nullptr) { assert_axiom(valueAssert); } } } else { - int lenTesterCount = fvar_lenTester_map[fVar].size(); + int lenTesterCount; + if (fvar_lenTester_map.contains(fVar)) { + lenTesterCount = fvar_lenTester_map[fVar].size(); + } else { + lenTesterCount = 0; + } - expr * effectiveLenInd = NULL; + expr * effectiveLenInd = nullptr; zstring effectiveLenIndiStr = ""; for (int i = 0; i < lenTesterCount; ++i) { expr * len_indicator_pre = fvar_lenTester_map[fVar][i]; @@ -7012,7 +8002,7 @@ namespace smt { } expr * valueAssert = gen_free_var_options(fVar, effectiveLenInd, effectiveLenIndiStr, valTester, valTesterValue); TRACE("str", tout << "asserting more value tests for free variable " << mk_ismt2_pp(fVar, m) << std::endl;); - if (valueAssert != NULL) { + if (valueAssert != nullptr) { assert_axiom(valueAssert); } } @@ -7108,7 +8098,7 @@ namespace smt { rational nn1Len, nn2Len; bool nn1Len_exists = get_len_value(lhs, nn1Len); bool nn2Len_exists = get_len_value(rhs, nn2Len); - expr * emptyStr = mk_string(""); + expr_ref emptyStr(mk_string(""), m); if (nn1Len_exists && nn1Len.is_zero()) { if (!in_same_eqc(lhs, emptyStr) && rhs != emptyStr) { @@ -7257,20 +8247,20 @@ namespace smt { simplify_parent(lhs, nn2_value); } - expr * nn1EqConst = NULL; + expr * nn1EqConst = nullptr; std::set nn1EqUnrollFuncs; get_eqc_allUnroll(lhs, nn1EqConst, nn1EqUnrollFuncs); - expr * nn2EqConst = NULL; + expr * nn2EqConst = nullptr; std::set nn2EqUnrollFuncs; get_eqc_allUnroll(rhs, nn2EqConst, nn2EqUnrollFuncs); - if (nn2EqConst != NULL) { + if (nn2EqConst != nullptr) { for (std::set::iterator itor1 = nn1EqUnrollFuncs.begin(); itor1 != nn1EqUnrollFuncs.end(); itor1++) { process_unroll_eq_const_str(*itor1, nn2EqConst); } } - if (nn1EqConst != NULL) { + if (nn1EqConst != nullptr) { for (std::set::iterator itor2 = nn2EqUnrollFuncs.begin(); itor2 != nn2EqUnrollFuncs.end(); itor2++) { process_unroll_eq_const_str(*itor2, nn1EqConst); } @@ -7363,8 +8353,7 @@ namespace smt { if (is_app(ex)) { app * ap = to_app(ex); - // TODO indexof2/lastindexof - if (u.str.is_index(ap) /* || is_Indexof2(ap) || is_LastIndexof(ap) */) { + if (u.str.is_index(ap)) { m_library_aware_axiom_todo.push_back(n); } else if (u.str.is_stoi(ap)) { TRACE("str", tout << "found string-integer conversion term: " << mk_pp(ex, get_manager()) << std::endl;); @@ -7399,13 +8388,12 @@ namespace smt { lbool theory_str::validate_unsat_core(expr_ref_vector & unsat_core) { app * target_term = to_app(get_manager().mk_not(m_theoryStrOverlapAssumption_term)); get_context().internalize(target_term, false); + enode* e1 = get_context().get_enode(target_term); for (unsigned i = 0; i < unsat_core.size(); ++i) { app * core_term = to_app(unsat_core.get(i)); // not sure if this is the correct way to compare terms in this context - enode * e1; - enode * e2; - e1 = get_context().get_enode(target_term); - e2 = get_context().get_enode(core_term); + if (!get_context().e_internalized(core_term)) continue; + enode *e2 = get_context().get_enode(core_term); if (e1 == e2) { TRACE("str", tout << "overlap detected in unsat core, changing UNSAT to UNKNOWN" << std::endl;); return l_undef; @@ -7537,6 +8525,10 @@ namespace smt { } } + void theory_str::add_persisted_axiom(expr * a) { + m_persisted_axioms.push_back(a); + } + void theory_str::pop_scope_eh(unsigned num_scopes) { sLevel -= num_scopes; TRACE("str", tout << "pop " << num_scopes << " to " << sLevel << std::endl;); @@ -7582,6 +8574,11 @@ namespace smt { m_basicstr_axiom_todo.reset(); m_basicstr_axiom_todo = new_m_basicstr; + for (expr * e : m_persisted_axioms) { + TRACE("str", tout << "persist axiom: " << mk_pp(e, get_manager()) << std::endl;); + m_persisted_axiom_todo.push_back(e); + } + m_trail_stack.pop_scope(num_scopes); theory::pop_scope_eh(num_scopes); @@ -7602,6 +8599,27 @@ namespace smt { ); } + // returns true if needle appears as a subterm anywhere under haystack, + // or if needle appears in the same EQC as a subterm anywhere under haystack + bool theory_str::term_appears_as_subterm(expr * needle, expr * haystack) { + if (in_same_eqc(needle, haystack)) { + return true; + } + + if (is_app(haystack)) { + app * a_haystack = to_app(haystack); + for (unsigned i = 0; i < a_haystack->get_num_args(); ++i) { + expr * subterm = a_haystack->get_arg(i); + if (term_appears_as_subterm(needle, subterm)) { + return true; + } + } + } + + // not found + return false; + } + void theory_str::classify_ast_by_type(expr * node, std::map & varMap, std::map & concatMap, std::map & unrollMap) { @@ -7897,11 +8915,12 @@ namespace smt { // Step 1: get variables / concat AST appearing in the context // the thing we iterate over should just be variable_set - internal_variable_set // so we avoid computing the set difference (but this might be slower) - for(obj_hashtable::iterator it = variable_set.begin(); it != variable_set.end(); ++it) { - expr* var = *it; + for (expr* var : variable_set) { + //for(obj_hashtable::iterator it = variable_set.begin(); it != variable_set.end(); ++it) { + //expr* var = *it; if (internal_variable_set.find(var) == internal_variable_set.end()) { TRACE("str", tout << "new variable: " << mk_pp(var, m) << std::endl;); - strVarMap[*it] = 1; + strVarMap[var] = 1; } } classify_ast_by_type_in_positive_context(strVarMap, concatMap, unrollMap); @@ -7912,13 +8931,13 @@ namespace smt { if (aliasUnrollSet.find(unrollItor->first) != aliasUnrollSet.end()) { continue; } - expr * aRoot = NULL; + expr * aRoot = nullptr; enode * e_currEqc = ctx.get_enode(unrollItor->first); enode * e_curr = e_currEqc; do { app * curr = e_currEqc->get_owner(); if (u.re.is_unroll(curr)) { - if (aRoot == NULL) { + if (aRoot == nullptr) { aRoot = curr; } aliasUnrollSet[curr] = aRoot; @@ -7943,11 +8962,11 @@ namespace smt { if (aliasIndexMap.find(varItor->first) != aliasIndexMap.end()) { continue; } - expr * aRoot = NULL; + expr * aRoot = nullptr; expr * curr = varItor->first; do { if (variable_set.find(curr) != variable_set.end()) { - if (aRoot == NULL) { + if (aRoot == nullptr) { aRoot = curr; } else { aliasIndexMap[curr] = aRoot; @@ -8035,11 +9054,11 @@ namespace smt { if (concats_eq_index_map.find(concatItor->first) != concats_eq_index_map.end()) { continue; } - expr * aRoot = NULL; + expr * aRoot = nullptr; expr * curr = concatItor->first; do { if (u.str.is_concat(to_app(curr))) { - if (aRoot == NULL) { + if (aRoot == nullptr) { aRoot = curr; } else { concats_eq_index_map[curr] = aRoot; @@ -8051,7 +9070,7 @@ namespace smt { concatItor = concatMap.begin(); for(; concatItor != concatMap.end(); ++concatItor) { - expr * deAliasConcat = NULL; + expr * deAliasConcat = nullptr; if (concats_eq_index_map.find(concatItor->first) != concats_eq_index_map.end()) { deAliasConcat = concats_eq_index_map[concatItor->first]; } else { @@ -8189,15 +9208,15 @@ namespace smt { mostLeftNodes.clear(); mostRightNodes.clear(); - expr * mLConst = NULL; - expr * mRConst = NULL; + expr * mLConst = nullptr; + expr * mRConst = nullptr; for (std::map::iterator itor1 = itor->second.begin(); itor1 != itor->second.end(); itor1++) { expr * concatNode = itor1->first; expr * mLNode = getMostLeftNodeInConcat(concatNode); zstring strval; if (u.str.is_string(to_app(mLNode), strval)) { - if (mLConst == NULL && strval.empty()) { + if (mLConst == nullptr && strval.empty()) { mLConst = mLNode; } } else { @@ -8206,7 +9225,7 @@ namespace smt { expr * mRNode = getMostRightNodeInConcat(concatNode); if (u.str.is_string(to_app(mRNode), strval)) { - if (mRConst == NULL && strval.empty()) { + if (mRConst == nullptr && strval.empty()) { mRConst = mRNode; } } else { @@ -8214,7 +9233,7 @@ namespace smt { } } - if (mLConst != NULL) { + if (mLConst != nullptr) { // ------------------------------------------------------------------------------------- // The left most variable in a concat is constrained by a constant string in eqc concat // ------------------------------------------------------------------------------------- @@ -8268,7 +9287,7 @@ namespace smt { } } - if (mRConst != NULL) { + if (mRConst != nullptr) { for (std::map::iterator itor1 = mostRightNodes.begin(); itor1 != mostRightNodes.end(); itor1++) { expr * deVar = get_alias_index_ast(aliasIndexMap, itor1->first); @@ -8478,7 +9497,7 @@ namespace smt { bool Ival_exists = get_arith_value(a, Ival); if (Ival_exists) { TRACE("str", tout << "integer theory assigns " << mk_pp(a, m) << " = " << Ival.to_string() << std::endl;); - // if that value is not -1, we can assert (str.to-int S) = Ival --> S = "Ival" + // if that value is not -1, we can assert (str.to.int S) = Ival --> S = "Ival" if (!Ival.is_minus_one()) { zstring Ival_str(Ival.to_string().c_str()); expr_ref premise(ctx.mk_eq_atom(a, m_autil.mk_numeral(Ival, true)), m); @@ -8500,6 +9519,54 @@ namespace smt { // NOT_IMPLEMENTED_YET(); } + bool S_hasEqcValue; + expr * S_str = get_eqc_value(S, S_hasEqcValue); + if (S_hasEqcValue) { + zstring str; + u.str.is_string(S_str, str); + bool valid = true; + rational convertedRepresentation(0); + rational ten(10); + if (str.length() == 0) { + valid = false; + } else { + for (unsigned i = 0; i < str.length(); ++i) { + if (!('0' <= str[i] && str[i] <= '9')) { + valid = false; + break; + } else { + // accumulate + char digit = (int)str[i]; + std::string sDigit(1, digit); + int val = atoi(sDigit.c_str()); + convertedRepresentation = (ten * convertedRepresentation) + rational(val); + } + } + } + // TODO this duplicates code a bit, we can simplify the branch on "conclusion" only + if (valid) { + expr_ref premise(ctx.mk_eq_atom(S, mk_string(str)), m); + expr_ref conclusion(ctx.mk_eq_atom(a, m_autil.mk_numeral(convertedRepresentation, true)), m); + expr_ref axiom(rewrite_implication(premise, conclusion), m); + if (!string_int_axioms.contains(axiom)) { + string_int_axioms.insert(axiom); + assert_axiom(axiom); + m_trail_stack.push(insert_obj_trail(string_int_axioms, axiom)); + axiomAdd = true; + } + } else { + expr_ref premise(ctx.mk_eq_atom(S, mk_string(str)), m); + expr_ref conclusion(ctx.mk_eq_atom(a, m_autil.mk_numeral(rational::minus_one(), true)), m); + expr_ref axiom(rewrite_implication(premise, conclusion), m); + if (!string_int_axioms.contains(axiom)) { + string_int_axioms.insert(axiom); + assert_axiom(axiom); + m_trail_stack.push(insert_obj_trail(string_int_axioms, axiom)); + axiomAdd = true; + } + } + } + return axiomAdd; } @@ -8716,8 +9783,8 @@ namespace smt { context & ctx = get_context(); ast_manager & m = get_manager(); - expr_ref_vector assignments(m); - ctx.get_assignments(assignments); + //expr_ref_vector assignments(m); + //ctx.get_assignments(assignments); if (opt_VerifyFinalCheckProgress) { finalCheckProgressIndicator = false; @@ -8857,6 +9924,730 @@ 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; + } + } // RegexAutomata + bool needToAssignFreeVars = false; std::set free_variables; std::set unused_internal_variables; @@ -8913,6 +10704,27 @@ namespace smt { return FC_CONTINUE; } + // We must be be 100% certain that if there are any regex constraints, + // the string assignment for each variable is consistent with the automaton. + // The (probably) easiest way to do this is to ensure + // that we have path constraints set up for every assigned regex term. + if (m_params.m_RegexAutomata && !regex_terms.empty()) { + for (obj_hashtable::iterator it = regex_terms.begin(); it != regex_terms.end(); ++it) { + expr * str_in_re = *it; + expr * str; + expr * re; + u.str.is_in_re(str_in_re, str, re); + lbool current_assignment = ctx.get_assignment(str_in_re); + if (current_assignment == l_undef) { + continue; + } + if (!regex_terms_with_path_constraints.contains(str_in_re)) { + TRACE("str", tout << "assigned regex term " << mk_pp(str_in_re, m) << " has no path constraints -- continuing search" << std::endl;); + return FC_CONTINUE; + } + } // foreach (str.in.re in regex_terms) + } + if (unused_internal_variables.empty()) { TRACE("str", tout << "All variables are assigned. Done!" << std::endl;); return FC_DONE; @@ -8929,12 +10741,12 @@ namespace smt { CTRACE("str", needToAssignFreeVars, tout << "Need to assign values to the following free variables:" << std::endl; - for (std::set::iterator itx = free_variables.begin(); itx != free_variables.end(); ++itx) { - tout << mk_ismt2_pp(*itx, m) << std::endl; + for (expr* v : free_variables) { + tout << mk_ismt2_pp(v, m) << std::endl; } tout << "freeVar_map has the following entries:" << std::endl; - for (std::map::iterator fvIt = freeVar_map.begin(); fvIt != freeVar_map.end(); fvIt++) { - expr * var = fvIt->first; + for (auto const& kv : freeVar_map) { + expr * var = kv.first; tout << mk_ismt2_pp(var, m) << std::endl; } ); @@ -8947,7 +10759,7 @@ namespace smt { // ----------------------------------------------------------- std::map > fv_unrolls_map; std::set tmpSet; - expr * constValue = NULL; + expr * constValue = nullptr; for (std::map::iterator fvIt2 = freeVar_map.begin(); fvIt2 != freeVar_map.end(); fvIt2++) { expr * var = fvIt2->first; tmpSet.clear(); @@ -9031,7 +10843,7 @@ namespace smt { // Assign free variables std::set fSimpUnroll; - constValue = NULL; + constValue = nullptr; { TRACE("str", tout << "free var map (#" << freeVar_map.size() << "):" << std::endl; @@ -9069,8 +10881,8 @@ namespace smt { continue; } */ - expr * toAssert = gen_len_val_options_for_free_var(freeVar, NULL, ""); - if (toAssert != NULL) { + expr * toAssert = gen_len_val_options_for_free_var(freeVar, nullptr, ""); + if (toAssert != nullptr) { assert_axiom(toAssert); } } @@ -9090,7 +10902,7 @@ namespace smt { gen_assign_unroll_reg(fv_unrolls_map[var]); } else { expr * toAssert = gen_assign_unroll_Str2Reg(var, fSimpUnroll); - if (toAssert != NULL) { + if (toAssert != nullptr) { assert_axiom(toAssert); } } @@ -9238,7 +11050,7 @@ namespace smt { h++; coverAll = get_next_val_encode(options[options.size() - 1], base); } - val_range_map[val_indicator] = options[options.size() - 1]; + val_range_map.insert(val_indicator, options[options.size() - 1]); TRACE("str", tout << "value tester encoding " << "{" << std::endl; @@ -9312,13 +11124,12 @@ namespace smt { // check whether any value tester is actually in scope TRACE("str", tout << "checking scope of previous value testers" << std::endl;); bool map_effectively_empty = true; - if (fvar_valueTester_map[freeVar].find(len) != fvar_valueTester_map[freeVar].end()) { + if (fvar_valueTester_map.contains(freeVar) && + fvar_valueTester_map[freeVar].find(len) != fvar_valueTester_map[freeVar].end()) { // there's *something* in the map, but check its scope - svector > entries = fvar_valueTester_map[freeVar][len]; - for (svector >::iterator it = entries.begin(); it != entries.end(); ++it) { - std::pair entry = *it; + for (auto const& entry : fvar_valueTester_map[freeVar][len]) { expr * aTester = entry.second; - if (internal_variable_set.find(aTester) == internal_variable_set.end()) { + if (!internal_variable_set.contains(aTester)) { TRACE("str", tout << mk_pp(aTester, m) << " out of scope" << std::endl;); } else { TRACE("str", tout << mk_pp(aTester, m) << " in scope" << std::endl;); @@ -9332,7 +11143,10 @@ namespace smt { TRACE("str", tout << "no previous value testers, or none of them were in scope" << std::endl;); int tries = 0; expr * val_indicator = mk_internal_valTest_var(freeVar, len, tries); - valueTester_fvar_map[val_indicator] = freeVar; + valueTester_fvar_map.insert(val_indicator, freeVar); + if (!fvar_valueTester_map.contains(freeVar)) { + fvar_valueTester_map.insert(freeVar, std::map > >()); + } fvar_valueTester_map[freeVar][len].push_back(std::make_pair(sLevel, val_indicator)); print_value_tester_list(fvar_valueTester_map[freeVar][len]); return gen_val_options(freeVar, len_indicator, val_indicator, len_valueStr, tries); @@ -9348,7 +11162,7 @@ namespace smt { expr * aTester = fvar_valueTester_map[freeVar][len][i].second; // it's probably worth checking scope here, actually - if (internal_variable_set.find(aTester) == internal_variable_set.end()) { + if (!internal_variable_set.contains(aTester)) { TRACE("str", tout << "value tester " << mk_pp(aTester, m) << " out of scope, skipping" << std::endl;); continue; } @@ -9376,20 +11190,20 @@ namespace smt { } if (valTesterValueStr == "more") { - expr * valTester = NULL; + expr * valTester = nullptr; if (i + 1 < testerTotal) { valTester = fvar_valueTester_map[freeVar][len][i + 1].second; refresh_theory_var(valTester); } else { valTester = mk_internal_valTest_var(freeVar, len, i + 1); - valueTester_fvar_map[valTester] = freeVar; + valueTester_fvar_map.insert(valTester, freeVar); fvar_valueTester_map[freeVar][len].push_back(std::make_pair(sLevel, valTester)); print_value_tester_list(fvar_valueTester_map[freeVar][len]); } return gen_val_options(freeVar, len_indicator, valTester, len_valueStr, i + 1); } - return NULL; + return nullptr; } } @@ -9547,7 +11361,7 @@ namespace smt { if (low.is_neg()) { toAssert = m_autil.mk_ge(cntInUnr, mk_int(0)); } else { - if (unroll_var_map.find(unrFunc) == unroll_var_map.end()) { + if (!unroll_var_map.contains(unrFunc)) { expr_ref newVar1(mk_regex_rep_var(), mgr); expr_ref newVar2(mk_regex_rep_var(), mgr); @@ -9579,8 +11393,9 @@ namespace smt { // put together toAssert = mgr.mk_and(ctx.mk_eq_atom(op0, and1), toAssert); - unroll_var_map[unrFunc] = toAssert; - } else { + unroll_var_map.insert(unrFunc, toAssert); + } + else { toAssert = unroll_var_map[unrFunc]; } } @@ -9622,14 +11437,14 @@ namespace smt { int lcm = 1; int coreValueCount = 0; - expr * oneUnroll = NULL; + expr * oneUnroll = nullptr; zstring oneCoreStr(""); for (std::set::iterator itor = unrolls.begin(); itor != unrolls.end(); itor++) { expr * str2RegFunc = to_app(*itor)->get_arg(0); expr * coreVal = to_app(str2RegFunc)->get_arg(0); zstring coreStr; u.str.is_string(coreVal, coreStr); - if (oneUnroll == NULL) { + if (oneUnroll == nullptr) { oneUnroll = *itor; oneCoreStr = coreStr; } @@ -9672,8 +11487,8 @@ namespace smt { int dist = opt_LCMUnrollStep; expr_ref_vector litems(mgr); expr_ref moreAst(mk_string("more"), mgr); - for (std::set::iterator itor = unrolls.begin(); itor != unrolls.end(); itor++) { - expr_ref item(ctx.mk_eq_atom(var, *itor), mgr); + for (expr * e : unrolls) { + expr_ref item(ctx.mk_eq_atom(var, e), mgr); TRACE("str", tout << "considering unroll " << mk_pp(item, mgr) << std::endl;); litems.push_back(item); } @@ -9682,10 +11497,8 @@ namespace smt { ptr_vector outOfScopeTesters; - for (ptr_vector::iterator it = unroll_tries_map[var][unrolls].begin(); - it != unroll_tries_map[var][unrolls].end(); ++it) { - expr * tester = *it; - bool inScope = (internal_unrollTest_vars.find(tester) != internal_unrollTest_vars.end()); + for (expr * tester : unroll_tries_map[var][unrolls]) { + bool inScope = internal_unrollTest_vars.contains(tester); TRACE("str", tout << "unroll test var " << mk_pp(tester, mgr) << (inScope ? " in scope" : " out of scope") << std::endl;); @@ -9694,12 +11507,10 @@ namespace smt { } } - for (ptr_vector::iterator it = outOfScopeTesters.begin(); - it != outOfScopeTesters.end(); ++it) { - unroll_tries_map[var][unrolls].erase(*it); + for (expr* e : outOfScopeTesters) { + unroll_tries_map[var][unrolls].erase(e); } - if (unroll_tries_map[var][unrolls].size() == 0) { unroll_tries_map[var][unrolls].push_back(mk_unroll_test_var()); } @@ -10055,7 +11866,7 @@ namespace smt { TRACE("str", tout << "invoked with previousLenTester info matching top of stack" << std::endl;); } else { TRACE("str", tout << "WARNING: unexpected reordering of length testers!" << std::endl;); - UNREACHABLE(); return NULL; + UNREACHABLE(); return nullptr; } } else { u.str.is_string(lastTesterValue, lastTesterConstant); @@ -10071,7 +11882,7 @@ namespace smt { } TRACE("str", tout << "last bounds are [" << lastBounds.lowerBound << " | " << lastBounds.midPoint << " | " << lastBounds.upperBound << "]!" << lastBounds.windowSize << std::endl;); binary_search_info newBounds; - expr * newTester = 0; + expr * newTester = nullptr; if (lastTesterConstant == "more") { // special case: if the midpoint, upper bound, and window size are all equal, // we double the window size and adjust the bounds @@ -10139,7 +11950,7 @@ namespace smt { return axiom; } // length is fixed - expr * valueAssert = gen_free_var_options(freeVar, lastTester, lastTesterConstant, NULL, zstring("")); + expr * valueAssert = gen_free_var_options(freeVar, lastTester, lastTesterConstant, nullptr, zstring("")); return valueAssert; } } else { @@ -10215,14 +12026,14 @@ namespace smt { // assume empty and find a counterexample map_effectively_empty = true; - ptr_vector indicator_set = fvar_lenTester_map[freeVar]; - for (ptr_vector::iterator it = indicator_set.begin(); it != indicator_set.end(); ++it) { - expr * indicator = *it; - if (internal_variable_set.find(indicator) != internal_variable_set.end()) { - TRACE("str", tout <<"found active internal variable " << mk_ismt2_pp(indicator, m) - << " in fvar_lenTester_map[freeVar]" << std::endl;); - map_effectively_empty = false; - break; + if (fvar_lenTester_map.contains(freeVar)) { + for (expr * indicator : fvar_lenTester_map[freeVar]) { + if (internal_variable_set.find(indicator) != internal_variable_set.end()) { + TRACE("str", tout <<"found active internal variable " << mk_ismt2_pp(indicator, m) + << " in fvar_lenTester_map[freeVar]" << std::endl;); + map_effectively_empty = false; + break; + } } } CTRACE("str", map_effectively_empty, tout << "all variables in fvar_lenTester_map[freeVar] out of scope" << std::endl;); @@ -10239,6 +12050,9 @@ namespace smt { SASSERT(indicator); // since the map is "effectively empty", we can remove those variables that have left scope... + if (!fvar_lenTester_map.contains(freeVar)) { + fvar_lenTester_map.insert(freeVar, ptr_vector()); + } fvar_lenTester_map[freeVar].shrink(0); fvar_lenTester_map[freeVar].push_back(indicator); lenTester_fvar_map.insert(indicator, freeVar); @@ -10249,9 +12063,14 @@ namespace smt { } else { TRACE("str", tout << "found previous in-scope length assertions" << std::endl;); - expr * effectiveLenInd = NULL; + expr * effectiveLenInd = nullptr; zstring effectiveLenIndiStr(""); - int lenTesterCount = (int) fvar_lenTester_map[freeVar].size(); + int lenTesterCount; + if (fvar_lenTester_map.contains(freeVar)) { + lenTesterCount = fvar_lenTester_map[freeVar].size(); + } else { + lenTesterCount = 0; + } TRACE("str", tout << lenTesterCount << " length testers in fvar_lenTester_map[" << mk_pp(freeVar, m) << "]:" << std::endl; @@ -10348,9 +12167,27 @@ namespace smt { SASSERT(lenTestAssert != NULL); return lenTestAssert; } else { + // if we are performing automata-based reasoning and the term associated with + // this length tester is in any way constrained by regex terms, + // do not perform value testing -- this term is not a free variable. + if (m_params.m_RegexAutomata) { + std::map, expr*>::iterator it = regex_in_bool_map.begin(); + for (; it != regex_in_bool_map.end(); ++it) { + expr * re = it->second; + expr * re_str = to_app(re)->get_arg(0); + // recursive descent through all subterms of re_str to see if any of them are + // - the same as freeVar + // - 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; + } + } + } + TRACE("str", tout << "length is fixed; generating models for free var" << std::endl;); // length is fixed - expr * valueAssert = gen_free_var_options(freeVar, effectiveLenInd, effectiveLenIndiStr, NULL, zstring("")); + expr * valueAssert = gen_free_var_options(freeVar, effectiveLenInd, effectiveLenIndiStr, nullptr, zstring("")); return valueAssert; } } // fVarLenCountMap.find(...) @@ -10388,14 +12225,14 @@ namespace smt { void theory_str::process_free_var(std::map & freeVar_map) { context & ctx = get_context(); - std::set eqcRepSet; - std::set leafVarSet; - std::map > aloneVars; + obj_hashtable eqcRepSet; + obj_hashtable leafVarSet; + std::map > aloneVars; - for (std::map::iterator fvIt = freeVar_map.begin(); fvIt != freeVar_map.end(); fvIt++) { - expr * freeVar = fvIt->first; + for (auto const& kv : freeVar_map) { + expr * freeVar = kv.first; // skip all regular expression vars - if (regex_variable_set.find(freeVar) != regex_variable_set.end()) { + if (regex_variable_set.contains(freeVar)) { continue; } @@ -10404,15 +12241,15 @@ namespace smt { std::set eqVarSet; get_var_in_eqc(freeVar, eqVarSet); bool duplicated = false; - expr * dupVar = NULL; - for (std::set::iterator itorEqv = eqVarSet.begin(); itorEqv != eqVarSet.end(); itorEqv++) { - if (eqcRepSet.find(*itorEqv) != eqcRepSet.end()) { + expr * dupVar = nullptr; + for (expr* eq : eqVarSet) { + if (eqcRepSet.contains(eq)) { duplicated = true; - dupVar = *itorEqv; + dupVar = eq; break; } } - if (duplicated && dupVar != NULL) { + if (duplicated && dupVar != nullptr) { TRACE("str", tout << "Duplicated free variable found:" << mk_pp(freeVar, get_manager()) << " = " << mk_ismt2_pp(dupVar, get_manager()) << " (SKIP)" << std::endl;); continue; @@ -10421,13 +12258,9 @@ namespace smt { } } - for (std::set::iterator fvIt = eqcRepSet.begin(); fvIt != eqcRepSet.end(); fvIt++) { - bool standAlone = true; - expr * freeVar = *fvIt; + for (expr * freeVar : eqcRepSet) { // has length constraint initially - if (input_var_in_len.find(freeVar) != input_var_in_len.end()) { - standAlone = false; - } + bool standAlone = !input_var_in_len.contains(freeVar); // iterate parents if (standAlone) { // I hope this works! @@ -10458,25 +12291,19 @@ namespace smt { } } - for(std::set::iterator itor1 = leafVarSet.begin(); - itor1 != leafVarSet.end(); ++itor1) { - expr * toAssert = gen_len_val_options_for_free_var(*itor1, NULL, ""); + for (expr* lv : leafVarSet) { + expr * toAssert = gen_len_val_options_for_free_var(lv, nullptr, ""); // gen_len_val_options_for_free_var() can legally return NULL, // as methods that it calls may assert their own axioms instead. - if (toAssert != NULL) { - assert_axiom(toAssert); - } + if (toAssert) assert_axiom(toAssert); + } - for (std::map >::iterator mItor = aloneVars.begin(); - mItor != aloneVars.end(); ++mItor) { - std::set::iterator itor2 = mItor->second.begin(); - for(; itor2 != mItor->second.end(); ++itor2) { - expr * toAssert = gen_len_val_options_for_free_var(*itor2, NULL, ""); + for (auto const& kv : aloneVars) { + for (expr* av : kv.second) { + expr * toAssert = gen_len_val_options_for_free_var(av, nullptr, ""); // same deal with returning a NULL axiom here - if(toAssert != NULL) { - assert_axiom(toAssert); - } + if (toAssert) assert_axiom(toAssert); } } } @@ -10486,7 +12313,7 @@ namespace smt { * and constant string in eqc of node n */ void theory_str::get_eqc_allUnroll(expr * n, expr * &constStr, std::set & unrollFuncSet) { - constStr = NULL; + constStr = nullptr; unrollFuncSet.clear(); expr * curr = n; @@ -10504,7 +12331,7 @@ namespace smt { // Collect simple Unroll functions (whose core is Str2Reg) and constant strings in the EQC of n. void theory_str::get_eqc_simpleUnroll(expr * n, expr * &constStr, std::set & unrollFuncSet) { - constStr = NULL; + constStr = nullptr; unrollFuncSet.clear(); expr * curr = n; @@ -10551,7 +12378,7 @@ namespace smt { app * a0_conststr = mk_value_helper(to_app(a0)); app * a1_conststr = mk_value_helper(to_app(a1)); - if (a0_conststr != NULL && a1_conststr != NULL) { + if (a0_conststr != nullptr && a1_conststr != nullptr) { zstring a0_s, a1_s; u.str.is_string(a0_conststr, a0_s); u.str.is_string(a1_conststr, a1_s); @@ -10566,7 +12393,7 @@ namespace smt { if (hasEqc) { return to_app(n_eqc); } else { - return NULL; + return nullptr; } } @@ -10581,7 +12408,7 @@ namespace smt { SASSERT(get_context().e_internalized(owner)); app * val = mk_value_helper(owner); - if (val != NULL) { + if (val != nullptr) { return alloc(expr_wrapper_proc, val); } else { TRACE("str", tout << "WARNING: failed to find a concrete value, falling back" << std::endl;); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 03fd31162..419084091 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -20,9 +20,11 @@ #include "util/trail.h" #include "util/union_find.h" #include "util/scoped_ptr_vector.h" +#include "util/hashtable.h" #include "ast/ast_pp.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" +#include "ast/rewriter/seq_rewriter.h" #include "ast/seq_decl_plugin.h" #include "smt/smt_theory.h" #include "smt/params/theory_str_params.h" @@ -36,6 +38,7 @@ namespace smt { typedef hashtable symbol_set; +typedef int_hashtable > integer_set; class str_value_factory : public value_factory { seq_util u; @@ -46,16 +49,16 @@ public: str_value_factory(ast_manager & m, family_id fid) : value_factory(m, fid), u(m), delim("!"), m_next(0) {} - virtual ~str_value_factory() {} - virtual expr * get_some_value(sort * s) { + ~str_value_factory() override {} + expr * get_some_value(sort * s) override { return u.str.mk_string(symbol("some value")); } - virtual bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) { + bool get_some_values(sort * s, expr_ref & v1, expr_ref & v2) override { v1 = u.str.mk_string(symbol("value 1")); v2 = u.str.mk_string(symbol("value 2")); return true; } - virtual expr * get_fresh_value(sort * s) { + expr * get_fresh_value(sort * s) override { if (u.is_string(s)) { while (true) { std::ostringstream strm; @@ -66,36 +69,19 @@ public: return u.str.mk_string(sym); } } - sort* seq = 0; + sort* seq = nullptr; if (u.is_re(s, seq)) { expr* v0 = get_fresh_value(seq); return u.re.mk_to_re(v0); } TRACE("t_str", tout << "unexpected sort in get_fresh_value(): " << mk_pp(s, m_manager) << std::endl;); - UNREACHABLE(); return NULL; + UNREACHABLE(); return nullptr; } - virtual void register_value(expr * n) { /* Ignore */ } + void register_value(expr * n) override { /* Ignore */ } }; -// rather than modify obj_pair_map I inherit from it and add my own helper methods -class theory_str_contain_pair_bool_map_t : public obj_pair_map { -public: - expr * operator[](std::pair key) const { - expr * value; - bool found = this->find(key.first, key.second, value); - if (found) { - return value; - } else { - TRACE("t_str", tout << "WARNING: lookup miss in contain_pair_bool_map!" << std::endl;); - return NULL; - } - } - - bool contains(std::pair key) const { - expr * unused; - return this->find(key.first, key.second, unused); - } -}; +// NSB: added operator[] and contains to obj_pair_hashtable +class theory_str_contain_pair_bool_map_t : public obj_pair_map {}; template class binary_search_trail : public trail { @@ -104,8 +90,8 @@ class binary_search_trail : public trail { public: binary_search_trail(obj_map > & target, expr * entry) : target(target), entry(entry) {} - virtual ~binary_search_trail() {} - virtual void undo(Ctx & ctx) { + ~binary_search_trail() override {} + void undo(Ctx & ctx) override { TRACE("t_str_binary_search", tout << "in binary_search_trail::undo()" << std::endl;); if (target.contains(entry)) { if (!target[entry].empty()) { @@ -165,11 +151,75 @@ public: bool matches(zstring input); }; +class regex_automaton_under_assumptions { +protected: + expr * re_term; + eautomaton * aut; + bool polarity; + + bool assume_lower_bound; + rational lower_bound; + + bool assume_upper_bound; + rational upper_bound; +public: + regex_automaton_under_assumptions() : + re_term(NULL), aut(NULL), polarity(false), + assume_lower_bound(false), assume_upper_bound(false) {} + + regex_automaton_under_assumptions(expr * re_term, eautomaton * aut, bool polarity) : + re_term(re_term), aut(aut), polarity(polarity), + assume_lower_bound(false), assume_upper_bound(false) {} + + void set_lower_bound(rational & lb) { + lower_bound = lb; + assume_lower_bound = true; + } + void unset_lower_bound() { + assume_lower_bound = false; + } + + void set_upper_bound(rational & ub) { + upper_bound = ub; + assume_upper_bound = true; + } + void unset_upper_bound() { + assume_upper_bound = false; + } + + bool get_lower_bound(rational & lb) const { + if (assume_lower_bound) { + lb = lower_bound; + return true; + } else { + return false; + } + } + + bool get_upper_bound(rational & ub) const { + if (assume_upper_bound) { + ub = upper_bound; + return true; + } else { + return false; + } + } + + eautomaton * get_automaton() const { return aut; } + expr * get_regex_term() const { return re_term; } + bool get_polarity() const { return polarity; } + + virtual ~regex_automaton_under_assumptions() { + // don't free str_in_re or aut; + // they are managed separately + } +}; + class theory_str : public theory { struct T_cut { int level; - std::map vars; + obj_map vars; T_cut() { level = -100; @@ -267,6 +317,8 @@ protected: str_value_factory * m_factory; + re2automaton m_mk_aut; + // Unique identifier appended to unused variables to ensure that model construction // does not introduce equalities when they weren't enforced. unsigned m_unused_id; @@ -279,10 +331,15 @@ protected: ptr_vector m_concat_axiom_todo; ptr_vector m_string_constant_length_todo; ptr_vector m_concat_eval_todo; + expr_ref_vector m_delayed_assertions_todo; // enode lists for library-aware/high-level string terms (e.g. substr, contains) ptr_vector m_library_aware_axiom_todo; + // list of axioms that are re-asserted every time the scope is popped + expr_ref_vector m_persisted_axioms; + expr_ref_vector m_persisted_axiom_todo; + // hashtable of all exprs for which we've already set up term-specific axioms -- // this prevents infinite recursive descent with respect to axioms that // include an occurrence of the term for which axioms are being generated @@ -292,8 +349,8 @@ protected: int tmpXorVarCount; int tmpLenTestVarCount; int tmpValTestVarCount; - std::map, std::map > varForBreakConcat; - + // obj_pair_map > varForBreakConcat; + std::map, std::map > varForBreakConcat; bool avoidLoopCut; bool loopDetected; obj_map > cut_var_map; @@ -303,7 +360,7 @@ protected: obj_hashtable variable_set; obj_hashtable internal_variable_set; obj_hashtable regex_variable_set; - std::map > internal_variable_scope_levels; + std::map > internal_variable_scope_levels; obj_hashtable internal_lenTest_vars; obj_hashtable internal_valTest_vars; @@ -312,30 +369,55 @@ protected: obj_hashtable input_var_in_len; obj_map fvar_len_count_map; - std::map > fvar_lenTester_map; + obj_map > fvar_lenTester_map; obj_map lenTester_fvar_map; - std::map > > > fvar_valueTester_map; - std::map valueTester_fvar_map; - std::map val_range_map; + obj_map > > > fvar_valueTester_map; + + obj_map valueTester_fvar_map; + + obj_map val_range_map; // This can't be an expr_ref_vector because the constructor is wrong, // we would need to modify the allocator so we pass in ast_manager - std::map, ptr_vector > > unroll_tries_map; - std::map unroll_var_map; - std::map, expr*> concat_eq_unroll_ast_map; + obj_map, ptr_vector > > unroll_tries_map; + obj_map unroll_var_map; + obj_pair_map concat_eq_unroll_ast_map; expr_ref_vector contains_map; theory_str_contain_pair_bool_map_t contain_pair_bool_map; - //obj_map > contain_pair_idx_map; - std::map > > contain_pair_idx_map; + obj_map > > contain_pair_idx_map; + // TBD: do a curried map for determinism. std::map, expr*> regex_in_bool_map; - std::map > regex_in_var_reg_str_map; + obj_map > regex_in_var_reg_str_map; - std::map regex_nfa_cache; // Regex term --> NFA + // regex automata + scoped_ptr_vector m_automata; + ptr_vector regex_automata; + obj_hashtable regex_terms; + obj_map > regex_terms_by_string; // S --> [ (str.in.re S *) ] + obj_map > regex_automaton_assumptions; // RegEx --> [ aut+assumptions ] + obj_map regex_nfa_cache; // Regex term --> NFA + obj_hashtable regex_terms_with_path_constraints; // set of string terms which have had path constraints asserted in the current scope + obj_hashtable regex_terms_with_length_constraints; // set of regex terms which had had length constraints asserted in the current scope + obj_map regex_term_to_length_constraint; // (str.in.re S R) -> (length constraint over S wrt. R) + obj_map > regex_term_to_extra_length_vars; // extra length vars used in regex_term_to_length_constraint entries + + // keep track of the last lower/upper bound we saw for each string term + // so we don't perform duplicate work + obj_map regex_last_lower_bound; + obj_map regex_last_upper_bound; + + // each counter maps a (str.in.re) expression to an integer. + // use helper functions regex_inc_counter() and regex_get_counter() to access + obj_map regex_length_attempt_count; + obj_map regex_fail_count; + obj_map regex_intersection_fail_count; + + obj_map > string_chars; // S --> [S_0, S_1, ...] for character terms S_i svector char_set; std::map charSetLookupTable; @@ -447,21 +529,39 @@ protected: void instantiate_axiom_suffixof(enode * e); void instantiate_axiom_Contains(enode * e); void instantiate_axiom_Indexof(enode * e); - void instantiate_axiom_Indexof2(enode * e); + void instantiate_axiom_Indexof_extended(enode * e); void instantiate_axiom_LastIndexof(enode * e); void instantiate_axiom_Substr(enode * e); void instantiate_axiom_Replace(enode * e); void instantiate_axiom_str_to_int(enode * e); void instantiate_axiom_int_to_str(enode * e); + void add_persisted_axiom(expr * a); + expr * mk_RegexIn(expr * str, expr * regexp); void instantiate_axiom_RegexIn(enode * e); app * mk_unroll(expr * n, expr * bound); - void process_unroll_eq_const_str(expr * unrollFunc, expr * constStr); void unroll_str2reg_constStr(expr * unrollFunc, expr * eqConstStr); void process_concat_eq_unroll(expr * concat, expr * unroll); + // regex automata and length-aware regex + unsigned estimate_regex_complexity(expr * re); + unsigned estimate_regex_complexity_under_complement(expr * re); + unsigned estimate_automata_intersection_difficulty(eautomaton * aut1, eautomaton * aut2); + bool check_regex_length_linearity(expr * re); + bool check_regex_length_linearity_helper(expr * re, bool already_star); + expr_ref infer_all_regex_lengths(expr * lenVar, expr * re, expr_ref_vector & freeVariables); + void check_subterm_lengths(expr * re, integer_set & lens); + void find_automaton_initial_bounds(expr * str_in_re, eautomaton * aut); + bool refine_automaton_lower_bound(eautomaton * aut, rational current_lower_bound, rational & refined_lower_bound); + bool refine_automaton_upper_bound(eautomaton * aut, rational current_upper_bound, rational & refined_upper_bound); + expr_ref generate_regex_path_constraints(expr * stringTerm, eautomaton * aut, rational lenVal, expr_ref & characterConstraints); + void aut_path_add_next(u_map& next, expr_ref_vector& trail, unsigned idx, expr* cond); + expr_ref aut_path_rewrite_constraint(expr * cond, expr * ch_var); + void regex_inc_counter(obj_map & counter_map, expr * key); + unsigned regex_get_counter(obj_map & counter_map, expr * key); + void set_up_axioms(expr * ex); void handle_equality(expr * lhs, expr * rhs); @@ -495,10 +595,11 @@ protected: std::map & concatAliasMap, std::map & varConstMap, std::map & concatConstMap, std::map > & varEqConcatMap); expr * dealias_node(expr * node, std::map & varAliasMap, std::map & concatAliasMap); - void get_grounded_concats(expr* node, std::map & varAliasMap, - std::map & concatAliasMap, std::map & varConstMap, - std::map & concatConstMap, std::map > & varEqConcatMap, - std::map, std::set > > & groundedMap); + void get_grounded_concats(unsigned depth, + expr* node, std::map & varAliasMap, + std::map & concatAliasMap, std::map & varConstMap, + std::map & concatConstMap, std::map > & varEqConcatMap, + std::map, std::set > > & groundedMap); void print_grounded_concat(expr * node, std::map, std::set > > & groundedMap); void check_subsequence(expr* str, expr* strDeAlias, expr* subStr, expr* subStrDeAlias, expr* boolVar, std::map, std::set > > & groundedMap); @@ -549,6 +650,7 @@ protected: std::map > & concat_eq_concat_map, std::map > & unrollGroupMap); + bool term_appears_as_subterm(expr * needle, expr * haystack); void classify_ast_by_type(expr * node, std::map & varMap, std::map & concatMap, std::map & unrollMap); void classify_ast_by_type_in_positive_context(std::map & varMap, @@ -616,10 +718,10 @@ protected: public: theory_str(ast_manager & m, theory_str_params const & params); - virtual ~theory_str(); + ~theory_str() override; - virtual char const * get_name() const { return "seq"; } - virtual void display(std::ostream & out) const; + char const * get_name() const override { return "seq"; } + void display(std::ostream & out) const override; bool overlapping_variables_detected() const { return loopDetected; } @@ -628,33 +730,34 @@ public: void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { } void unmerge_eh(theory_var v1, theory_var v2) {} protected: - virtual bool internalize_atom(app * atom, bool gate_ctx); - virtual bool internalize_term(app * term); + bool internalize_atom(app * atom, bool gate_ctx) override; + bool internalize_term(app * term) override; virtual enode* ensure_enode(expr* e); - virtual theory_var mk_var(enode * n); + theory_var mk_var(enode * n) override; - virtual void new_eq_eh(theory_var, theory_var); - virtual void new_diseq_eh(theory_var, theory_var); + void new_eq_eh(theory_var, theory_var) override; + void new_diseq_eh(theory_var, theory_var) override; - virtual theory* mk_fresh(context*) { return alloc(theory_str, get_manager(), m_params); } - virtual void init_search_eh(); - virtual void add_theory_assumptions(expr_ref_vector & assumptions); - virtual lbool validate_unsat_core(expr_ref_vector & unsat_core); - virtual void relevant_eh(app * n); - virtual void assign_eh(bool_var v, bool is_true); - virtual void push_scope_eh(); - virtual void pop_scope_eh(unsigned num_scopes); - virtual void reset_eh(); + theory* mk_fresh(context*) override { return alloc(theory_str, get_manager(), m_params); } + void init(context * ctx) override; + void init_search_eh() override; + void add_theory_assumptions(expr_ref_vector & assumptions) override; + lbool validate_unsat_core(expr_ref_vector & unsat_core) override; + void relevant_eh(app * n) override; + void assign_eh(bool_var v, bool is_true) override; + void push_scope_eh() override; + void pop_scope_eh(unsigned num_scopes) override; + void reset_eh() override; - virtual bool can_propagate(); - virtual void propagate(); + bool can_propagate() override; + void propagate() override; - virtual final_check_status final_check_eh(); + final_check_status final_check_eh() override; virtual void attach_new_th_var(enode * n); - virtual void init_model(model_generator & m); - virtual model_value_proc * mk_value(enode * n, model_generator & mg); - virtual void finalize_model(model_generator & mg); + void init_model(model_generator & m) override; + model_value_proc * mk_value(enode * n, model_generator & mg) override; + void finalize_model(model_generator & mg) override; }; }; diff --git a/src/smt/theory_utvpi.h b/src/smt/theory_utvpi.h index 55bbb9838..64aa9f2c5 100644 --- a/src/smt/theory_utvpi.h +++ b/src/smt/theory_utvpi.h @@ -166,7 +166,7 @@ namespace smt { void new_edge(dl_var src, dl_var dst, unsigned num_edges, edge_id const* edges) {} // Create a new theory variable. - virtual th_var mk_var(enode* n); + th_var mk_var(enode* n) override; virtual th_var mk_var(expr* n); @@ -181,83 +181,83 @@ namespace smt { public: theory_utvpi(ast_manager& m); - virtual ~theory_utvpi(); + ~theory_utvpi() override; - virtual theory * mk_fresh(context * new_ctx); + theory * mk_fresh(context * new_ctx) override; - virtual char const * get_name() const { return "utvpi-logic"; } + char const * get_name() const override { return "utvpi-logic"; } /** \brief See comment in theory::mk_eq_atom */ - virtual app * mk_eq_atom(expr * lhs, expr * rhs) { return a.mk_eq(lhs, rhs); } + app * mk_eq_atom(expr * lhs, expr * rhs) override { return a.mk_eq(lhs, rhs); } - virtual void init(context * ctx); + void init(context * ctx) override; - virtual bool internalize_atom(app * atom, bool gate_ctx); + bool internalize_atom(app * atom, bool gate_ctx) override; - virtual bool internalize_term(app * term); + bool internalize_term(app * term) override; - virtual void internalize_eq_eh(app * atom, bool_var v); + void internalize_eq_eh(app * atom, bool_var v) override; - virtual void assign_eh(bool_var v, bool is_true); + void assign_eh(bool_var v, bool is_true) override; - virtual void new_eq_eh(th_var v1, th_var v2) { + void new_eq_eh(th_var v1, th_var v2) override { m_stats.m_num_core2th_eqs++; m_arith_eq_adapter.new_eq_eh(v1, v2); } - virtual bool use_diseqs() const { return true; } + bool use_diseqs() const override { return true; } - virtual void new_diseq_eh(th_var v1, th_var v2) { + void new_diseq_eh(th_var v1, th_var v2) override { m_arith_eq_adapter.new_diseq_eh(v1, v2); } - virtual void push_scope_eh(); + void push_scope_eh() override; - virtual void pop_scope_eh(unsigned num_scopes); + void pop_scope_eh(unsigned num_scopes) override; - virtual void restart_eh() { + void restart_eh() override { m_arith_eq_adapter.restart_eh(); } - virtual void relevant_eh(app* e) {} + void relevant_eh(app* e) override {} - virtual void init_search_eh() { + void init_search_eh() override { m_arith_eq_adapter.init_search_eh(); } - virtual final_check_status final_check_eh(); + final_check_status final_check_eh() override; - virtual bool is_shared(th_var v) const { + bool is_shared(th_var v) const override { return false; } - virtual bool can_propagate() { + bool can_propagate() override { SASSERT(m_asserted_qhead <= m_asserted_atoms.size()); return m_asserted_qhead != m_asserted_atoms.size(); } - virtual void propagate(); + void propagate() override; - virtual justification * why_is_diseq(th_var v1, th_var v2) { + justification * why_is_diseq(th_var v1, th_var v2) override { UNREACHABLE(); - return 0; + return nullptr; } - virtual void reset_eh(); + void reset_eh() override; - virtual void init_model(model_generator & m); + void init_model(model_generator & m) override; - virtual model_value_proc * mk_value(enode * n, model_generator & mg); + model_value_proc * mk_value(enode * n, model_generator & mg) override; - virtual bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const { + bool validate_eq_in_model(th_var v1, th_var v2, bool is_true) const override { return true; } - virtual void display(std::ostream & out) const; + void display(std::ostream & out) const override; - virtual void collect_statistics(::statistics & st) const; + void collect_statistics(::statistics & st) const override; private: diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 7c1edb585..b051c504a 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -70,7 +70,7 @@ namespace smt { m_lra(false), m_non_utvpi_exprs(false), m_test(m), - m_factory(0) { + m_factory(nullptr) { } template @@ -106,7 +106,7 @@ namespace smt { template theory_var theory_utvpi::mk_var(expr* n) { context & ctx = get_context(); - enode* e = 0; + enode* e = nullptr; th_var v = null_theory_var; m_lia |= a.is_int(n); m_lra |= a.is_real(n); @@ -239,7 +239,7 @@ namespace smt { ctx.mk_justification( ext_theory_conflict_justification( get_id(), ctx.get_region(), - lits.size(), lits.c_ptr(), 0, 0, params.size(), params.c_ptr()))); + lits.size(), lits.c_ptr(), 0, nullptr, params.size(), params.c_ptr()))); m_nc_functor.reset(); } diff --git a/src/smt/theory_wmaxsat.cpp b/src/smt/theory_wmaxsat.cpp index 88ba89610..75528a07e 100644 --- a/src/smt/theory_wmaxsat.cpp +++ b/src/smt/theory_wmaxsat.cpp @@ -25,7 +25,7 @@ Notes: namespace smt { - theory_wmaxsat::theory_wmaxsat(ast_manager& m, filter_model_converter& mc): + theory_wmaxsat::theory_wmaxsat(ast_manager& m, generic_model_converter& mc): theory(m.mk_family_id("weighted_maxsat")), m_mc(mc), m_vars(m), @@ -92,7 +92,7 @@ namespace smt { ast_manager& m = get_manager(); app_ref var(m), wfml(m); var = m.mk_fresh_const("w", m.mk_bool_sort()); - m_mc.insert(var->get_decl()); + m_mc.hide(var); wfml = m.mk_or(var, fml); ctx.assert_expr(wfml); m_rweights.push_back(w); @@ -287,7 +287,7 @@ namespace smt { ctx.set_conflict( ctx.mk_justification( - ext_theory_conflict_justification(get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), 0, 0, 0, 0))); + ext_theory_conflict_justification(get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), 0, nullptr, 0, nullptr))); } bool theory_wmaxsat::max_unassigned_is_blocked() { @@ -337,7 +337,7 @@ namespace smt { region& r = ctx.get_region(); ctx.assign(lit, ctx.mk_justification( ext_theory_propagation_justification( - get_id(), r, lits.size(), lits.c_ptr(), 0, 0, lit, 0, 0))); + get_id(), r, lits.size(), lits.c_ptr(), 0, nullptr, lit, 0, nullptr))); } diff --git a/src/smt/theory_wmaxsat.h b/src/smt/theory_wmaxsat.h index 739a22c71..b2b28ef3d 100644 --- a/src/smt/theory_wmaxsat.h +++ b/src/smt/theory_wmaxsat.h @@ -22,7 +22,7 @@ Notes: #include "smt/smt_theory.h" #include "smt/smt_clause.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" namespace smt { class theory_wmaxsat : public theory { @@ -32,7 +32,7 @@ namespace smt { void reset() { memset(this, 0, sizeof(*this)); } stats() { reset(); } }; - filter_model_converter& m_mc; + generic_model_converter& m_mc; mutable unsynch_mpz_manager m_mpz; app_ref_vector m_vars; // Auxiliary variables per soft clause expr_ref_vector m_fmls; // Formulas per soft clause @@ -56,8 +56,8 @@ namespace smt { svector m_assigned, m_enabled; stats m_stats; public: - theory_wmaxsat(ast_manager& m, filter_model_converter& mc); - virtual ~theory_wmaxsat(); + theory_wmaxsat(ast_manager& m, generic_model_converter& mc); + ~theory_wmaxsat() override; void get_assignment(svector& result); expr* assert_weighted(expr* fml, rational const& w); void disable_var(expr* var); @@ -76,43 +76,43 @@ namespace smt { old.push_back(value); } - virtual ~numeral_trail() { + ~numeral_trail() override { } - virtual void undo(context & ctx) { + void undo(context & ctx) override { m_value = m_old_values.back(); m_old_values.shrink(m_old_values.size() - 1); } }; - virtual void init_search_eh(); - virtual void assign_eh(bool_var v, bool is_true); - virtual final_check_status final_check_eh(); - virtual bool use_diseqs() const { + void init_search_eh() override; + void assign_eh(bool_var v, bool is_true) override; + final_check_status final_check_eh() override; + bool use_diseqs() const override { return false; } - virtual bool build_models() const { + bool build_models() const override { return false; } void reset_local(); - virtual void reset_eh(); - virtual theory * mk_fresh(context * new_ctx) { return 0; } - virtual bool internalize_atom(app * atom, bool gate_ctx) { return false; } - virtual bool internalize_term(app * term) { return false; } - virtual void new_eq_eh(theory_var v1, theory_var v2) { } - virtual void new_diseq_eh(theory_var v1, theory_var v2) { } - virtual void display(std::ostream& out) const {} - virtual void restart_eh(); + void reset_eh() override; + theory * mk_fresh(context * new_ctx) override { return nullptr; } + bool internalize_atom(app * atom, bool gate_ctx) override { return false; } + bool internalize_term(app * term) override { return false; } + void new_eq_eh(theory_var v1, theory_var v2) override { } + void new_diseq_eh(theory_var v1, theory_var v2) override { } + void display(std::ostream& out) const override {} + void restart_eh() override; - virtual void collect_statistics(::statistics & st) const { + void collect_statistics(::statistics & st) const override { st.update("wmaxsat num blocks", m_stats.m_num_blocks); st.update("wmaxsat num props", m_stats.m_num_propagations); } - virtual bool can_propagate() { + bool can_propagate() override { return m_propagate || m_can_propagate; } - virtual void propagate(); + void propagate() override; bool is_optimal() const; expr_ref mk_block(); diff --git a/src/smt/watch_list.cpp b/src/smt/watch_list.cpp index edd6923d7..9835142f6 100644 --- a/src/smt/watch_list.cpp +++ b/src/smt/watch_list.cpp @@ -35,7 +35,7 @@ namespace smt { } void watch_list::expand() { - if (m_data == 0) { + if (m_data == nullptr) { unsigned size = DEFAULT_WATCH_LIST_SIZE + HEADER_SIZE; unsigned * mem = reinterpret_cast(alloc_svect(char, size)); #ifdef _AMD64_ diff --git a/src/smt/watch_list.h b/src/smt/watch_list.h index 1cc29da5a..6d7a509d5 100644 --- a/src/smt/watch_list.h +++ b/src/smt/watch_list.h @@ -83,10 +83,10 @@ namespace smt { public: watch_list(): - m_data(0) { + m_data(nullptr) { } - watch_list(watch_list && other) : m_data(0) { + watch_list(watch_list && other) : m_data(nullptr) { std::swap(m_data, other.m_data); } @@ -115,7 +115,7 @@ namespace smt { void reset_and_release_memory() { destroy(); - m_data = 0; + m_data = nullptr; } clause_iterator begin_clause() { @@ -155,7 +155,7 @@ namespace smt { } void insert_clause(clause * c) { - if (m_data == 0 || end_cls_core() + sizeof(clause *) >= begin_lits_core()) { + if (m_data == nullptr || end_cls_core() + sizeof(clause *) >= begin_lits_core()) { expand(); } *(reinterpret_cast(m_data + end_cls_core())) = c; @@ -163,7 +163,7 @@ namespace smt { } void insert_literal(literal const & l) { - if (m_data == 0 || begin_lits_core() <= end_cls_core() + sizeof(literal)) { + if (m_data == nullptr || begin_lits_core() <= end_cls_core() + sizeof(literal)) { expand(); } SASSERT(begin_lits_core() >= sizeof(literal)); diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index 1ffdc35e1..c8e206f7f 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(solver check_sat_result.cpp combined_solver.cpp mus.cpp + parallel_tactic.cpp smt_logics.cpp solver.cpp solver_na2as.cpp @@ -14,4 +15,6 @@ z3_add_component(solver tactic PYG_FILES combined_solver_params.pyg + parallel_params.pyg + ) diff --git a/src/solver/check_sat_result.cpp b/src/solver/check_sat_result.cpp index f1bedfc08..28e6afeca 100644 --- a/src/solver/check_sat_result.cpp +++ b/src/solver/check_sat_result.cpp @@ -48,20 +48,22 @@ void simple_check_sat_result::collect_statistics(statistics & st) const { st.copy(m_stats); } -void simple_check_sat_result::get_unsat_core(ptr_vector & r) { - if (m_status == l_false) +void simple_check_sat_result::get_unsat_core(expr_ref_vector & r) { + if (m_status == l_false) { + r.reset(); r.append(m_core.size(), m_core.c_ptr()); + } } -void simple_check_sat_result::get_model(model_ref & m) { +void simple_check_sat_result::get_model_core(model_ref & m) { if (m_status != l_false) m = m_model; else - m = 0; + m = nullptr; } proof * simple_check_sat_result::get_proof() { - return m_status == l_false ? m_proof.get() : 0; + return m_status == l_false ? m_proof.get() : nullptr; } std::string simple_check_sat_result::reason_unknown() const { diff --git a/src/solver/check_sat_result.h b/src/solver/check_sat_result.h index 38720a5e9..7e698b806 100644 --- a/src/solver/check_sat_result.h +++ b/src/solver/check_sat_result.h @@ -23,6 +23,7 @@ Notes: #include "util/lbool.h" #include "util/statistics.h" #include "util/event_handler.h" +#include "tactic/model_converter.h" /** \brief Abstract interface for the result of a (check-sat) like command. @@ -40,21 +41,23 @@ class check_sat_result { protected: unsigned m_ref_count; lbool m_status; + model_converter_ref m_mc0; public: check_sat_result():m_ref_count(0), m_status(l_undef) {} virtual ~check_sat_result() {} void inc_ref() { m_ref_count++; } void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } - void set_status(lbool r) { m_status = r; } + lbool set_status(lbool r) { return m_status = r; } lbool status() const { return m_status; } virtual void collect_statistics(statistics & st) const = 0; - virtual void get_unsat_core(ptr_vector & r) = 0; - virtual void get_unsat_core(expr_ref_vector & r) { - ptr_vector core; - get_unsat_core(core); - r.append(core.size(), core.c_ptr()); + virtual void get_unsat_core(expr_ref_vector & r) = 0; + void set_model_converter(model_converter* mc) { m_mc0 = mc; } + model_converter* mc0() const { return m_mc0.get(); } + virtual void get_model_core(model_ref & m) = 0; + void get_model(model_ref & m) { + get_model_core(m); + if (m && mc0()) (*mc0())(m); } - virtual void get_model(model_ref & m) = 0; virtual proof * get_proof() = 0; virtual std::string reason_unknown() const = 0; virtual void set_reason_unknown(char const* msg) = 0; @@ -76,15 +79,15 @@ struct simple_check_sat_result : public check_sat_result { simple_check_sat_result(ast_manager & m); - virtual ~simple_check_sat_result(); - virtual ast_manager& get_manager() const { return m_proof.get_manager(); } - virtual void collect_statistics(statistics & st) const; - virtual void get_unsat_core(ptr_vector & r); - virtual void get_model(model_ref & m); - virtual proof * get_proof(); - virtual std::string reason_unknown() const; - virtual void get_labels(svector & r); - virtual void set_reason_unknown(char const* msg) { m_unknown = msg; } + ~simple_check_sat_result() override; + ast_manager& get_manager() const override { return m_proof.get_manager(); } + void collect_statistics(statistics & st) const override; + void get_unsat_core(expr_ref_vector & r) override; + void get_model_core(model_ref & m) override; + proof * get_proof() override; + std::string reason_unknown() const override; + void get_labels(svector & r) override; + void set_reason_unknown(char const* msg) override { m_unknown = msg; } }; #endif diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index 81e10e443..ad166d425 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -18,10 +18,11 @@ Author: Notes: --*/ -#include "solver/solver.h" #include "util/scoped_timer.h" -#include "solver/combined_solver_params.hpp" #include "util/common_msgs.h" +#include "ast/ast_pp.h" +#include "solver/solver.h" +#include "solver/combined_solver_params.hpp" #define PS_VB_LVL 15 /** @@ -58,7 +59,8 @@ private: ref m_solver2; // We delay sending assertions to solver 2 // This is relevant for big benchmarks that are meant to be solved - // by a non-incremental solver. + // by a non-incremental solver. ); + bool m_solver2_initialized; bool m_ignore_solver1; @@ -84,12 +86,12 @@ private: solver * m_solver; volatile bool m_canceled; aux_timeout_eh(solver * s):m_solver(s), m_canceled(false) {} - ~aux_timeout_eh() { + ~aux_timeout_eh() override { if (m_canceled) { m_solver->get_manager().limit().dec_cancel(); } } - virtual void operator()(event_handler_caller_t caller_id) { + void operator()(event_handler_caller_t caller_id) override { m_canceled = true; m_solver->get_manager().limit().inc_cancel(); } @@ -102,7 +104,7 @@ private: m_inc_unknown_behavior = static_cast(p.solver2_unknown()); } - virtual ast_manager& get_manager() const { return m_solver1->get_manager(); } + ast_manager& get_manager() const override { return m_solver1->get_manager(); } bool has_quantifiers() const { unsigned sz = get_num_assertions(); @@ -135,7 +137,8 @@ public: m_use_solver1_results = true; } - solver* translate(ast_manager& m, params_ref const& p) { + solver* translate(ast_manager& m, params_ref const& p) override { + TRACE("solver", tout << "translate\n";); solver* s1 = m_solver1->translate(m, p); solver* s2 = m_solver2->translate(m, p); combined_solver* r = alloc(combined_solver, s1, s2, p); @@ -146,25 +149,25 @@ public: return r; } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { solver::updt_params(p); m_solver1->updt_params(p); m_solver2->updt_params(p); updt_local_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { m_solver1->collect_param_descrs(r); m_solver2->collect_param_descrs(r); combined_solver_params::collect_param_descrs(r); } - virtual void set_produce_models(bool f) { + void set_produce_models(bool f) override { m_solver1->set_produce_models(f); m_solver2->set_produce_models(f); } - virtual void assert_expr(expr * t) { + void assert_expr_core(expr * t) override { if (m_check_sat_executed) switch_inc_mode(); m_solver1->assert_expr(t); @@ -172,7 +175,7 @@ public: m_solver2->assert_expr(t); } - virtual void assert_expr(expr * t, expr * a) { + void assert_expr_core2(expr * t, expr * a) override { if (m_check_sat_executed) switch_inc_mode(); m_solver1->assert_expr(t, a); @@ -180,23 +183,23 @@ public: m_solver2->assert_expr(t, a); } - virtual void push() { + void push() override { switch_inc_mode(); m_solver1->push(); m_solver2->push(); } - virtual void pop(unsigned n) { + void pop(unsigned n) override { switch_inc_mode(); m_solver1->pop(n); m_solver2->pop(n); } - virtual unsigned get_scope_level() const { + unsigned get_scope_level() const override { return m_solver1->get_scope_level(); } - virtual lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { + lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { switch_inc_mode(); m_use_solver1_results = false; try { @@ -204,7 +207,7 @@ public: } catch (z3_exception& ex) { if (get_manager().canceled()) { - set_reason_unknown(Z3_CANCELED_MSG); + throw; } else { set_reason_unknown(ex.msg()); @@ -213,7 +216,7 @@ public: return l_undef; } - virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions) { + lbool check_sat(unsigned num_assumptions, expr * const * assumptions) override { m_check_sat_executed = true; m_use_solver1_results = false; @@ -258,73 +261,77 @@ public: return m_solver1->check_sat(num_assumptions, assumptions); } - virtual void set_progress_callback(progress_callback * callback) { + void set_progress_callback(progress_callback * callback) override { m_solver1->set_progress_callback(callback); m_solver2->set_progress_callback(callback); } - virtual unsigned get_num_assertions() const { + unsigned get_num_assertions() const override { return m_solver1->get_num_assertions(); } - virtual expr * get_assertion(unsigned idx) const { + expr * get_assertion(unsigned idx) const override { return m_solver1->get_assertion(idx); } - virtual unsigned get_num_assumptions() const { + unsigned get_num_assumptions() const override { return m_solver1->get_num_assumptions() + m_solver2->get_num_assumptions(); } - virtual expr * get_assumption(unsigned idx) const { + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { + return m_solver1->cube(vars, backtrack_level); + } + + expr * get_assumption(unsigned idx) const override { unsigned c1 = m_solver1->get_num_assumptions(); if (idx < c1) return m_solver1->get_assumption(idx); return m_solver2->get_assumption(idx - c1); } - virtual std::ostream& display(std::ostream & out, unsigned n, expr* const* es) const { + std::ostream& display(std::ostream & out, unsigned n, expr* const* es) const override { return m_solver1->display(out, n, es); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { m_solver2->collect_statistics(st); if (m_use_solver1_results) m_solver1->collect_statistics(st); } - virtual void get_unsat_core(ptr_vector & r) { + void get_unsat_core(expr_ref_vector & r) override { if (m_use_solver1_results) m_solver1->get_unsat_core(r); else m_solver2->get_unsat_core(r); } - virtual void get_model(model_ref & m) { + void get_model_core(model_ref & m) override { if (m_use_solver1_results) m_solver1->get_model(m); else m_solver2->get_model(m); } - virtual proof * get_proof() { + proof * get_proof() override { if (m_use_solver1_results) return m_solver1->get_proof(); else return m_solver2->get_proof(); } - virtual std::string reason_unknown() const { + std::string reason_unknown() const override { if (m_use_solver1_results) return m_solver1->reason_unknown(); else return m_solver2->reason_unknown(); } - virtual void set_reason_unknown(char const* msg) { + void set_reason_unknown(char const* msg) override { m_solver1->set_reason_unknown(msg); m_solver2->set_reason_unknown(msg); } - virtual void get_labels(svector & r) { + void get_labels(svector & r) override { if (m_use_solver1_results) return m_solver1->get_labels(r); else @@ -343,9 +350,9 @@ class combined_solver_factory : public solver_factory { scoped_ptr m_f2; public: combined_solver_factory(solver_factory * f1, solver_factory * f2):m_f1(f1), m_f2(f2) {} - virtual ~combined_solver_factory() {} + ~combined_solver_factory() override {} - virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { return mk_combined_solver((*m_f1)(m, p, proofs_enabled, models_enabled, unsat_core_enabled, logic), (*m_f2)(m, p, proofs_enabled, models_enabled, unsat_core_enabled, logic), p); diff --git a/src/solver/mus.cpp b/src/solver/mus.cpp index 7eb692567..094b27ed3 100644 --- a/src/solver/mus.cpp +++ b/src/solver/mus.cpp @@ -50,12 +50,12 @@ struct mus::imp { } bool is_literal(expr* lit) const { - expr* l; + expr* l; return is_uninterp_const(lit) || (m.is_not(lit, l) && is_uninterp_const(l)); } unsigned add_soft(expr* lit) { - SASSERT(is_literal(lit)); + //SASSERT(is_literal(lit)); unsigned idx = m_lit2expr.size(); m_expr2lit.insert(lit, idx); m_lit2expr.push_back(lit); @@ -64,7 +64,6 @@ struct mus::imp { } void add_assumption(expr* lit) { - SASSERT(is_literal(lit)); m_assumptions.push_back(lit); } @@ -78,17 +77,9 @@ struct mus::imp { return get_mus1(mus); } - lbool get_mus(ptr_vector& mus) { - mus.reset(); - expr_ref_vector result(m); - lbool r = get_mus(result); - mus.append(result.size(), result.c_ptr()); - return r; - } - lbool get_mus1(expr_ref_vector& mus) { ptr_vector unknown(m_lit2expr.size(), m_lit2expr.c_ptr()); - ptr_vector core_exprs; + expr_ref_vector core_exprs(m); TRACE("mus", m_solver.display(tout);); while (!unknown.empty()) { IF_VERBOSE(12, verbose_stream() << "(mus reducing core: " << unknown.size() << " new core: " << mus.size() << ")\n";); @@ -116,12 +107,12 @@ struct mus::imp { if (!core_exprs.contains(not_lit)) { // unknown := core_exprs \ mus unknown.reset(); - for (unsigned i = 0; i < core_exprs.size(); ++i) { - if (!mus.contains(core_exprs[i])) { - unknown.push_back(core_exprs[i]); + for (expr* c : core_exprs) { + if (!mus.contains(c)) { + unknown.push_back(c); } } - TRACE("mus", display_vec(tout << "core exprs:", core_exprs); + TRACE("mus", tout << "core exprs:" << core_exprs << "\n"; display_vec(tout << "core:", unknown); display_vec(tout << "mus:", mus); ); @@ -136,7 +127,7 @@ struct mus::imp { // use correction sets lbool get_mus2(expr_ref_vector& mus) { - expr* lit = 0; + expr* lit = nullptr; lbool is_sat; ptr_vector unknown(m_lit2expr.size(), m_lit2expr.c_ptr()); while (!unknown.empty()) { @@ -163,7 +154,7 @@ struct mus::imp { expr_ref_vector nmcs(m); expr_set core, min_core, nmcs_set; bool min_core_valid = false; - expr* min_lit = 0; + expr* min_lit = nullptr; while (!unknown.empty()) { expr* lit = unknown.back(); unknown.pop_back(); @@ -242,11 +233,11 @@ struct mus::imp { void get_core(expr_set& core) { core.reset(); - ptr_vector core_exprs; + expr_ref_vector core_exprs(m); m_solver.get_unsat_core(core_exprs); - for (unsigned i = 0; i < core_exprs.size(); ++i) { - if (m_expr2lit.contains(core_exprs[i])) { - core.insert(core_exprs[i]); + for (expr* c : core_exprs) { + if (m_expr2lit.contains(c)) { + core.insert(c); } } } @@ -341,8 +332,7 @@ struct mus::imp { m_solver.get_model(mdl); rational w; for (unsigned i = 0; i < m_soft.size(); ++i) { - mdl->eval(m_soft[i].get(), tmp); - if (!m.is_true(tmp)) { + if (!mdl->is_true(m_soft.get(i))) { w += m_weights[i]; } } @@ -375,9 +365,6 @@ void mus::add_assumption(expr* lit) { return m_imp->add_assumption(lit); } -lbool mus::get_mus(ptr_vector& mus) { - return m_imp->get_mus(mus); -} lbool mus::get_mus(expr_ref_vector& mus) { return m_imp->get_mus(mus); diff --git a/src/solver/mus.h b/src/solver/mus.h index f2e543f04..807b07873 100644 --- a/src/solver/mus.h +++ b/src/solver/mus.h @@ -47,8 +47,6 @@ class mus { */ void add_assumption(expr* lit); - lbool get_mus(ptr_vector& mus); - lbool get_mus(expr_ref_vector& mus); void reset(); diff --git a/src/solver/parallel_params.pyg b/src/solver/parallel_params.pyg new file mode 100644 index 000000000..2d58cbb81 --- /dev/null +++ b/src/solver/parallel_params.pyg @@ -0,0 +1,15 @@ +def_module_params('parallel', + description='parameters for parallel solver', + class_name='parallel_params', + export=True, + params=( + ('enable', BOOL, False, 'enable parallel solver by default on selected tactics (for QF_BV)'), + ('threads.max', UINT, 10000, 'caps maximal number of threads below the number of processors'), + ('conquer.batch_size', UINT, 100, 'number of cubes to batch together for fast conquer'), + ('conquer.restart.max', UINT, 5, 'maximal number of restarts during conquer phase'), + ('conquer.delay', UINT, 10, 'delay of cubes until applying conquer'), + ('conquer.backtrack_frequency', UINT, 10, 'frequency to apply core minimization during conquer'), + ('simplify.exp', DOUBLE, 1, 'restart and inprocess max is multipled by simplify.exp ^ depth'), + ('simplify.restart.max', UINT, 5000, 'maximal number of restarts during simplification phase'), + ('simplify.inprocess.max', UINT, 2, 'maximal number of inprocessing steps during simplification'), + )) diff --git a/src/solver/parallel_tactic.cpp b/src/solver/parallel_tactic.cpp new file mode 100644 index 000000000..451a62064 --- /dev/null +++ b/src/solver/parallel_tactic.cpp @@ -0,0 +1,753 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + parallel_tactic.cpp + +Abstract: + + Parallel tactic based on cubing. + + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-9 + Miguel Neves + +Notes: + + A task comprises of a non-empty sequence of cubes, a type and parameters + + It invokes the following procedure: + 1. Clone the state with the remaining cubes if there is more than one cube. Re-enqueue the remaining cubes. + 2. Apply simplifications and pre-processing according to configuration. + 3. Cube using the parameter settings prescribed in m_params. + 4. Optionally pass the cubes as assumptions and solve each sub-cube with a prescribed resource bound. + 5. Assemble cubes that could not be solved and create a cube state. + +--*/ + +#include +#include +#include +#include +#include "util/scoped_ptr_vector.h" +#include "ast/ast_util.h" +#include "ast/ast_translation.h" +#include "solver/solver.h" +#include "solver/solver2tactic.h" +#include "tactic/tactic.h" +#include "tactic/tactical.h" +#include "solver/parallel_tactic.h" +#include "solver/parallel_params.hpp" + +class parallel_tactic : public tactic { + + + class solver_state; + + class task_queue { + std::mutex m_mutex; + std::condition_variable m_cond; + ptr_vector m_tasks; + ptr_vector m_active; + unsigned m_num_waiters; + volatile bool m_shutdown; + + void inc_wait() { + std::lock_guard lock(m_mutex); + ++m_num_waiters; + } + + void dec_wait() { + std::lock_guard lock(m_mutex); + --m_num_waiters; + } + + solver_state* try_get_task() { + solver_state* st = nullptr; + std::lock_guard lock(m_mutex); + if (!m_tasks.empty()) { + st = m_tasks.back(); + m_tasks.pop_back(); + m_active.push_back(st); + } + return st; + } + + public: + + task_queue(): + m_num_waiters(0), + m_shutdown(false) {} + + ~task_queue() { reset(); } + + void shutdown() { + if (!m_shutdown) { + m_shutdown = true; + m_cond.notify_all(); + std::lock_guard lock(m_mutex); + for (solver_state* st : m_active) { + st->m().limit().cancel(); + } + } + } + + bool in_shutdown() const { return m_shutdown; } + + void add_task(solver_state* task) { + std::lock_guard lock(m_mutex); + m_tasks.push_back(task); + if (m_num_waiters > 0) { + m_cond.notify_one(); + } + } + + bool is_idle() { + std::lock_guard lock(m_mutex); + return m_tasks.empty() && m_num_waiters > 0; + } + + solver_state* get_task() { + while (!m_shutdown) { + inc_wait(); + solver_state* st = try_get_task(); + if (st) { + dec_wait(); + return st; + } + { + std::unique_lock lock(m_mutex); + m_cond.wait(lock); + } + dec_wait(); + } + return nullptr; + } + + void task_done(solver_state* st) { + std::lock_guard lock(m_mutex); + m_active.erase(st); + if (m_tasks.empty() && m_active.empty()) { + m_shutdown = true; + m_cond.notify_all(); + } + } + + void reset() { + for (auto* t : m_tasks) dealloc(t); + for (auto* t : m_active) dealloc(t); + m_tasks.reset(); + m_active.reset(); + } + + std::ostream& display(std::ostream& out) { + std::lock_guard lock(m_mutex); + out << "num_tasks " << m_tasks.size() << " active: " << m_active.size() << "\n"; + for (solver_state* st : m_tasks) { + st->display(out); + } + return out; + } + + }; + + class cube_var { + expr_ref_vector m_vars; + expr_ref_vector m_cube; + public: + cube_var(expr_ref_vector const& c, expr_ref_vector const& vs): + m_vars(vs), m_cube(c) {} + + cube_var(cube_var const& other): + m_vars(other.m_vars), m_cube(other.m_cube) {} + + cube_var operator()(ast_translation& tr) { + expr_ref_vector vars(tr(m_vars)); + expr_ref_vector cube(tr(m_cube)); + return cube_var(cube, vars); + } + + expr_ref_vector const& cube() const { return m_cube; } + expr_ref_vector const& vars() const { return m_vars; } + }; + + class solver_state { + scoped_ptr m_manager; // ownership handle to ast_manager + vector m_cubes; // set of cubes to process by task + expr_ref_vector m_asserted_cubes; // set of cubes asserted on the current solver + expr_ref_vector m_assumptions; // set of auxiliary assumptions passed in + params_ref m_params; // configuration parameters + ref m_solver; // solver state + unsigned m_depth; // number of nested calls to cubing + double m_width; // estimate of fraction of problem handled by state + + public: + solver_state(ast_manager* m, solver* s, params_ref const& p): + m_manager(m), + m_asserted_cubes(s->get_manager()), + m_assumptions(s->get_manager()), + m_params(p), + m_solver(s), + m_depth(0), + m_width(1.0) + { + } + + ast_manager& m() { return m_solver->get_manager(); } + + solver& get_solver() { return *m_solver; } + + solver* copy_solver() { return m_solver->translate(m_solver->get_manager(), m_params); } + + solver const& get_solver() const { return *m_solver; } + + void set_assumptions(ptr_vector const& asms) { + m_assumptions.append(asms.size(), asms.c_ptr()); + } + + bool has_assumptions() const { return !m_assumptions.empty(); } + + solver_state* clone() { + SASSERT(!m_cubes.empty()); + ast_manager& m = m_solver->get_manager(); + ast_manager* new_m = alloc(ast_manager, m, m.proof_mode()); + ast_translation tr(m, *new_m); + solver* s = m_solver.get()->translate(*new_m, m_params); + solver_state* st = alloc(solver_state, new_m, s, m_params); + for (auto& c : m_cubes) st->m_cubes.push_back(c(tr)); + for (expr* c : m_asserted_cubes) st->m_asserted_cubes.push_back(tr(c)); + for (expr* c : m_assumptions) st->m_assumptions.push_back(tr(c)); + st->m_depth = m_depth; + st->m_width = m_width; + return st; + } + + vector const& cubes() const { return m_cubes; } + + // remove up to n cubes from list of cubes. + vector split_cubes(unsigned n) { + vector result; + while (n-- > 0 && !m_cubes.empty()) { + result.push_back(m_cubes.back()); + m_cubes.pop_back(); + } + return result; + } + + void set_cubes(vector& c) { + m_cubes.reset(); + m_cubes.append(c); + } + + void inc_depth(unsigned inc) { m_depth += inc; } + + void inc_width(unsigned w) { m_width *= w; } + + double get_width() const { return m_width; } + + unsigned get_depth() const { return m_depth; } + + lbool simplify() { + lbool r = l_undef; + IF_VERBOSE(2, verbose_stream() << "(parallel.tactic simplify-1)\n";); + set_simplify_params(true); // retain blocked + r = get_solver().check_sat(m_assumptions); + if (r != l_undef) return r; + IF_VERBOSE(2, verbose_stream() << "(parallel.tactic simplify-2)\n";); + set_simplify_params(false); // remove blocked + r = get_solver().check_sat(m_assumptions); + return r; + } + + void assert_cube(expr_ref_vector const& cube) { + get_solver().assert_expr(cube); + m_asserted_cubes.append(cube); + } + + void set_cube_params() { + } + + void set_conquer_params() { + set_conquer_params(get_solver()); + } + + void set_conquer_params(solver& s) { + parallel_params pp(m_params); + params_ref p; + p.copy(m_params); + p.set_bool("gc.burst", true); // apply eager gc + p.set_uint("simplify.delay", 1000); // delay simplification by 1000 conflicts + p.set_bool("lookahead_simplify", false); + p.set_uint("restart.max", pp.conquer_restart_max()); + p.set_uint("inprocess.max", UINT_MAX); // base bounds on restart.max + s.updt_params(p); + } + + void set_simplify_params(bool retain_blocked) { + parallel_params pp(m_params); + params_ref p; + p.copy(m_params); + double exp = pp.simplify_exp(); + exp = std::max(exp, 1.0); + unsigned mult = static_cast(pow(exp, m_depth - 1)); + p.set_uint("inprocess.max", pp.simplify_inprocess_max() * mult); + p.set_uint("restart.max", pp.simplify_restart_max() * mult); + p.set_bool("lookahead_simplify", true); + p.set_bool("retain_blocked_clauses", retain_blocked); + if (m_depth > 1) p.set_uint("bce_delay", 0); + get_solver().updt_params(p); + } + + bool canceled() { + return m().canceled(); + } + + std::ostream& display(std::ostream& out) { + out << ":depth " << m_depth << " :width " << m_width << "\n"; + out << ":asserted " << m_asserted_cubes.size() << "\n"; + return out; + } + }; + +private: + + solver_ref m_solver; + ast_manager& m_manager; + params_ref m_params; + sref_vector m_models; + expr_ref_vector m_core; + unsigned m_num_threads; + statistics m_stats; + task_queue m_queue; + std::mutex m_mutex; + double m_progress; + unsigned m_branches; + unsigned m_backtrack_frequency; + unsigned m_conquer_delay; + volatile bool m_has_undef; + bool m_allsat; + unsigned m_num_unsat; + int m_exn_code; + std::string m_exn_msg; + + void init() { + parallel_params pp(m_params); + m_num_threads = std::min((unsigned)omp_get_num_procs(), pp.threads_max()); + m_progress = 0; + m_has_undef = false; + m_allsat = false; + m_branches = 0; + m_num_unsat = 0; + m_backtrack_frequency = pp.conquer_backtrack_frequency(); + m_conquer_delay = pp.conquer_delay(); + m_exn_code = 0; + m_params.set_bool("override_incremental", true); + m_core.reset(); + } + + void log_branches(lbool status) { + IF_VERBOSE(0, verbose_stream() << "(tactic.parallel :progress " << m_progress << "% "; + if (status == l_true) verbose_stream() << ":status sat "; + if (status == l_undef) verbose_stream() << ":status unknown "; + verbose_stream() << ":closed " << m_num_unsat << " :open " << m_branches << ")\n";); + } + + void add_branches(unsigned b) { + if (b == 0) return; + { + std::lock_guard lock(m_mutex); + m_branches += b; + } + log_branches(l_false); + } + + void dec_branch() { + std::lock_guard lock(m_mutex); + --m_branches; + } + + void collect_core(expr_ref_vector const& core) { + std::lock_guard lock(m_mutex); + ast_translation tr(core.get_manager(), m_manager); + expr_ref_vector core1(tr(core)); + for (expr* c : core1) { + if (!m_core.contains(c)) m_core.push_back(c); + } + } + + void close_branch(solver_state& s, lbool status) { + double f = 100.0 / s.get_width(); + { + std::lock_guard lock(m_mutex); + m_progress += f; + --m_branches; + } + log_branches(status); + } + + void report_sat(solver_state& s) { + close_branch(s, l_true); + model_ref mdl; + s.get_solver().get_model(mdl); + if (mdl) { + std::lock_guard lock(m_mutex); + if (&s.m() != &m_manager) { + ast_translation tr(s.m(), m_manager); + mdl = mdl->translate(tr); + } + m_models.push_back(mdl.get()); + } + if (!m_allsat) { + m_queue.shutdown(); + } + } + + void inc_unsat() { + std::lock_guard lock(m_mutex); + ++m_num_unsat; + } + + void report_unsat(solver_state& s) { + inc_unsat(); + close_branch(s, l_false); + if (s.has_assumptions()) { + expr_ref_vector core(s.m()); + s.get_solver().get_unsat_core(core); + collect_core(core); + } + } + + void report_undef(solver_state& s) { + m_has_undef = true; + close_branch(s, l_undef); + } + + void cube_and_conquer(solver_state& s) { + ast_manager& m = s.m(); + vector cube, hard_cubes, cubes; + expr_ref_vector vars(m); + + cube_again: + // extract up to one cube and add it. + cube.reset(); + cube.append(s.split_cubes(1)); + SASSERT(cube.size() <= 1); + IF_VERBOSE(2, verbose_stream() << "(tactic.parallel :split-cube " << cube.size() << ")\n";); + if (!s.cubes().empty()) m_queue.add_task(s.clone()); + if (!cube.empty()) { + s.assert_cube(cube.get(0).cube()); + vars.reset(); + vars.append(cube.get(0).vars()); + } + + simplify_again: + s.inc_depth(1); + // simplify + if (canceled(s)) return; + switch (s.simplify()) { + case l_undef: break; + case l_true: report_sat(s); return; + case l_false: report_unsat(s); return; + } + if (canceled(s)) return; + + if (memory_pressure()) { + goto simplify_again; + } + // extract cubes. + cubes.reset(); + s.set_cube_params(); + solver_ref conquer; + + unsigned cutoff = UINT_MAX; + bool first = true; + unsigned num_backtracks = 0, width = 0; + while (cutoff > 0 && !canceled(s)) { + expr_ref_vector c = s.get_solver().cube(vars, cutoff); + if (c.empty()) { + goto simplify_again; + } + if (m.is_false(c.back())) { + break; + } + lbool is_sat = l_undef; + if (!s.has_assumptions() && width >= m_conquer_delay && !conquer) { + conquer = s.copy_solver(); + s.set_conquer_params(*conquer.get()); + } + if (conquer) { + is_sat = conquer->check_sat(c); + } + switch (is_sat) { + case l_false: + cutoff = c.size(); + backtrack(*conquer.get(), c, (num_backtracks++) % m_backtrack_frequency == 0); + if (cutoff != c.size()) { + IF_VERBOSE(0, verbose_stream() << "(tactic.parallel :backtrack " << cutoff << " -> " << c.size() << ")\n"); + cutoff = c.size(); + } + inc_unsat(); + log_branches(l_false); + break; + + case l_true: + report_sat(s); + if (conquer) { + collect_statistics(*conquer.get()); + } + return; + + case l_undef: + ++width; + IF_VERBOSE(2, verbose_stream() << "(tactic.parallel :cube " << c.size() << " :vars " << vars.size() << ")\n"); + cubes.push_back(cube_var(c, vars)); + cutoff = UINT_MAX; + break; + + } + if (cubes.size() >= conquer_batch_size()) { + spawn_cubes(s, 10*width, cubes); + first = false; + cubes.reset(); + } + } + + if (conquer) { + collect_statistics(*conquer.get()); + } + + if (cubes.empty() && first) { + report_unsat(s); + } + else if (cubes.empty()) { + dec_branch(); + } + else { + s.inc_width(width); + add_branches(cubes.size()-1); + s.set_cubes(cubes); + goto cube_again; + } + } + + void spawn_cubes(solver_state& s, unsigned width, vector& cubes) { + if (cubes.empty()) return; + add_branches(cubes.size()); + s.set_cubes(cubes); + solver_state* s1 = s.clone(); + s1->inc_width(width); + m_queue.add_task(s1); + } + + /* + * \brief backtrack from unsatisfiable core + */ + void backtrack(solver& s, expr_ref_vector& asms, bool full) { + ast_manager& m = s.get_manager(); + expr_ref_vector core(m); + obj_hashtable hcore; + s.get_unsat_core(core); + while (!asms.empty() && !core.contains(asms.back())) asms.pop_back(); + if (!full) return; + + //s.assert_expr(m.mk_not(mk_and(core))); + if (!asms.empty()) { + expr* last = asms.back(); + expr_ref not_last(mk_not(m, last), m); + asms.pop_back(); + asms.push_back(not_last); + lbool r = s.check_sat(asms); + asms.pop_back(); + if (r != l_false) { + asms.push_back(last); + return; + } + core.reset(); + s.get_unsat_core(core); + if (core.contains(not_last)) { + //s.assert_expr(m.mk_not(mk_and(core))); + r = s.check_sat(asms); + } + if (r == l_false) { + backtrack(s, asms, full); + } + } + } + + bool canceled(solver_state& s) { + if (s.canceled()) { + m_has_undef = true; + return true; + } + else { + return false; + } + } + + bool memory_pressure() { + return memory::above_high_watermark(); + } + + void run_solver() { + try { + while (solver_state* st = m_queue.get_task()) { + cube_and_conquer(*st); + collect_statistics(*st); + m_queue.task_done(st); + if (st->m().canceled()) m_queue.shutdown(); + IF_VERBOSE(1, display(verbose_stream());); + dealloc(st); + } + } + catch (z3_exception& ex) { + IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";); + if (m_queue.in_shutdown()) return; + m_queue.shutdown(); + std::lock_guard lock(m_mutex); + if (ex.has_error_code()) { + m_exn_code = ex.error_code(); + } + else { + m_exn_msg = ex.msg(); + m_exn_code = -1; + } + } + } + + void collect_statistics(solver_state& s) { + collect_statistics(s.get_solver()); + } + + void collect_statistics(solver& s) { + std::lock_guard lock(m_mutex); + s.collect_statistics(m_stats); + } + + lbool solve(model_ref& mdl) { + add_branches(1); + vector threads; + for (unsigned i = 0; i < m_num_threads; ++i) + threads.push_back(std::thread([this]() { run_solver(); })); + for (std::thread& t : threads) + t.join(); + m_manager.limit().reset_cancel(); + if (m_exn_code == -1) + throw default_exception(m_exn_msg); + if (m_exn_code != 0) + throw z3_error(m_exn_code); + if (!m_models.empty()) { + mdl = m_models.back(); + return l_true; + } + if (m_has_undef) + return l_undef; + return l_false; + } + + std::ostream& display(std::ostream& out) { + unsigned n_models, n_unsat; + double n_progress; + { + std::lock_guard lock(m_mutex); + n_models = m_models.size(); + n_unsat = m_num_unsat; + n_progress = m_progress; + } + m_stats.display(out); + m_queue.display(out); + out << "(tactic.parallel :unsat " << n_unsat << " :progress " << n_progress << "% :models " << n_models << ")\n"; + return out; + } + +public: + + parallel_tactic(solver* s, params_ref const& p) : + m_solver(s), + m_manager(s->get_manager()), + m_params(p), + m_core(m_manager) { + init(); + } + + void operator ()(const goal_ref & g,goal_ref_buffer & result) { + 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); + m_queue.add_task(st); + expr_ref_vector clauses(m); + ptr_vector assumptions; + obj_map bool2dep; + ref fmc; + expr_dependency * lcore = nullptr; + proof* pr = nullptr; + extract_clauses_and_dependencies(g, clauses, assumptions, bool2dep, fmc); + for (expr * clause : clauses) { + s->assert_expr(clause); + } + st->set_assumptions(assumptions); + model_ref mdl; + lbool is_sat = solve(mdl); + switch (is_sat) { + case l_true: + g->reset(); + if (g->models_enabled()) { + g->add(concat(fmc.get(), model2model_converter(mdl.get()))); + } + break; + case l_false: + SASSERT(!g->proofs_enabled()); + for (expr * c : m_core) { + lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(c))); + } + g->assert_expr(m.mk_false(), pr, lcore); + break; + case l_undef: + if (m.canceled()) { + throw tactic_exception(Z3_CANCELED_MSG); + } + break; + } + result.push_back(g.get()); + } + + unsigned conquer_batch_size() const { + parallel_params pp(m_params); + return pp.conquer_batch_size(); + } + + void cleanup() { + m_queue.reset(); + } + + tactic* translate(ast_manager& m) { + solver* s = m_solver->translate(m, m_params); + return alloc(parallel_tactic, s, m_params); + } + + virtual void updt_params(params_ref const & p) { + m_params.copy(p); + parallel_params pp(p); + m_conquer_delay = pp.conquer_delay(); + } + + virtual void collect_statistics(statistics & st) const { + 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() { + m_stats.reset(); + } +}; + + +tactic * mk_parallel_tactic(solver* s, params_ref const& p) { + return alloc(parallel_tactic, s, p); +} + diff --git a/src/solver/parallel_tactic.h b/src/solver/parallel_tactic.h new file mode 100644 index 000000000..ae8fb6041 --- /dev/null +++ b/src/solver/parallel_tactic.h @@ -0,0 +1,27 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + parallel_tactic.h + +Abstract: + + Parallel tactic in the style of Treengeling. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-9 + +Notes: + +--*/ +#ifndef PARALLEL_TACTIC_H_ +#define PARALLEL_TACTIC_H_ + +class tactic; +class solver; + +tactic * mk_parallel_tactic(solver* s, params_ref const& p); + +#endif diff --git a/src/solver/smt_logics.cpp b/src/solver/smt_logics.cpp index 874f1cfcc..7ed2b0445 100644 --- a/src/solver/smt_logics.cpp +++ b/src/solver/smt_logics.cpp @@ -22,14 +22,14 @@ Revision History: bool smt_logics::supported_logic(symbol const & s) { - return logic_has_uf(s) || logic_is_all(s) || logic_has_fd(s) || + return logic_has_uf(s) || logic_is_all(s) || logic_has_fd(s) || logic_has_arith(s) || logic_has_bv(s) || logic_has_array(s) || logic_has_seq(s) || logic_has_str(s) || logic_has_horn(s) || logic_has_fpa(s); } bool smt_logics::logic_has_reals_only(symbol const& s) { - return + return s == "QF_RDL" || s == "QF_LRA" || s == "UFLRA" || @@ -84,8 +84,9 @@ bool smt_logics::logic_has_arith(symbol const & s) { s == "QF_BVFP" || s == "QF_S" || s == "ALL" || - s == "QF_FD" || - s == "HORN"; + s == "QF_FD" || + s == "HORN" || + s == "QF_FPLRA"; } bool smt_logics::logic_has_bv(symbol const & s) { @@ -137,7 +138,7 @@ bool smt_logics::logic_has_str(symbol const & s) { } bool smt_logics::logic_has_fpa(symbol const & s) { - return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "ALL"; + return s == "QF_FP" || s == "QF_FPBV" || s == "QF_BVFP" || s == "QF_FPLRA" || s == "ALL"; } bool smt_logics::logic_has_uf(symbol const & s) { diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index bf53cb669..4044c4a85 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -16,12 +16,14 @@ Author: Notes: --*/ -#include "solver/solver.h" -#include "model/model_evaluator.h" +#include "util/common_msgs.h" +#include "util/stopwatch.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" #include "ast/ast_pp_util.h" -#include "util/common_msgs.h" +#include "tactic/model_converter.h" +#include "solver/solver.h" +#include "model/model_evaluator.h" unsigned solver::get_num_assertions() const { @@ -31,17 +33,27 @@ unsigned solver::get_num_assertions() const { expr * solver::get_assertion(unsigned idx) const { NOT_IMPLEMENTED_YET(); - return 0; + return nullptr; } std::ostream& solver::display(std::ostream & out, unsigned n, expr* const* assumptions) const { expr_ref_vector fmls(get_manager()); - get_assertions(fmls); + stopwatch sw; + sw.start(); + get_assertions(fmls); ast_pp_util visitor(get_manager()); + model_converter_ref mc = get_model_converter(); + if (mc.get()) { + mc->set_env(&visitor); + } visitor.collect(fmls); visitor.collect(n, assumptions); visitor.display_decls(out); visitor.display_asserts(out, fmls, true); + if (mc.get()) { + mc->display(out); + mc->set_env(nullptr); + } return out; } @@ -52,6 +64,13 @@ void solver::get_assertions(expr_ref_vector& fmls) const { } } +expr_ref_vector solver::get_assertions() const { + expr_ref_vector result(get_manager()); + get_assertions(result); + return result; +} + + struct scoped_assumption_push { expr_ref_vector& m_vec; scoped_assumption_push(expr_ref_vector& v, expr* e): m_vec(v) { v.push_back(e); } @@ -156,10 +175,84 @@ lbool solver::find_mutexes(expr_ref_vector const& vars, vector& } lbool solver::preferred_sat(expr_ref_vector const& asms, vector& cores) { - return check_sat(0, 0); + 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)); } + +void solver::assert_expr(expr* f) { + expr_ref fml(f, get_manager()); + if (m_enforce_model_conversion) { + model_converter_ref mc = get_model_converter(); + if (mc) { + (*mc)(fml); + } + } + assert_expr_core(fml); +} + +void solver::assert_expr(expr* f, expr* t) { + ast_manager& m = get_manager(); + expr_ref fml(f, m); + expr_ref a(t, m); + if (m_enforce_model_conversion) { + IF_VERBOSE(0, verbose_stream() << "enforce model conversion\n";); + exit(0); + model_converter_ref mc = get_model_converter(); + if (mc) { + (*mc)(fml); + // (*mc)(a); + } + } + assert_expr_core2(fml, a); +} + +void solver::collect_param_descrs(param_descrs & r) { + r.insert("solver.enforce_model_conversion", CPK_BOOL, "(default: false) enforce model conversion when asserting formulas"); +} + +void solver::reset_params(params_ref const & p) { + m_params = p; + m_enforce_model_conversion = m_params.get_bool("solver.enforce_model_conversion", false); +} + +void solver::updt_params(params_ref const & p) { + m_params.copy(p); + m_enforce_model_conversion = m_params.get_bool("solver.enforce_model_conversion", false); +} + + +expr_ref_vector solver::get_units(ast_manager& m) { + expr_ref_vector fmls(m), result(m), tmp(m); + get_assertions(fmls); + obj_map units; + for (expr* f : fmls) { + if (m.is_not(f, f) && is_literal(m, f)) { + m.inc_ref(f); + units.insert(f, false); + } + else if (is_literal(m, f)) { + m.inc_ref(f); + units.insert(f, true); + } + } + model_converter_ref mc = get_model_converter(); + if (mc) { + mc->get_units(units); + } + for (auto const& kv : units) { + tmp.push_back(kv.m_key); + if (kv.m_value) + result.push_back(kv.m_key); + else + result.push_back(m.mk_not(kv.m_key)); + } + for (expr* e : tmp) { + m.dec_ref(e); + } + + return result; +} diff --git a/src/solver/solver.h b/src/solver/solver.h index 0a406455b..c371be284 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -24,6 +24,7 @@ Notes: #include "util/params.h" class solver; +class model_converter; class solver_factory { public: @@ -44,8 +45,10 @@ public: */ class solver : public check_sat_result { params_ref m_params; + bool m_enforce_model_conversion; public: - virtual ~solver() {} + solver(): m_enforce_model_conversion(false) {} + ~solver() override {} /** \brief Creates a clone of the solver. @@ -55,18 +58,34 @@ public: /** \brief Update the solver internal settings. */ - virtual void updt_params(params_ref const & p) { m_params.copy(p); } + virtual void updt_params(params_ref const & p); + + /** + \brief reset parameters. + */ + virtual void reset_params(params_ref const& p); /** \brief Retrieve set of parameters set on solver. */ - virtual params_ref const& get_params() { return m_params; } + virtual params_ref const& get_params() const { return m_params; } /** \brief Store in \c r a description of the configuration parameters available in this solver. */ - virtual void collect_param_descrs(param_descrs & r) {} + virtual void collect_param_descrs(param_descrs & r); + + /** + \brief Push a parameter state. It is restored upon pop. + Only a single scope of push is supported. + */ + virtual void push_params() {} + + /** + \brief Pop a parameter state. \sa push_params. + */ + virtual void pop_params() {} /** \brief Enable/Disable model generation for this solver object. @@ -79,14 +98,16 @@ public: /** \brief Add a new formula to the assertion stack. */ - virtual void assert_expr(expr * t) = 0; + void assert_expr(expr* f); + + virtual void assert_expr_core(expr * t) = 0; void assert_expr(expr_ref_vector const& ts) { - for (unsigned i = 0; i < ts.size(); ++i) assert_expr(ts[i]); + for (expr* e : ts) assert_expr(e); } void assert_expr(ptr_vector const& ts) { - for (unsigned i = 0; i < ts.size(); ++i) assert_expr(ts[i]); + for (expr* e : ts) assert_expr(e); } /** @@ -94,7 +115,10 @@ public: The propositional variable \c a is used to track the use of \c t in a proof of unsatisfiability. */ - virtual void assert_expr(expr * t, expr * a) = 0; + + void assert_expr(expr * t, expr* a); + + virtual void assert_expr_core2(expr * t, expr * a) = 0; /** \brief Create a backtracking point. @@ -122,6 +146,16 @@ public: lbool check_sat(app_ref_vector const& asms) { return check_sat(asms.size(), (expr* const*)asms.c_ptr()); } + /** + \brief Check satisfiability modulo a cube and a clause. + + The cube corresponds to auxiliary assumptions. The clause as an auxiliary disjunction that is also + assumed for the check. + */ + virtual lbool check_sat_cc(expr_ref_vector const& cube, vector const& clauses) { + if (clauses.empty()) return check_sat(cube.size(), cube.c_ptr()); + NOT_IMPLEMENTED_YET(); + } /** \brief Set a progress callback procedure that is invoked by this solver during check_sat. @@ -145,6 +179,8 @@ public: */ void get_assertions(expr_ref_vector& fmls) const; + expr_ref_vector get_assertions() const; + /** \brief The number of tracked assumptions (see assert_expr(t, a)). */ @@ -178,11 +214,28 @@ public: */ virtual lbool preferred_sat(expr_ref_vector const& asms, vector& cores); + /** + \brief extract a lookahead candidates for branching. + */ + + virtual expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) = 0; + /** \brief Display the content of this solver. */ virtual std::ostream& display(std::ostream & out, unsigned n = 0, expr* const* assumptions = nullptr) const; + /** + \brief expose model converter when solver produces partially reduced set of assertions. + */ + + virtual model_converter_ref get_model_converter() const { return m_mc0; } + + /** + \brief extract units from solver. + */ + expr_ref_vector get_units(ast_manager& m); + class scoped_push { solver& s; bool m_nopop; @@ -191,13 +244,21 @@ public: ~scoped_push() { if (!m_nopop) s.pop(1); } void disable_pop() { m_nopop = true; } }; + protected: virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences); + bool is_literal(ast_manager& m, expr* e); }; +typedef ref solver_ref; + +inline std::ostream& operator<<(std::ostream& out, solver const& s) { + return s.display(out); +} + #endif diff --git a/src/solver/solver2tactic.cpp b/src/solver/solver2tactic.cpp index fa0711d70..3c4f291a5 100644 --- a/src/solver/solver2tactic.cpp +++ b/src/solver/solver2tactic.cpp @@ -19,13 +19,13 @@ Notes: #include "solver/solver.h" #include "tactic/tactic.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "solver/solver2tactic.h" #include "ast/ast_util.h" typedef obj_map expr2expr_map; -void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, expr2expr_map& bool2dep, ref& fmc) { +void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, expr2expr_map& bool2dep, ref& fmc) { expr2expr_map dep2bool; ptr_vector deps; ast_manager& m = g->m(); @@ -34,7 +34,7 @@ void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clause for (unsigned i = 0; i < sz; i++) { expr * f = g->form(i); expr_dependency * d = g->dep(i); - if (d == 0 || !g->unsat_core_enabled()) { + if (d == nullptr || !g->unsat_core_enabled()) { clauses.push_back(f); } else { @@ -58,16 +58,16 @@ void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clause } else { // must normalize assumption - expr * b = 0; + expr * b = nullptr; if (!dep2bool.find(d, b)) { - b = m.mk_fresh_const(0, m.mk_bool_sort()); + b = m.mk_fresh_const(nullptr, m.mk_bool_sort()); dep2bool.insert(d, b); bool2dep.insert(b, d); assumptions.push_back(b); if (!fmc) { - fmc = alloc(filter_model_converter, m); + fmc = alloc(generic_model_converter, m, "solver2tactic"); } - fmc->insert(to_app(b)->get_decl()); + fmc->hide(to_app(b)->get_decl()); } clause.push_back(m.mk_not(b)); } @@ -92,25 +92,21 @@ public: m_solver(s) {} - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params.append(p); m_solver->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); } - virtual void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) { - pc = 0; mc = 0; core = 0; + void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result) override { expr_ref_vector clauses(m); expr2expr_map bool2dep; ptr_vector assumptions; - ref fmc; + ref fmc; extract_clauses_and_dependencies(in, clauses, assumptions, bool2dep, fmc); ref local_solver = m_solver->translate(m, m_params); local_solver->assert_expr(clauses); @@ -120,56 +116,69 @@ public: if (in->models_enabled()) { model_ref mdl; local_solver->get_model(mdl); + model_converter_ref mc; mc = model2model_converter(mdl.get()); mc = concat(fmc.get(), mc.get()); + mc = concat(local_solver->mc0(), mc.get()); + in->add(mc.get()); } in->reset(); result.push_back(in.get()); break; case l_false: { in->reset(); - proof* pr = 0; - expr_dependency* lcore = 0; + expr_dependency_ref lcore(m); + proof* pr = nullptr; if (in->proofs_enabled()) { pr = local_solver->get_proof(); - pc = proof2proof_converter(m, pr); + in->set(proof2proof_converter(m, pr)); } if (in->unsat_core_enabled()) { - ptr_vector core; + expr_ref_vector core(m); local_solver->get_unsat_core(core); - for (unsigned i = 0; i < core.size(); ++i) { - lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(core[i]))); + for (expr* c : core) { + lcore = m.mk_join(lcore, m.mk_leaf(bool2dep.find(c))); } } in->assert_expr(m.mk_false(), pr, lcore); result.push_back(in.get()); - core = lcore; + in->set(dependency_converter::unit(lcore)); break; } case l_undef: if (m.canceled()) { throw tactic_exception(Z3_CANCELED_MSG); } - throw tactic_exception(local_solver->reason_unknown().c_str()); + model_converter_ref mc; + mc = local_solver->get_model_converter(); + mc = concat(fmc.get(), mc.get()); + in->reset(); + in->add(mc.get()); + unsigned sz = local_solver->get_num_assertions(); + for (unsigned i = 0; i < sz; ++i) { + in->assert_expr(local_solver->get_assertion(i)); + } + result.push_back(in.get()); + break; } local_solver->collect_statistics(m_st); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.copy(m_st); } - virtual void reset_statistics() { m_st.reset(); } + void reset_statistics() override { m_st.reset(); } - virtual void cleanup() { } - virtual void reset() { cleanup(); } + void cleanup() override { } + void reset() override { cleanup(); } - virtual void set_logic(symbol const & l) {} + void set_logic(symbol const & l) override {} - virtual void set_progress_callback(progress_callback * callback) { + void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(solver2tactic, m_solver->translate(m, m_params)); } }; diff --git a/src/solver/solver2tactic.h b/src/solver/solver2tactic.h index 647a1cee4..8c6466ddb 100644 --- a/src/solver/solver2tactic.h +++ b/src/solver/solver2tactic.h @@ -20,11 +20,11 @@ Notes: #define SOLVER2TACTIC_H_ #include "tactic/tactic.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" class solver; tactic * mk_solver2tactic(solver* s); -void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, obj_map& bool2dep, ref& fmc); +void extract_clauses_and_dependencies(goal_ref const& g, expr_ref_vector& clauses, ptr_vector& assumptions, obj_map& bool2dep, ref& fmc); #endif diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index 2628380c5..db745597c 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -30,9 +30,9 @@ solver_na2as::solver_na2as(ast_manager & m): solver_na2as::~solver_na2as() {} -void solver_na2as::assert_expr(expr * t, expr * a) { +void solver_na2as::assert_expr_core2(expr * t, expr * a) { if (a == 0) { - assert_expr(t); + assert_expr_core(t); } else { SASSERT(is_uninterp_const(a)); @@ -41,7 +41,7 @@ void solver_na2as::assert_expr(expr * t, expr * a) { m_assumptions.push_back(a); expr_ref new_t(m); new_t = m.mk_implies(a, t); - assert_expr(new_t); + assert_expr_core(new_t); } } @@ -67,6 +67,12 @@ lbool solver_na2as::check_sat(unsigned num_assumptions, expr * const * assumptio return check_sat_core(m_assumptions.size(), m_assumptions.c_ptr()); } +lbool solver_na2as::check_sat_cc(const expr_ref_vector &assumptions, vector const &clauses) { + if (clauses.empty()) return check_sat(assumptions.size(), assumptions.c_ptr()); + append_assumptions app(m_assumptions, assumptions.size(), assumptions.c_ptr()); + return check_sat_cc_core(m_assumptions, clauses); +} + lbool solver_na2as::get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { append_assumptions app(m_assumptions, asms.size(), asms.c_ptr()); return get_consequences_core(m_assumptions, vars, consequences); diff --git a/src/solver/solver_na2as.h b/src/solver/solver_na2as.h index 3e726be12..d1515a206 100644 --- a/src/solver/solver_na2as.h +++ b/src/solver/solver_na2as.h @@ -32,23 +32,25 @@ class solver_na2as : public solver { void restore_assumptions(unsigned old_sz); public: solver_na2as(ast_manager & m); - virtual ~solver_na2as(); + ~solver_na2as() override; + + void assert_expr_core2(expr * t, expr * a) override; + // virtual void assert_expr_core(expr * t) = 0; - virtual void assert_expr(expr * t, expr * a); - virtual void assert_expr(expr * t) = 0; - // Subclasses of solver_na2as should redefine the following *_core methods instead of these ones. - virtual lbool check_sat(unsigned num_assumptions, expr * const * assumptions); - virtual void push(); - virtual void pop(unsigned n); - virtual unsigned get_scope_level() const; - - virtual unsigned get_num_assumptions() const { return m_assumptions.size(); } - virtual expr * get_assumption(unsigned idx) const { return m_assumptions[idx]; } - virtual lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences); - virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes); + lbool check_sat(unsigned num_assumptions, expr * const * assumptions) override; + lbool check_sat_cc(const expr_ref_vector &assumptions, vector const &clauses) override; + void push() override; + void pop(unsigned n) override; + unsigned get_scope_level() const override; + + unsigned get_num_assumptions() const override { return m_assumptions.size(); } + expr * get_assumption(unsigned idx) const override { return m_assumptions[idx]; } + lbool get_consequences(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override; + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override; protected: virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) = 0; + virtual lbool check_sat_cc_core(const expr_ref_vector &assumptions, vector const &clauses) { NOT_IMPLEMENTED_YET(); } virtual void push_core() = 0; virtual void pop_core(unsigned n) = 0; }; diff --git a/src/solver/solver_pool.cpp b/src/solver/solver_pool.cpp index c8899e365..9dacfb5ce 100644 --- a/src/solver/solver_pool.cpp +++ b/src/solver/solver_pool.cpp @@ -32,8 +32,11 @@ class pool_solver : public solver_na2as { expr_ref_vector m_flat; bool m_pushed; bool m_in_delayed_scope; + bool m_dump_benchmarks; + double m_dump_threshold; unsigned m_dump_counter; + bool is_virtual() const { return !m.is_true(m_pred); } public: pool_solver(solver* b, solver_pool& pool, app_ref& pred): @@ -47,13 +50,16 @@ public: m_flat(m), m_pushed(false), m_in_delayed_scope(false), + m_dump_benchmarks(false), + m_dump_threshold(5.0), m_dump_counter(0) { if (is_virtual()) { - solver_na2as::assert_expr(m.mk_true(), pred); + solver_na2as::assert_expr_core2(m.mk_true(), pred); } + updt_params(m_base->get_params()); } - virtual ~pool_solver() { + ~pool_solver() override { if (m_pushed) pop(get_scope_level()); if (is_virtual()) { m_pred = m.mk_not(m_pred); @@ -63,26 +69,36 @@ public: solver* base_solver() { return m_base.get(); } - virtual solver* translate(ast_manager& m, params_ref const& p) { UNREACHABLE(); return nullptr; } - virtual void updt_params(params_ref const& p) { solver::updt_params(p); m_base->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { m_base->collect_param_descrs(r); } - virtual void collect_statistics(statistics & st) const { m_base->collect_statistics(st); } + solver* translate(ast_manager& m, params_ref const& p) override { UNREACHABLE(); return nullptr; } + void updt_params(params_ref const& p) override { + solver::updt_params(p); m_base->updt_params(p); + m_dump_benchmarks = solver::get_params().get_bool("dump_benchmarks", false); + m_dump_threshold = solver::get_params().get_double("dump_threshold", 5.0); + } + void push_params() override {m_base->push_params();} + void pop_params() override {m_base->pop_params();} - virtual void get_unsat_core(ptr_vector & r) { + void collect_param_descrs(param_descrs & r) override { m_base->collect_param_descrs(r); } + void collect_statistics(statistics & st) const override { m_base->collect_statistics(st); } + unsigned get_num_assertions() const override { return m_base->get_num_assertions(); } + expr * get_assertion(unsigned idx) const override { return m_base->get_assertion(idx); } + + void get_unsat_core(expr_ref_vector& r) override { m_base->get_unsat_core(r); unsigned j = 0; - for (unsigned i = 0; i < r.size(); ++i) - if (m_pred != r[i]) - r[j++] = r[i]; + for (unsigned i = 0; i < r.size(); ++i) + if (m_pred != r.get(i)) + r[j++] = r.get(i); r.shrink(j); } - virtual unsigned get_num_assumptions() const { + unsigned get_num_assumptions() const override { unsigned sz = solver_na2as::get_num_assumptions(); return is_virtual() ? sz - 1 : sz; } - virtual proof * get_proof() { + + proof * get_proof() override { scoped_watch _t_(m_pool.m_proof_watch); if (!m_proof.get()) { elim_aux_assertions pc(m_pred); @@ -101,7 +117,7 @@ public: } } - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { SASSERT(!m_pushed || get_scope_level() > 0); m_proof.reset(); scoped_watch _t_(m_pool.m_check_watch); @@ -125,40 +141,48 @@ public: break; } set_status(res); - - if (false /*m_dump_benchmarks && sw.get_seconds() >= m_pool.fparams().m_dump_min_time*/) { - std::stringstream file_name; - file_name << "virt_solver"; - if (is_virtual()) { file_name << "_" << m_pred->get_decl()->get_name(); } - file_name << "_" << (m_dump_counter++) << ".smt2"; - - std::ofstream out(file_name.str().c_str()); - if (!out) { verbose_stream() << "could not open file " << file_name.str() << " for output\n"; } - - out << "(set-info :status "; - switch (res) { - case l_true: out << "sat"; break; - case l_false: out << "unsat"; break; - case l_undef: out << "unknown"; break; - } - out << ")\n"; - m_base->display(out, num_assumptions, assumptions); - out << "(check-sat"; - for (unsigned i = 0; i < num_assumptions; ++i) { - out << " " << mk_pp(assumptions[i], m); - } - out << ")"; - out << "(exit)\n"; - ::statistics st; - m_base->collect_statistics(st); - st.update("time", sw.get_seconds()); - st.display_smt2(out); - out.close(); + + if (m_dump_benchmarks && sw.get_seconds() >= m_dump_threshold) { + expr_ref_vector cube(m, num_assumptions, assumptions); + vector clauses; + dump_benchmark(cube, clauses, res, sw.get_seconds()); } return res; } - virtual void push_core() { + lbool check_sat_cc_core(expr_ref_vector const & cube, + vector const & clauses) override { + SASSERT(!m_pushed || get_scope_level() > 0); + m_proof.reset(); + scoped_watch _t_(m_pool.m_check_watch); + m_pool.m_stats.m_num_checks++; + + stopwatch sw; + sw.start(); + internalize_assertions(); + lbool res = m_base->check_sat_cc(cube, clauses); + sw.stop(); + switch (res) { + case l_true: + m_pool.m_check_sat_watch.add(sw); + m_pool.m_stats.m_num_sat_checks++; + break; + case l_undef: + m_pool.m_check_undef_watch.add(sw); + m_pool.m_stats.m_num_undef_checks++; + break; + default: + break; + } + set_status(res); + + if (m_dump_benchmarks && sw.get_seconds() >= m_dump_threshold) { + dump_benchmark(cube, clauses, res, sw.get_seconds()); + } + return res; + } + + void push_core() override { SASSERT(!m_pushed || get_scope_level() > 0); if (m_in_delayed_scope) { // second push @@ -167,41 +191,41 @@ public: m_pushed = true; m_in_delayed_scope = false; } - - if (!m_pushed) { - m_in_delayed_scope = true; + + if (!m_pushed) { + m_in_delayed_scope = true; } else { - SASSERT(m_pushed); SASSERT(!m_in_delayed_scope); m_base->push(); } } - virtual void pop_core(unsigned n) { - SASSERT(!m_pushed || get_scope_level() > 0); + void pop_core(unsigned n) override { + unsigned lvl = get_scope_level(); + SASSERT(!m_pushed || lvl > 0); if (m_pushed) { SASSERT(!m_in_delayed_scope); m_base->pop(n); - m_pushed = get_scope_level() - n > 0; - } - else { - m_in_delayed_scope = get_scope_level() - n > 0; + m_pushed = lvl - n > 0; + } + else { + m_in_delayed_scope = lvl - n > 0; } } - - virtual void assert_expr(expr * e) { + + void assert_expr_core(expr * e) override { SASSERT(!m_pushed || get_scope_level() > 0); - if (m.is_true(e)) return; + if (m.is_true(e)) return; if (m_in_delayed_scope) { internalize_assertions(); m_base->push(); m_pushed = true; m_in_delayed_scope = false; } - + if (m_pushed) { - m_base->assert_expr(e); + m_base->assert_expr(e); } else { m_flat.push_back(e); @@ -209,20 +233,22 @@ public: m_assertions.append(m_flat); m_flat.reset(); } - } + } - virtual void get_model(model_ref & _m) { m_base->get_model(_m); } + void get_model_core(model_ref & _m) override { m_base->get_model_core(_m); } - virtual expr * get_assumption(unsigned idx) const { + expr * get_assumption(unsigned idx) const override { return solver_na2as::get_assumption(idx + is_virtual()); } - virtual std::string reason_unknown() const { return m_base->reason_unknown(); } - virtual void set_reason_unknown(char const* msg) { return m_base->set_reason_unknown(msg); } - virtual void get_labels(svector & r) { return m_base->get_labels(r); } - virtual void set_progress_callback(progress_callback * callback) { m_base->set_progress_callback(callback); } + std::string reason_unknown() const override { return m_base->reason_unknown(); } + void set_reason_unknown(char const* msg) override { return m_base->set_reason_unknown(msg); } + void get_labels(svector & r) override { return m_base->get_labels(r); } + void set_progress_callback(progress_callback * callback) override { m_base->set_progress_callback(callback); } - virtual ast_manager& get_manager() const { return m_base->get_manager(); } + expr_ref_vector cube(expr_ref_vector& vars, unsigned ) override { return expr_ref_vector(m); } + + ast_manager& get_manager() const override { return m_base->get_manager(); } void refresh(solver* new_base) { SASSERT(!m_pushed); @@ -236,13 +262,65 @@ public: m_assertions.reset(); m_pool.refresh(m_base.get()); } + +private: + + void dump_benchmark(const expr_ref_vector &cube, vector const & clauses, + lbool last_status, double last_time) { + std::string file_name = mk_file_name(); + std::ofstream out(file_name); + if (!out) { + IF_VERBOSE(0, verbose_stream() << "could not open file " << file_name << " for output\n"); + return; + } + + out << "(set-info :status " << lbool2status(last_status) << ")\n"; + m_base->display(out, cube.size(), cube.c_ptr()); + for (auto const& clause : clauses) { + out << ";; extra clause\n"; + out << "(assert (or "; + for (auto *lit : clause) out << mk_pp(lit, m) << " "; + out << "))\n"; + } + + out << "(check-sat"; + for (auto * lit : cube) out << " " << mk_pp(lit, m); + out << ")\n"; + + out << "(exit)\n"; + ::statistics st; + m_base->collect_statistics(st); + st.update("time", last_time); + st.display_smt2(out); + out.close(); + } + + char const* lbool2status(lbool r) const { + switch (r) { + case l_true: return "sat"; + case l_false: return "unsat"; + case l_undef: return "unknown"; + } + return "?"; + } + + std::string mk_file_name() { + std::stringstream file_name; + file_name << "pool_solver"; + if (is_virtual()) file_name << "_" << m_pred->get_decl()->get_name(); + file_name << "_" << (m_dump_counter++) << ".smt2"; + return file_name.str(); + } + }; -solver_pool::solver_pool(solver* base_solver, unsigned num_solvers_per_pool): +solver_pool::solver_pool(solver* base_solver, unsigned num_pools): m_base_solver(base_solver), - m_num_solvers_per_pool(num_solvers_per_pool), - m_num_solvers_in_last_pool(0) -{} + m_num_pools(num_pools), + m_current_pool(0) +{ + SASSERT(num_pools > 0); +} ptr_vector solver_pool::get_base_solvers() const { @@ -256,9 +334,13 @@ ptr_vector solver_pool::get_base_solvers() const { return solvers; } +void solver_pool::updt_params(const params_ref &p) { + m_base_solver->updt_params(p); + for (solver *s : m_solvers) s->updt_params(p); +} void solver_pool::collect_statistics(statistics &st) const { ptr_vector solvers = get_base_solvers(); - for (solver* s : solvers) s->collect_statistics(st); + for (solver* s : solvers) s->collect_statistics(st); st.update("time.pool_solver.smt.total", m_check_watch.get_seconds()); st.update("time.pool_solver.smt.total.sat", m_check_sat_watch.get_seconds()); st.update("time.pool_solver.smt.total.undef", m_check_undef_watch.get_seconds()); @@ -272,7 +354,7 @@ void solver_pool::reset_statistics() { #if 0 ptr_vector solvers = get_base_solvers(); for (solver* s : solvers) { - s->reset_statistics(); + s->reset_statistics(); } #endif m_stats.reset(); @@ -282,17 +364,23 @@ void solver_pool::reset_statistics() { m_proof_watch.reset(); } +/** + \brief Create a fresh solver instance. + The first num_pools solvers are independent and + use a fresh instance of the base solver. + Subsequent solvers reuse the first num_polls base solvers, rotating + among the first num_pools. +*/ solver* solver_pool::mk_solver() { ref base_solver; ast_manager& m = m_base_solver->get_manager(); - if (m_solvers.empty() || m_num_solvers_in_last_pool == m_num_solvers_per_pool) { + if (m_solvers.size() < m_num_pools) { base_solver = m_base_solver->translate(m, m_base_solver->get_params()); - m_num_solvers_in_last_pool = 0; } else { - base_solver = dynamic_cast(m_solvers.back())->base_solver(); + solver* s = m_solvers[(m_current_pool++) % m_num_pools]; + base_solver = dynamic_cast(s)->base_solver(); } - m_num_solvers_in_last_pool++; std::stringstream name; name << "vsolver#" << m_solvers.size(); app_ref pred(m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort()), m); @@ -304,7 +392,7 @@ solver* solver_pool::mk_solver() { void solver_pool::reset_solver(solver* s) { pool_solver* ps = dynamic_cast(s); SASSERT(ps); - if (ps) ps->reset(); + if (ps) ps->reset(); } void solver_pool::refresh(solver* base_solver) { diff --git a/src/solver/solver_pool.h b/src/solver/solver_pool.h index d676ca54d..42c13fc58 100644 --- a/src/solver/solver_pool.h +++ b/src/solver/solver_pool.h @@ -16,7 +16,10 @@ Author: Notes: - This is a revision of spacer_virtual_solver by Arie Gurfinkel + This is a revision of spacer_virtual_solver + consolidated with spacer_smt_context_manager + by Arie Gurfinkel + Use this module as a replacement for spacer_smt_context_manager. --*/ #ifndef SOLVER_POOL_H_ @@ -39,11 +42,11 @@ class solver_pool { void reset() { memset(this, 0, sizeof(*this)); } }; - ref m_base_solver; - unsigned m_num_solvers_per_pool; - unsigned m_num_solvers_in_last_pool; + ref m_base_solver; + unsigned m_num_pools; + unsigned m_current_pool; sref_vector m_solvers; - stats m_stats; + stats m_stats; stopwatch m_check_watch; stopwatch m_check_sat_watch; @@ -55,7 +58,7 @@ class solver_pool { ptr_vector get_base_solvers() const; public: - solver_pool(solver* base_solver, unsigned num_solvers_per_pool); + solver_pool(solver* base_solver, unsigned num_pools); void collect_statistics(statistics &st) const; void reset_statistics(); @@ -63,6 +66,8 @@ public: solver* mk_solver(); void reset_solver(solver* s); + void updt_params(const params_ref &p); + }; diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index a24f1d4c7..94509e3f6 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -36,6 +36,7 @@ class tactic2solver : public solver_na2as { unsigned_vector m_scopes; ref m_result; tactic_ref m_tactic; + ref m_mc; symbol m_logic; bool m_produce_models; bool m_produce_proofs; @@ -44,36 +45,42 @@ class tactic2solver : public solver_na2as { public: tactic2solver(ast_manager & m, tactic * t, params_ref const & p, bool produce_proofs, bool produce_models, bool produce_unsat_cores, symbol const & logic); - virtual ~tactic2solver(); + ~tactic2solver() override; - virtual solver* translate(ast_manager& m, params_ref const& p); + solver* translate(ast_manager& m, params_ref const& p) override; - virtual void updt_params(params_ref const & p); - virtual void collect_param_descrs(param_descrs & r); + void updt_params(params_ref const & p) override; + void collect_param_descrs(param_descrs & r) override; - virtual void set_produce_models(bool f) { m_produce_models = f; } + void set_produce_models(bool f) override { m_produce_models = f; } - virtual void assert_expr(expr * t); + void assert_expr_core(expr * t) override; + ast_manager& get_manager() const override; - virtual void push_core(); - virtual void pop_core(unsigned n); - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions); + void push_core() override; + void pop_core(unsigned n) override; + lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override; + + void collect_statistics(statistics & st) const override; + void get_unsat_core(expr_ref_vector & r) override; + void get_model_core(model_ref & m) override; + proof * get_proof() override; + std::string reason_unknown() const override; + void set_reason_unknown(char const* msg) override; + void get_labels(svector & r) override {} + + void set_progress_callback(progress_callback * callback) override {} + + unsigned get_num_assertions() const override; + expr * get_assertion(unsigned idx) const override; - virtual void collect_statistics(statistics & st) const; - virtual void get_unsat_core(ptr_vector & r); - virtual void get_model(model_ref & m); - virtual proof * get_proof(); - virtual std::string reason_unknown() const; - virtual void set_reason_unknown(char const* msg); - virtual void get_labels(svector & r) {} + expr_ref_vector cube(expr_ref_vector& vars, unsigned ) override { + return expr_ref_vector(get_manager()); + } - virtual void set_progress_callback(progress_callback * callback) {} + model_converter_ref get_model_converter() const override { return m_mc; } - virtual unsigned get_num_assertions() const; - virtual expr * get_assertion(unsigned idx) const; - - virtual ast_manager& get_manager() const; }; ast_manager& tactic2solver::get_manager() const { return m_assertions.get_manager(); } @@ -103,14 +110,15 @@ void tactic2solver::collect_param_descrs(param_descrs & r) { m_tactic->collect_param_descrs(r); } -void tactic2solver::assert_expr(expr * t) { +void tactic2solver::assert_expr_core(expr * t) { m_assertions.push_back(t); - m_result = 0; + m_result = nullptr; } + void tactic2solver::push_core() { m_scopes.push_back(m_assertions.size()); - m_result = 0; + m_result = nullptr; } void tactic2solver::pop_core(unsigned n) { @@ -118,11 +126,11 @@ void tactic2solver::pop_core(unsigned n) { unsigned old_sz = m_scopes[new_lvl]; m_assertions.shrink(old_sz); m_scopes.shrink(new_lvl); - m_result = 0; + m_result = nullptr; } lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * assumptions) { - if (m_tactic.get() == 0) + if (m_tactic.get() == nullptr) return l_false; ast_manager & m = m_assertions.m(); m_result = alloc(simple_check_sat_result, m); @@ -142,7 +150,7 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass } model_ref md; - proof_ref pr(m); + proof_ref pr(m); expr_dependency_ref core(m); std::string reason_unknown = "unknown"; labels_vec labels; @@ -158,8 +166,14 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass m_result->set_status(l_undef); if (reason_unknown != "") m_result->m_unknown = reason_unknown; + if (num_assumptions == 0) { + m_assertions.reset(); + g->get_formulas(m_assertions); + } break; } + m_mc = g->mc(); + TRACE("tactic", if (m_mc) m_mc->display(tout);); } catch (z3_error & ex) { TRACE("tactic2solver", tout << "exception: " << ex.msg() << "\n";); @@ -187,7 +201,7 @@ lbool tactic2solver::check_sat_core(unsigned num_assumptions, expr * const * ass solver* tactic2solver::translate(ast_manager& m, params_ref const& p) { tactic* t = m_tactic->translate(m); tactic2solver* r = alloc(tactic2solver, m, t, p, m_produce_proofs, m_produce_models, m_produce_unsat_cores, m_logic); - r->m_result = 0; + r->m_result = nullptr; if (!m_scopes.empty()) { throw default_exception("translation of contexts is only supported at base level"); } @@ -205,22 +219,23 @@ void tactic2solver::collect_statistics(statistics & st) const { //SASSERT(m_stats.size() > 0); } -void tactic2solver::get_unsat_core(ptr_vector & r) { +void tactic2solver::get_unsat_core(expr_ref_vector & r) { if (m_result.get()) { m_result->get_unsat_core(r); } } -void tactic2solver::get_model(model_ref & m) { - if (m_result.get()) - m_result->get_model(m); +void tactic2solver::get_model_core(model_ref & m) { + if (m_result.get()) { + m_result->get_model_core(m); + } } proof * tactic2solver::get_proof() { if (m_result.get()) return m_result->get_proof(); else - return 0; + return nullptr; } std::string tactic2solver::reason_unknown() const { @@ -261,9 +276,9 @@ public: tactic2solver_factory(tactic * t):m_tactic(t) { } - virtual ~tactic2solver_factory() {} + ~tactic2solver_factory() override {} - virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { return mk_tactic2solver(m, m_tactic.get(), p, proofs_enabled, models_enabled, unsat_core_enabled, logic); } }; @@ -274,9 +289,9 @@ public: tactic_factory2solver_factory(tactic_factory * f):m_factory(f) { } - virtual ~tactic_factory2solver_factory() {} + ~tactic_factory2solver_factory() override {} - virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { tactic * t = (*m_factory)(m, p); return mk_tactic2solver(m, t, p, proofs_enabled, models_enabled, unsat_core_enabled, logic); } diff --git a/src/solver/tactic2solver.h b/src/solver/tactic2solver.h index 2da0f69eb..ddab66dc5 100644 --- a/src/solver/tactic2solver.h +++ b/src/solver/tactic2solver.h @@ -30,7 +30,7 @@ class solver; class solver_factory; solver * mk_tactic2solver(ast_manager & m, - tactic * t = 0, + tactic * t = nullptr, params_ref const & p = params_ref(), bool produce_proofs = false, bool produce_models = true, diff --git a/src/tactic/CMakeLists.txt b/src/tactic/CMakeLists.txt index e7cfdb644..c9554b76a 100644 --- a/src/tactic/CMakeLists.txt +++ b/src/tactic/CMakeLists.txt @@ -1,8 +1,8 @@ z3_add_component(tactic SOURCES + dependency_converter.cpp equiv_proof_converter.cpp - extension_model_converter.cpp - filter_model_converter.cpp + generic_model_converter.cpp goal.cpp goal_num_occurs.cpp goal_shared_occs.cpp diff --git a/src/tactic/aig/aig.cpp b/src/tactic/aig/aig.cpp index e8226e159..faedf0e0c 100644 --- a/src/tactic/aig/aig.cpp +++ b/src/tactic/aig/aig.cpp @@ -30,13 +30,13 @@ class aig_lit { friend class aig_ref; aig * m_ref; public: - aig_lit(aig * n = 0):m_ref(n) {} + aig_lit(aig * n = nullptr):m_ref(n) {} aig_lit(aig_ref const & r):m_ref(static_cast(r.m_ref)) {} bool is_inverted() const { return (reinterpret_cast(m_ref) & static_cast(1)) == static_cast(1); } void invert() { m_ref = reinterpret_cast(reinterpret_cast(m_ref) ^ static_cast(1)); } aig * ptr() const { return reinterpret_cast(reinterpret_cast(m_ref) & ~static_cast(1)); } aig * ptr_non_inverted() const { SASSERT(!is_inverted()); return m_ref; } - bool is_null() const { return m_ref == 0; } + bool is_null() const { return m_ref == nullptr; } friend bool operator==(aig_lit const & r1, aig_lit const & r2) { return r1.m_ref == r2.m_ref; } friend bool operator!=(aig_lit const & r1, aig_lit const & r2) { return r1.m_ref != r2.m_ref; } aig_lit & operator=(aig_lit const & r) { m_ref = r.m_ref; return *this; } @@ -151,7 +151,7 @@ struct aig_manager::imp { m_num_aigs--; if (is_var(n)) { m_var_id_gen.recycle(n->m_id); - m_var2exprs.set(n->m_id, 0); + m_var2exprs.set(n->m_id, nullptr); } else { m_table.erase(n); @@ -267,7 +267,7 @@ struct aig_manager::imp { } if (b == r) { if (sign1) { - // subsitution + // substitution // not (a and b) and r --> (not a) and r IF b == r l = a; l.invert(); @@ -490,7 +490,6 @@ struct aig_manager::imp { case OP_NOT: case OP_OR: case OP_AND: - case OP_IFF: case OP_XOR: case OP_IMPLIES: case OP_ITE: @@ -582,9 +581,6 @@ struct aig_manager::imp { SASSERT(m.m().is_bool(fr.m_t->get_arg(0))); mk_iff(fr.m_spos); break; - case OP_IFF: - mk_iff(fr.m_spos); - break; case OP_XOR: mk_xor(fr.m_spos); break; @@ -635,10 +631,8 @@ struct aig_manager::imp { } bool check_cache() const { - obj_map::iterator it = m_cache.begin(); - obj_map::iterator end = m_cache.end(); - for (; it != end; ++it) { - SASSERT(ref_count(it->m_value) > 0); + for (auto const& kv : m_cache) { + VERIFY(ref_count(kv.m_value) > 0); } return true; } @@ -797,7 +791,7 @@ struct aig_manager::imp { m_cache.resize(idx+1); return false; } - return m_cache.get(idx) != 0; + return m_cache.get(idx) != nullptr; } void cache_result(aig * n, expr * t) { @@ -960,14 +954,14 @@ struct aig_manager::imp { } unsigned idx = to_idx(t); cache.reserve(idx+1); - if (cache.get(idx) != 0) { + if (cache.get(idx) != nullptr) { todo.pop_back(); continue; } bool ok = true; for (unsigned i = 0; i < 2; i++) { aig * c = t->m_children[i].ptr(); - if (!is_var(c) && cache.get(to_idx(c), 0) == 0) { + if (!is_var(c) && cache.get(to_idx(c), nullptr) == nullptr) { todo.push_back(c); ok = false; } @@ -981,7 +975,7 @@ struct aig_manager::imp { if (is_var(c)) args[i] = m.m_var2exprs.get(c->m_id); else - args[i] = cache.get(to_idx(c), 0); + args[i] = cache.get(to_idx(c), nullptr); if (!l.is_inverted()) args[i] = invert(args[i]); } @@ -1009,16 +1003,16 @@ struct aig_manager::imp { aig_lit n = roots.back(); roots.pop_back(); if (n.is_inverted()) { - g.assert_expr(invert(process_root(n.ptr())), 0, 0); + g.assert_expr(invert(process_root(n.ptr())), nullptr, nullptr); continue; } aig * p = n.ptr(); if (m.is_ite(p)) { - g.assert_expr(process_root(p), 0, 0); + g.assert_expr(process_root(p), nullptr, nullptr); continue; } if (is_var(p)) { - g.assert_expr(m.var2expr(p), 0, 0); + g.assert_expr(m.var2expr(p), nullptr, nullptr); continue; } roots.push_back(left(p)); @@ -1081,7 +1075,7 @@ struct aig_manager::imp { bool visit(aig * p) { if (is_var(p)) { - push_result(0); + push_result(nullptr); return true; } if (is_cached(p)) @@ -1522,7 +1516,7 @@ public: } SASSERT(ref_count(r) >= 1); } - catch (aig_exception ex) { + catch (const aig_exception & ex) { dec_ref(r); throw ex; } @@ -1654,8 +1648,8 @@ public: aig_ref::aig_ref(): - m_manager(0), - m_ref(0) { + m_manager(nullptr), + m_ref(nullptr) { } aig_ref::aig_ref(aig_manager & m, aig_lit const & l): @@ -1665,15 +1659,15 @@ aig_ref::aig_ref(aig_manager & m, aig_lit const & l): } aig_ref::~aig_ref() { - if (m_ref != 0) { + if (m_ref != nullptr) { m_manager->m_imp->dec_ref(aig_lit(*this)); } } aig_ref & aig_ref::operator=(aig_ref const & r) { - if (r.m_ref != 0) + if (r.m_ref != nullptr) r.m_manager->m_imp->inc_ref(aig_lit(r)); - if (m_ref != 0) + if (m_ref != nullptr) m_manager->m_imp->dec_ref(aig_lit(*this)); m_ref = r.m_ref; m_manager = r.m_manager; diff --git a/src/tactic/aig/aig_tactic.cpp b/src/tactic/aig/aig_tactic.cpp index 37ffc6124..720c799e0 100644 --- a/src/tactic/aig/aig_tactic.cpp +++ b/src/tactic/aig/aig_tactic.cpp @@ -37,16 +37,16 @@ class aig_tactic : public tactic { ~mk_aig_manager() { dealloc(m_owner.m_aig_manager); - m_owner.m_aig_manager = 0; + m_owner.m_aig_manager = nullptr; } }; public: - aig_tactic(params_ref const & p = params_ref()):m_aig_manager(0) { + aig_tactic(params_ref const & p = params_ref()):m_aig_manager(nullptr) { updt_params(p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { aig_tactic * t = alloc(aig_tactic); t->m_max_memory = m_max_memory; t->m_aig_gate_encoding = m_aig_gate_encoding; @@ -54,20 +54,19 @@ public: return t; } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); m_aig_gate_encoding = p.get_bool("aig_default_gate_encoding", true); m_aig_per_assertion = p.get_bool("aig_per_assertion", true); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); r.insert("aig_per_assertion", CPK_BOOL, "(default: true) process one assertion at a time."); } void operator()(goal_ref const & g) { SASSERT(g->is_well_sorted()); - tactic_report report("aig", *g); mk_aig_manager mk(*this, g->m()); if (m_aig_per_assertion) { @@ -77,7 +76,7 @@ public: expr_ref new_f(g->m()); m_aig_manager->to_formula(r, new_f); expr_dependency * ed = g->dep(i); - g->update(i, new_f, 0, ed); + g->update(i, new_f, nullptr, ed); } } else { @@ -90,19 +89,16 @@ public: SASSERT(g->is_well_sorted()); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer & result) override { fail_if_proof_generation("aig", g); - mc = 0; pc = 0; core = 0; + tactic_report report("aig", *g); operator()(g); g->inc_depth(); + TRACE("aig", g->display(tout);); result.push_back(g.get()); } - virtual void cleanup() {} + void cleanup() override {} }; diff --git a/src/tactic/arith/CMakeLists.txt b/src/tactic/arith/CMakeLists.txt index 335020838..cb025b206 100644 --- a/src/tactic/arith/CMakeLists.txt +++ b/src/tactic/arith/CMakeLists.txt @@ -9,7 +9,6 @@ z3_add_component(arith_tactics card2bv_tactic.cpp degree_shift_tactic.cpp diff_neq_tactic.cpp - elim01_tactic.cpp eq2bv_tactic.cpp factor_tactic.cpp fix_dl_var_tactic.cpp @@ -33,7 +32,6 @@ z3_add_component(arith_tactics card2bv_tactic.h degree_shift_tactic.h diff_neq_tactic.h - elim01_tactic.h eq2bv_tactic.h factor_tactic.h fix_dl_var_tactic.h diff --git a/src/tactic/arith/add_bounds_tactic.cpp b/src/tactic/arith/add_bounds_tactic.cpp index ac105eb06..b1367ea21 100644 --- a/src/tactic/arith/add_bounds_tactic.cpp +++ b/src/tactic/arith/add_bounds_tactic.cpp @@ -48,10 +48,10 @@ bool is_unbounded(goal const & g) { class is_unbounded_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_unbounded(g); } - virtual ~is_unbounded_probe() {} + ~is_unbounded_probe() override {} }; probe * mk_is_unbounded_probe() { @@ -111,11 +111,7 @@ class add_bounds_tactic : public tactic { }; void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; + goal_ref_buffer & result) { tactic_report report("add-bounds", *g); bound_manager bm(m); expr_fast_mark1 visited; @@ -142,33 +138,30 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(add_bounds_tactic, m, m_params); } - virtual ~add_bounds_tactic() { + ~add_bounds_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("add_bound_lower", CPK_NUMERAL, "(default: -2) lower bound to be added to unbounded variables."); r.insert("add_bound_upper", CPK_NUMERAL, "(default: 2) upper bound to be added to unbounded variables."); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(g, result, mc, pc, core); + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { + (*m_imp)(g, result); } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); diff --git a/src/tactic/arith/arith_bounds_tactic.cpp b/src/tactic/arith/arith_bounds_tactic.cpp index b750689b1..e4af53f23 100644 --- a/src/tactic/arith/arith_bounds_tactic.cpp +++ b/src/tactic/arith/arith_bounds_tactic.cpp @@ -23,15 +23,12 @@ struct arith_bounds_tactic : public tactic { ast_manager& get_manager() { return m; } - virtual void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) { + void operator()(/* in */ goal_ref const & in, + /* out */ goal_ref_buffer & result) override { bounds_arith_subsumption(in, result); } - virtual tactic* translate(ast_manager & mgr) { + tactic* translate(ast_manager & mgr) override { return alloc(arith_bounds_tactic, mgr); } @@ -50,7 +47,7 @@ struct arith_bounds_tactic : public tactic { void mk_proof(proof_ref& pr, goal_ref const& s, unsigned i, unsigned j) { if (s->proofs_enabled()) { - proof* th_lemma = m.mk_th_lemma(a.get_family_id(), m.mk_implies(s->form(i), s->form(j)), 0, 0); + proof* th_lemma = m.mk_th_lemma(a.get_family_id(), m.mk_implies(s->form(i), s->form(j)), 0, nullptr); pr = m.mk_modus_ponens(s->pr(i), th_lemma); } } @@ -146,7 +143,7 @@ struct arith_bounds_tactic : public tactic { TRACE("arith_subsumption", s->display(tout); ); } - virtual void cleanup() {} + void cleanup() override {} }; diff --git a/src/tactic/arith/bound_manager.cpp b/src/tactic/arith/bound_manager.cpp index da507ea10..7f662810b 100644 --- a/src/tactic/arith/bound_manager.cpp +++ b/src/tactic/arith/bound_manager.cpp @@ -28,6 +28,19 @@ bound_manager::~bound_manager() { reset(); } +bound_manager* bound_manager::translate(ast_manager& dst_m) { + bound_manager* result = alloc(bound_manager, dst_m); + ast_translation tr(m(), dst_m); + expr_dependency_translation edtr(tr); + for (auto& kv : m_lowers) result->m_lowers.insert(tr(kv.m_key), kv.m_value); + for (auto& kv : m_uppers) result->m_uppers.insert(tr(kv.m_key), kv.m_value); + for (auto& kv : m_lower_deps) result->m_lower_deps.insert(tr(kv.m_key), edtr(kv.m_value)); + for (auto& kv : m_upper_deps) result->m_upper_deps.insert(tr(kv.m_key), edtr(kv.m_value)); + for (expr* e : m_bounded_vars) result->m_bounded_vars.push_back(tr(e)); + return result; +} + + static decl_kind swap_decl(decl_kind k) { switch (k) { case OP_LE: return OP_GE; @@ -201,22 +214,22 @@ bool bound_manager::is_disjunctive_bound(expr * f, expr_dependency * d) { if (!m().is_or(f)) return false; unsigned sz = to_app(f)->get_num_args(); if (sz == 0) return false; - expr * x, * y, * v = 0; + expr * x, * y, * v = nullptr; bool is_int; for (unsigned i = 0; i < sz; ++i) { expr * e = to_app(f)->get_arg(i); if (!m().is_eq(e, x, y)) return false; if (is_uninterp_const(x) && is_numeral(y, n, is_int) && is_int && - (x == v || v == 0)) { - if (v == 0) { v = x; lo = hi = n; } + (x == v || v == nullptr)) { + if (v == nullptr) { v = x; lo = hi = n; } if (n < lo) lo = n; if (n > hi) hi = n; } else if (is_uninterp_const(y) && is_numeral(x, n, is_int) && is_int && - (y == v || v == 0)) { - if (v == 0) { v = y; lo = hi = n; } + (y == v || v == nullptr)) { + if (v == nullptr) { v = y; lo = hi = n; } if (n < lo) lo = n; if (n > hi) hi = n; } diff --git a/src/tactic/arith/bound_manager.h b/src/tactic/arith/bound_manager.h index a08a53614..5a22778d4 100644 --- a/src/tactic/arith/bound_manager.h +++ b/src/tactic/arith/bound_manager.h @@ -47,10 +47,12 @@ public: bound_manager(ast_manager & m); ~bound_manager(); + bound_manager* translate(ast_manager& dst_m); + ast_manager & m() const { return m_util.get_manager(); } void operator()(goal const & g); - void operator()(expr * n, expr_dependency * d = 0); + void operator()(expr * n, expr_dependency * d = nullptr); bool has_lower(expr * c, numeral & v, bool & strict) const { limit l; @@ -76,14 +78,14 @@ public: expr_dependency * d; if (m_lower_deps.find(c, d)) return d; - return 0; + return nullptr; } expr_dependency * upper_dep(expr * c) const { expr_dependency * d; if (m_upper_deps.find(c, d)) return d; - return 0; + return nullptr; } bool has_lower(expr * c) const { diff --git a/src/tactic/arith/bound_propagator.cpp b/src/tactic/arith/bound_propagator.cpp index 51499674b..4ddfe9721 100644 --- a/src/tactic/arith/bound_propagator.cpp +++ b/src/tactic/arith/bound_propagator.cpp @@ -182,7 +182,7 @@ void bound_propagator::mk_eq(unsigned sz, mpz * as, var * xs) { } void bound_propagator::init_eq(linear_equation * eq) { - if (eq == 0) + if (eq == nullptr) return; unsigned c_idx = m_constraints.size(); m_constraints.push_back(constraint()); @@ -383,7 +383,7 @@ bool bound_propagator::relevant_bound(var x, double new_k) const { if (LOWER && has_lower(x)) tout << "old: " << m.to_string(m_lowers[x]->m_k) << " | " << m_lowers[x]->m_approx_k << "\n"; if (!LOWER && has_upper(x)) tout << "old: " << m.to_string(m_uppers[x]->m_k) << " | " << m_uppers[x]->m_approx_k << "\n";); bound * b = LOWER ? m_lowers[x] : m_uppers[x]; - if (b == 0) + if (b == nullptr) return true; // variable did not have a bound double interval_size; @@ -537,7 +537,7 @@ bool bound_propagator::propagate_eq(unsigned c_idx) { bound * u_i = m_uppers[x_i]; if (a_i < 0.0) { if (!ll_failed) { - if (l_i == 0) { + if (l_i == nullptr) { if (ll_i == UINT_MAX) ll_i = i; else @@ -549,7 +549,7 @@ bool bound_propagator::propagate_eq(unsigned c_idx) { } if (!uu_failed) { - if (u_i == 0) { + if (u_i == nullptr) { if (uu_i == UINT_MAX) uu_i = i; else @@ -562,7 +562,7 @@ bool bound_propagator::propagate_eq(unsigned c_idx) { } else { if (!ll_failed) { - if (u_i == 0) { + if (u_i == nullptr) { if (ll_i == UINT_MAX) ll_i = i; else @@ -574,7 +574,7 @@ bool bound_propagator::propagate_eq(unsigned c_idx) { } if (!uu_failed) { - if (l_i == 0) { + if (l_i == nullptr) { if (uu_i == UINT_MAX) uu_i = i; else @@ -780,7 +780,7 @@ bool bound_propagator::upper(var x, mpq & k, bool & strict, unsigned & ts) const bound_propagator::bound * bound_propagator::bound::at(unsigned timestamp) { bound * r = this; - while (r != 0 && r->m_timestamp >= timestamp) + while (r != nullptr && r->m_timestamp >= timestamp) r = r->m_prev; return r; } diff --git a/src/tactic/arith/bv2int_rewriter.cpp b/src/tactic/arith/bv2int_rewriter.cpp index 4315b0f5f..c4c13e316 100644 --- a/src/tactic/arith/bv2int_rewriter.cpp +++ b/src/tactic/arith/bv2int_rewriter.cpp @@ -38,7 +38,7 @@ void bv2int_rewriter_ctx::collect_power2(goal const& s) { expr* f = s.form(j); if (!m.is_or(f)) continue; unsigned sz = to_app(f)->get_num_args(); - expr* x, *y, *v = 0; + expr* x, *y, *v = nullptr; rational n; vector bounds; bool is_int, ok = true; @@ -50,12 +50,12 @@ void bv2int_rewriter_ctx::collect_power2(goal const& s) { break; } if (arith.is_numeral(y, n, is_int) && is_int && - (x == v || v == 0)) { + (x == v || v == nullptr)) { v = x; bounds.push_back(n); } else if (arith.is_numeral(x, n, is_int) && is_int && - (y == v || v == 0)) { + (y == v || v == nullptr)) { v = y; bounds.push_back(n); } diff --git a/src/tactic/arith/bv2int_rewriter.h b/src/tactic/arith/bv2int_rewriter.h index 9ef256197..89a572d12 100644 --- a/src/tactic/arith/bv2int_rewriter.h +++ b/src/tactic/arith/bv2int_rewriter.h @@ -103,7 +103,7 @@ struct bv2int_rewriter_cfg : public default_rewriter_cfg { bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return m_r.mk_app_core(f, num, args, result); } bv2int_rewriter_cfg(ast_manager & m, bv2int_rewriter_ctx& ctx):m_r(m, ctx) {} diff --git a/src/tactic/arith/bv2real_rewriter.h b/src/tactic/arith/bv2real_rewriter.h index f020f9f1e..e03637453 100644 --- a/src/tactic/arith/bv2real_rewriter.h +++ b/src/tactic/arith/bv2real_rewriter.h @@ -181,7 +181,7 @@ struct bv2real_rewriter_cfg : public default_rewriter_cfg { bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return m_r.mk_app_core(f, num, args, result); } bv2real_rewriter_cfg(ast_manager & m, bv2real_util& u):m_r(m, u) {} @@ -216,7 +216,7 @@ struct bv2real_elim_rewriter_cfg : public default_rewriter_cfg { bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return m_r.mk_app_core(f, num, args, result); } bv2real_elim_rewriter_cfg(bv2real_util& u):m_r(u) {} diff --git a/src/tactic/arith/card2bv_tactic.cpp b/src/tactic/arith/card2bv_tactic.cpp index d2453eba2..97649cc2f 100644 --- a/src/tactic/arith/card2bv_tactic.cpp +++ b/src/tactic/arith/card2bv_tactic.cpp @@ -23,7 +23,7 @@ Notes: #include "ast/rewriter/pb2bv_rewriter.h" #include "ast/ast_util.h" #include "ast/ast_pp.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" class card2bv_tactic : public tactic { ast_manager & m; @@ -36,29 +36,27 @@ public: m_params(p) { } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(card2bv_tactic, m, m_params); } - virtual ~card2bv_tactic() { + ~card2bv_tactic() override { } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { + r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints for solver"); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { TRACE("card2bv-before", g->display(tout);); SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); tactic_report report("card2bv", *g); th_rewriter rw1(m, m_params); pb2bv_rewriter rw2(m, m_params); @@ -73,7 +71,7 @@ public: for (unsigned idx = 0; !g->inconsistent() && idx < g->size(); idx++) { rw1(g->form(idx), new_f1, new_pr1); TRACE("card2bv", tout << "Rewriting " << mk_ismt2_pp(new_f1.get(), m) << std::endl;); - rw2(new_f1, new_f2, new_pr2); + rw2(false, new_f1, new_f2, new_pr2); if (m.proofs_enabled()) { new_pr1 = m.mk_modus_ponens(g->pr(idx), new_pr1); new_pr1 = m.mk_modus_ponens(new_pr1, new_pr2); @@ -88,11 +86,9 @@ public: func_decl_ref_vector const& fns = rw2.fresh_constants(); if (!fns.empty()) { - filter_model_converter* filter = alloc(filter_model_converter, m); - for (unsigned i = 0; i < fns.size(); ++i) { - filter->insert(fns[i]); - } - mc = filter; + generic_model_converter* filter = alloc(generic_model_converter, m, "card2bv"); + for (func_decl* f : fns) filter->hide(f); + g->add(filter); } g->inc_depth(); @@ -101,7 +97,7 @@ public: SASSERT(g->is_well_sorted()); } - virtual void cleanup() { + void cleanup() override { } }; diff --git a/src/tactic/arith/card2bv_tactic.h b/src/tactic/arith/card2bv_tactic.h index 287dfa27e..5e2051b50 100644 --- a/src/tactic/arith/card2bv_tactic.h +++ b/src/tactic/arith/card2bv_tactic.h @@ -34,8 +34,8 @@ namespace pb { class card2bv_rewriter { public: - typedef expr* literal; - typedef ptr_vector literal_vector; + typedef expr* pliteral; + typedef ptr_vector pliteral_vector; private: ast_manager& m; arith_util au; @@ -54,7 +54,7 @@ namespace pb { bool is_and(func_decl* f); bool is_atmost1(func_decl* f, unsigned sz, expr * const* args, expr_ref& result); expr_ref mk_atmost1(unsigned sz, expr * const* args); - void mk_at_most_1_small(bool last, unsigned n, literal const* xs, expr_ref_vector& result, expr_ref_vector& ors); + void mk_at_most_1_small(bool last, unsigned n, pliteral const* xs, expr_ref_vector& result, expr_ref_vector& ors); public: card2bv_rewriter(ast_manager& m); @@ -62,15 +62,15 @@ namespace pb { void mk_assert(func_decl * f, unsigned sz, expr * const* args, expr_ref & result, expr_ref_vector& lemmas); // definitions used for sorting network - literal mk_false() { return m.mk_false(); } - literal mk_true() { return m.mk_true(); } - literal mk_max(literal a, literal b) { return trail(m.mk_or(a, b)); } - literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } - literal mk_not(literal a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } - std::ostream& pp(std::ostream& out, literal lit); - literal fresh(); - literal trail(literal l); - void mk_clause(unsigned n, literal const* lits); + pliteral mk_false() { return m.mk_false(); } + pliteral mk_true() { return m.mk_true(); } + pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } + pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } + pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } + std::ostream& pp(std::ostream& out, pliteral lit); + pliteral fresh(); + pliteral trail(pliteral l); + void mk_clause(unsigned n, pliteral const* lits); }; @@ -79,7 +79,7 @@ namespace pb { bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return m_r.mk_app_core(f, num, args, result); } card2bv_rewriter_cfg(ast_manager & m):m_r(m) {} diff --git a/src/tactic/arith/degree_shift_tactic.cpp b/src/tactic/arith/degree_shift_tactic.cpp index 6344c4c51..c15285703 100644 --- a/src/tactic/arith/degree_shift_tactic.cpp +++ b/src/tactic/arith/degree_shift_tactic.cpp @@ -20,8 +20,7 @@ Revision History: --*/ #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "util/cooperate.h" #include "ast/arith_decl_plugin.h" #include "tactic/core/simplify_tactic.h" @@ -94,7 +93,7 @@ class degree_shift_tactic : public tactic { m_autil(_m), m_pinned(_m), m_one(1), - m_rw(0) { + m_rw(nullptr) { } @@ -127,9 +126,7 @@ class degree_shift_tactic : public tactic { void visit_args(expr * t, expr_fast_mark1 & visited) { if (is_app(t)) { - unsigned num_args = to_app(t)->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = to_app(t)->get_arg(i); + for (expr * arg : *to_app(t)) { save_degree(arg, m_one); visit(arg, visited); } @@ -166,11 +163,9 @@ class degree_shift_tactic : public tactic { void display_candidates(std::ostream & out) { out << "candidates:\n"; - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - if (!it->m_value.is_one()) { - out << "POWER: " << it->m_value << "\n" << mk_ismt2_pp(it->m_key, m) << "\n"; + for (auto const& kv : m_var2degree) { + if (!kv.m_value.is_one()) { + out << "POWER: " << kv.m_value << "\n" << mk_ismt2_pp(kv.m_key, m) << "\n"; } } } @@ -189,63 +184,52 @@ class degree_shift_tactic : public tactic { void discard_non_candidates() { m_pinned.reset(); ptr_vector to_delete; - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - if (it->m_value.is_one()) - to_delete.push_back(it->m_key); + for (auto const& kv : m_var2degree) { + if (kv.m_value.is_one()) + to_delete.push_back(kv.m_key); else - m_pinned.push_back(it->m_key); // make sure it is not deleted during simplifications + m_pinned.push_back(kv.m_key); // make sure it is not deleted during simplifications } - ptr_vector::iterator it2 = to_delete.begin(); - ptr_vector::iterator end2 = to_delete.end(); - for (; it2 != end2; ++it2) - m_var2degree.erase(*it2); + for (app* a : to_delete) + m_var2degree.erase(a); } void prepare_substitution(model_converter_ref & mc) { SASSERT(!m_var2degree.empty()); - filter_model_converter * fmc = 0; - extension_model_converter * xmc = 0; + generic_model_converter * xmc = nullptr; if (m_produce_models) { - fmc = alloc(filter_model_converter, m); - xmc = alloc(extension_model_converter, m); - mc = concat(fmc, xmc); + xmc = alloc(generic_model_converter, m, "degree_shift"); + mc = xmc; } - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - SASSERT(it->m_value.is_int()); - SASSERT(it->m_value >= rational(2)); - app * fresh = m.mk_fresh_const(0, it->m_key->get_decl()->get_range()); + 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()); m_pinned.push_back(fresh); - m_var2var.insert(it->m_key, fresh); + m_var2var.insert(kv.m_key, fresh); if (m_produce_models) { - fmc->insert(fresh->get_decl()); - xmc->insert(it->m_key->get_decl(), mk_power(fresh, rational(1)/it->m_value)); + xmc->hide(fresh->get_decl()); + xmc->add(kv.m_key->get_decl(), mk_power(fresh, rational(1)/kv.m_value)); } if (m_produce_proofs) { - expr * s = mk_power(it->m_key, it->m_value); + expr * s = mk_power(kv.m_key, kv.m_value); expr * eq = m.mk_eq(fresh, s); proof * pr1 = m.mk_def_intro(eq); proof * result_pr = m.mk_apply_def(fresh, s, pr1); m_pinned.push_back(result_pr); - m_var2pr.insert(it->m_key, result_pr); + m_var2pr.insert(kv.m_key, result_pr); } } } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; m_produce_proofs = g->proofs_enabled(); m_produce_models = g->models_enabled(); tactic_report report("degree_shift", *g); collect(*g); + model_converter_ref mc; discard_non_candidates(); if (!m_var2degree.empty()) { prepare_substitution(mc); @@ -267,24 +251,23 @@ class degree_shift_tactic : public tactic { } // add >= 0 constraints for variables with even degree - obj_map::iterator it = m_var2degree.begin(); - obj_map::iterator end = m_var2degree.end(); - for (; it != end; ++it) { - SASSERT(it->m_value.is_int()); - SASSERT(it->m_value >= rational(2)); - if (it->m_value.is_even()) { - app * new_var = m_var2var.find(it->m_key); + for (auto const& kv : m_var2degree) { + SASSERT(kv.m_value.is_int()); + SASSERT(kv.m_value >= rational(2)); + if (kv.m_value.is_even()) { + app * new_var = m_var2var.find(kv.m_key); app * new_c = m_autil.mk_ge(new_var, m_autil.mk_numeral(rational(0), false)); - proof * new_pr = 0; + proof * new_pr = nullptr; if (m_produce_proofs) { - proof * pr = m_var2pr.find(it->m_key); + proof * pr = m_var2pr.find(kv.m_key); new_pr = m.mk_th_lemma(m_autil.get_family_id(), new_c, 1, &pr); } - g->assert_expr(new_c, new_pr, 0); + g->assert_expr(new_c, new_pr, nullptr); } } } g->inc_depth(); + g->add(mc.get()); result.push_back(g.get()); TRACE("degree_shift", g->display(tout); if (mc) mc->display(tout);); SASSERT(g->is_well_sorted()); @@ -297,23 +280,20 @@ public: m_imp = alloc(imp, m); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(degree_shift_tactic, m); } - virtual ~degree_shift_tactic() { + ~degree_shift_tactic() override { dealloc(m_imp); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m); std::swap(d, m_imp); dealloc(d); diff --git a/src/tactic/arith/diff_neq_tactic.cpp b/src/tactic/arith/diff_neq_tactic.cpp index c66483750..a7907b24a 100644 --- a/src/tactic/arith/diff_neq_tactic.cpp +++ b/src/tactic/arith/diff_neq_tactic.cpp @@ -313,13 +313,10 @@ class diff_neq_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); m_produce_models = g->models_enabled(); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); tactic_report report("diff-neq", *g); fail_if_proof_generation("diff-neq", g); fail_if_unsat_core_generation("diff-neq", g); @@ -332,8 +329,9 @@ class diff_neq_tactic : public tactic { bool r = search(); report_tactic_progress(":conflicts", m_num_conflicts); if (r) { - if (m_produce_models) - mc = model2model_converter(mk_model()); + if (m_produce_models) { + g->add(model2model_converter(mk_model())); + } g->reset(); } else { @@ -354,28 +352,28 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(diff_neq_tactic, m, m_params); } - virtual ~diff_neq_tactic() { + ~diff_neq_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("diff_neq_max_k", CPK_UINT, "(default: 1024) maximum variable upper bound for diff neq solver."); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.update("conflicts", m_imp->m_num_conflicts); } - virtual void reset_statistics() { + void reset_statistics() override { m_imp->m_num_conflicts = 0; } @@ -383,15 +381,12 @@ public: \brief Fix a DL variable in s to 0. If s is not really in the difference logic fragment, then this is a NOOP. */ - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); d->m_num_conflicts = m_imp->m_num_conflicts; std::swap(d, m_imp); diff --git a/src/tactic/arith/elim01_tactic.cpp b/src/tactic/arith/elim01_tactic.cpp deleted file mode 100644 index b6fd5eee7..000000000 --- a/src/tactic/arith/elim01_tactic.cpp +++ /dev/null @@ -1,267 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - elim01_tactic.cpp - -Abstract: - - Replace 0-1 integer variables by Booleans. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-12-7 - -Notes: - ---*/ -#include "tactic/tactical.h" -#include "util/cooperate.h" -#include "tactic/arith/bound_manager.h" -#include "ast/ast_pp.h" -#include "ast/rewriter/expr_safe_replace.h" -#include "ast/arith_decl_plugin.h" -#include "tactic/arith/elim01_tactic.h" -#include "model/model_smt2_pp.h" -#include "ast/rewriter/th_rewriter.h" - -class bool2int_model_converter : public model_converter { - ast_manager& m; - arith_util a; - func_decl_ref_vector m_refs; - obj_hashtable m_bools; - vector > m_nums_as_bool; - ptr_vector m_nums_as_int; -public: - - bool2int_model_converter(ast_manager& m): - m(m), - a(m), - m_refs(m) - {} - - virtual void operator()(model_ref & old_model, unsigned goal_idx) { - SASSERT(goal_idx == 0); - model * new_model = alloc(model, m); - unsigned num = old_model->get_num_constants(); - for (unsigned i = 0; i < m_nums_as_int.size(); ++i) { - func_decl* f_old = m_nums_as_int[i]; - rational val(0); - rational po(1); - bool is_value = true; - for (unsigned j = 0; is_value && j < m_nums_as_bool[i].size(); ++j) { - func_decl* f = m_nums_as_bool[i][j]; - expr* fi = old_model->get_const_interp(f); - if (!fi) { - is_value = false; - } - else if (m.is_true(fi)) { - val += po; - } - else if (!m.is_false(fi)) { - is_value = false; - } - po *= rational(2); - } - if (is_value) { - expr* fi = a.mk_numeral(val, true); - new_model->register_decl(f_old, fi); - } - } - for (unsigned i = 0; i < num; ++i) { - func_decl* f = old_model->get_constant(i); - expr* fi = old_model->get_const_interp(f); - if (!m_bools.contains(f)) { - new_model->register_decl(f, fi); - } - } - num = old_model->get_num_functions(); - for (unsigned i = 0; i < num; i++) { - func_decl * f = old_model->get_function(i); - func_interp * fi = old_model->get_func_interp(f); - new_model->register_decl(f, fi->copy()); - } - new_model->copy_usort_interps(*old_model); - old_model = new_model; - } - - void insert(func_decl* x_new, func_decl* x_old) { - m_refs.push_back(x_new); - m_refs.push_back(x_old); - m_bools.insert(x_new); - m_nums_as_int.push_back(x_old); - m_nums_as_bool.push_back(ptr_vector()); - m_nums_as_bool.back().push_back(x_new); - } - - void insert(func_decl* x_old, unsigned sz, func_decl * const* x_new) { - m_nums_as_int.push_back(x_old); - m_nums_as_bool.push_back(ptr_vector()); - m_refs.push_back(x_old); - for (unsigned i = 0; i < sz; ++i) { - m_refs.push_back(x_new[i]); - m_nums_as_bool.back().push_back(x_new[i]); - m_bools.insert(x_new[i]); - } - } - - virtual model_converter * translate(ast_translation & translator) { - bool2int_model_converter* mc = alloc(bool2int_model_converter, translator.to()); - for (unsigned i = 0; i < m_nums_as_int.size(); ++i) { - mc->insert(m_nums_as_int[i], m_nums_as_bool[i].size(), m_nums_as_bool[i].c_ptr()); - } - return mc; - } -}; - - -class elim01_tactic : public tactic { -public: - typedef obj_hashtable expr_set; - ast_manager & m; - arith_util a; - th_rewriter m_rewriter; - params_ref m_params; - unsigned m_max_hi_default; - rational m_max_hi; - - elim01_tactic(ast_manager & _m, params_ref const & p): - m(_m), - a(m), - m_rewriter(m), - m_max_hi_default(8), - m_max_hi(rational(m_max_hi_default)) { - } - - virtual ~elim01_tactic() { - } - - virtual void updt_params(params_ref const & p) { - m_max_hi = rational(p.get_uint("max_coefficient", m_max_hi_default)); - m_params = p; - } - - virtual void collect_param_descrs(param_descrs & r) { - r.insert("max_coefficient", CPK_UINT, "(default: 1) maximal upper bound for finite range -> Bool conversion"); - } - - - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; - - tactic_report report("elim01", *g); - - expr_safe_replace sub(m); - bool2int_model_converter* b2i = alloc(bool2int_model_converter, m); - mc = b2i; - bound_manager bounds(m); - expr_ref_vector axioms(m); - bounds(*g); - - rational zero(0); - bound_manager::iterator bit = bounds.begin(), bend = bounds.end(); - for (; bit != bend; ++bit) { - if (!is_app(*bit)) continue; - app* x = to_app(*bit); - bool s1 = false, s2 = false; - rational lo, hi; - if (a.is_int(x) && - bounds.has_lower(x, lo, s1) && !s1 && zero <= lo && - bounds.has_upper(x, hi, s2) && !s2 && hi <= m_max_hi && lo <= hi) { - add_variable(b2i, sub, x, lo.get_unsigned(), hi.get_unsigned(), axioms); - } - else if (a.is_int(x)) { - TRACE("pb", tout << "Not adding variable " << mk_pp(x, m) << " has lower: " - << bounds.has_lower(x, lo, s1) << " " << lo << " has upper: " - << bounds.has_upper(x, hi, s2) << " " << hi << "\n";); - } - } - - if (sub.empty()) { - result.push_back(g.get()); - return; - } - - expr_ref new_curr(m), tmp_curr(m); - proof_ref new_pr(m); - for (unsigned i = 0; i < g->size(); i++) { - expr * curr = g->form(i); - sub(curr, tmp_curr); - m_rewriter(tmp_curr, new_curr); - if (m.proofs_enabled()) { - new_pr = m.mk_rewrite(curr, new_curr); - new_pr = m.mk_modus_ponens(g->pr(i), new_pr); - } - g->update(i, new_curr, new_pr, g->dep(i)); - } - for (unsigned i = 0; i < axioms.size(); ++i) { - g->assert_expr(axioms[i].get()); - } - g->inc_depth(); - result.push_back(g.get()); - TRACE("pb", g->display(tout);); - SASSERT(g->is_well_sorted()); - - // TBD: support proof conversion (or not..) - } - - virtual tactic * translate(ast_manager & m) { - return alloc(elim01_tactic, m, m_params); - } - - virtual void cleanup() {} - - void add_variable(bool2int_model_converter* b2i, - expr_safe_replace& sub, - app* x, - unsigned min_value, - unsigned max_value, - expr_ref_vector& axioms) { - std::string name = x->get_decl()->get_name().str(); - unsigned sh = 0; - app_ref_vector xs(m), ites(m); - func_decl_ref_vector xfs(m); - app_ref zero(m), sum(m); - zero = a.mk_numeral(rational(0), true); - while (max_value >= (1ul << sh)) { - xs.push_back(m.mk_fresh_const(name.c_str(), m.mk_bool_sort())); - xfs.push_back(xs.back()->get_decl()); - ites.push_back(m.mk_ite(xs.back(), a.mk_numeral(rational(1 << sh), true), zero)); - ++sh; - } - switch (ites.size()) { - case 0: - sum = zero; - break; - case 1: - sum = ites[0].get(); - break; - default: - sum = a.mk_add(ites.size(), (expr*const*)ites.c_ptr()); - break; - } - TRACE("pb", tout << mk_pp(x, m) << " " << sum << " max: " << max_value << "\n";); - - sub.insert(x, sum); - b2i->insert(x->get_decl(), xfs.size(), xfs.c_ptr()); - // if max_value+1 is not a power of two: - if ((max_value & (max_value + 1)) != 0) { - axioms.push_back(a.mk_le(sum, a.mk_numeral(rational(max_value), true))); - } - if (min_value > 0) { - axioms.push_back(a.mk_ge(sum, a.mk_numeral(rational(min_value), true))); - } - } - -}; - -tactic * mk_elim01_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(elim01_tactic, m, p)); -} - diff --git a/src/tactic/arith/elim01_tactic.h b/src/tactic/arith/elim01_tactic.h deleted file mode 100644 index d9cd3a2ed..000000000 --- a/src/tactic/arith/elim01_tactic.h +++ /dev/null @@ -1,33 +0,0 @@ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - elim01_tactic.h - -Abstract: - - Replace 0-1 integer variables by Booleans. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-12-7 - -Notes: - ---*/ -#ifndef ELIM01_TACTIC_H_ -#define ELIM01_TACTIC_H_ - -#include "util/params.h" -class ast_manager; -class tactic; - -tactic * mk_elim01_tactic(ast_manager & m, params_ref const & p = params_ref()); - -/* - ADD_TACTIC("elim01", "eliminate 0-1 integer variables, replace them by Booleans.", "mk_elim01_tactic(m, p)") -*/ - - -#endif diff --git a/src/tactic/arith/eq2bv_tactic.cpp b/src/tactic/arith/eq2bv_tactic.cpp index 255665b56..616ba69c9 100644 --- a/src/tactic/arith/eq2bv_tactic.cpp +++ b/src/tactic/arith/eq2bv_tactic.cpp @@ -59,7 +59,7 @@ class eq2bv_tactic : public tactic { bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return mk_app_core(f, num, args, result); } eq_rewriter_cfg(eq2bv_tactic& t):m(t.m), t(t) {} @@ -82,7 +82,7 @@ class eq2bv_tactic : public tactic { m_map.insert(c_new, c_old); } - virtual void operator()(model_ref& mdl) { + void operator()(model_ref& mdl) override { ast_manager& m = mdl->get_manager(); bv_util bv(m); arith_util a(m); @@ -105,14 +105,22 @@ class eq2bv_tactic : public tactic { mdl = new_m; } - virtual model_converter* translate(ast_translation & translator) { + model_converter* translate(ast_translation & translator) override { bvmc* v = alloc(bvmc); - obj_map::iterator it = m_map.begin(), end = m_map.end(); - for (; it != end; ++it) { - v->m_map.insert(translator(it->m_key), translator(it->m_value)); + for (auto const& kv : m_map) { + v->m_map.insert(translator(kv.m_key), translator(kv.m_value)); } return v; } + + void display(std::ostream & out) override { + for (auto const& kv : m_map) { + out << "(model-set " << kv.m_key->get_name() << " " << kv.m_value->get_name() << ")\n"; + } + } + + void get_units(obj_map& units) override { units.reset(); } + }; public: @@ -136,21 +144,15 @@ public: m_bounds(m) { } - virtual ~eq2bv_tactic() { + ~eq2bv_tactic() override { } - void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { } - virtual void operator()( - goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; m_trail.reset(); m_fd.reset(); m_max.reset(); @@ -176,7 +178,7 @@ public: expr_ref new_curr(m); proof_ref new_pr(m); if (is_bound(g->form(i))) { - g->update(i, m.mk_true(), 0, 0); + g->update(i, m.mk_true(), nullptr, nullptr); continue; } m_rw(g->form(i), new_curr, new_pr); @@ -206,21 +208,21 @@ public: } } g->inc_depth(); - mc = mc1.get(); + g->add(mc1.get()); result.push_back(g.get()); TRACE("pb", g->display(tout);); SASSERT(g->is_well_sorted()); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(eq2bv_tactic, m); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { } - virtual void cleanup() { + void cleanup() override { } void cleanup_fd(ref& mc) { diff --git a/src/tactic/arith/factor_tactic.cpp b/src/tactic/arith/factor_tactic.cpp index d187c0078..0ad246852 100644 --- a/src/tactic/arith/factor_tactic.cpp +++ b/src/tactic/arith/factor_tactic.cpp @@ -257,12 +257,8 @@ class factor_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("factor", *g); bool produce_proofs = g->proofs_enabled(); @@ -293,32 +289,29 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(factor_tactic, m, m_params); } - virtual ~factor_tactic() { + ~factor_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("split_factors", CPK_BOOL, "(default: true) apply simplifications such as (= (* p1 p2) 0) --> (or (= p1 0) (= p2 0))."); polynomial::factor_params::get_param_descrs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (z3_error & ex) { throw ex; @@ -328,7 +321,7 @@ public: } } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); diff --git a/src/tactic/arith/fix_dl_var_tactic.cpp b/src/tactic/arith/fix_dl_var_tactic.cpp index 7ff7bd835..669ded49d 100644 --- a/src/tactic/arith/fix_dl_var_tactic.cpp +++ b/src/tactic/arith/fix_dl_var_tactic.cpp @@ -23,7 +23,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" @@ -44,7 +44,7 @@ class fix_dl_var_tactic : public tactic { m_util(u) { } - void throw_failed(expr * ctx1, expr * ctx2 = 0) { + void throw_failed(expr * ctx1, expr * ctx2 = nullptr) { TRACE("fix_dl_var", tout << mk_ismt2_pp(ctx1, m) << "\n"; if (ctx2) tout << mk_ismt2_pp(ctx2, m) << "\n";); throw failed(); } @@ -178,7 +178,7 @@ class fix_dl_var_tactic : public tactic { } app * most_occs(obj_map & occs, unsigned & best) { - app * r = 0; + app * r = nullptr; best = 0; obj_map::iterator it = occs.begin(); obj_map::iterator end = occs.end(); @@ -196,7 +196,7 @@ class fix_dl_var_tactic : public tactic { app * most_occs() { // We try to choose a variable that when set to 0 will generate many bounded variables. - // That is why we give preference to variables occuring in non-nested inequalities. + // That is why we give preference to variables occurring in non-nested inequalities. unsigned best1, best2; app * r1, * r2; r1 = most_occs(m_non_nested_occs, best1); @@ -227,7 +227,7 @@ class fix_dl_var_tactic : public tactic { return most_occs(); } catch (failed) { - return 0; + return nullptr; } } }; @@ -249,18 +249,14 @@ class fix_dl_var_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("fix-dl-var", *g); bool produce_proofs = g->proofs_enabled(); m_produce_models = g->models_enabled(); app * var = is_target(u)(*g); - if (var != 0) { + if (var != nullptr) { IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(fixing-at-zero " << var->get_decl()->get_name() << ")\n";); tactic_report report("fix-dl-var", *g); @@ -270,9 +266,9 @@ class fix_dl_var_tactic : public tactic { m_rw.set_substitution(&subst); if (m_produce_models) { - extension_model_converter * _mc = alloc(extension_model_converter, m); - _mc->insert(var->get_decl(), zero); - mc = _mc; + generic_model_converter * mc = alloc(generic_model_converter, m, "fix_dl"); + mc->add(var, zero); + g->add(mc); } expr_ref new_curr(m); @@ -303,37 +299,34 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(fix_dl_var_tactic, m, m_params); } - virtual ~fix_dl_var_tactic() { + ~fix_dl_var_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { th_rewriter::get_param_descrs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); diff --git a/src/tactic/arith/fm_tactic.cpp b/src/tactic/arith/fm_tactic.cpp index 48f584169..fc41f54a4 100644 --- a/src/tactic/arith/fm_tactic.cpp +++ b/src/tactic/arith/fm_tactic.cpp @@ -54,7 +54,7 @@ class fm_tactic : public tactic { bool is_false(model_ref & md, app * p) { SASSERT(is_uninterp_const(p)); expr * val = md->get_const_interp(p->get_decl()); - if (val == 0) { + if (val == nullptr) { // if it is don't care, then set to false md->register_decl(p->get_decl(), m.mk_false()); return true; @@ -62,7 +62,7 @@ class fm_tactic : public tactic { return m.is_false(val); } - r_kind process(func_decl * x, expr * cls, arith_util & u, model_evaluator & ev, rational & r) { + r_kind process(func_decl * x, expr * cls, arith_util & u, model& ev, rational & r) { unsigned num_lits; expr * const * lits; if (m.is_or(cls)) { @@ -80,9 +80,7 @@ class fm_tactic : public tactic { expr * l = lits[i]; expr * atom; if (is_uninterp_const(l) || (m.is_not(l, atom) && is_uninterp_const(atom))) { - expr_ref val(m); - ev(l, val); - if (m.is_true(val)) + if (ev.is_true(l)) return NONE; // clause was satisfied } else { @@ -131,7 +129,7 @@ class fm_tactic : public tactic { } else { expr_ref val(m); - ev(monomial, val); + val = ev(monomial); SASSERT(u.is_numeral(val)); rational tmp; u.is_numeral(val, tmp); @@ -164,7 +162,7 @@ class fm_tactic : public tactic { public: fm_model_converter(ast_manager & _m):m(_m) {} - virtual ~fm_model_converter() { + ~fm_model_converter() override { m.dec_array_ref(m_xs.size(), m_xs.c_ptr()); vector::iterator it = m_clauses.begin(); vector::iterator end = m_clauses.end(); @@ -180,10 +178,13 @@ class fm_tactic : public tactic { m_clauses.back().swap(c); } - virtual void operator()(model_ref & md, unsigned goal_idx) { + void get_units(obj_map& units) override { units.reset(); } + + void operator()(model_ref & md) override { TRACE("fm_mc", model_v2_pp(tout, *md); display(tout);); - model_evaluator ev(*(md.get())); - ev.set_model_completion(true); + model::scoped_model_completion _sc(*md, true); + //model_evaluator ev(*(md.get())); + //ev.set_model_completion(true); arith_util u(m); unsigned i = m_xs.size(); while (i > 0) { @@ -199,7 +200,7 @@ class fm_tactic : public tactic { clauses::iterator end = m_clauses[i].end(); for (; it != end; ++it) { if (m.canceled()) throw tactic_exception(m.limit().get_cancel_msg()); - switch (process(x, *it, u, ev, val)) { + switch (process(x, *it, u, *md, val)) { case NONE: TRACE("fm_mc", tout << "no bound for:\n" << mk_ismt2_pp(*it, m) << "\n";); break; @@ -244,7 +245,7 @@ class fm_tactic : public tactic { } - virtual void display(std::ostream & out) { + void display(std::ostream & out) override { out << "(fm-model-converter"; SASSERT(m_xs.size() == m_clauses.size()); unsigned sz = m_xs.size(); @@ -261,7 +262,7 @@ class fm_tactic : public tactic { out << ")\n"; } - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { ast_manager & to_m = translator.to(); fm_model_converter * res = alloc(fm_model_converter, to_m); unsigned sz = m_xs.size(); @@ -828,7 +829,7 @@ class fm_tactic : public tactic { reset_constraints(); m_bvar2expr.reset(); m_bvar2sign.reset(); - m_bvar2expr.push_back(0); // bvar 0 is not used + m_bvar2expr.push_back(nullptr); // bvar 0 is not used m_bvar2sign.push_back(0); m_expr2var.reset(); m_is_int.reset(); @@ -838,11 +839,11 @@ class fm_tactic : public tactic { m_expr2var.reset(); m_lowers.reset(); m_uppers.reset(); - m_new_goal = 0; - m_mc = 0; + m_new_goal = nullptr; + m_mc = nullptr; m_counter = 0; m_inconsistent = false; - m_inconsistent_core = 0; + m_inconsistent_core = nullptr; init_forbidden_set(g); } @@ -878,7 +879,7 @@ class fm_tactic : public tactic { // 0 <= 0 -- > true if (c.m_c.is_pos() || (!c.m_strict && c.m_c.is_zero())) return m.mk_true(); - ineq = 0; + ineq = nullptr; } else { bool int_cnstr = all_int(c); @@ -1115,7 +1116,7 @@ class fm_tactic : public tactic { } else { TRACE("add_constraint_bug", tout << "all variables are forbidden "; display(tout, *c); tout << "\n";); - m_new_goal->assert_expr(to_expr(*c), 0, c->m_dep); + m_new_goal->assert_expr(to_expr(*c), nullptr, c->m_dep); del_constraint(c); return false; } @@ -1130,7 +1131,7 @@ class fm_tactic : public tactic { if (is_occ(f)) add_constraint(f, g.dep(i)); else - m_new_goal->assert_expr(f, 0, g.dep(i)); + m_new_goal->assert_expr(f, nullptr, g.dep(i)); } } @@ -1367,7 +1368,7 @@ class fm_tactic : public tactic { display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); - return 0; // no constraint needs to be created. + return nullptr; // no constraint needs to be created. } new_lits.reset(); @@ -1411,7 +1412,7 @@ class fm_tactic : public tactic { display(tout, l); tout << "\n"; display(tout, u); tout << "\n";); - return 0; + return nullptr; } expr_dependency * new_dep = m.mk_join(l.m_dep, u.m_dep); @@ -1423,7 +1424,7 @@ class fm_tactic : public tactic { display(tout, u); tout << "\n";); m_inconsistent = true; m_inconsistent_core = new_dep; - return 0; + return nullptr; } constraint * new_cnstr = mk_constraint(new_lits.size(), @@ -1493,7 +1494,7 @@ class fm_tactic : public tactic { constraint const & l_c = *(l[i]); constraint const & u_c = *(u[j]); constraint * new_c = resolve(l_c, u_c, x); - if (new_c != 0) { + if (new_c != nullptr) { num_new_cnstrs++; new_constraints.push_back(new_c); } @@ -1528,7 +1529,7 @@ class fm_tactic : public tactic { c->m_dead = true; expr * new_f = to_expr(*c); TRACE("fm_bug", tout << "asserting...\n" << mk_ismt2_pp(new_f, m) << "\nnew_dep: " << c->m_dep << "\n";); - m_new_goal->assert_expr(new_f, 0, c->m_dep); + m_new_goal->assert_expr(new_f, nullptr, c->m_dep); } } } @@ -1550,12 +1551,8 @@ class fm_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("fm", *g); fail_if_proof_generation("fm", g); m_produce_models = g->models_enabled(); @@ -1571,7 +1568,7 @@ class fm_tactic : public tactic { if (m_inconsistent) { m_new_goal->reset(); - m_new_goal->assert_expr(m.mk_false(), 0, m_inconsistent_core); + m_new_goal->assert_expr(m.mk_false(), nullptr, m_inconsistent_core); } else { TRACE("fm", display(tout);); @@ -1595,7 +1592,7 @@ class fm_tactic : public tactic { eliminated++; if (m_inconsistent) { m_new_goal->reset(); - m_new_goal->assert_expr(m.mk_false(), 0, m_inconsistent_core); + m_new_goal->assert_expr(m.mk_false(), nullptr, m_inconsistent_core); break; } } @@ -1603,7 +1600,7 @@ class fm_tactic : public tactic { report_tactic_progress(":fm-cost", m_counter); if (!m_inconsistent) { copy_remaining(); - mc = m_mc.get(); + m_new_goal->add(concat(g->mc(), m_mc.get())); } } reset_constraints(); @@ -1643,20 +1640,20 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(fm_tactic, m, m_params); } - virtual ~fm_tactic() { + ~fm_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_produce_models(r); insert_max_memory(r); r.insert("fm_real_only", CPK_BOOL, "(default: true) consider only real variables for fourier-motzkin elimination."); @@ -1668,18 +1665,15 @@ public: } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } }; diff --git a/src/tactic/arith/lia2card_tactic.cpp b/src/tactic/arith/lia2card_tactic.cpp index ec41f5845..ec18ae2bf 100644 --- a/src/tactic/arith/lia2card_tactic.cpp +++ b/src/tactic/arith/lia2card_tactic.cpp @@ -16,35 +16,29 @@ Author: Notes: --*/ -/*++ -Copyright (c) 2013 Microsoft Corporation - -Module Name: - - lia2card_tactic.cpp - -Abstract: - - Convert 0-1 integer variables cardinality constraints to built-in cardinality operator. - -Author: - - Nikolaj Bjorner (nbjorner) 2013-11-5 - -Notes: - ---*/ -#include "tactic/tactical.h" #include "util/cooperate.h" -#include "tactic/arith/bound_manager.h" #include "ast/ast_pp.h" #include "ast/pb_decl_plugin.h" #include "ast/arith_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" +#include "ast/rewriter/expr_safe_replace.h" #include "ast/ast_util.h" #include "ast/ast_pp_util.h" +#include "tactic/tactical.h" +#include "tactic/arith/bound_manager.h" +#include "tactic/generic_model_converter.h" class lia2card_tactic : public tactic { + + struct bound { + unsigned m_lo; + unsigned m_hi; + 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) {} + }; + struct lia_rewriter_cfg : public default_rewriter_cfg { ast_manager& m; lia2card_tactic& t; @@ -58,7 +52,7 @@ class lia2card_tactic : public tactic { coeffs.reset(); coeff.reset(); return - t.get_pb_sum(x, rational::one(), args, coeffs, coeff) && + t.get_pb_sum(x, rational::one(), args, coeffs, coeff) && t.get_pb_sum(y, -rational::one(), args, coeffs, coeff); } @@ -91,28 +85,13 @@ class lia2card_tactic : public tactic { } TRACE("pbsum", tout << expr_ref(m.mk_app(f, sz, es), m) << " ==>\n" << result << "\n";); -#if 0 - expr_ref vc(m); - vc = m.mk_not(m.mk_eq(m.mk_app(f, sz, es), result)); - ast_pp_util pp(m); - pp.collect(vc); - std::cout - << "(push)\n" - << "(echo \"" << result << "\")\n" - ; - pp.display_decls(std::cout); - std::cout - << "(assert " << vc << ")\n" - << "(check-sat)\n" - << "(pop)\n"; -#endif return BR_DONE; } bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return mk_app_core(f, num, args, result); } lia_rewriter_cfg(lia2card_tactic& t):m(t.m), t(t), a(m), args(m) {} @@ -128,15 +107,17 @@ class lia2card_tactic : public tactic { }; public: - typedef obj_hashtable expr_set; + typedef obj_map bounds_map; ast_manager & m; arith_util a; lia_rewriter m_rw; params_ref m_params; pb_util m_pb; mutable ptr_vector* m_todo; - expr_set* m_01s; + bounds_map m_bounds; bool m_compile_equality; + unsigned m_max_ub; + ref m_mc; lia2card_tactic(ast_manager & _m, params_ref const & p): m(_m), @@ -144,107 +125,91 @@ public: m_rw(*this), m_pb(m), m_todo(alloc(ptr_vector)), - m_01s(alloc(expr_set)), - m_compile_equality(false) { + m_compile_equality(true) { + m_max_ub = 100; } - virtual ~lia2card_tactic() { + ~lia2card_tactic() override { dealloc(m_todo); - dealloc(m_01s); } - void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; - m_compile_equality = p.get_bool("compile_equality", false); + m_compile_equality = p.get_bool("compile_equality", true); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + expr_ref mk_bounded(expr_ref_vector& axioms, app* x, unsigned lo, unsigned hi) { + expr_ref_vector xs(m); + expr_ref last_v(m); + if (!m_mc) m_mc = alloc(generic_model_converter, m, "lia2card"); + if (hi == 0) { + return expr_ref(a.mk_int(0), m); + } + if (lo > 0) { + xs.push_back(a.mk_int(lo)); + } + for (unsigned i = lo; i < hi; ++i) { + std::string name(x->get_decl()->get_name().str()); + expr_ref v(m.mk_fresh_const(name.c_str(), m.mk_bool_sort()), m); + if (last_v) axioms.push_back(m.mk_implies(v, last_v)); + xs.push_back(m.mk_ite(v, a.mk_int(1), a.mk_int(0))); + m_mc->hide(v); + last_v = v; + } + expr* r = a.mk_add(xs.size(), xs.c_ptr()); + m_mc->add(x->get_decl(), r); + return expr_ref(r, m); + } + + void operator()(goal_ref const & g, goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; - m_01s->reset(); + m_bounds.reset(); + m_mc.reset(); + expr_ref_vector axioms(m); + expr_safe_replace rep(m); - tactic_report report("cardinality-intro", *g); + tactic_report report("lia2card", *g); bound_manager bounds(m); bounds(*g); - - bound_manager::iterator bit = bounds.begin(), bend = bounds.end(); - for (; bit != bend; ++bit) { - expr* x = *bit; + for (expr* x : bounds) { bool s1 = false, s2 = false; rational lo, hi; if (a.is_int(x) && - bounds.has_lower(x, lo, s1) && !s1 && lo.is_zero() && - bounds.has_upper(x, hi, s2) && !s2 && hi.is_one()) { - m_01s->insert(x); + is_uninterp_const(x) && + bounds.has_lower(x, lo, s1) && !s1 && lo.is_unsigned() && + bounds.has_upper(x, hi, s2) && !s2 && hi.is_unsigned() && hi.get_unsigned() - lo.get_unsigned() <= m_max_ub) { + expr_ref b = mk_bounded(axioms, to_app(x), lo.get_unsigned(), hi.get_unsigned()); + rep.insert(x, b); + m_bounds.insert(x, bound(lo.get_unsigned(), hi.get_unsigned(), b)); TRACE("pb", tout << "add bound " << mk_pp(x, m) << "\n";); } } - expr_mark subfmls; for (unsigned i = 0; i < g->size(); i++) { - expr_ref new_curr(m); + expr_ref new_curr(m), tmp(m); proof_ref new_pr(m); - m_rw(g->form(i), new_curr, new_pr); + rep(g->form(i), tmp); + m_rw(tmp, new_curr, new_pr); if (m.proofs_enabled() && !new_pr) { new_pr = m.mk_rewrite(g->form(i), new_curr); new_pr = m.mk_modus_ponens(g->pr(i), new_pr); } + // IF_VERBOSE(0, verbose_stream() << mk_pp(g->form(i), m) << "\n--->\n" << new_curr << "\n";); g->update(i, new_curr, new_pr, g->dep(i)); - mark_rec(subfmls, new_curr); + } - expr_set::iterator it = m_01s->begin(), end = m_01s->end(); - for (; it != end; ++it) { - expr* v = *it; - if (subfmls.is_marked(v)) { - g->assert_expr(a.mk_le(v, a.mk_numeral(rational(1), true))); - g->assert_expr(a.mk_le(a.mk_numeral(rational(0), true), v)); - } + for (expr* a : axioms) { + g->assert_expr(a); } + if (m_mc) g->add(m_mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("pb", g->display(tout);); SASSERT(g->is_well_sorted()); - - // TBD: convert models for 0-1 variables. - // TBD: support proof conversion (or not..) + m_bounds.reset(); } - void mark_rec(expr_mark& mark, expr* e) { - ptr_vector todo; - todo.push_back(e); - while (!todo.empty()) { - e = todo.back(); - todo.pop_back(); - if (!mark.is_marked(e)) { - mark.mark(e); - if (is_app(e)) { - for (unsigned i = 0; i < to_app(e)->get_num_args(); ++i) { - todo.push_back(to_app(e)->get_arg(i)); - } - } - else if (is_quantifier(e)) { - todo.push_back(to_quantifier(e)->get_expr()); - } - } - } - } - - - bool is_01var(expr* x) const { - return m_01s->contains(x); - } - - expr_ref mk_01(expr* x) { - expr* r = m.mk_eq(x, a.mk_numeral(rational(1), m.get_sort(x))); - return expr_ref(r, m); - } - - expr* mk_le(unsigned sz, rational const* weights, expr* const* args, rational const& w) { if (sz == 0) { return w.is_neg()?m.mk_false():m.mk_true(); @@ -332,9 +297,6 @@ public: ok &= get_sum(u, mul, conds, args, coeffs, coeff); conds.pop_back(); } - else if (is_01var(x)) { - insert_arg(mul, conds, mk_01(x), args, coeffs, coeff); - } else if (is_numeral(x, r)) { insert_arg(mul*r, conds, m.mk_true(), args, coeffs, coeff); } @@ -389,22 +351,20 @@ public: } } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(lia2card_tactic, m, m_params); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("compile_equality", CPK_BOOL, "(default:false) compile equalities into pseudo-Boolean equality"); } - virtual void cleanup() { - expr_set* d = alloc(expr_set); + void cleanup() override { ptr_vector* todo = alloc(ptr_vector); - std::swap(m_01s, d); std::swap(m_todo, todo); - dealloc(d); dealloc(todo); + m_bounds.reset(); } }; diff --git a/src/tactic/arith/lia2pb_tactic.cpp b/src/tactic/arith/lia2pb_tactic.cpp index d303463db..db1c22866 100644 --- a/src/tactic/arith/lia2pb_tactic.cpp +++ b/src/tactic/arith/lia2pb_tactic.cpp @@ -20,8 +20,7 @@ Revision History: #include "tactic/arith/bound_manager.h" #include "ast/rewriter/th_rewriter.h" #include "ast/for_each_expr.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" @@ -163,10 +162,8 @@ class lia2pb_tactic : public tactic { } bool has_target() { - bound_manager::iterator it = m_bm.begin(); - bound_manager::iterator end = m_bm.end(); - for (; it != end; ++it) { - if (is_target(*it)) + for (expr * x : m_bm) { + if (is_target(x)) return true; } return false; @@ -175,10 +172,7 @@ class lia2pb_tactic : public tactic { bool check_num_bits() { unsigned num_bits = 0; rational u; - bound_manager::iterator it = m_bm.begin(); - bound_manager::iterator end = m_bm.end(); - for (; it != end; ++it) { - expr * x = *it; + for (expr * x : m_bm) { if (is_target_core(x, u) && u > rational(1)) { num_bits += u.get_num_bits(); if (num_bits > m_total_bits) @@ -189,15 +183,12 @@ class lia2pb_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("lia2pb", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); tactic_report report("lia2pb", *g); m_bm.reset(); m_rw.reset(); m_new_deps.reset(); @@ -224,12 +215,9 @@ class lia2pb_tactic : public tactic { if (!check_num_bits()) throw tactic_exception("lia2pb failed, number of necessary bits exceeds specified threshold (use option :lia2pb-total-bits to increase threshold)"); - extension_model_converter * mc1 = 0; - filter_model_converter * mc2 = 0; + ref gmc; if (m_produce_models) { - mc1 = alloc(extension_model_converter, m); - mc2 = alloc(filter_model_converter, m); - mc = concat(mc2, mc1); + gmc = alloc(generic_model_converter, m, "lia2pb"); } expr_ref zero(m); @@ -241,39 +229,36 @@ class lia2pb_tactic : public tactic { expr_substitution subst(m, m_produce_unsat_cores, false); rational u; ptr_buffer def_args; - bound_manager::iterator it = m_bm.begin(); - bound_manager::iterator end = m_bm.end(); - for (; it != end; ++it) { - expr * x = *it; + for (expr * x : m_bm) { if (is_target_core(x, u) && u > rational(1)) { num_converted++; def_args.reset(); rational a(1); unsigned num_bits = u.get_num_bits(); for (unsigned i = 0; i < num_bits; i++) { - app * x_prime = m.mk_fresh_const(0, m_util.mk_int()); + app * x_prime = m.mk_fresh_const(nullptr, m_util.mk_int()); g->assert_expr(m_util.mk_le(zero, x_prime)); g->assert_expr(m_util.mk_le(x_prime, one)); if (a.is_one()) def_args.push_back(x_prime); else def_args.push_back(m_util.mk_mul(m_util.mk_numeral(a, true), x_prime)); - if (m_produce_models) - mc2->insert(x_prime->get_decl()); + if (m_produce_models) + gmc->hide(x_prime->get_decl()); a *= rational(2); } SASSERT(def_args.size() > 1); expr * def = m_util.mk_add(def_args.size(), def_args.c_ptr()); - expr_dependency * dep = 0; + expr_dependency * dep = nullptr; if (m_produce_unsat_cores) { dep = m.mk_join(m_bm.lower_dep(x), m_bm.upper_dep(x)); - if (dep != 0) + if (dep != nullptr) m_new_deps.push_back(dep); } TRACE("lia2pb", tout << mk_ismt2_pp(x, m) << " -> " << dep << "\n";); - subst.insert(x, def, 0, dep); + subst.insert(x, def, nullptr, dep); if (m_produce_models) { - mc1->insert(to_app(x)->get_decl(), def); + gmc->add(x, def); } } } @@ -287,7 +272,7 @@ class lia2pb_tactic : public tactic { unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); - expr_dependency * dep = 0; + expr_dependency * dep = nullptr; m_rw(curr, new_curr, new_pr); if (m_produce_unsat_cores) { dep = m.mk_join(m_rw.get_used_dependencies(), g->dep(idx)); @@ -299,6 +284,7 @@ class lia2pb_tactic : public tactic { g->update(idx, new_curr, new_pr, dep); } g->inc_depth(); + g->add(gmc.get()); result.push_back(g.get()); TRACE("lia2pb", g->display(tout);); SASSERT(g->is_well_sorted()); @@ -313,39 +299,36 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(lia2pb_tactic, m, m_params); } - virtual ~lia2pb_tactic() { + ~lia2pb_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("lia2pb_partial", CPK_BOOL, "(default: false) partial lia2pb conversion."); r.insert("lia2pb_max_bits", CPK_UINT, "(default: 32) maximum number of bits to be used (per variable) in lia2pb."); r.insert("lia2pb_total_bits", CPK_UINT, "(default: 2048) total number of bits to be used (per problem) in lia2pb."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); diff --git a/src/tactic/arith/linear_equation.cpp b/src/tactic/arith/linear_equation.cpp index ed158b778..5db0a4cf9 100644 --- a/src/tactic/arith/linear_equation.cpp +++ b/src/tactic/arith/linear_equation.cpp @@ -174,7 +174,7 @@ linear_equation * linear_equation_manager::mk(unsigned sz, mpz * as, var * xs, b } sz = j; if (sz <= 1) - return 0; + return nullptr; } else { DEBUG_CODE({ @@ -265,7 +265,7 @@ linear_equation * linear_equation_manager::mk(mpz const & b1, linear_equation co m.del(new_a); SASSERT(m_int_buffer.size() == m_var_buffer.size()); if (m_int_buffer.empty()) - return 0; + return nullptr; return mk_core(m_int_buffer.size(), m_int_buffer.c_ptr(), m_var_buffer.c_ptr()); } diff --git a/src/tactic/arith/nla2bv_tactic.cpp b/src/tactic/arith/nla2bv_tactic.cpp index 6f5f49aee..06150e18b 100644 --- a/src/tactic/arith/nla2bv_tactic.cpp +++ b/src/tactic/arith/nla2bv_tactic.cpp @@ -26,8 +26,7 @@ Notes: #include "util/optional.h" #include "tactic/arith/bv2int_rewriter.h" #include "tactic/arith/bv2real_rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "tactic/arith/bound_manager.h" #include "util/obj_pair_hashtable.h" #include "ast/ast_smt2_pp.h" @@ -60,7 +59,7 @@ class nla2bv_tactic : public tactic { expr_ref_vector m_trail; unsigned m_num_bits; unsigned m_default_bv_size; - filter_model_converter_ref m_fmc; + generic_model_converter_ref m_fmc; public: imp(ast_manager & m, params_ref const& p): @@ -75,7 +74,7 @@ class nla2bv_tactic : public tactic { m_vars(m), m_defs(m), m_trail(m), - m_fmc(0) { + m_fmc(nullptr) { m_default_bv_size = m_num_bits = p.get_uint("nla2bv_bv_size", 4); } @@ -86,7 +85,7 @@ class nla2bv_tactic : public tactic { TRACE("nla2bv", g.display(tout); tout << "Muls: " << count_mul(g) << "\n"; ); - m_fmc = alloc(filter_model_converter, m_manager); + m_fmc = alloc(generic_model_converter, m_manager, "nla2bv"); m_bounds(g); collect_power2(g); if(!collect_vars(g)) { @@ -98,13 +97,12 @@ class nla2bv_tactic : public tactic { reduce_bv2int(g); reduce_bv2real(g); TRACE("nla2bv", g.display(tout << "after reduce\n");); - extension_model_converter * evc = alloc(extension_model_converter, m_manager); - mc = concat(m_fmc.get(), evc); + mc = m_fmc.get(); for (unsigned i = 0; i < m_vars.size(); ++i) { - evc->insert(m_vars[i].get(), m_defs[i].get()); + m_fmc->add(m_vars[i].get(), m_defs[i].get()); } for (unsigned i = 0; i < m_bv2real.num_aux_decls(); ++i) { - m_fmc->insert(m_bv2real.get_aux_decl(i)); + m_fmc->hide(m_bv2real.get_aux_decl(i)); } IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(nla->bv :sat-preserving " << m_is_sat_preserving << ")\n";); TRACE("nla2bv_verbose", g.display(tout);); @@ -233,7 +231,7 @@ class nla2bv_tactic : public tactic { bv_sort = m_bv.mk_sort(num_bits); std::string name = n->get_decl()->get_name().str(); s_bv = m_manager.mk_fresh_const(name.c_str(), bv_sort); - m_fmc->insert(to_app(s_bv)->get_decl()); + m_fmc->hide(s_bv); s_bv = m_bv.mk_bv2int(s_bv); if (low) { if (!(*low).is_zero()) { @@ -271,8 +269,8 @@ class nla2bv_tactic : public tactic { s = m_manager.mk_fresh_const(name.c_str(), bv_sort); name += "_r"; t = m_manager.mk_fresh_const(name.c_str(), bv_sort); - m_fmc->insert(to_app(s)->get_decl()); - m_fmc->insert(to_app(t)->get_decl()); + m_fmc->hide(s); + m_fmc->hide(t); s_bv = m_bv2real.mk_bv2real(s, t); m_trail.push_back(s_bv); m_subst.insert(n, s_bv); @@ -408,28 +406,28 @@ class nla2bv_tactic : public tactic { } ~scoped_set_imp() { - m_owner.m_imp = 0; + m_owner.m_imp = nullptr; } }; public: nla2bv_tactic(params_ref const & p): m_params(p), - m_imp(0) { + m_imp(nullptr) { } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(nla2bv_tactic, m_params); } - virtual ~nla2bv_tactic() { + ~nla2bv_tactic() override { } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("nla2bv_max_bv_size", CPK_UINT, "(default: inf) maximum bit-vector size used by nla2bv tactic"); r.insert("nla2bv_bv_size", CPK_UINT, "(default: 4) default bit-vector size used by nla2bv tactic."); r.insert("nla2bv_root", CPK_UINT, "(default: 2) nla2bv tactic encodes reals into bit-vectors using expressions of the form a+b*sqrt(c), this parameter sets the value of c used in the encoding."); @@ -441,25 +439,23 @@ public: arithmetic in place of non-linear integer arithmetic. \return false if transformation is not possible. */ - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); fail_if_proof_generation("nla2bv", g); fail_if_unsat_core_generation("nla2bv", g); - mc = 0; pc = 0; core = 0; result.reset(); - + result.reset(); + imp proc(g->m(), m_params); scoped_set_imp setter(*this, proc); + model_converter_ref mc; proc(*(g.get()), mc); - + g->add(mc.get()); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } - virtual void cleanup(void) { + void cleanup() override { } }; diff --git a/src/tactic/arith/normalize_bounds_tactic.cpp b/src/tactic/arith/normalize_bounds_tactic.cpp index 2b3ff5ab0..60d583798 100644 --- a/src/tactic/arith/normalize_bounds_tactic.cpp +++ b/src/tactic/arith/normalize_bounds_tactic.cpp @@ -21,8 +21,7 @@ Revision History: #include "tactic/tactical.h" #include "tactic/arith/bound_manager.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "ast/ast_smt2_pp.h" @@ -80,12 +79,7 @@ class normalize_bounds_tactic : public tactic { return false; } - void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; + void operator()(goal_ref const & in, goal_ref_buffer & result) { bool produce_models = in->models_enabled(); bool produce_proofs = in->proofs_enabled(); tactic_report report("normalize-bounds", *in); @@ -98,30 +92,25 @@ class normalize_bounds_tactic : public tactic { return; } - extension_model_converter * mc1 = 0; - filter_model_converter * mc2 = 0; + generic_model_converter * gmc = nullptr; if (produce_models) { - mc1 = alloc(extension_model_converter, m); - mc2 = alloc(filter_model_converter, m); - mc = concat(mc2, mc1); + gmc = alloc(generic_model_converter, m, "normalize_bounds"); + in->add(gmc); } unsigned num_norm_bounds = 0; expr_substitution subst(m); rational val; - bound_manager::iterator it = m_bm.begin(); - bound_manager::iterator end = m_bm.end(); - for (; it != end; ++it) { - expr * x = *it; + for (expr * x : m_bm) { if (is_target(x, val)) { num_norm_bounds++; sort * s = m.get_sort(x); - app * x_prime = m.mk_fresh_const(0, s); + app * x_prime = m.mk_fresh_const(nullptr, s); expr * def = m_util.mk_add(x_prime, m_util.mk_numeral(val, s)); subst.insert(x, def); if (produce_models) { - mc1->insert(to_app(x)->get_decl(), def); - mc2->insert(x_prime->get_decl()); + gmc->hide(x_prime->get_decl()); + gmc->add(to_app(x)->get_decl(), def); } } } @@ -156,37 +145,34 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(normalize_bounds_tactic, m, m_params); } - virtual ~normalize_bounds_tactic() { + ~normalize_bounds_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_produce_models(r); r.insert("norm_int_only", CPK_BOOL, "(default: true) normalize only the bounds of integer constants."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); diff --git a/src/tactic/arith/pb2bv_model_converter.cpp b/src/tactic/arith/pb2bv_model_converter.cpp index df5bd5745..772680e6c 100644 --- a/src/tactic/arith/pb2bv_model_converter.cpp +++ b/src/tactic/arith/pb2bv_model_converter.cpp @@ -27,75 +27,64 @@ pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m) : m(_m) { pb2bv_model_converter::pb2bv_model_converter(ast_manager & _m, obj_map const & c2bit, bound_manager const & bm): m(_m) { - obj_map::iterator it = c2bit.begin(); - obj_map::iterator end = c2bit.end(); - for ( ; it != end; it++) { - m_c2bit.push_back(func_decl_pair(it->m_key, to_app(it->m_value)->get_decl())); - m.inc_ref(it->m_key); - m.inc_ref(to_app(it->m_value)->get_decl()); + for (auto const& kv : c2bit) { + m_c2bit.push_back(func_decl_pair(kv.m_key, to_app(kv.m_value)->get_decl())); + m.inc_ref(kv.m_key); + m.inc_ref(to_app(kv.m_value)->get_decl()); } - bound_manager::iterator it2 = bm.begin(); - bound_manager::iterator end2 = bm.end(); - for (; it2 != end2; ++it2) { - expr * c = *it2; + for (expr* c : bm) { SASSERT(is_uninterp_const(c)); func_decl * d = to_app(c)->get_decl(); if (!c2bit.contains(d)) { SASSERT(d->get_arity() == 0); m.inc_ref(d); - m_c2bit.push_back(func_decl_pair(d, static_cast(0))); + m_c2bit.push_back(func_decl_pair(d, static_cast(nullptr))); } } } pb2bv_model_converter::~pb2bv_model_converter() { - svector::const_iterator it = m_c2bit.begin(); - svector::const_iterator end = m_c2bit.end(); - for (; it != end; ++it) { - m.dec_ref(it->first); - m.dec_ref(it->second); + for (auto const& kv : m_c2bit) { + m.dec_ref(kv.first); + m.dec_ref(kv.second); } } -void pb2bv_model_converter::operator()(model_ref & md) { - (*this)(md, 0); +void pb2bv_model_converter::get_units(obj_map& units) { + if (!m_c2bit.empty()) units.reset(); } -void pb2bv_model_converter::operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); + +void pb2bv_model_converter::operator()(model_ref & md) { TRACE("pb2bv", tout << "converting model:\n"; model_v2_pp(tout, *md); display(tout);); arith_util a_util(m); - svector::const_iterator it = m_c2bit.begin(); - svector::const_iterator end = m_c2bit.end(); - for (; it != end; ++it) { - if (it->second) { - expr * val = md->get_const_interp(it->second); - if (val == 0 || m.is_false(val)) { + for (auto const& kv : m_c2bit) { + if (kv.second) { + expr * val = md->get_const_interp(kv.second); + if (val == nullptr || m.is_false(val)) { /* false's and don't cares get the integer 0 solution*/ - md->register_decl(it->first, a_util.mk_numeral(rational(0), true)); + md->register_decl(kv.first, a_util.mk_numeral(rational(0), true)); } else { - md->register_decl(it->first, a_util.mk_numeral(rational(1), true)); + md->register_decl(kv.first, a_util.mk_numeral(rational(1), true)); } } else { - // it->first is a don't care. - md->register_decl(it->first, a_util.mk_numeral(rational(0), true)); + // kv.first is a don't care. + md->register_decl(kv.first, a_util.mk_numeral(rational(0), true)); } } } void pb2bv_model_converter::display(std::ostream & out) { out << "(pb2bv-model-converter"; - svector::const_iterator it = m_c2bit.begin(); - svector::const_iterator end = m_c2bit.end(); - for (; it != end; ++it) { - out << "\n (" << it->first->get_name() << " "; - if (it->second == 0) + for (auto const& kv : m_c2bit) { + out << "\n (" << kv.first->get_name() << " "; + if (kv.second == 0) out << "0"; else - out << it->second->get_name(); + out << kv.second->get_name(); out << ")"; } out << ")\n"; @@ -104,11 +93,9 @@ void pb2bv_model_converter::display(std::ostream & out) { model_converter * pb2bv_model_converter::translate(ast_translation & translator) { ast_manager & to = translator.to(); pb2bv_model_converter * res = alloc(pb2bv_model_converter, to); - svector::iterator it = m_c2bit.begin(); - svector::iterator end = m_c2bit.end(); - for (; it != end; it++) { - func_decl * f1 = translator(it->first); - func_decl * f2 = translator(it->second); + for (auto const& kv : m_c2bit) { + func_decl * f1 = translator(kv.first); + func_decl * f2 = translator(kv.second); res->m_c2bit.push_back(func_decl_pair(f1, f2)); to.inc_ref(f1); to.inc_ref(f2); diff --git a/src/tactic/arith/pb2bv_model_converter.h b/src/tactic/arith/pb2bv_model_converter.h index 0c2826573..2d69bf7e4 100644 --- a/src/tactic/arith/pb2bv_model_converter.h +++ b/src/tactic/arith/pb2bv_model_converter.h @@ -30,11 +30,11 @@ class pb2bv_model_converter : public model_converter { public: pb2bv_model_converter(ast_manager & _m); pb2bv_model_converter(ast_manager & _m, obj_map const & c2bit, bound_manager const & bm); - virtual ~pb2bv_model_converter(); - virtual void operator()(model_ref & md); - virtual void operator()(model_ref & md, unsigned goal_idx); - virtual void display(std::ostream & out); - virtual model_converter * translate(ast_translation & translator); + ~pb2bv_model_converter() override; + void operator()(model_ref & md) override; + void display(std::ostream & out) override; + void get_units(obj_map& units) override; + model_converter * translate(ast_translation & translator) override; }; #endif diff --git a/src/tactic/arith/pb2bv_tactic.cpp b/src/tactic/arith/pb2bv_tactic.cpp index 259cbc0c8..836808de1 100644 --- a/src/tactic/arith/pb2bv_tactic.cpp +++ b/src/tactic/arith/pb2bv_tactic.cpp @@ -26,7 +26,7 @@ Notes: #include "util/trace.h" #include "ast/ast_smt2_pp.h" #include "ast/expr_substitution.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "tactic/arith/pb2bv_model_converter.h" #include "tactic/arith/pb2bv_tactic.h" #include "ast/ast_pp.h" @@ -216,7 +216,7 @@ private: } bool get_subst(expr * s, expr * & t, proof * & t_pr) { - t_pr = 0; + t_pr = nullptr; if (owner.is_constraint_core(s)) { owner.convert(to_app(s), m_saved_res, true, false); t = m_saved_res; @@ -328,12 +328,12 @@ private: func_decl * fd = x->get_decl(); obj_map & const2lit = sign ? m_not_const2bit : m_const2bit; - expr * r = 0; + expr * r = nullptr; const2lit.find(fd, r); - if (r != 0) + if (r != nullptr) return r; - r = m.mk_fresh_const(0, m.mk_bool_sort()); + r = m.mk_fresh_const(nullptr, m.mk_bool_sort()); expr * not_r = m.mk_not(r); m_const2bit.insert(fd, r); m_not_const2bit.insert(fd, not_r); @@ -490,7 +490,7 @@ private: for (unsigned j = 0; j < i; j++) m_clause.push_back(monomial(numeral(1), m_p[j].m_lit)); - app * new_var = m.mk_fresh_const(0, m_arith_util.mk_int()); + app * new_var = m.mk_fresh_const(nullptr, m_arith_util.mk_int()); m_temporary_ints.push_back(new_var); m_clause.push_back(monomial(numeral(1), lit(new_var, true))); @@ -886,16 +886,13 @@ private: } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { TRACE("pb2bv", g->display(tout);); SASSERT(g->is_well_sorted()); fail_if_proof_generation("pb2bv", g); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); tactic_report report("pb2bv", *g); m_bm.reset(); m_rw.reset(); m_new_deps.reset(); @@ -946,20 +943,20 @@ private: } for (unsigned idx = 0; idx < size; idx++) - g->update(idx, new_exprs[idx].get(), 0, (m_produce_unsat_cores) ? new_deps[idx].get() : g->dep(idx)); + g->update(idx, new_exprs[idx].get(), nullptr, (m_produce_unsat_cores) ? new_deps[idx].get() : g->dep(idx)); if (m_produce_models) { - filter_model_converter * mc1 = alloc(filter_model_converter, m); - obj_map::iterator it = m_const2bit.begin(); - obj_map::iterator end = m_const2bit.end(); - for (; it != end; ++it) - mc1->insert(to_app(it->m_value)->get_decl()); + model_converter_ref mc; + generic_model_converter * mc1 = alloc(generic_model_converter, m, "pb2bv"); + for (auto const& kv : m_const2bit) + mc1->hide(kv.m_value); // store temp int constants in the filter unsigned num_temps = m_temporary_ints.size(); for (unsigned i = 0; i < num_temps; i++) - mc1->insert(to_app(m_temporary_ints.get(i))->get_decl()); + mc1->hide(m_temporary_ints.get(i)); pb2bv_model_converter * mc2 = alloc(pb2bv_model_converter, m, m_const2bit, m_bm); - mc = concat(mc1, mc2); + mc = concat(mc1, mc2); + g->add(mc.get()); } g->inc_depth(); @@ -983,32 +980,29 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(pb2bv_tactic, m, m_params); } - virtual ~pb2bv_tactic() { + ~pb2bv_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { m_imp->collect_param_descrs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); @@ -1023,7 +1017,7 @@ tactic * mk_pb2bv_tactic(ast_manager & m, params_ref const & p) { } struct is_pb_probe : public probe { - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { try { ast_manager & m = g.m(); bound_manager bm(m); diff --git a/src/tactic/arith/probe_arith.cpp b/src/tactic/arith/probe_arith.cpp index edd4483e7..36ee89a1b 100644 --- a/src/tactic/arith/probe_arith.cpp +++ b/src/tactic/arith/probe_arith.cpp @@ -74,7 +74,7 @@ class arith_degree_probe : public probe { public: arith_degree_probe(bool avg):m_avg(avg) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { proc p(g.m()); for_each_expr_at(p, g); if (m_avg) @@ -117,7 +117,7 @@ class arith_bw_probe : public probe { public: arith_bw_probe(bool avg):m_avg(avg) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { proc p(g.m()); for_each_expr_at(p, g); if (m_avg) @@ -269,14 +269,14 @@ static bool is_qfauflia(goal const & g) { class is_qflia_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_qflia(g); } }; class is_qfauflia_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_qfauflia(g); } }; @@ -288,7 +288,7 @@ static bool is_qflra(goal const & g) { class is_qflra_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_qflra(g); } }; @@ -300,7 +300,7 @@ static bool is_qflira(goal const & g) { class is_qflira_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_qflira(g); } }; @@ -344,14 +344,14 @@ static bool is_mip(goal const & g) { class is_ilp_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_ilp(g); } }; class is_mip_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_mip(g); } }; @@ -583,56 +583,56 @@ struct is_non_qfufnra_functor { class is_qfnia_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_qfnia(g); } }; class is_qfnra_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_qfnra(g); } }; class is_nia_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_nia(g); } }; class is_nra_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_nra(g); } }; class is_nira_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_nira(g); } }; class is_lia_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_lia(g); } }; class is_lra_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_lra(g); } }; class is_lira_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_lira(g); } }; @@ -644,7 +644,7 @@ static bool is_qfufnra(goal const& g) { class is_qfufnra_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return is_qfufnra(g); } }; diff --git a/src/tactic/arith/propagate_ineqs_tactic.cpp b/src/tactic/arith/propagate_ineqs_tactic.cpp index d7209740f..8bde74c8c 100644 --- a/src/tactic/arith/propagate_ineqs_tactic.cpp +++ b/src/tactic/arith/propagate_ineqs_tactic.cpp @@ -43,18 +43,18 @@ class propagate_ineqs_tactic : public tactic { public: propagate_ineqs_tactic(ast_manager & m, params_ref const & p); - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(propagate_ineqs_tactic, m, m_params); } - virtual ~propagate_ineqs_tactic(); + ~propagate_ineqs_tactic() override; - virtual void updt_params(params_ref const & p); - virtual void collect_param_descrs(param_descrs & r) {} + void updt_params(params_ref const & p) override; + void collect_param_descrs(param_descrs & r) override {} - virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); + void operator()(goal_ref const & g, goal_ref_buffer & result) override; - virtual void cleanup(); + void cleanup() override; }; tactic * mk_propagate_ineqs_tactic(ast_manager & m, params_ref const & p) { @@ -354,7 +354,7 @@ struct propagate_ineqs_tactic::imp { void find_ite_bounds(expr * root) { TRACE("find_ite_bounds_bug", display_bounds(tout);); expr * n = root; - expr * target = 0; + expr * target = nullptr; expr * c, * t, * e; expr * x, * y; bool has_l, has_u; @@ -374,7 +374,7 @@ struct propagate_ineqs_tactic::imp { break; } else if (is_x_minus_y_eq_0(n, x, y)) { - n = 0; + n = nullptr; } else { break; @@ -394,7 +394,7 @@ struct propagate_ineqs_tactic::imp { break; } - if (target == 0) { + if (target == nullptr) { target = x; if (lower(y, curr, curr_strict)) { has_l = true; @@ -448,7 +448,7 @@ struct propagate_ineqs_tactic::imp { if (!has_l && !has_u) break; - if (n == 0) { + if (n == nullptr) { TRACE("find_ite_bounds", tout << "found bounds for: " << mk_ismt2_pp(target, m) << "\n"; tout << "has_l: " << has_l << " " << nm.to_string(l_min) << " l_strict: " << l_strict << "\n"; tout << "has_u: " << has_u << " " << nm.to_string(u_max) << " u_strict: " << u_strict << "\n"; @@ -484,7 +484,7 @@ struct propagate_ineqs_tactic::imp { m_new_goal->inc_depth(); r = m_new_goal.get(); if (!collect_bounds(*g)) { - m_new_goal = 0; + m_new_goal = nullptr; r = g; return; // nothing to be done } @@ -527,14 +527,11 @@ void propagate_ineqs_tactic::updt_params(params_ref const & p) { } void propagate_ineqs_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("propagate-ineqs", g); fail_if_unsat_core_generation("propagate-ineqs", g); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); goal_ref r; (*m_imp)(g.get(), r); result.push_back(r.get()); diff --git a/src/tactic/arith/purify_arith_tactic.cpp b/src/tactic/arith/purify_arith_tactic.cpp index 7e89794ce..67dadd34b 100644 --- a/src/tactic/arith/purify_arith_tactic.cpp +++ b/src/tactic/arith/purify_arith_tactic.cpp @@ -27,8 +27,7 @@ Revision History: #include "tactic/core/nnf_tactic.h" #include "tactic/core/simplify_tactic.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" #include "ast/rewriter/expr_replacer.h" @@ -170,8 +169,8 @@ struct purify_arith_proc { } std::pair pair; if (!m_sin_cos.find(to_app(theta), pair)) { - pair.first = m().mk_fresh_const(0, u().mk_real()); - pair.second = m().mk_fresh_const(0, u().mk_real()); + pair.first = m().mk_fresh_const(nullptr, u().mk_real()); + pair.second = m().mk_fresh_const(nullptr, u().mk_real()); m_sin_cos.insert(to_app(theta), pair); m_pinned.push_back(pair.first); m_pinned.push_back(pair.second); @@ -214,7 +213,7 @@ struct purify_arith_proc { bool elim_inverses() const { return m_owner.m_elim_inverses; } expr * mk_fresh_var(bool is_int) { - expr * r = m().mk_fresh_const(0, is_int ? u().mk_int() : u().mk_real()); + expr * r = m().mk_fresh_const(nullptr, is_int ? u().mk_int() : u().mk_real()); m_new_vars.push_back(r); return r; } @@ -241,7 +240,7 @@ struct purify_arith_proc { } void mk_def_proof(expr * k, expr * def, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; if (produce_proofs()) { expr * eq = m().mk_eq(k, def); proof * pr1 = m().mk_def_intro(eq); @@ -691,7 +690,7 @@ struct purify_arith_proc { }; void process_quantifier(quantifier * q, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; rw r(*this); expr_ref new_body(m()); proof_ref new_body_pr(m()); @@ -761,28 +760,26 @@ struct purify_arith_proc { sz = r.cfg().m_new_cnstrs.size(); TRACE("purify_arith", tout << r.cfg().m_new_cnstrs << "\n";); for (unsigned i = 0; i < sz; i++) { - m_goal.assert_expr(r.cfg().m_new_cnstrs.get(i), m_produce_proofs ? r.cfg().m_new_cnstr_prs.get(i) : 0, 0); + m_goal.assert_expr(r.cfg().m_new_cnstrs.get(i), m_produce_proofs ? r.cfg().m_new_cnstr_prs.get(i) : nullptr, nullptr); } - // add filter_model_converter to eliminate auxiliary variables from model + // add generic_model_converter to eliminate auxiliary variables from model if (produce_models) { - filter_model_converter * fmc = alloc(filter_model_converter, m()); + generic_model_converter * fmc = alloc(generic_model_converter, m(), "purify"); mc = fmc; obj_map & f2v = r.cfg().m_app2fresh; - obj_map::iterator it = f2v.begin(); - obj_map::iterator end = f2v.end(); - for (; it != end; ++it) { - app * v = to_app(it->m_value); + for (auto const& kv : f2v) { + app * v = to_app(kv.m_value); SASSERT(is_uninterp_const(v)); - fmc->insert(v->get_decl()); + fmc->hide(v->get_decl()); } } if (produce_models && !m_sin_cos.empty()) { - extension_model_converter* emc = alloc(extension_model_converter, m()); + generic_model_converter* emc = alloc(generic_model_converter, m(), "purify_sin_cos"); mc = concat(mc.get(), emc); obj_map >::iterator it = m_sin_cos.begin(), end = m_sin_cos.end(); for (; it != end; ++it) { - emc->insert(it->m_key->get_decl(), + emc->add(it->m_key->get_decl(), m().mk_ite(u().mk_ge(it->m_value.first, mk_real_zero()), u().mk_acos(it->m_value.second), u().mk_add(u().mk_acos(u().mk_uminus(it->m_value.second)), u().mk_pi()))); } @@ -802,18 +799,18 @@ public: m_params(p) { } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(purify_arith_tactic, m, m_params); } - virtual ~purify_arith_tactic() { + ~purify_arith_tactic() override { } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("complete", CPK_BOOL, "(default: true) add constraints to make sure that any interpretation of a underspecified arithmetic operators is a function. The result will include additional uninterpreted functions/constants: /0, div0, mod0, 0^0, neg-root"); r.insert("elim_root_objects", CPK_BOOL, @@ -823,14 +820,10 @@ public: th_rewriter::get_param_descrs(r); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { try { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("purify-arith", *g); TRACE("purify_arith", g->display(tout);); bool produce_proofs = g->proofs_enabled(); @@ -838,10 +831,10 @@ public: bool elim_root_objs = m_params.get_bool("elim_root_objects", true); bool elim_inverses = m_params.get_bool("elim_inverses", true); bool complete = m_params.get_bool("complete", true); - purify_arith_proc proc(*(g.get()), m_util, produce_proofs, elim_root_objs, elim_inverses, complete); - + purify_arith_proc proc(*(g.get()), m_util, produce_proofs, elim_root_objs, elim_inverses, complete); + model_converter_ref mc; proc(mc, produce_models); - + g->add(mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("purify_arith", g->display(tout);); @@ -852,7 +845,7 @@ public: } } - virtual void cleanup() { + void cleanup() override { } }; diff --git a/src/tactic/arith/purify_arith_tactic.h b/src/tactic/arith/purify_arith_tactic.h index bbb62710c..44b4f9b58 100644 --- a/src/tactic/arith/purify_arith_tactic.h +++ b/src/tactic/arith/purify_arith_tactic.h @@ -16,7 +16,7 @@ Abstract: Remarks: - The semantics of division by zero is not specified. Thus, uninterpreted functions are used. An ExRCF procedure may - treat the unintepreted function applications as fresh + treat the uninterpreted function applications as fresh constants. Then, in any model produced by this procedure, the interpretation for division by zero must be checked. diff --git a/src/tactic/arith/recover_01_tactic.cpp b/src/tactic/arith/recover_01_tactic.cpp index d3b9ecc3e..015ee1a86 100644 --- a/src/tactic/arith/recover_01_tactic.cpp +++ b/src/tactic/arith/recover_01_tactic.cpp @@ -32,8 +32,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/th_rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/arith_decl_plugin.h" #include "ast/expr_substitution.h" #include "util/dec_ref_util.h" @@ -70,7 +69,7 @@ class recover_01_tactic : public tactic { bool save_clause(expr * c) { if (!m.is_or(c)) return false; - func_decl * x = 0; + func_decl * x = nullptr; app * cls = to_app(c); if (cls->get_num_args() <= 1 || cls->get_num_args() >= m_cls_max_size) return false; @@ -84,7 +83,7 @@ class recover_01_tactic : public tactic { else if (m.is_not(lit, arg) && is_uninterp_const(arg)) { // negative literal } - else if (x == 0 && m.is_eq(lit, lhs, rhs)) { + else if (x == nullptr && m.is_eq(lit, lhs, rhs)) { // x = k literal if (is_uninterp_const(lhs) && m_util.is_numeral(rhs)) { x = to_app(lhs)->get_decl(); @@ -101,7 +100,7 @@ class recover_01_tactic : public tactic { } } - if (x != 0) { + if (x != nullptr) { var2clauses::obj_map_entry * entry = m_var2clauses.insert_if_not_there2(x, ptr_vector()); if (entry->get_data().m_value.empty() || entry->get_data().m_value.back()->get_num_args() == cls->get_num_args()) { entry->get_data().m_value.push_back(cls); @@ -112,8 +111,7 @@ class recover_01_tactic : public tactic { } // temporary fields used by operator() and process - extension_model_converter * mc1; - filter_model_converter * mc2; + generic_model_converter * gmc; expr_substitution * subst; goal_ref new_goal; obj_map bool2int; @@ -134,7 +132,7 @@ class recover_01_tactic : public tactic { } } } - return 0; + return nullptr; } // Find coeff (the k of literal (x = k)) of clause cls. @@ -199,14 +197,14 @@ class recover_01_tactic : public tactic { SASSERT(is_uninterp_const(atom)); expr * var; if (!bool2int.find(atom, var)) { - var = m.mk_fresh_const(0, m_util.mk_int()); + var = m.mk_fresh_const(nullptr, m_util.mk_int()); new_goal->assert_expr(m_util.mk_le(m_util.mk_numeral(rational(0), true), var)); new_goal->assert_expr(m_util.mk_le(var, m_util.mk_numeral(rational(1), true))); expr * bool_def = m.mk_eq(var, m_util.mk_numeral(rational(1), true)); subst->insert(atom, bool_def); if (m_produce_models) { - mc2->insert(to_app(var)->get_decl()); - mc1->insert(to_app(atom)->get_decl(), bool_def); + gmc->hide(var); + gmc->add(to_app(atom)->get_decl(), bool_def); } m.inc_ref(atom); m.inc_ref(var); @@ -225,7 +223,7 @@ class recover_01_tactic : public tactic { if (clauses.size() < expected_num_clauses) // using < instead of != because we tolerate duplicates return false; app * zero_cls = find_zero_cls(x, clauses); - if (zero_cls == 0) + if (zero_cls == nullptr) return false; buffer found; // marks which idx were found @@ -288,21 +286,18 @@ class recover_01_tactic : public tactic { TRACE("recover_01", tout << x->get_name() << " --> " << mk_ismt2_pp(x_def, m) << "\n";); subst->insert(m.mk_const(x), x_def); if (m_produce_models) { - mc1->insert(x, x_def); + gmc->add(x, x_def); } return true; } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("recover-01", g); fail_if_unsat_core_generation("recover-01", g); m_produce_models = g->models_enabled(); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); tactic_report report("recover-01", *g); bool saved = false; @@ -310,7 +305,9 @@ class recover_01_tactic : public tactic { SASSERT(new_goal->depth() == g->depth()); SASSERT(new_goal->prec() == g->prec()); new_goal->inc_depth(); - + new_goal->add(g->mc()); + new_goal->add(g->pc()); + unsigned sz = g->size(); for (unsigned i = 0; i < sz; i++) { expr * f = g->form(i); @@ -328,9 +325,8 @@ class recover_01_tactic : public tactic { } if (m_produce_models) { - mc1 = alloc(extension_model_converter, m); - mc2 = alloc(filter_model_converter, m); - mc = concat(mc2, mc1); + gmc = alloc(generic_model_converter, m, "recover_01"); + new_goal->add(gmc); } dec_ref_key_values(m, bool2int); @@ -339,25 +335,20 @@ class recover_01_tactic : public tactic { bool recovered = false; expr_substitution _subst(m); subst = &_subst; - var2clauses::iterator it = m_var2clauses.begin(); - var2clauses::iterator end = m_var2clauses.end(); - for (; it != end; ++it) { - if (process(it->m_key, it->m_value)) { + for (auto& kv : m_var2clauses) { + if (process(kv.m_key, kv.m_value)) { recovered = true; counter++; } else { - ptr_vector::iterator it2 = it->m_value.begin(); - ptr_vector::iterator end2 = it->m_value.end(); - for (; it2 != end2; ++it2) { - new_goal->assert_expr(*it2); + for (app* a : kv.m_value) { + new_goal->assert_expr(a); } } } if (!recovered) { result.push_back(g.get()); - mc = 0; return; } @@ -390,38 +381,35 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(recover_01_tactic, m, m_params); } - virtual ~recover_01_tactic() { + ~recover_01_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { th_rewriter::get_param_descrs(r); r.insert("recover_01_max_bits", CPK_UINT, "(default: 10) maximum number of bits to consider in a clause."); } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) override { try { - (*m_imp)(g, result, mc, pc, core); + (*m_imp)(g, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); diff --git a/src/tactic/bv/bit_blaster_model_converter.cpp b/src/tactic/bv/bit_blaster_model_converter.cpp index 373cf3113..5474700c3 100644 --- a/src/tactic/bv/bit_blaster_model_converter.cpp +++ b/src/tactic/bv/bit_blaster_model_converter.cpp @@ -21,6 +21,7 @@ Notes: #include "tactic/model_converter.h" #include "ast/bv_decl_plugin.h" #include "ast/ast_smt2_pp.h" +#include "ast/ast_util.h" /** If TO_BOOL == true, then bit-vectors of size n were blasted into n-tuples of Booleans. @@ -30,23 +31,28 @@ template struct bit_blaster_model_converter : public model_converter { func_decl_ref_vector m_vars; expr_ref_vector m_bits; + func_decl_ref_vector m_newbits; ast_manager & m() const { return m_vars.get_manager(); } - bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits):m_vars(m), m_bits(m) { - obj_map::iterator it = const2bits.begin(); - obj_map::iterator end = const2bits.end(); - for (; it != end; ++it) { - func_decl * v = it->m_key; - expr * bits = it->m_value; + bit_blaster_model_converter( + ast_manager & m, + obj_map const & const2bits, + ptr_vector const& newbits): + m_vars(m), m_bits(m), m_newbits(m) { + for (auto const& kv : const2bits) { + func_decl * v = kv.m_key; + expr * bits = kv.m_value; SASSERT(!TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_MKBV)); SASSERT(TO_BOOL || is_app_of(bits, m.get_family_id("bv"), OP_CONCAT)); m_vars.push_back(v); m_bits.push_back(bits); } + for (func_decl* f : newbits) + m_newbits.push_back(f); } - virtual ~bit_blaster_model_converter() { + ~bit_blaster_model_converter() override { } void collect_bits(obj_hashtable & bits) { @@ -117,8 +123,7 @@ struct bit_blaster_model_converter : public model_converter { SASSERT(is_uninterp_const(bit)); func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); - // remark: if old_model does not assign bit_val, then assume it is false. - if (bit_val != 0 && m().is_true(bit_val)) + if (bit_val != nullptr && m().is_true(bit_val)) val++; } } @@ -133,7 +138,7 @@ struct bit_blaster_model_converter : public model_converter { func_decl * bit_decl = to_app(bit)->get_decl(); expr * bit_val = old_model->get_const_interp(bit_decl); // remark: if old_model does not assign bit_val, then assume it is false. - if (bit_val != 0 && !util.is_zero(bit_val)) + if (bit_val != nullptr && !util.is_zero(bit_val)) val++; } } @@ -142,8 +147,30 @@ struct bit_blaster_model_converter : public model_converter { } } - virtual void operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); + app_ref mk_bv(expr* bs, model& old_model) { + bv_util util(m()); + unsigned bv_sz = to_app(bs)->get_num_args(); + expr_ref_vector args(m()); + app_ref result(m()); + for (expr * bit : *to_app(bs)) { + SASSERT(is_uninterp_const(bit)); + func_decl * bit_decl = to_app(bit)->get_decl(); + expr * bit_val = old_model.get_const_interp(bit_decl); + args.push_back(bit_val ? bit_val : bit); + } + + if (TO_BOOL) { + SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_MKBV)); + result = util.mk_bv(bv_sz, args.c_ptr()); + } + else { + SASSERT(is_app_of(bs, m().get_family_id("bv"), OP_CONCAT)); + result = util.mk_concat(bv_sz, args.c_ptr()); + } + return result; + } + + void operator()(model_ref & md) override { model * new_model = alloc(model, m()); obj_hashtable bits; collect_bits(bits); @@ -152,41 +179,58 @@ struct bit_blaster_model_converter : public model_converter { md = new_model; } - virtual void operator()(model_ref & md) { - operator()(md, 0); + /** + \brief simplisic expansion operator for formulas. + It just adds back bit-vector definitions to the formula whether they are used or not. + + */ + void operator()(expr_ref& fml) override { + unsigned sz = m_vars.size(); + if (sz == 0) return; + expr_ref_vector fmls(m()); + fmls.push_back(fml); + for (unsigned i = 0; i < sz; i++) { + fmls.push_back(m().mk_eq(m().mk_const(m_vars.get(i)), m_bits.get(i))); + } + m_vars.reset(); + m_bits.reset(); + fml = mk_and(fmls); } - virtual void display(std::ostream & out) { - out << "(bit-blaster-model-converter"; + void display(std::ostream & out) override { + for (func_decl * f : m_newbits) + display_del(out, f); unsigned sz = m_vars.size(); - for (unsigned i = 0; i < sz; i++) { - out << "\n (" << m_vars.get(i)->get_name() << " "; - unsigned indent = m_vars.get(i)->get_name().size() + 4; - out << mk_ismt2_pp(m_bits.get(i), m(), indent) << ")"; - } - out << ")" << std::endl; + for (unsigned i = 0; i < sz; i++) + display_add(out, m(), m_vars.get(i), m_bits.get(i)); + } + + void get_units(obj_map& units) override { + // no-op } protected: - bit_blaster_model_converter(ast_manager & m):m_vars(m), m_bits(m) { } + bit_blaster_model_converter(ast_manager & m):m_vars(m), m_bits(m), m_newbits(m) { } public: - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { bit_blaster_model_converter * res = alloc(bit_blaster_model_converter, translator.to()); - for (unsigned i = 0; i < m_vars.size(); i++) - res->m_vars.push_back(translator(m_vars[i].get())); - for (unsigned i = 0; i < m_bits.size(); i++) - res->m_bits.push_back(translator(m_bits[i].get())); + for (func_decl * v : m_vars) + res->m_vars.push_back(translator(v)); + for (expr* b : m_bits) + res->m_bits.push_back(translator(b)); + for (func_decl* f : m_newbits) + res->m_newbits.push_back(translator(f)); return res; } }; -model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits) { - return alloc(bit_blaster_model_converter, m, const2bits); +model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits) { + return const2bits.empty() ? nullptr : alloc(bit_blaster_model_converter, m, const2bits, newbits); } -model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits) { - return alloc(bit_blaster_model_converter, m, const2bits); +model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits) { + return const2bits.empty() ? nullptr : alloc(bit_blaster_model_converter, m, const2bits, newbits); } diff --git a/src/tactic/bv/bit_blaster_model_converter.h b/src/tactic/bv/bit_blaster_model_converter.h index f7dd254b4..057891ec6 100644 --- a/src/tactic/bv/bit_blaster_model_converter.h +++ b/src/tactic/bv/bit_blaster_model_converter.h @@ -21,7 +21,7 @@ Notes: #include "tactic/model_converter.h" -model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits); -model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits); +model_converter * mk_bit_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits); +model_converter * mk_bv1_blaster_model_converter(ast_manager & m, obj_map const & const2bits, ptr_vector const& newbits); #endif diff --git a/src/tactic/bv/bit_blaster_tactic.cpp b/src/tactic/bv/bit_blaster_tactic.cpp index 5c6c43778..3018b49a4 100644 --- a/src/tactic/bv/bit_blaster_tactic.cpp +++ b/src/tactic/bv/bit_blaster_tactic.cpp @@ -51,11 +51,7 @@ class bit_blaster_tactic : public tactic { void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; + goal_ref_buffer & result) { bool proofs_enabled = g->proofs_enabled(); if (proofs_enabled && m_blast_quant) @@ -66,6 +62,7 @@ class bit_blaster_tactic : public tactic { TRACE("before_bit_blaster", g->display(tout);); m_num_steps = 0; + m_rewriter->start_rewrite(); expr_ref new_curr(m()); proof_ref new_pr(m()); unsigned size = g->size(); @@ -81,19 +78,21 @@ class bit_blaster_tactic : public tactic { new_pr = m().mk_modus_ponens(pr, new_pr); } if (curr != new_curr) { - change = true; - TRACE("bit_blaster", tout << mk_pp(curr, m()) << " -> " << mk_pp(new_curr, m()) << "\n";); + change = true; + TRACE("bit_blaster", tout << mk_pp(curr, m()) << " -> " << new_curr << "\n";); g->update(idx, new_curr, new_pr, g->dep(idx)); } } - if (change && g->models_enabled()) - mc = mk_bit_blaster_model_converter(m(), m_rewriter->const2bits()); - else - mc = 0; + if (change && g->models_enabled()) { + obj_map const2bits; + ptr_vector newbits; + m_rewriter->end_rewrite(const2bits, newbits); + g->add(mk_bit_blaster_model_converter(m(), const2bits, newbits)); + } g->inc_depth(); result.push_back(g.get()); - TRACE("after_bit_blaster", g->display(tout); if (mc) mc->display(tout); tout << "\n";); + TRACE("after_bit_blaster", g->display(tout); if (g->mc()) g->mc()->display(tout); tout << "\n";); m_rewriter->cleanup(); } @@ -111,21 +110,21 @@ public: m_imp = alloc(imp, m, m_rewriter, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { SASSERT(!m_rewriter); // assume translation isn't used where rewriter is external. - return alloc(bit_blaster_tactic, m, 0, m_params); + return alloc(bit_blaster_tactic, m, nullptr, m_params); } - virtual ~bit_blaster_tactic() { + ~bit_blaster_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); r.insert("blast_mul", CPK_BOOL, "(default: true) bit-blast multipliers (and dividers, remainders)."); @@ -134,20 +133,17 @@ public: r.insert("blast_full", CPK_BOOL, "(default: false) bit-blast any term with bit-vector sort, this option will make E-matching ineffective in any pattern containing bit-vector terms."); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { try { - (*m_imp)(g, result, mc, pc, core); + (*m_imp)(g, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m(), m_rewriter, m_params); std::swap(d, m_imp); dealloc(d); @@ -157,13 +153,15 @@ public: return m_imp->get_num_steps(); } + }; tactic * mk_bit_blaster_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(bit_blaster_tactic, m, 0, p)); + return clean(alloc(bit_blaster_tactic, m, nullptr, p)); } tactic * mk_bit_blaster_tactic(ast_manager & m, bit_blaster_rewriter* rw, params_ref const & p) { return clean(alloc(bit_blaster_tactic, m, rw, p)); } + diff --git a/src/tactic/bv/bv1_blaster_tactic.cpp b/src/tactic/bv/bv1_blaster_tactic.cpp index e7e374184..b81bc5687 100644 --- a/src/tactic/bv/bv1_blaster_tactic.cpp +++ b/src/tactic/bv/bv1_blaster_tactic.cpp @@ -36,6 +36,7 @@ class bv1_blaster_tactic : public tactic { ast_manager & m_manager; bv_util m_util; obj_map m_const2bits; + ptr_vector m_newbits; expr_ref_vector m_saved; expr_ref m_bit1; expr_ref m_bit0; @@ -106,7 +107,8 @@ class bv1_blaster_tactic : public tactic { sort * b = butil().mk_sort(1); ptr_buffer bits; for (unsigned i = 0; i < bv_size; i++) { - bits.push_back(m().mk_fresh_const(0, b)); + bits.push_back(m().mk_fresh_const(nullptr, b)); + m_newbits.push_back(to_app(bits.back())->get_decl()); } r = butil().mk_concat(bits.size(), bits.c_ptr()); m_saved.push_back(r); @@ -253,7 +255,7 @@ class bv1_blaster_tactic : public tactic { } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; if (num == 0 && f->get_family_id() == null_family_id && butil().is_bv_sort(f->get_range())) { mk_const(f, result); return BR_DONE; @@ -379,11 +381,7 @@ class bv1_blaster_tactic : public tactic { void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; + goal_ref_buffer & result) { if (!is_target(*g)) throw tactic_exception("bv1 blaster cannot be applied to goal"); @@ -409,7 +407,7 @@ class bv1_blaster_tactic : public tactic { } if (g->models_enabled()) - mc = mk_bv1_blaster_model_converter(m(), m_rw.cfg().m_const2bits); + g->add(mk_bv1_blaster_model_converter(m(), m_rw.cfg().m_const2bits, m_rw.cfg().m_newbits)); g->inc_depth(); result.push_back(g.get()); m_rw.cfg().cleanup(); @@ -426,20 +424,20 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(bv1_blaster_tactic, m, m_params); } - virtual ~bv1_blaster_tactic() { + ~bv1_blaster_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); } @@ -454,15 +452,12 @@ public: It also does not support quantifiers. Return a model_converter that converts any model for the updated set into a model for the old set. */ - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(g, result, mc, pc, core); + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { + (*m_imp)(g, result); } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m(), m_params); std::swap(d, m_imp); dealloc(d); @@ -480,7 +475,7 @@ tactic * mk_bv1_blaster_tactic(ast_manager & m, params_ref const & p) { class is_qfbv_eq_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { bv1_blaster_tactic t(g.m()); return t.is_target(g); diff --git a/src/tactic/bv/bv_bound_chk_tactic.cpp b/src/tactic/bv/bv_bound_chk_tactic.cpp index b71b44bd0..75b772720 100644 --- a/src/tactic/bv/bv_bound_chk_tactic.cpp +++ b/src/tactic/bv/bv_bound_chk_tactic.cpp @@ -68,7 +68,7 @@ struct bv_bound_chk_rewriter_cfg : public default_rewriter_cfg { } br_status reduce_app_core(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; const family_id fid = f->get_family_id(); if (fid != m_b_rw.get_fid()) return BR_FAILED; bv_bounds bvb(m()); @@ -111,7 +111,7 @@ struct bv_bound_chk_rewriter : public rewriter_tpl { updt_params(p); } - virtual ~bv_bound_chk_rewriter() {} + ~bv_bound_chk_rewriter() override {} void updt_params(params_ref const & _p) { m_cfg.updt_params(_p); @@ -135,17 +135,13 @@ class bv_bound_chk_tactic : public tactic { bv_bound_chk_stats m_stats; public: bv_bound_chk_tactic(ast_manager & m, params_ref const & p); - virtual ~bv_bound_chk_tactic(); - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core); - virtual tactic * translate(ast_manager & m); - virtual void updt_params(params_ref const & p); - void cleanup(); - void collect_statistics(statistics & st) const; - void reset_statistics(); + void operator()(goal_ref const & g, goal_ref_buffer & result) override; + ~bv_bound_chk_tactic() override; + tactic * translate(ast_manager & m) override; + void updt_params(params_ref const & p) override; + void cleanup() override; + void collect_statistics(statistics & st) const override; + void reset_statistics() override; }; class bv_bound_chk_tactic::imp { @@ -197,16 +193,12 @@ bv_bound_chk_tactic::~bv_bound_chk_tactic() { dealloc(m_imp); } -void bv_bound_chk_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { +void bv_bound_chk_tactic::operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("bv-bound-chk", g); fail_if_unsat_core_generation("bv-bound-chk", g); TRACE("bv-bound-chk", g->display(tout << "before:"); tout << std::endl;); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); m_imp->operator()(g); g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index a279e441b..2704d92ff 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -25,7 +25,7 @@ Author: #include "ast/ast_pp.h" #include -static uint64 uMaxInt(unsigned sz) { +static uint64_t uMaxInt(unsigned sz) { SASSERT(sz <= 64); return ULLONG_MAX >> (64u - sz); } @@ -35,12 +35,12 @@ namespace { struct interval { // l < h: [l, h] // l > h: [0, h] U [l, UMAX_INT] - uint64 l, h; + uint64_t l, h; unsigned sz; bool tight; interval() {} - interval(uint64 l, uint64 h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { + interval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { // canonicalize full set if (is_wrapped() && l == h + 1) { this->l = 0; @@ -183,7 +183,7 @@ namespace { svector m_expr_vars; svector m_bound_exprs; - bool is_number(expr *e, uint64& n, unsigned& sz) const { + bool is_number(expr *e, uint64_t& n, unsigned& sz) const { rational r; if (m_bv.is_numeral(e, r, sz) && sz <= 64) { n = r.get_uint64(); @@ -193,8 +193,8 @@ namespace { } bool is_bound(expr *e, expr*& v, interval& b) const { - uint64 n; - expr *lhs = 0, *rhs = 0; + uint64_t n; + expr *lhs = nullptr, *rhs = nullptr; unsigned sz; if (m_bv.is_bv_ule(e, lhs, rhs)) { @@ -305,7 +305,7 @@ namespace { updt_params(p); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_propagate_eq = p.get_bool("propagate_eq", false); } @@ -313,7 +313,7 @@ namespace { r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); } - virtual ~bv_bounds_simplifier() { + ~bv_bounds_simplifier() override { for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) { dealloc(m_expr_vars[i]); } @@ -322,7 +322,7 @@ namespace { } } - virtual bool assert_expr(expr * t, bool sign) { + bool assert_expr(expr * t, bool sign) override { while (m.is_not(t, t)) { sign = !sign; } @@ -353,7 +353,7 @@ namespace { return true; } - virtual bool simplify(expr* t, expr_ref& result) { + bool simplify(expr* t, expr_ref& result) override { expr* t1; interval b; @@ -382,7 +382,7 @@ namespace { } interval ctx, intr; - result = 0; + result = nullptr; if (b.is_full() && b.tight) { result = m.mk_true(); @@ -465,7 +465,7 @@ namespace { return false; } - virtual bool may_simplify(expr* t) { + bool may_simplify(expr* t) override { if (m_bv.is_numeral(t)) return false; @@ -504,7 +504,7 @@ namespace { return false; } - virtual void pop(unsigned num_scopes) { + void pop(unsigned num_scopes) override { TRACE("bv", tout << "pop: " << num_scopes << "\n";); if (m_scopes.empty()) return; @@ -526,11 +526,11 @@ namespace { m_scopes.shrink(target); } - virtual simplifier * translate(ast_manager & m) { + simplifier * translate(ast_manager & m) override { return alloc(bv_bounds_simplifier, m, m_params); } - virtual unsigned scope_level() const { + unsigned scope_level() const override { return m_scopes.size(); } }; @@ -550,7 +550,7 @@ namespace { svector m_expr_vars; svector m_bound_exprs; - bool is_number(expr *e, uint64& n, unsigned& sz) const { + bool is_number(expr *e, uint64_t& n, unsigned& sz) const { rational r; if (m_bv.is_numeral(e, r, sz) && sz <= 64) { n = r.get_uint64(); @@ -560,8 +560,8 @@ namespace { } bool is_bound(expr *e, expr*& v, interval& b) const { - uint64 n; - expr *lhs = 0, *rhs = 0; + uint64_t n; + expr *lhs = nullptr, *rhs = nullptr; unsigned sz = 0; if (m_bv.is_bv_ule(e, lhs, rhs)) { @@ -622,7 +622,7 @@ namespace { r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); } - virtual ~dom_bv_bounds_simplifier() { + ~dom_bv_bounds_simplifier() override { for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) { dealloc(m_expr_vars[i]); } @@ -631,7 +631,7 @@ namespace { } } - virtual bool assert_expr(expr * t, bool sign) { + bool assert_expr(expr * t, bool sign) override { while (m.is_not(t, t)) { sign = !sign; } @@ -662,7 +662,7 @@ namespace { return true; } - virtual void operator()(expr_ref& r) { + void operator()(expr_ref& r) override { expr* t1, * t = r; interval b; @@ -781,7 +781,7 @@ namespace { return false; } - virtual void pop(unsigned num_scopes) { + void pop(unsigned num_scopes) override { TRACE("bv", tout << "pop: " << num_scopes << "\n";); if (m_scopes.empty()) return; @@ -803,11 +803,11 @@ namespace { m_scopes.shrink(target); } - virtual dom_simplifier * translate(ast_manager & m) { + dom_simplifier * translate(ast_manager & m) override { return alloc(dom_bv_bounds_simplifier, m, m_params); } - virtual unsigned scope_level() const { + unsigned scope_level() const override { return m_scopes.size(); } diff --git a/src/tactic/bv/bv_size_reduction_tactic.cpp b/src/tactic/bv/bv_size_reduction_tactic.cpp index ec4c21fe8..964102825 100644 --- a/src/tactic/bv/bv_size_reduction_tactic.cpp +++ b/src/tactic/bv/bv_size_reduction_tactic.cpp @@ -24,8 +24,7 @@ Notes: #include "tactic/tactical.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" class bv_size_reduction_tactic : public tactic { @@ -34,15 +33,15 @@ class bv_size_reduction_tactic : public tactic { public: bv_size_reduction_tactic(ast_manager & m); - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(bv_size_reduction_tactic, m); } - virtual ~bv_size_reduction_tactic(); + ~bv_size_reduction_tactic() override; - virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); + void operator()(goal_ref const & g, goal_ref_buffer & result) override; - virtual void cleanup(); + void cleanup() override; }; tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p) { @@ -51,7 +50,7 @@ tactic * mk_bv_size_reduction_tactic(ast_manager & m, params_ref const & p) { struct bv_size_reduction_tactic::imp { typedef rational numeral; - typedef extension_model_converter bv_size_reduction_mc; + typedef generic_model_converter bv_size_reduction_mc; ast_manager & m; bv_util m_util; @@ -60,7 +59,7 @@ struct bv_size_reduction_tactic::imp { obj_map m_unsigned_lowers; obj_map m_unsigned_uppers; ref m_mc; - filter_model_converter_ref m_fmc; + generic_model_converter_ref m_fmc; scoped_ptr m_replacer; bool m_produce_models; @@ -184,8 +183,8 @@ struct bv_size_reduction_tactic::imp { return; TRACE("before_bv_size_reduction", g.display(tout);); m_produce_models = g.models_enabled(); - mc = 0; - m_mc = 0; + mc = nullptr; + m_mc = nullptr; unsigned num_reduced = 0; { tactic_report report("bv-size-reduction", g); @@ -211,11 +210,11 @@ struct bv_size_reduction_tactic::imp { unsigned bv_sz = m_util.get_bv_size(v); numeral l = m_util.norm(it->m_value, bv_sz, true); obj_map::obj_map_entry * entry = m_signed_uppers.find_core(v); - if (entry != 0) { + if (entry != nullptr) { numeral u = m_util.norm(entry->get_data().m_value, bv_sz, true); TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << "\n";); - expr * new_def = 0; - app * new_const = 0; + expr * new_def = nullptr; + app * new_const = nullptr; if (l > u) { g.assert_expr(m.mk_false()); return; @@ -235,7 +234,7 @@ struct bv_size_reduction_tactic::imp { unsigned i_nb = l_nb; TRACE("bv_size_reduction", tout << " l <= " << v->get_decl()->get_name() << " <= u <= 0 " << " --> " << i_nb << " bits\n";); if (i_nb < v_nb) { - new_const = m.mk_fresh_const(0, m_util.mk_sort(i_nb)); + new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(i_nb)); new_def = m_util.mk_concat(m_util.mk_numeral(numeral(-1), v_nb - i_nb), new_const); } } @@ -245,7 +244,7 @@ struct bv_size_reduction_tactic::imp { unsigned i_nb = ((l_nb > u_nb) ? l_nb : u_nb) + 1; TRACE("bv_size_reduction", tout << " l <= " << v->get_decl()->get_name() << " <= 0 <= u " << " --> " << i_nb << " bits\n";); if (i_nb < v_nb) { - new_const = m.mk_fresh_const(0, m_util.mk_sort(i_nb)); + new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(i_nb)); new_def = m_util.mk_sign_extend(v_nb - i_nb, new_const); } } @@ -256,7 +255,7 @@ struct bv_size_reduction_tactic::imp { unsigned v_nb = m_util.get_bv_size(v); TRACE("bv_size_reduction", tout << l << " <= " << v->get_decl()->get_name() << " <= " << u << " --> " << u_nb << " bits\n";); if (u_nb < v_nb) { - new_const = m.mk_fresh_const(0, m_util.mk_sort(u_nb)); + new_const = m.mk_fresh_const(nullptr, m_util.mk_sort(u_nb)); new_def = m_util.mk_concat(m_util.mk_numeral(numeral(0), v_nb - u_nb), new_const); } } @@ -266,12 +265,12 @@ struct bv_size_reduction_tactic::imp { subst.insert(v, new_def); if (m_produce_models) { if (!m_mc) - m_mc = alloc(bv_size_reduction_mc, m); - m_mc->insert(v->get_decl(), new_def); + m_mc = alloc(bv_size_reduction_mc, m, "bv_size_reduction"); + m_mc->add(v, new_def); if (!m_fmc && new_const) - m_fmc = alloc(filter_model_converter, m); + m_fmc = alloc(generic_model_converter, m, "bv_size_reduction"); if (new_const) - m_fmc->insert(new_const->get_decl()); + m_fmc->hide(new_const); } num_reduced++; } @@ -335,9 +334,9 @@ struct bv_size_reduction_tactic::imp { m_mc = alloc(bv_size_reduction_mc, m); m_mc->insert(v->get_decl(), new_def); if (!m_fmc && new_const) - m_fmc = alloc(filter_model_converter, m); + m_fmc = alloc(generic_model_converter, m, "bv_size_reduction"); if (new_const) - m_fmc->insert(new_const->get_decl()); + m_fmc->hide(new_const); } num_reduced++; TRACE("bv_size_reduction", tout << "New definition = " << mk_ismt2_pp(new_def, m) << "\n";); @@ -365,8 +364,8 @@ struct bv_size_reduction_tactic::imp { if (m_fmc) { mc = concat(m_fmc.get(), mc.get()); } - m_mc = 0; - m_fmc = 0; + m_mc = nullptr; + m_fmc = nullptr; } report_tactic_progress(":bv-reduced", num_reduced); TRACE("after_bv_size_reduction", g.display(tout); if (m_mc) m_mc->display(tout);); @@ -383,16 +382,15 @@ bv_size_reduction_tactic::~bv_size_reduction_tactic() { } void bv_size_reduction_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("bv-size-reduction", g); fail_if_unsat_core_generation("bv-size-reduction", g); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); + model_converter_ref mc; m_imp->operator()(*(g.get()), mc); g->inc_depth(); + g->add(mc.get()); result.push_back(g.get()); SASSERT(g->is_well_sorted()); } diff --git a/src/tactic/bv/bvarray2uf_rewriter.cpp b/src/tactic/bv/bvarray2uf_rewriter.cpp index b92092739..3ca296eb7 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.cpp +++ b/src/tactic/bv/bvarray2uf_rewriter.cpp @@ -37,8 +37,7 @@ bvarray2uf_rewriter_cfg::bvarray2uf_rewriter_cfg(ast_manager & m, params_ref con m_bindings(m), m_bv_util(m), m_array_util(m), - m_emc(0), - m_fmc(0), + m_fmc(nullptr), extra_assertions(m) { updt_params(p); // We need to make sure that the mananger has the BV and array plugins loaded. @@ -108,19 +107,18 @@ func_decl_ref bvarray2uf_rewriter_cfg::mk_uf_for_array(expr * e) { if (m_array_util.is_as_array(e)) return func_decl_ref(static_cast(to_app(e)->get_decl()->get_parameter(0).get_ast()), m_manager); else { - func_decl * bv_f = 0; + func_decl * bv_f = nullptr; if (!m_arrays_fs.find(e, bv_f)) { sort * domain = get_index_sort(e); sort * range = get_value_sort(e); bv_f = m_manager.mk_fresh_func_decl("f_t", "", 1, &domain, range); TRACE("bvarray2uf_rw", tout << "for " << mk_ismt2_pp(e, m_manager) << " new func_decl is " << mk_ismt2_pp(bv_f, m_manager) << std::endl; ); if (is_uninterp_const(e)) { - if (m_emc) - m_emc->insert(to_app(e)->get_decl(), - m_array_util.mk_as_array(bv_f)); + if (m_fmc) + m_fmc->add(e, m_array_util.mk_as_array(bv_f)); } else if (m_fmc) - m_fmc->insert(bv_f); + m_fmc->hide(bv_f); m_arrays_fs.insert(e, bv_f); m_manager.inc_ref(e); m_manager.inc_ref(bv_f); @@ -184,19 +182,18 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr func_decl_ref itefd(m_manager); e = m_manager.mk_ite(c, f_ta, f_fa); - func_decl * bv_f = 0; + func_decl * bv_f = nullptr; if (!m_arrays_fs.find(f_a, bv_f)) { sort * domain = get_index_sort(args[1]); sort * range = get_value_sort(args[1]); bv_f = m_manager.mk_fresh_func_decl("f_t", "", 1, &domain, range); TRACE("bvarray2uf_rw", tout << mk_ismt2_pp(e, m_manager) << " -> " << bv_f->get_name() << std::endl; ); if (is_uninterp_const(e)) { - if (m_emc) - m_emc->insert(e->get_decl(), - m_array_util.mk_as_array(bv_f)); + if (m_fmc) + m_fmc->add(e, m_array_util.mk_as_array(bv_f)); } else if (m_fmc) - m_fmc->insert(bv_f); + m_fmc->hide(bv_f); m_arrays_fs.insert(e, bv_f); m_manager.inc_ref(e); m_manager.inc_ref(bv_f); diff --git a/src/tactic/bv/bvarray2uf_rewriter.h b/src/tactic/bv/bvarray2uf_rewriter.h index bc4014b5b..9984d3469 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.h +++ b/src/tactic/bv/bvarray2uf_rewriter.h @@ -21,8 +21,7 @@ Notes: #define BVARRAY2UF_REWRITER_H_ #include "ast/rewriter/rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" class bvarray2uf_rewriter_cfg : public default_rewriter_cfg { ast_manager & m_manager; @@ -30,8 +29,7 @@ class bvarray2uf_rewriter_cfg : public default_rewriter_cfg { sort_ref_vector m_bindings; bv_util m_bv_util; array_util m_array_util; - extension_model_converter * m_emc; - filter_model_converter * m_fmc; + generic_model_converter * m_fmc; obj_map m_arrays_fs; @@ -59,7 +57,7 @@ public: expr_ref_vector extra_assertions; - void set_mcs(extension_model_converter * emc, filter_model_converter * fmc) { m_emc = emc; m_fmc = fmc; } + void set_mcs(generic_model_converter * fmc) { m_fmc = fmc; } protected: sort * get_index_sort(expr * e); @@ -79,7 +77,7 @@ struct bvarray2uf_rewriter : public rewriter_tpl { m_cfg(m, p) { } - void set_mcs(extension_model_converter * emc, filter_model_converter * fmc) { m_cfg.set_mcs(emc, fmc); } + void set_mcs(generic_model_converter * fmc) { m_cfg.set_mcs(fmc); } }; #endif diff --git a/src/tactic/bv/bvarray2uf_tactic.cpp b/src/tactic/bv/bvarray2uf_tactic.cpp index 87f43ae8d..6bda3969b 100644 --- a/src/tactic/bv/bvarray2uf_tactic.cpp +++ b/src/tactic/bv/bvarray2uf_tactic.cpp @@ -20,8 +20,7 @@ Notes: #include "tactic/tactical.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/expr_replacer.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_smt2_pp.h" #include "tactic/bv/bvarray2uf_tactic.h" @@ -54,24 +53,21 @@ class bvarray2uf_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); tactic_report report("bvarray2uf", *g); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); fail_if_unsat_core_generation("bvarray2uf", g); TRACE("bvarray2uf", tout << "Before: " << std::endl; g->display(tout); ); m_produce_models = g->models_enabled(); + model_converter_ref mc; if (m_produce_models) { - extension_model_converter * emc = alloc(extension_model_converter, m_manager); - filter_model_converter * fmc = alloc(filter_model_converter, m_manager); - mc = concat(emc, fmc); - m_rw.set_mcs(emc, fmc); + generic_model_converter * fmc = alloc(generic_model_converter, m_manager, "bvarray2uf"); + mc = fmc; + m_rw.set_mcs(fmc); } @@ -95,6 +91,7 @@ class bvarray2uf_tactic : public tactic { g->assert_expr(m_rw.m_cfg.extra_assertions[i].get()); g->inc_depth(); + g->add(mc.get()); result.push_back(g.get()); TRACE("bvarray2uf", tout << "After: " << std::endl; g->display(tout);); SASSERT(g->is_well_sorted()); @@ -113,32 +110,29 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(bvarray2uf_tactic, m, m_params); } - virtual ~bvarray2uf_tactic() { + ~bvarray2uf_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_produce_models(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); diff --git a/src/tactic/bv/dt2bv_tactic.cpp b/src/tactic/bv/dt2bv_tactic.cpp index fe59480e7..39d8cf5bb 100644 --- a/src/tactic/bv/dt2bv_tactic.cpp +++ b/src/tactic/bv/dt2bv_tactic.cpp @@ -21,12 +21,10 @@ Revision History: #include "tactic/bv/dt2bv_tactic.h" #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/datatype_decl_plugin.h" #include "ast/bv_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_util.h" #include "ast/rewriter/enum2bv_rewriter.h" @@ -95,8 +93,8 @@ class dt2bv_tactic : public tactic { struct sort_pred : public i_sort_pred { dt2bv_tactic& m_t; sort_pred(dt2bv_tactic& t): m_t(t) {} - virtual ~sort_pred() {} - virtual bool operator()(sort* s) { + ~sort_pred() override {} + bool operator()(sort* s) override { return m_t.m_fd_sorts.contains(s); } }; @@ -107,22 +105,17 @@ public: dt2bv_tactic(ast_manager& m, params_ref const& p): m(m), m_params(p), m_dt(m), m_bv(m), m_is_fd(*this) {} - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(dt2bv_tactic, m, m_params); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; + void operator()(goal_ref const & g, goal_ref_buffer & result) override { bool produce_proofs = g->proofs_enabled(); tactic_report report("dt2bv", *g); unsigned size = g->size(); @@ -131,13 +124,10 @@ public: for (unsigned i = 0; i < size; ++i) { quick_for_each_expr(proc, visited, g->form(i)); } - obj_hashtable::iterator it = m_non_fd_sorts.begin(), end = m_non_fd_sorts.end(); - for (; it != end; ++it) { - m_fd_sorts.remove(*it); - } + for (sort* s : m_non_fd_sorts) + m_fd_sorts.remove(s); if (!m_fd_sorts.empty()) { - ref ext = alloc(extension_model_converter, m); - ref filter = alloc(filter_model_converter, m); + ref filter = alloc(generic_model_converter, m, "dt2bv"); enum2bv_rewriter rw(m, m_params); rw.set_is_fd(&m_is_fd); expr_ref new_curr(m); @@ -152,23 +142,14 @@ public: } expr_ref_vector bounds(m); rw.flush_side_constraints(bounds); - for (unsigned i = 0; i < bounds.size(); ++i) { - g->assert_expr(bounds[i].get()); - } - { - obj_map::iterator it = rw.enum2bv().begin(), end = rw.enum2bv().end(); - for (; it != end; ++it) { - filter->insert(it->m_value); - } - } - { - obj_map::iterator it = rw.enum2def().begin(), end = rw.enum2def().end(); - for (; it != end; ++it) { - ext->insert(it->m_key, it->m_value); - } - } + for (expr* b : bounds) + g->assert_expr(b); + for (auto const& kv : rw.enum2bv()) + filter->hide(kv.m_value); + for (auto const& kv : rw.enum2def()) + filter->add(kv.m_key, kv.m_value); - mc = concat(filter.get(), ext.get()); + g->add(filter.get()); report_tactic_progress(":fd-num-translated", rw.num_translated()); } g->inc_depth(); @@ -177,7 +158,7 @@ public: SASSERT(g->is_well_sorted()); } - virtual void cleanup() { + void cleanup() override { m_fd_sorts.reset(); m_non_fd_sorts.reset(); } diff --git a/src/tactic/bv/elim_small_bv_tactic.cpp b/src/tactic/bv/elim_small_bv_tactic.cpp index ab4b3920d..9959780b7 100644 --- a/src/tactic/bv/elim_small_bv_tactic.cpp +++ b/src/tactic/bv/elim_small_bv_tactic.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "util/cooperate.h" #include "ast/bv_decl_plugin.h" #include "ast/used_vars.h" @@ -35,7 +35,7 @@ class elim_small_bv_tactic : public tactic { params_ref m_params; bv_util m_util; th_rewriter m_simp; - ref m_mc; + ref m_mc; goal * m_goal; unsigned m_max_bits; unsigned long long m_max_steps; @@ -52,7 +52,7 @@ class elim_small_bv_tactic : public tactic { m_bindings(_m), m_num_eliminated(0) { updt_params(p); - m_goal = 0; + m_goal = nullptr; m_max_steps = UINT_MAX; } @@ -77,13 +77,13 @@ class elim_small_bv_tactic : public tactic { expr_ref res(m); expr_ref_vector substitution(m); - substitution.resize(num_decls, 0); + substitution.resize(num_decls, nullptr); substitution[num_decls - idx - 1] = replacement; // (VAR 0) is in the first position of substitution; (VAR num_decls-1) is in the last position. for (unsigned i = 0; i < max_var_idx_p1; i++) - substitution.push_back(0); + substitution.push_back(nullptr); // (VAR num_decls) ... (VAR num_decls+sz-1); are in positions num_decls .. num_decls+sz-1 @@ -175,7 +175,7 @@ class elim_small_bv_tactic : public tactic { TRACE("elim_small_bv", tout << "elimination result: " << mk_ismt2_pp(result, m) << std::endl; ); - result_pr = 0; // proofs NIY + result_pr = nullptr; // proofs NIY m_bindings.shrink(old_sz); return true; } @@ -224,13 +224,8 @@ class elim_small_bv_tactic : public tactic { m_rw.cfg().updt_params(p); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("elim-small-bv", *g); bool produce_proofs = g->proofs_enabled(); fail_if_proof_generation("elim-small-bv", g); @@ -250,7 +245,7 @@ class elim_small_bv_tactic : public tactic { } g->update(idx, new_curr, new_pr, g->dep(idx)); } - mc = m_rw.m_cfg.m_mc.get(); + g->add(m_rw.m_cfg.m_mc.get()); report_tactic_progress(":elim-small-bv-num-eliminated", m_rw.m_cfg.m_num_eliminated); g->inc_depth(); @@ -268,34 +263,31 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(elim_small_bv_tactic, m, m_params); } - virtual ~elim_small_bv_tactic() { + ~elim_small_bv_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); r.insert("max_bits", CPK_UINT, "(default: 4) maximum bit-vector size of quantified bit-vectors to be eliminated."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m; m_imp->~imp(); m_imp = new (m_imp) imp(m, m_params); diff --git a/src/tactic/bv/max_bv_sharing_tactic.cpp b/src/tactic/bv/max_bv_sharing_tactic.cpp index becaed276..98936ccad 100644 --- a/src/tactic/bv/max_bv_sharing_tactic.cpp +++ b/src/tactic/bv/max_bv_sharing_tactic.cpp @@ -84,7 +84,7 @@ class max_bv_sharing_tactic : public tactic { return m().mk_app(f, arg1, arg2); if (s.contains(expr_pair(arg2, arg1))) return m().mk_app(f, arg2, arg1); - return 0; + return nullptr; } struct ref_count_lt { @@ -106,10 +106,10 @@ class max_bv_sharing_tactic : public tactic { ptr_buffer _args; bool first = false; - expr * num = 0; + expr * num = nullptr; for (unsigned i = 0; i < num_args; i++) { expr * arg = args[i]; - if (num == 0 && m_util.is_numeral(arg)) { + if (num == nullptr && m_util.is_numeral(arg)) { if (i == 0) first = true; num = arg; } @@ -128,7 +128,7 @@ class max_bv_sharing_tactic : public tactic { for (unsigned i = 0; i < num_args - 1; i++) { for (unsigned j = i + 1; j < num_args; j++) { expr * r = reuse(s, f, _args[i], _args[j]); - if (r != 0) { + if (r != nullptr) { TRACE("bv_sharing_detail", tout << "reusing args: " << i << " " << j << "\n";); _args[i] = r; SASSERT(num_args > 1); @@ -186,7 +186,7 @@ class max_bv_sharing_tactic : public tactic { } num_args = j; if (num_args == 1) { - if (num == 0) { + if (num == nullptr) { result = _args[0]; } else { @@ -209,7 +209,7 @@ class max_bv_sharing_tactic : public tactic { case OP_BMUL: case OP_BOR: case OP_BXOR: - result_pr = 0; + result_pr = nullptr; return reduce_ac_app(f, num, args, result); default: return BR_FAILED; @@ -237,12 +237,8 @@ class max_bv_sharing_tactic : public tactic { ast_manager & m() const { return m_rw.m(); } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("max-bv-sharing", *g); bool produce_proofs = g->proofs_enabled(); @@ -278,35 +274,32 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(max_bv_sharing_tactic, m, m_params); } - virtual ~max_bv_sharing_tactic() { + ~max_bv_sharing_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); r.insert("max_args", CPK_UINT, "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m(); params_ref p = std::move(m_params); m_imp->~imp(); diff --git a/src/tactic/converter.h b/src/tactic/converter.h index 9de8218c3..5db95c320 100644 --- a/src/tactic/converter.h +++ b/src/tactic/converter.h @@ -29,17 +29,18 @@ public: converter():m_ref_count(0) {} virtual ~converter() {} - void inc_ref() { ++m_ref_count; } + void inc_ref() { ++m_ref_count; } + void dec_ref() { --m_ref_count; - if (m_ref_count == 0) + if (m_ref_count == 0) { dealloc(this); + } } virtual void cancel() {} - // for debugging purposes - virtual void display(std::ostream & out) {} + virtual void display(std::ostream & out) = 0; }; template @@ -58,20 +59,18 @@ protected: public: concat_converter(T * c1, T * c2):m_c1(c1), m_c2(c2) {} - virtual ~concat_converter() {} + ~concat_converter() override {} - virtual void cancel() { + void cancel() override { m_c2->cancel(); m_c1->cancel(); } virtual char const * get_name() const = 0; - virtual void display(std::ostream & out) { - out << "(" << get_name() << "\n"; + void display(std::ostream & out) override { m_c1->display(out); m_c2->display(out); - out << ")\n"; } }; @@ -84,12 +83,11 @@ protected: template T * translate_core(ast_translation & translator) { - T * t1 = m_c1 ? m_c1->translate(translator) : 0; + T * t1 = m_c1 ? m_c1->translate(translator) : nullptr; ptr_buffer t2s; - unsigned num = m_c2s.size(); - for (unsigned i = 0; i < num; i++) - t2s.push_back(m_c2s[i] ? m_c2s[i]->translate(translator) : 0); - return alloc(T2, t1, num, t2s.c_ptr(), m_szs.c_ptr()); + for (T* c : m_c2s) + t2s.push_back(c ? c->translate(translator) : nullptr); + return alloc(T2, t1, m_c2s.size(), t2s.c_ptr(), m_szs.c_ptr()); } public: @@ -104,37 +102,25 @@ public: } } - virtual ~concat_star_converter() { - unsigned sz = m_c2s.size(); - for (unsigned i = 0; i < sz; i++) { - T * c2 = m_c2s[i]; - if (c2) - c2->dec_ref(); - } + ~concat_star_converter() override { + for (T* c : m_c2s) + if (c) c->dec_ref(); } - virtual void cancel() { + void cancel() override { if (m_c1) m_c1->cancel(); - unsigned num = m_c2s.size(); - for (unsigned i = 0; i < num; i++) { - if (m_c2s[i]) - m_c2s[i]->cancel(); - } + for (T* c : m_c2s) + if (c) c->cancel(); } virtual char const * get_name() const = 0; - virtual void display(std::ostream & out) { - out << "(" << get_name() << "\n"; + void display(std::ostream & out) override { if (m_c1) m_c1->display(out); - out << "(\n"; - unsigned num = m_c2s.size(); - for (unsigned i = 0; i < num; i++) - if (m_c2s[i]) - m_c2s[i]->display(out); - out << "))\n"; + for (T* c : m_c2s) + if (c) c->display(out); } }; diff --git a/src/tactic/core/blast_term_ite_tactic.cpp b/src/tactic/core/blast_term_ite_tactic.cpp index ae0ea019b..eefb9418e 100644 --- a/src/tactic/core/blast_term_ite_tactic.cpp +++ b/src/tactic/core/blast_term_ite_tactic.cpp @@ -19,7 +19,6 @@ Notes: #include "tactic/tactical.h" #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" #include "util/cooperate.h" #include "ast/scoped_proof.h" @@ -34,50 +33,65 @@ Notes: class blast_term_ite_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { - ast_manager& m; - unsigned long long m_max_memory; // in bytes - unsigned m_num_fresh; // number of expansions + ast_manager& m; + unsigned long long m_max_memory; // in bytes + unsigned m_num_fresh; // number of expansions + unsigned m_max_steps; + unsigned m_max_inflation; + unsigned m_init_term_size; rw_cfg(ast_manager & _m, params_ref const & p): m(_m), - m_num_fresh(0) { + m_num_fresh(0), + m_max_steps(UINT_MAX), + m_max_inflation(UINT_MAX), + m_init_term_size(0) { updt_params(p); } void updt_params(params_ref const & p) { - m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_max_memory = megabytes_to_bytes(p.get_uint("max_memory", UINT_MAX)); + m_max_steps = p.get_uint("max_steps", UINT_MAX); + m_max_inflation = p.get_uint("max_inflation", UINT_MAX); // multiplicative factor of initial term size. } + + bool max_steps_exceeded(unsigned num_steps) const { cooperate("blast term ite"); // if (memory::get_allocation_size() > m_max_memory) // throw tactic_exception(TACTIC_MAX_MEMORY_MSG); - return false; + return num_steps >= m_max_steps; } br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) { if (m.is_ite(f)) { return BR_FAILED; } + if (m_max_inflation < UINT_MAX && + m_init_term_size > 0 && + m_max_inflation * m_init_term_size < m_num_fresh) + return BR_FAILED; + for (unsigned i = 0; i < num_args; ++i) { expr* c, *t, *e; if (!m.is_bool(args[i]) && m.is_ite(args[i], c, t, e)) { - // enable_trace("blast_term_ite"); TRACE("blast_term_ite", result = m.mk_app(f, num_args, args); tout << result << "\n";); expr_ref e1(m), e2(m); ptr_vector args1(num_args, args); args1[i] = t; - ++m_num_fresh; e1 = m.mk_app(f, num_args, args1.c_ptr()); - if (m.are_equal(t,e)) { + if (m.are_equal(t, e)) { result = e1; return BR_REWRITE1; } - args1[i] = e; - e2 = m.mk_app(f, num_args, args1.c_ptr()); - result = m.mk_app(f, num_args, args); - result = m.mk_ite(c, e1, e2); - return BR_REWRITE3; + else { + args1[i] = e; + e2 = m.mk_app(f, num_args, args1.c_ptr()); + result = m.mk_ite(c, e1, e2); + ++m_num_fresh; + return BR_REWRITE3; + } } } return BR_FAILED; @@ -108,27 +122,27 @@ class blast_term_ite_tactic : public tactic { m(_m), m_rw(m, p) { } - - + void updt_params(params_ref const & p) { - m_rw.cfg().updt_params(p); + m_rw.m_cfg.updt_params(p); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("blast-term-ite", *g); bool produce_proofs = g->proofs_enabled(); expr_ref new_curr(m); proof_ref new_pr(m); unsigned size = g->size(); + unsigned num_fresh = 0; for (unsigned idx = 0; idx < size; idx++) { expr * curr = g->form(idx); + if (m_rw.m_cfg.m_max_inflation < UINT_MAX) { + m_rw.m_cfg.m_init_term_size = get_num_exprs(curr); + num_fresh += m_rw.m_cfg.m_num_fresh; + m_rw.m_cfg.m_num_fresh = 0; + } m_rw(curr, new_curr, new_pr); if (produce_proofs) { proof * pr = g->pr(idx); @@ -136,7 +150,7 @@ class blast_term_ite_tactic : public tactic { } g->update(idx, new_curr, new_pr, g->dep(idx)); } - report_tactic_progress(":blast-term-ite-consts", m_rw.m_cfg.m_num_fresh); + report_tactic_progress(":blast-term-ite-consts", m_rw.m_cfg.m_num_fresh + num_fresh); g->inc_depth(); result.push_back(g.get()); TRACE("blast_term_ite", g->display(tout);); @@ -152,48 +166,53 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(blast_term_ite_tactic, m, m_params); } - virtual ~blast_term_ite_tactic() { + ~blast_term_ite_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; - m_imp->m_rw.cfg().updt_params(p); + m_imp->m_rw.m_cfg.updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); r.insert("max_args", CPK_UINT, "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m; dealloc(m_imp); m_imp = alloc(imp, m, m_params); } - static void blast_term_ite(expr_ref& fml) { + static void blast_term_ite(expr_ref& fml, unsigned max_inflation) { ast_manager& m = fml.get_manager(); scoped_no_proof _sp(m); params_ref p; rw ite_rw(m, p); - expr_ref tmp(m); - ite_rw(fml, tmp); - fml = tmp; + ite_rw.m_cfg.m_max_inflation = max_inflation; + if (max_inflation < UINT_MAX) { + ite_rw.m_cfg.m_init_term_size = get_num_exprs(fml); + } + try { + expr_ref tmp(m); + ite_rw(fml, tmp); + fml = tmp; + } + catch (z3_exception &) { + // max steps exceeded. + } } }; @@ -201,6 +220,6 @@ tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(blast_term_ite_tactic, m, p)); } -void blast_term_ite(expr_ref& fml) { - blast_term_ite_tactic::blast_term_ite(fml); +void blast_term_ite(expr_ref& fml, unsigned max_inflation) { + blast_term_ite_tactic::blast_term_ite(fml, max_inflation); } diff --git a/src/tactic/core/blast_term_ite_tactic.h b/src/tactic/core/blast_term_ite_tactic.h index 1ecab98be..fcad6c068 100644 --- a/src/tactic/core/blast_term_ite_tactic.h +++ b/src/tactic/core/blast_term_ite_tactic.h @@ -33,6 +33,6 @@ tactic * mk_blast_term_ite_tactic(ast_manager & m, params_ref const & p = params ADD_TACTIC("blast-term-ite", "blast term if-then-else by hoisting them.", "mk_blast_term_ite_tactic(m, p)") */ -void blast_term_ite(expr_ref& fml); +void blast_term_ite(expr_ref& fml, unsigned max_inflation); #endif diff --git a/src/tactic/core/cofactor_elim_term_ite.cpp b/src/tactic/core/cofactor_elim_term_ite.cpp index b90be82b5..1b435791c 100644 --- a/src/tactic/core/cofactor_elim_term_ite.cpp +++ b/src/tactic/core/cofactor_elim_term_ite.cpp @@ -87,7 +87,6 @@ struct cofactor_elim_term_ite::imp { case OP_TRUE: case OP_FALSE: case OP_ITE: - case OP_IFF: return; case OP_EQ: case OP_DISTINCT: @@ -219,7 +218,7 @@ struct cofactor_elim_term_ite::imp { break; } } - return 0; + return nullptr; } /** @@ -286,7 +285,7 @@ struct cofactor_elim_term_ite::imp { break; } } - expr * best = 0; + expr * best = nullptr; unsigned best_occs = 0; obj_map::iterator it = occs.begin(); obj_map::iterator end = occs.end(); @@ -329,7 +328,7 @@ struct cofactor_elim_term_ite::imp { bool m_strict_upper; app * m_upper; - cofactor_rw_cfg(ast_manager & _m, imp & owner, obj_hashtable * has_term_ite = 0): + cofactor_rw_cfg(ast_manager & _m, imp & owner, obj_hashtable * has_term_ite = nullptr): m(_m), m_owner(owner), m_has_term_ite(has_term_ite), @@ -349,13 +348,13 @@ struct cofactor_elim_term_ite::imp { if (m.is_not(t)) { m_atom = to_app(t)->get_arg(0); m_sign = true; - m_term = 0; + m_term = nullptr; // TODO: bounds } else { m_atom = t; m_sign = false; - m_term = 0; + m_term = nullptr; expr * lhs; expr * rhs; if (m_owner.m_cofactor_equalities && m.is_eq(t, lhs, rhs)) { @@ -377,19 +376,19 @@ struct cofactor_elim_term_ite::imp { bool rewrite_patterns() const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return m_mk_app.mk_core(f, num, args, result); } bool get_subst(expr * s, expr * & t, proof * & pr) { - pr = 0; + pr = nullptr; if (s == m_atom) { t = m_sign ? m.mk_false() : m.mk_true(); return true; } - if (s == m_term && m_value != 0) { + if (s == m_term && m_value != nullptr) { t = m_value; return true; } @@ -406,7 +405,7 @@ struct cofactor_elim_term_ite::imp { struct cofactor_rw : rewriter_tpl { cofactor_rw_cfg m_cfg; public: - cofactor_rw(ast_manager & m, imp & owner, obj_hashtable * has_term_ite = 0): + cofactor_rw(ast_manager & m, imp & owner, obj_hashtable * has_term_ite = nullptr): rewriter_tpl(m, false, m_cfg), m_cfg(m, owner, has_term_ite) { } @@ -444,7 +443,7 @@ struct cofactor_elim_term_ite::imp { bool get_subst(expr * s, expr * & t, proof * & t_pr) { if (m_candidates.contains(s)) { - t_pr = 0; + t_pr = nullptr; if (m_cache.find(s, t)) return true; @@ -455,7 +454,7 @@ struct cofactor_elim_term_ite::imp { while (true) { // expr * c = m_owner.get_best(curr); expr * c = m_owner.get_first(curr); - if (c == 0) { + if (c == nullptr) { m_cache.insert(s, curr); m_cache_domain.push_back(curr); t = curr.get(); @@ -532,7 +531,7 @@ struct cofactor_elim_term_ite::imp { while (true) { expr * c = m_owner.get_best(curr); // expr * c = m_owner.get_first(curr); - if (c == 0) { + if (c == nullptr) { r = curr.get(); return; } @@ -607,7 +606,7 @@ struct cofactor_elim_term_ite::imp { unsigned num = to_app(t)->get_num_args(); for (unsigned i = 0; i < num; i++) { expr * arg = to_app(t)->get_arg(i); - expr * new_arg = 0; + expr * new_arg = nullptr; TRACE("cofactor_bug", tout << "collecting child: " << arg->get_id() << "\n";); m_cache.find(arg, new_arg); SASSERT(new_arg != 0); @@ -645,7 +644,7 @@ struct cofactor_elim_term_ite::imp { m_cache_domain.push_back(new_t); m_frames.pop_back(); } - expr * result = 0; + expr * result = nullptr; m_cache.find(t, result); r = result; } diff --git a/src/tactic/core/cofactor_term_ite_tactic.cpp b/src/tactic/core/cofactor_term_ite_tactic.cpp index 65cdef147..4f52599cd 100644 --- a/src/tactic/core/cofactor_term_ite_tactic.cpp +++ b/src/tactic/core/cofactor_term_ite_tactic.cpp @@ -36,7 +36,7 @@ class cofactor_term_ite_tactic : public tactic { expr * f = g.form(i); expr_ref new_f(m); m_elim_ite(f, new_f); - g.update(i, new_f, 0, g.dep(i)); + g.update(i, new_f, nullptr, g.dep(i)); } } @@ -46,24 +46,19 @@ public: m_elim_ite(m, p) { } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(cofactor_term_ite_tactic, m, m_params); } - virtual ~cofactor_term_ite_tactic() {} - virtual void updt_params(params_ref const & p) { m_params = p; m_elim_ite.updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { m_elim_ite.collect_param_descrs(r); } + ~cofactor_term_ite_tactic() override {} + void updt_params(params_ref const & p) override { m_params = p; m_elim_ite.updt_params(p); } + void collect_param_descrs(param_descrs & r) override { m_elim_ite.collect_param_descrs(r); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer& result) override { SASSERT(g->is_well_sorted()); fail_if_proof_generation("cofactor-term-ite", g); fail_if_unsat_core_generation("cofactor-term-ite", g); tactic_report report("cofactor-term-ite", *g); - mc = 0; pc = 0; core = 0; process(*(g.get())); g->inc_depth(); result.push_back(g.get()); @@ -71,7 +66,7 @@ public: SASSERT(g->is_well_sorted()); } - virtual void cleanup() { return m_elim_ite.cleanup(); } + void cleanup() override { return m_elim_ite.cleanup(); } }; diff --git a/src/tactic/core/collect_statistics_tactic.cpp b/src/tactic/core/collect_statistics_tactic.cpp index 26f6842e3..939541957 100644 --- a/src/tactic/core/collect_statistics_tactic.cpp +++ b/src/tactic/core/collect_statistics_tactic.cpp @@ -51,22 +51,19 @@ public: m_params(p) { } - virtual ~collect_statistics_tactic() {} + ~collect_statistics_tactic() override {} - virtual tactic * translate(ast_manager & m_) { + tactic * translate(ast_manager & m_) override { return alloc(collect_statistics_tactic, m_, m_params); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; } - virtual void collect_param_descrs(param_descrs & r) {} + void collect_param_descrs(param_descrs & r) override {} - virtual void operator()(goal_ref const & g, goal_ref_buffer & result, - model_converter_ref & mc, proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; + void operator()(goal_ref const & g, goal_ref_buffer & result) override { tactic_report report("collect-statistics", *g); collect_proc cp(m, m_stats); @@ -76,23 +73,21 @@ public: for_each_expr(cp, visited, g->form(i)); std::cout << "(" << std::endl; - stats_type::iterator it = m_stats.begin(); - stats_type::iterator end = m_stats.end(); - for (; it != end; it++) - std::cout << " :" << it->first << " " << it->second << std::endl; + for (auto const& kv : m_stats) + std::cout << " :" << kv.first << " " << kv.second << std::endl; std::cout << ")" << std::endl; g->inc_depth(); result.push_back(g.get()); } - virtual void cleanup() {} + void cleanup() override {} - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { } - virtual void reset_statistics() { reset(); } - virtual void reset() { cleanup(); } + void reset_statistics() override { reset(); } + void reset() override { cleanup(); } protected: class collect_proc { diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 5bb54073d..b143bf933 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -33,13 +33,13 @@ class ctx_propagate_assertions : public ctx_simplify_tactic::simplifier { void assert_eq_core(expr * t, app * val); public: ctx_propagate_assertions(ast_manager& m); - virtual ~ctx_propagate_assertions() {} - virtual bool assert_expr(expr * t, bool sign); - virtual bool simplify(expr* t, expr_ref& result); + ~ctx_propagate_assertions() override {} + bool assert_expr(expr * t, bool sign) override; + bool simplify(expr* t, expr_ref& result) override; void push(); - virtual void pop(unsigned num_scopes); - virtual unsigned scope_level() const { return m_scopes.size(); } - virtual simplifier * translate(ast_manager & m); + void pop(unsigned num_scopes) override; + unsigned scope_level() const override { return m_scopes.size(); } + simplifier * translate(ast_manager & m) override; }; @@ -148,7 +148,7 @@ struct ctx_simplify_tactic::imp { struct cache_cell { expr * m_from; cached_result * m_result; - cache_cell():m_from(0), m_result(0) {} + cache_cell():m_from(nullptr), m_result(nullptr) {} }; ast_manager & m; @@ -217,7 +217,7 @@ struct ctx_simplify_tactic::imp { bool check_cache() { for (unsigned i = 0; i < m_cache.size(); i++) { cache_cell & cell = m_cache[i]; - if (cell.m_from != 0) { + if (cell.m_from != nullptr) { SASSERT(cell.m_result != 0); cached_result * curr = cell.m_result; while (curr) { @@ -235,10 +235,10 @@ struct ctx_simplify_tactic::imp { m_cache.reserve(id+1); cache_cell & cell = m_cache[id]; void * mem = m_allocator.allocate(sizeof(cached_result)); - if (cell.m_from == 0) { + if (cell.m_from == nullptr) { // new_entry cell.m_from = from; - cell.m_result = new (mem) cached_result(to, scope_level(), 0); + cell.m_result = new (mem) cached_result(to, scope_level(), nullptr); m.inc_ref(from); m.inc_ref(to); } @@ -281,9 +281,9 @@ struct ctx_simplify_tactic::imp { if (to_delete->m_next) tout << mk_ismt2_pp(to_delete->m_next->m_to, m); else tout << ""; tout << "\n";); cell.m_result = to_delete->m_next; - if (cell.m_result == 0) { + if (cell.m_result == nullptr) { m.dec_ref(cell.m_from); - cell.m_from = 0; + cell.m_from = nullptr; } m_allocator.deallocate(sizeof(cached_result), to_delete); } @@ -316,7 +316,7 @@ struct ctx_simplify_tactic::imp { return false; cache_cell & cell = m_cache[id]; SASSERT(cell.m_result == 0 || cell.m_result->m_lvl <= scope_level()); - if (cell.m_result != 0 && cell.m_result->m_lvl == scope_level()) { + if (cell.m_result != nullptr && cell.m_result->m_lvl == scope_level()) { SASSERT(cell.m_from == t); SASSERT(cell.m_result->m_to != 0); r = cell.m_result->m_to; @@ -326,7 +326,7 @@ struct ctx_simplify_tactic::imp { } void simplify(expr * t, expr_ref & r) { - r = 0; + r = nullptr; if (m_depth >= m_max_depth || m_num_steps >= m_max_steps || !is_app(t) || !m_simp->may_simplify(t)) { r = t; return; @@ -534,7 +534,7 @@ struct ctx_simplify_tactic::imp { if (i < sz - 1 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !assert_expr(r, false)) { r = m.mk_false(); } - g.update(i, r, 0, g.dep(i)); + g.update(i, r, nullptr, g.dep(i)); } pop(scope_level() - old_lvl); @@ -549,7 +549,7 @@ struct ctx_simplify_tactic::imp { if (i > 0 && !m.is_true(r) && !m.is_false(r) && !g.dep(i) && !assert_expr(r, false)) { r = m.mk_false(); } - g.update(i, r, 0, g.dep(i)); + g.update(i, r, nullptr, g.dep(i)); } pop(scope_level() - old_lvl); SASSERT(scope_level() == 0); @@ -582,7 +582,7 @@ struct ctx_simplify_tactic::imp { for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { expr * t = g.form(i); process(t, r); - proof* new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite_star(t, r, 0, 0)); // TODO :-) + proof* new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite(t, r)); g.update(i, r, new_pr, g.dep(i)); } } @@ -621,11 +621,7 @@ void ctx_simplify_tactic::get_param_descrs(param_descrs & r) { } void ctx_simplify_tactic::operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; + goal_ref_buffer & result) { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); diff --git a/src/tactic/core/ctx_simplify_tactic.h b/src/tactic/core/ctx_simplify_tactic.h index 9efa7e7db..2d476660a 100644 --- a/src/tactic/core/ctx_simplify_tactic.h +++ b/src/tactic/core/ctx_simplify_tactic.h @@ -46,21 +46,17 @@ protected: public: ctx_simplify_tactic(ast_manager & m, simplifier* simp, params_ref const & p = params_ref()); - virtual tactic * translate(ast_manager & m); + tactic * translate(ast_manager & m) override; - virtual ~ctx_simplify_tactic(); + ~ctx_simplify_tactic() override; - virtual void updt_params(params_ref const & p); + void updt_params(params_ref const & p) override; static void get_param_descrs(param_descrs & r); - virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } + void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core); + void operator()(goal_ref const & in, goal_ref_buffer & result) override; - virtual void cleanup(); + void cleanup() override; }; tactic * mk_ctx_simplify_tactic(ast_manager & m, params_ref const & p = params_ref()); diff --git a/src/tactic/core/der_tactic.cpp b/src/tactic/core/der_tactic.cpp index 5df009969..8de00525e 100644 --- a/src/tactic/core/der_tactic.cpp +++ b/src/tactic/core/der_tactic.cpp @@ -65,26 +65,22 @@ public: m_imp = alloc(imp, m); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(der_tactic, m); } - virtual ~der_tactic() { + ~der_tactic() override { dealloc(m_imp); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m); std::swap(d, m_imp); diff --git a/src/tactic/core/distribute_forall_tactic.cpp b/src/tactic/core/distribute_forall_tactic.cpp index 3ee2697c4..98c942432 100644 --- a/src/tactic/core/distribute_forall_tactic.cpp +++ b/src/tactic/core/distribute_forall_tactic.cpp @@ -93,23 +93,20 @@ class distribute_forall_tactic : public tactic { rw * m_rw; public: - distribute_forall_tactic():m_rw(0) {} + distribute_forall_tactic():m_rw(nullptr) {} - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(distribute_forall_tactic); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); ast_manager & m = g->m(); bool produce_proofs = g->proofs_enabled(); rw r(m, produce_proofs); m_rw = &r; - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); tactic_report report("distribute-forall", *g); expr_ref new_curr(m); @@ -131,10 +128,10 @@ public: result.push_back(g.get()); TRACE("distribute-forall", g->display(tout);); SASSERT(g->is_well_sorted()); - m_rw = 0; + m_rw = nullptr; } - virtual void cleanup() {} + void cleanup() override {} }; tactic * mk_distribute_forall_tactic(ast_manager & m, params_ref const & p) { diff --git a/src/tactic/core/dom_simplify_tactic.cpp b/src/tactic/core/dom_simplify_tactic.cpp index 2099eebf0..27a5bdc94 100644 --- a/src/tactic/core/dom_simplify_tactic.cpp +++ b/src/tactic/core/dom_simplify_tactic.cpp @@ -100,7 +100,7 @@ bool expr_dominators::compute_dominators() { for (unsigned i = 0; i + 1 < m_post2expr.size(); ++i) { expr * child = m_post2expr[i]; ptr_vector const& p = m_parents[child]; - expr * new_idom = 0, *idom2 = 0; + expr * new_idom = nullptr, *idom2 = nullptr; for (expr * pred : p) { if (m_doms.contains(pred)) { @@ -183,19 +183,11 @@ tactic * dom_simplify_tactic::translate(ast_manager & m) { return alloc(dom_simplify_tactic, m, m_simplifier->translate(m), m_params); } -void dom_simplify_tactic::operator()( - goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; - +void dom_simplify_tactic::operator()(goal_ref const & in, goal_ref_buffer & result) { tactic_report report("dom-simplify", *in.get()); simplify_goal(*(in.get())); in->inc_depth(); result.push_back(in.get()); - } void dom_simplify_tactic::cleanup() { @@ -207,7 +199,7 @@ void dom_simplify_tactic::cleanup() { expr_ref dom_simplify_tactic::simplify_ite(app * ite) { expr_ref r(m); - expr * c = 0, *t = 0, *e = 0; + expr * c = nullptr, *t = nullptr, *e = nullptr; VERIFY(m.is_ite(ite, c, t, e)); unsigned old_lvl = scope_level(); expr_ref new_c = simplify_arg(c); @@ -262,7 +254,7 @@ expr_ref dom_simplify_tactic::simplify_arg(expr * e) { */ expr_ref dom_simplify_tactic::simplify_rec(expr * e0) { expr_ref r(m); - expr* e = 0; + expr* e = nullptr; TRACE("simplify", tout << "depth: " << m_depth << " " << mk_pp(e0, m) << "\n";); if (!m_result.find(e0, e)) { @@ -380,9 +372,9 @@ void dom_simplify_tactic::simplify_goal(goal& g) { } CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";); change |= r != g.form(i); - proof* new_pr = 0; + proof* new_pr = nullptr; if (g.proofs_enabled()) { - new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite_star(g.form(i), r, 0, 0)); + new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite(g.form(i), r)); } g.update(i, r, new_pr, g.dep(i)); } @@ -400,9 +392,9 @@ void dom_simplify_tactic::simplify_goal(goal& g) { } change |= r != g.form(i); CTRACE("simplify", r != g.form(i), tout << r << " " << mk_pp(g.form(i), m) << "\n";); - proof* new_pr = 0; + proof* new_pr = nullptr; if (g.proofs_enabled()) { - new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite_star(g.form(i), r, 0, 0)); + new_pr = m.mk_modus_ponens(g.pr(i), m.mk_rewrite(g.form(i), r)); } g.update(i, r, new_pr, g.dep(i)); } @@ -448,14 +440,14 @@ bool expr_substitution_simplifier::assert_expr(expr * t, bool sign) { m_scoped_substitution.push(); expr* tt; if (!sign) { - update_substitution(t, 0); + update_substitution(t, nullptr); } else if (m.is_not(t, tt)) { - update_substitution(tt, 0); + update_substitution(tt, nullptr); } else { expr_ref nt(m.mk_not(t), m); - update_substitution(nt, 0); + update_substitution(nt, nullptr); } return true; } @@ -494,7 +486,7 @@ bool expr_substitution_simplifier::is_gt(expr* lhs, expr* rhs) { void expr_substitution_simplifier::update_substitution(expr* n, proof* pr) { expr* lhs, *rhs, *n1; - if (is_ground(n) && (m.is_eq(n, lhs, rhs) || m.is_iff(n, lhs, rhs))) { + if (is_ground(n) && m.is_eq(n, lhs, rhs)) { compute_depth(lhs); compute_depth(rhs); m_trail.push_back(lhs); diff --git a/src/tactic/core/dom_simplify_tactic.h b/src/tactic/core/dom_simplify_tactic.h index 56eea8d9a..eb3500729 100644 --- a/src/tactic/core/dom_simplify_tactic.h +++ b/src/tactic/core/dom_simplify_tactic.h @@ -111,7 +111,7 @@ class dom_simplify_tactic : public tactic { bool is_subexpr(expr * a, expr * b); - expr_ref get_cached(expr* t) { expr* r = 0; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); } + expr_ref get_cached(expr* t) { expr* r = nullptr; if (!m_result.find(t, r)) r = t; return expr_ref(r, m); } void cache(expr *t, expr* r) { m_result.insert(t, r); m_trail.push_back(r); } ptr_vector const & tree(expr * e); @@ -129,21 +129,14 @@ public: m_trail(m), m_args(m), m_dominators(m), m_depth(0), m_max_depth(1024), m_forward(true) {} + ~dom_simplify_tactic() override; - virtual ~dom_simplify_tactic(); - - virtual tactic * translate(ast_manager & m); - virtual void updt_params(params_ref const & p) {} + tactic * translate(ast_manager & m) override; + void updt_params(params_ref const & p) override {} static void get_param_descrs(param_descrs & r) {} - virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } - - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core); - - virtual void cleanup(); + void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } + void operator()(goal_ref const & in, goal_ref_buffer & result) override; + void cleanup() override; }; class expr_substitution_simplifier : public dom_simplifier { @@ -160,18 +153,18 @@ class expr_substitution_simplifier : public dom_simplifier { public: expr_substitution_simplifier(ast_manager& m): m(m), m_subst(m), m_scoped_substitution(m_subst), m_trail(m) {} - virtual ~expr_substitution_simplifier() {} - virtual bool assert_expr(expr * t, bool sign); + ~expr_substitution_simplifier() override {} + bool assert_expr(expr * t, bool sign) override; void update_substitution(expr* n, proof* pr); - virtual void operator()(expr_ref& r) { r = m_scoped_substitution.find(r); } + void operator()(expr_ref& r) override { r = m_scoped_substitution.find(r); } - virtual void pop(unsigned num_scopes) { m_scoped_substitution.pop(num_scopes); } + void pop(unsigned num_scopes) override { m_scoped_substitution.pop(num_scopes); } - virtual unsigned scope_level() const { return m_scoped_substitution.scope_level(); } + unsigned scope_level() const override { return m_scoped_substitution.scope_level(); } - virtual dom_simplifier * translate(ast_manager & m) { + dom_simplifier * translate(ast_manager & m) override { SASSERT(m_subst.empty()); return alloc(expr_substitution_simplifier, m); } diff --git a/src/tactic/core/elim_term_ite_tactic.cpp b/src/tactic/core/elim_term_ite_tactic.cpp index 79526a101..0bb59afcf 100644 --- a/src/tactic/core/elim_term_ite_tactic.cpp +++ b/src/tactic/core/elim_term_ite_tactic.cpp @@ -20,7 +20,7 @@ Notes: #include "tactic/tactical.h" #include "ast/normal_forms/defined_names.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "util/cooperate.h" class elim_term_ite_tactic : public tactic { @@ -28,7 +28,7 @@ class elim_term_ite_tactic : public tactic { struct rw_cfg : public default_rewriter_cfg { ast_manager & m; defined_names m_defined_names; - ref m_mc; + ref m_mc; goal * m_goal; unsigned long long m_max_memory; // in bytes bool m_produce_models; @@ -51,12 +51,12 @@ class elim_term_ite_tactic : public tactic { proof_ref new_def_pr(m); app_ref _result(m); if (m_defined_names.mk_name(new_ite, new_def, new_def_pr, _result, result_pr)) { - m_goal->assert_expr(new_def, new_def_pr, 0); + m_goal->assert_expr(new_def, new_def_pr, nullptr); m_num_fresh++; if (m_produce_models) { if (!m_mc) - m_mc = alloc(filter_model_converter, m); - m_mc->insert(_result->get_decl()); + m_mc = alloc(generic_model_converter, m, "elim_term_ite"); + m_mc->hide(_result->get_decl()); } } result = _result.get(); @@ -65,9 +65,9 @@ class elim_term_ite_tactic : public tactic { rw_cfg(ast_manager & _m, params_ref const & p): m(_m), - m_defined_names(m, 0 /* don't use prefix */) { + m_defined_names(m, nullptr /* don't use prefix */) { updt_params(p); - m_goal = 0; + m_goal = nullptr; m_num_fresh = 0; } @@ -100,12 +100,8 @@ class elim_term_ite_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("elim-term-ite", *g); bool produce_proofs = g->proofs_enabled(); m_rw.cfg().m_produce_models = g->models_enabled(); @@ -124,7 +120,7 @@ class elim_term_ite_tactic : public tactic { } g->update(idx, new_curr, new_pr, g->dep(idx)); } - mc = m_rw.m_cfg.m_mc.get(); + g->add(m_rw.m_cfg.m_mc.get()); report_tactic_progress(":elim-term-ite-consts", m_rw.m_cfg.m_num_fresh); g->inc_depth(); result.push_back(g.get()); @@ -140,36 +136,33 @@ public: m_params(p) { m_imp = alloc(imp, m, p); } - - virtual tactic * translate(ast_manager & m) { - return alloc(elim_term_ite_tactic, m, m_params); - } - virtual ~elim_term_ite_tactic() { + ~elim_term_ite_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + tactic * translate(ast_manager & m) override { + return alloc(elim_term_ite_tactic, m, m_params); + } + + void updt_params(params_ref const & p) override { m_params = p; m_imp->m_rw.cfg().updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); r.insert("max_args", CPK_UINT, "(default: 128) maximum number of arguments (per application) that will be considered by the greedy (quadratic) heuristic."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m; m_imp->~imp(); m_imp = new (m_imp) imp(m, m_params); diff --git a/src/tactic/core/elim_uncnstr_tactic.cpp b/src/tactic/core/elim_uncnstr_tactic.cpp index 6a38f787e..577db30cd 100644 --- a/src/tactic/core/elim_uncnstr_tactic.cpp +++ b/src/tactic/core/elim_uncnstr_tactic.cpp @@ -17,8 +17,7 @@ Notes: --*/ #include "tactic/tactical.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/rewriter/rewriter_def.h" #include "ast/arith_decl_plugin.h" #include "ast/bv_decl_plugin.h" @@ -34,7 +33,7 @@ class elim_uncnstr_tactic : public tactic { struct imp { // unconstrained vars collector - typedef extension_model_converter mc; + typedef generic_model_converter mc; struct rw_cfg : public default_rewriter_cfg { bool m_produce_proofs; @@ -95,10 +94,11 @@ class elim_uncnstr_tactic : public tactic { return false; // variable already existed for this application } - v = m().mk_fresh_const(0, m().get_sort(t)); + v = m().mk_fresh_const(nullptr, m().get_sort(t)); TRACE("elim_uncnstr_bug", tout << "eliminating:\n" << mk_ismt2_pp(t, m()) << "\n";); TRACE("elim_uncnstr_bug_ll", tout << "eliminating:\n" << mk_bounded_pp(t, m()) << "\n";); m_fresh_vars.push_back(v); + if (m_mc) m_mc->hide(v); m_cache_domain.push_back(t); m_cache.insert(t, v); return true; @@ -120,7 +120,7 @@ class elim_uncnstr_tactic : public tactic { SASSERT(uncnstr(v)); SASSERT(to_app(v)->get_num_args() == 0); if (m_mc) - m_mc->insert(to_app(v)->get_decl(), def); + m_mc->add(to_app(v)->get_decl(), def); } void add_defs(unsigned num, expr * const * args, expr * u, expr * identity) { @@ -220,7 +220,7 @@ class elim_uncnstr_tactic : public tactic { t = arg1; } else { - return 0; + return nullptr; } sort * s = m().get_sort(arg1); @@ -243,14 +243,14 @@ class elim_uncnstr_tactic : public tactic { // variables. // if (!m().is_fully_interp(s)) - return 0; + return nullptr; // If the interpreted sort has only one element, // then it is unsound to eliminate the unconstrained variable in the equality sort_size sz = s->get_num_elements(); if (sz.is_finite() && sz.size() <= 1) - return 0; + return nullptr; if (!m_mc) { // easy case, model generation is disabled. @@ -267,7 +267,7 @@ class elim_uncnstr_tactic : public tactic { add_def(v, m().mk_ite(u, t, d)); return u; } - return 0; + return nullptr; } app * process_basic_app(func_decl * f, unsigned num, expr * const * args) { @@ -299,7 +299,7 @@ class elim_uncnstr_tactic : public tactic { add_def(args[2], r); return r; } - return 0; + return nullptr; case OP_NOT: SASSERT(num == 1); if (uncnstr(args[0])) { @@ -310,7 +310,7 @@ class elim_uncnstr_tactic : public tactic { add_def(args[0], m().mk_not(r)); return r; } - return 0; + return nullptr; case OP_AND: if (num > 0 && uncnstr(num, args)) { app * r; @@ -320,7 +320,7 @@ class elim_uncnstr_tactic : public tactic { add_defs(num, args, r, m().mk_true()); return r; } - return 0; + return nullptr; case OP_OR: if (num > 0 && uncnstr(num, args)) { app * r; @@ -330,13 +330,12 @@ class elim_uncnstr_tactic : public tactic { add_defs(num, args, r, m().mk_false()); return r; } - return 0; - case OP_IFF: + return nullptr; case OP_EQ: SASSERT(num == 2); return process_eq(f, args[0], args[1]); default: - return 0; + return nullptr; } } @@ -353,7 +352,7 @@ class elim_uncnstr_tactic : public tactic { le = !le; } else { - return 0; + return nullptr; } app * u; if (!mk_fresh_uncnstr_var_for(f, arg1, arg2, u)) @@ -368,9 +367,9 @@ class elim_uncnstr_tactic : public tactic { app * process_add(family_id fid, decl_kind add_k, decl_kind sub_k, unsigned num, expr * const * args) { if (num == 0) - return 0; + return nullptr; unsigned i; - expr * v = 0; + expr * v = nullptr; for (i = 0; i < num; i++) { expr * arg = args[i]; if (uncnstr(arg)) { @@ -378,8 +377,8 @@ class elim_uncnstr_tactic : public tactic { break; } } - if (v == 0) - return 0; + if (v == nullptr) + return nullptr; app * u; if (!mk_fresh_uncnstr_var_for(m().mk_app(fid, add_k, num, args), u)) return u; @@ -407,7 +406,7 @@ class elim_uncnstr_tactic : public tactic { app * process_arith_mul(func_decl * f, unsigned num, expr * const * args) { if (num == 0) - return 0; + return nullptr; sort * s = m().get_sort(args[0]); if (uncnstr(num, args)) { app * r; @@ -422,7 +421,7 @@ class elim_uncnstr_tactic : public tactic { rational val; if (num == 2 && uncnstr(args[1]) && m_a_util.is_numeral(args[0], val, is_int) && !is_int) { if (val.is_zero()) - return 0; + return nullptr; app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; @@ -432,7 +431,7 @@ class elim_uncnstr_tactic : public tactic { } return r; } - return 0; + return nullptr; } app * process_arith_app(func_decl * f, unsigned num, expr * const * args) { @@ -450,13 +449,13 @@ class elim_uncnstr_tactic : public tactic { SASSERT(num == 2); return process_le_ge(f, args[0], args[1], false); default: - return 0; + return nullptr; } } app * process_bv_mul(func_decl * f, unsigned num, expr * const * args) { if (num == 0) - return 0; + return nullptr; if (uncnstr(num, args)) { sort * s = m().get_sort(args[0]); app * r; @@ -482,12 +481,12 @@ class elim_uncnstr_tactic : public tactic { add_def(args[1], m_bv_util.mk_bv_mul(m_bv_util.mk_numeral(inv, s), r)); return r; } - return 0; + return nullptr; } app * process_extract(func_decl * f, expr * arg) { if (!uncnstr(arg)) - return 0; + return nullptr; app * r; if (!mk_fresh_uncnstr_var_for(f, arg, r)) return r; @@ -523,14 +522,14 @@ class elim_uncnstr_tactic : public tactic { add_def(arg2, m_bv_util.mk_numeral(rational(1), s)); return r; } - return 0; + return nullptr; } app * process_concat(func_decl * f, unsigned num, expr * const * args) { if (num == 0) - return 0; + return nullptr; if (!uncnstr(num, args)) - return 0; + return nullptr; app * r; if (!mk_fresh_uncnstr_var_for(f, num, args, r)) return r; @@ -553,7 +552,7 @@ class elim_uncnstr_tactic : public tactic { // The result of bv_le is not just introducing a new fresh name, // we need a side condition. // TODO: the correct proof step - return 0; + return nullptr; } if (uncnstr(arg1)) { // v <= t @@ -593,7 +592,7 @@ class elim_uncnstr_tactic : public tactic { add_def(v, m().mk_ite(r, t, m_bv_util.mk_bv_sub(t, m_bv_util.mk_numeral(rational(1), bv_sz)))); return r; } - return 0; + return nullptr; } app * process_bv_app(func_decl * f, unsigned num, expr * const * args) { @@ -630,7 +629,7 @@ class elim_uncnstr_tactic : public tactic { add_def(args[0], m().mk_app(f, r)); return r; } - return 0; + return nullptr; case OP_BOR: if (num > 0 && uncnstr(num, args)) { sort * s = m().get_sort(args[0]); @@ -641,9 +640,9 @@ class elim_uncnstr_tactic : public tactic { add_defs(num, args, r, m_bv_util.mk_numeral(rational(0), s)); return r; } - return 0; + return nullptr; default: - return 0; + return nullptr; } } @@ -660,7 +659,7 @@ class elim_uncnstr_tactic : public tactic { add_def(args[0], m_ar_util.mk_const_array(s, r)); return r; } - return 0; + return nullptr; case OP_STORE: if (uncnstr(args[0]) && uncnstr(args[num-1])) { app * r; @@ -673,7 +672,7 @@ class elim_uncnstr_tactic : public tactic { return r; } default: - return 0; + return nullptr; } } @@ -700,7 +699,7 @@ class elim_uncnstr_tactic : public tactic { func_decl * c = m_dt_util.get_accessor_constructor(f); for (unsigned i = 0; i < c->get_arity(); i++) if (!m().is_fully_interp(c->get_domain(i))) - return 0; + return nullptr; app * u; if (!mk_fresh_uncnstr_var_for(f, num, args, u)) return u; @@ -730,7 +729,7 @@ class elim_uncnstr_tactic : public tactic { return u; } } - return 0; + return nullptr; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { @@ -745,7 +744,7 @@ class elim_uncnstr_tactic : public tactic { return BR_FAILED; // non-ground terms are not handled. } - app * u = 0; + app * u = nullptr; if (fid == m().get_basic_family_id()) u = process_basic_app(f, num, args); @@ -758,7 +757,7 @@ class elim_uncnstr_tactic : public tactic { else if (fid == m_dt_util.get_family_id()) u = process_datatype_app(f, num, args); - if (u == 0) + if (u == nullptr) return BR_FAILED; result = u; @@ -805,26 +804,19 @@ class elim_uncnstr_tactic : public tactic { ast_manager & m() { return m_manager; } void init_mc(bool produce_models) { - if (!produce_models) { - m_mc = 0; - return; + m_mc = nullptr; + if (produce_models) { + m_mc = alloc(mc, m(), "elim_uncstr"); } - m_mc = alloc(mc, m()); } void init_rw(bool produce_proofs) { m_rw = alloc(rw, m(), produce_proofs, m_vars, m_mc.get(), m_max_memory, m_max_steps); } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; - bool produce_models = g->models_enabled(); + void operator()(goal_ref const & g, goal_ref_buffer & result) { bool produce_proofs = g->proofs_enabled(); - + TRACE("elim_uncnstr_bug", g->display(tout);); tactic_report report("elim-uncnstr-vars", *g); m_vars.reset(); @@ -837,14 +829,9 @@ class elim_uncnstr_tactic : public tactic { } bool modified = true; TRACE("elim_uncnstr", tout << "unconstrained variables...\n"; - obj_hashtable::iterator it = m_vars.begin(); - obj_hashtable::iterator end = m_vars.end(); - for (; it != end; ++it) { - expr * v = *it; - tout << mk_ismt2_pp(v, m()) << " "; - } + for (expr * v : m_vars) tout << mk_ismt2_pp(v, m()) << " "; tout << "\n";); - init_mc(produce_models); + init_mc(g->models_enabled()); init_rw(produce_proofs); expr_ref new_f(m()); @@ -866,25 +853,15 @@ class elim_uncnstr_tactic : public tactic { g->update(idx, new_f, new_pr, g->dep(idx)); } if (!modified) { - if (round == 0) { - mc = 0; + if (round == 0) { } else { - app_ref_vector & fresh_vars = m_rw->cfg().m_fresh_vars; - m_num_elim_apps = fresh_vars.size(); - if (produce_models && !fresh_vars.empty()) { - filter_model_converter * fmc = alloc(filter_model_converter, m()); - for (unsigned i = 0; i < fresh_vars.size(); i++) - fmc->insert(fresh_vars.get(i)->get_decl()); - mc = concat(fmc, m_mc.get()); - } - else { - mc = 0; - } + m_num_elim_apps = m_rw->cfg().m_fresh_vars.size(); + g->add(m_mc.get()); } - m_mc = 0; - m_rw = 0; - TRACE("elim_uncnstr", if (mc) mc->display(tout);); + TRACE("elim_uncnstr", if (m_mc) m_mc->display(tout); else tout << "no mc\n";); + m_mc = nullptr; + m_rw = nullptr; result.push_back(g.get()); g->inc_depth(); return; @@ -904,45 +881,42 @@ class elim_uncnstr_tactic : public tactic { idx = 0; } } - + }; imp * m_imp; params_ref m_params; public: - elim_uncnstr_tactic(ast_manager & m, params_ref const & p): + elim_uncnstr_tactic(ast_manager & m, params_ref const & p): m_params(p) { m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(elim_uncnstr_tactic, m, m_params); } - virtual ~elim_uncnstr_tactic() { + ~elim_uncnstr_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_max_steps(r); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(g, result, mc, pc, core); + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { + (*m_imp)(g, result); report_tactic_progress(":num-elim-apps", get_num_elim_apps()); } - virtual void cleanup() { + void cleanup() override { unsigned num_elim_apps = get_num_elim_apps(); ast_manager & m = m_imp->m_manager; imp * d = alloc(imp, m, m_params); @@ -955,11 +929,11 @@ public: return m_imp->m_num_elim_apps; } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.update("eliminated applications", get_num_elim_apps()); } - virtual void reset_statistics() { + void reset_statistics() override { m_imp->m_num_elim_apps = 0; } diff --git a/src/tactic/core/injectivity_tactic.cpp b/src/tactic/core/injectivity_tactic.cpp index 7d90a2155..64947eca0 100644 --- a/src/tactic/core/injectivity_tactic.cpp +++ b/src/tactic/core/injectivity_tactic.cpp @@ -144,12 +144,8 @@ class injectivity_tactic : public tactic { } void operator()(goal_ref const & goal, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(goal->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("injectivity", *goal); fail_if_unsat_core_generation("injectivity", goal); // TODO: Support UNSAT cores fail_if_proof_generation("injectivity", goal); @@ -250,32 +246,29 @@ public: m_eq = alloc(rewriter_eq, m, *m_map, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(injectivity_tactic, m, m_params); } - virtual ~injectivity_tactic() { + ~injectivity_tactic() override { dealloc(m_finder); dealloc(m_eq); dealloc(m_map); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_finder->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_produce_models(r); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_finder)(g, result, mc, pc, core); + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { + (*m_finder)(g, result); for (unsigned i = 0; i < g->size(); ++i) { expr* curr = g->form(i); @@ -287,7 +280,7 @@ public: result.push_back(g.get()); } - virtual void cleanup() { + void cleanup() override { InjHelper * m = alloc(InjHelper, m_manager); finder * f = alloc(finder, m_manager, *m, m_params); rewriter_eq * r = alloc(rewriter_eq, m_manager, *m, m_params); diff --git a/src/tactic/core/nnf_tactic.cpp b/src/tactic/core/nnf_tactic.cpp index 6b360e711..f482181d2 100644 --- a/src/tactic/core/nnf_tactic.cpp +++ b/src/tactic/core/nnf_tactic.cpp @@ -18,7 +18,7 @@ Revision History: --*/ #include "ast/normal_forms/nnf.h" #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" class nnf_tactic : public tactic { params_ref m_params; @@ -33,34 +33,29 @@ class nnf_tactic : public tactic { } ~set_nnf() { - m_owner.m_nnf = 0; + m_owner.m_nnf = nullptr; } }; public: nnf_tactic(params_ref const & p): m_params(p), - m_nnf(0) { + m_nnf(nullptr) { TRACE("nnf", tout << "nnf_tactic constructor: " << p << "\n";); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(nnf_tactic, m_params); } - virtual ~nnf_tactic() {} + ~nnf_tactic() override {} - virtual void updt_params(params_ref const & p) { m_params = p; } + void updt_params(params_ref const & p) override { m_params = p; } - virtual void collect_param_descrs(param_descrs & r) { nnf::get_param_descrs(r); } + void collect_param_descrs(param_descrs & r) override { nnf::get_param_descrs(r); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer & result) override { TRACE("nnf", tout << "params: " << m_params << "\n"; g->display(tout);); SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("nnf", *g); bool produce_proofs = g->proofs_enabled(); @@ -89,24 +84,24 @@ public: sz = defs.size(); for (unsigned i = 0; i < sz; i++) { if (produce_proofs) - g->assert_expr(defs.get(i), def_prs.get(i), 0); + g->assert_expr(defs.get(i), def_prs.get(i), nullptr); else - g->assert_expr(defs.get(i), 0, 0); + g->assert_expr(defs.get(i), nullptr, nullptr); } g->inc_depth(); result.push_back(g.get()); unsigned num_extra_names = dnames.get_num_names(); if (num_extra_names > 0) { - filter_model_converter * fmc = alloc(filter_model_converter, m); - mc = fmc; + generic_model_converter * fmc = alloc(generic_model_converter, m, "nnf"); + g->add(fmc); for (unsigned i = 0; i < num_extra_names; i++) - fmc->insert(dnames.get_name_decl(i)); + fmc->hide(dnames.get_name_decl(i)); } TRACE("nnf", g->display(tout);); SASSERT(g->is_well_sorted()); } - virtual void cleanup() {} + void cleanup() override {} }; tactic * mk_snf_tactic(ast_manager & m, params_ref const & p) { diff --git a/src/tactic/core/occf_tactic.cpp b/src/tactic/core/occf_tactic.cpp index 6d9d63971..9bd6300ba 100644 --- a/src/tactic/core/occf_tactic.cpp +++ b/src/tactic/core/occf_tactic.cpp @@ -9,7 +9,7 @@ Abstract: Put clauses in the assertion set in OOC (one constraint per clause) form. - Constraints occuring in formulas that + Constraints occurring in formulas that are not clauses are ignored. The formula can be put into CNF by using mk_sat_preprocessor strategy. @@ -23,13 +23,13 @@ Revision History: --*/ #include "tactic/tactical.h" #include "tactic/core/occf_tactic.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "util/cooperate.h" class occf_tactic : public tactic { struct imp { ast_manager & m; - filter_model_converter * m_mc; + generic_model_converter * m_mc; imp(ast_manager & _m): m(_m) { @@ -68,7 +68,7 @@ class occf_tactic : public tactic { expr * m_bvar; unsigned m_gen_pos:1; unsigned m_gen_neg:1; - bvar_info():m_bvar(0), m_gen_pos(false), m_gen_neg(false) {} + bvar_info():m_bvar(nullptr), m_gen_pos(false), m_gen_neg(false) {} bvar_info(expr * var, bool sign): m_bvar(var), m_gen_pos(!sign), @@ -86,20 +86,20 @@ class occf_tactic : public tactic { } cnstr2bvar::obj_map_entry * entry = c2b.find_core(cnstr); - if (entry == 0) - return 0; + if (entry == nullptr) + return nullptr; bvar_info & info = entry->get_data().m_value; if (sign) { if (!info.m_gen_neg) { info.m_gen_neg = true; - g->assert_expr(m.mk_or(info.m_bvar, m.mk_not(cnstr)), 0, 0); + g->assert_expr(m.mk_or(info.m_bvar, m.mk_not(cnstr)), nullptr, nullptr); } return m.mk_not(info.m_bvar); } else { if (!info.m_gen_pos) { info.m_gen_pos = true; - g->assert_expr(m.mk_or(m.mk_not(info.m_bvar), cnstr), 0, 0); + g->assert_expr(m.mk_or(m.mk_not(info.m_bvar), cnstr), nullptr, nullptr); } return info.m_bvar; } @@ -113,34 +113,29 @@ class occf_tactic : public tactic { } SASSERT(!c2b.contains(cnstr)); - expr * bvar = m.mk_fresh_const(0, m.mk_bool_sort()); + expr * bvar = m.mk_fresh_const(nullptr, m.mk_bool_sort()); if (produce_models) - m_mc->insert(to_app(bvar)->get_decl()); + m_mc->hide(to_app(bvar)->get_decl()); c2b.insert(cnstr, bvar_info(bvar, sign)); if (sign) { - g->assert_expr(m.mk_or(bvar, m.mk_not(cnstr)), 0, 0); + g->assert_expr(m.mk_or(bvar, m.mk_not(cnstr)), nullptr, nullptr); return m.mk_not(bvar); } else { - g->assert_expr(m.mk_or(m.mk_not(bvar), cnstr), 0, 0); + g->assert_expr(m.mk_or(m.mk_not(bvar), cnstr), nullptr, nullptr); return bvar; } } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; - fail_if_proof_generation("occf", g); bool produce_models = g->models_enabled(); tactic_report report("occf", *g); - m_mc = 0; + m_mc = nullptr; ptr_vector new_lits; @@ -157,20 +152,20 @@ class occf_tactic : public tactic { if (!is_target(cls)) continue; if (produce_models && !m_mc) { - m_mc = alloc(filter_model_converter, m); - mc = m_mc; + m_mc = alloc(generic_model_converter, m, "occf"); + g->add(m_mc); } - expr * keep = 0; + expr * keep = nullptr; new_lits.reset(); unsigned num = cls->get_num_args(); for (unsigned j = 0; j < num; j++) { expr * l = cls->get_arg(j); if (is_constraint(l)) { expr * new_l = get_aux_lit(c2b, l, g); - if (new_l != 0) { + if (new_l != nullptr) { new_lits.push_back(new_l); } - else if (keep == 0) { + else if (keep == nullptr) { keep = l; } else { @@ -182,9 +177,9 @@ class occf_tactic : public tactic { new_lits.push_back(l); } } - if (keep != 0) + if (keep != nullptr) new_lits.push_back(keep); - g->update(i, m.mk_or(new_lits.size(), new_lits.c_ptr()), 0, d); + g->update(i, m.mk_or(new_lits.size(), new_lits.c_ptr()), nullptr, d); } g->inc_depth(); result.push_back(g.get()); @@ -199,26 +194,23 @@ public: m_imp = alloc(imp, m); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(occf_tactic, m); } - virtual ~occf_tactic() { + ~occf_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) {} - virtual void collect_param_descrs(param_descrs & r) {} + void updt_params(params_ref const & p) override {} + void collect_param_descrs(param_descrs & r) override {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m); std::swap(d, m_imp); dealloc(d); diff --git a/src/tactic/core/occf_tactic.h b/src/tactic/core/occf_tactic.h index 80f8f6042..0ef7eb1f6 100644 --- a/src/tactic/core/occf_tactic.h +++ b/src/tactic/core/occf_tactic.h @@ -9,7 +9,7 @@ Abstract: Put clauses in the assertion set in OOC (one constraint per clause) form. - Constraints occuring in formulas that + Constraints occurring in formulas that are not clauses are ignored. The formula can be put into CNF by using mk_sat_preprocessor strategy. diff --git a/src/tactic/core/pb_preprocess_tactic.cpp b/src/tactic/core/pb_preprocess_tactic.cpp index 6a0d7205b..7f17c8dae 100644 --- a/src/tactic/core/pb_preprocess_tactic.cpp +++ b/src/tactic/core/pb_preprocess_tactic.cpp @@ -33,55 +33,13 @@ Notes: --*/ #include "tactic/core/pb_preprocess_tactic.h" #include "tactic/tactical.h" +#include "tactic/generic_model_converter.h" #include "ast/for_each_expr.h" #include "ast/pb_decl_plugin.h" #include "ast/rewriter/th_rewriter.h" #include "ast/expr_substitution.h" #include "ast/ast_pp.h" -class pb_preproc_model_converter : public model_converter { - ast_manager& m; - pb_util pb; - expr_ref_vector m_refs; - svector > m_const; - -public: - pb_preproc_model_converter(ast_manager& m):m(m), pb(m), m_refs(m) {} - - virtual void operator()(model_ref & mdl, unsigned goal_idx) { - SASSERT(goal_idx == 0); - for (unsigned i = 0; i < m_const.size(); ++i) { - mdl->register_decl(m_const[i].first->get_decl(), m_const[i].second); - } - } - - void set_value(expr* e, bool p) { - while (m.is_not(e, e)) { - p = !p; - } - SASSERT(is_app(e)); - set_value_p(to_app(e), p?m.mk_true():m.mk_false()); - } - - virtual model_converter * translate(ast_translation & translator) { - pb_preproc_model_converter* mc = alloc(pb_preproc_model_converter, translator.to()); - for (unsigned i = 0; i < m_const.size(); ++i) { - mc->set_value_p(translator(m_const[i].first), translator(m_const[i].second)); - } - return mc; - } - -private: - void set_value_p(app* e, expr* v) { - SASSERT(e->get_num_args() == 0); - SASSERT(is_uninterp_const(e)); - m_const.push_back(std::make_pair(e, v)); - m_refs.push_back(e); - m_refs.push_back(v); - } - -}; - class pb_preprocess_tactic : public tactic { struct rec { unsigned_vector pos, neg; rec() { } }; typedef obj_map var_map; @@ -114,49 +72,52 @@ class pb_preprocess_tactic : public tactic { for (unsigned i = 0; i < m_other.size(); ++i) { out << "ot " << m_other[i] << ": " << mk_pp(g->form(m_other[i]), m) << "\n"; } - - var_map::iterator it = m_vars.begin(); - var_map::iterator end = m_vars.end(); - for (; it != end; ++it) { - app* e = it->m_key; - unsigned_vector const& pos = it->m_value.pos; - unsigned_vector const& neg = it->m_value.neg; + + for (auto const& kv : m_vars) { + app* e = kv.m_key; + unsigned_vector const& pos = kv.m_value.pos; + unsigned_vector const& neg = kv.m_value.neg; out << mk_pp(e, m) << ": "; - for (unsigned i = 0; i < pos.size(); ++i) { - out << "p: " << pos[i] << " "; + for (unsigned p : pos) { + out << "p: " << p << " "; } - for (unsigned i = 0; i < neg.size(); ++i) { - out << "n: " << neg[i] << " "; + for (unsigned n : neg) { + out << "n: " << n << " "; } out << "\n"; } } + void set_value(generic_model_converter& mc, expr* e, bool p) { + while (m.is_not(e, e)) { + p = !p; + } + SASSERT(is_app(e)); + mc.add(to_app(e), p?m.mk_true():m.mk_false()); + } + + public: pb_preprocess_tactic(ast_manager& m, params_ref const& p = params_ref()): m(m), pb(m), m_r(m) {} - virtual ~pb_preprocess_tactic() {} + ~pb_preprocess_tactic() override {} - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(pb_preprocess_tactic, m); } - virtual void operator()( + void operator()( goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); - pc = 0; core = 0; if (g->proofs_enabled()) { throw tactic_exception("pb-preprocess does not support proofs"); } - pb_preproc_model_converter* pp = alloc(pb_preproc_model_converter, m); - mc = pp; + generic_model_converter* pp = alloc(generic_model_converter, m, "pb-preprocess"); + g->add(pp); g->inc_depth(); result.push_back(g.get()); @@ -164,7 +125,7 @@ public: // decompose(g); } - bool simplify(goal_ref const& g, pb_preproc_model_converter& mc) { + bool simplify(goal_ref const& g, generic_model_converter& mc) { reset(); normalize(g); if (g->inconsistent()) { @@ -202,11 +163,11 @@ public: TRACE("pb", tout << mk_pp(e, m) << " " << r.pos.size() << " " << r.neg.size() << "\n";); if (r.pos.empty()) { replace(r.neg, e, m.mk_false(), g); - mc.set_value(e, false); + set_value(mc, e, false); } else if (r.neg.empty()) { replace(r.pos, e, m.mk_true(), g); - mc.set_value(e, true); + set_value(mc, e, true); } if (g->inconsistent()) return false; ++it; @@ -251,7 +212,7 @@ public: if (!to_ge(g->form(k), args2, coeffs2, k2)) continue; if (subsumes(args1, coeffs1, k1, args2, coeffs2, k2)) { IF_VERBOSE(3, verbose_stream() << "replace " << mk_pp(g->form(k), m) << "\n";); - g->update(k, m.mk_true(), 0, m.mk_join(g->dep(m_ge[i]), g->dep(k))); + g->update(k, m.mk_true(), nullptr, m.mk_join(g->dep(m_ge[i]), g->dep(k))); m_progress = true; } } @@ -262,15 +223,15 @@ public: return m_progress; } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { } - virtual void cleanup() { + void cleanup() override { } private: - void reset() { + void reset() override { m_ge.reset(); m_other.reset(); m_vars.reset(); @@ -329,12 +290,12 @@ private: for (unsigned j = 0; j < cuts.size(); ++j) { unsigned end = cuts[j]; fml1 = decompose_cut(a, start, end, cut_args, cut_coeffs); - g->assert_expr(fml1, 0, g->dep(i)); + g->assert_expr(fml1, nullptr, g->dep(i)); start = end; TRACE("pb", tout << fml1 << "\n";); } fml2 = pb.mk_ge(cut_args.size(), cut_coeffs.c_ptr(), cut_args.c_ptr(), pb.get_k(e)); - g->update(i, fml2, 0, g->dep(i)); + g->update(i, fml2, nullptr, g->dep(i)); TRACE("pb", tout << fml2 << "\n";); } } @@ -508,7 +469,7 @@ private: // Implement very special case of resolution. - void resolve(pb_preproc_model_converter& mc, unsigned idx1, + void resolve(generic_model_converter& mc, unsigned idx1, unsigned_vector const& positions, app* e, bool pos, goal_ref const& g) { if (positions.size() != 1) return; unsigned idx2 = positions[0]; @@ -557,7 +518,7 @@ private: else { args2[j] = m.mk_true(); } - mc.set_value(arg, j != min_index); + set_value(mc, arg, j != min_index); } tmp1 = pb.mk_ge(args2.size(), coeffs2.c_ptr(), args2.c_ptr(), k2); @@ -579,8 +540,8 @@ private: tout << "resolve: " << mk_pp(fml1, m) << "\n" << mk_pp(fml2, m) << "\n" << tmp1 << "\n"; tout << "to\n" << mk_pp(fml2, m) << " -> " << tmp2 << "\n";); - g->update(idx2, tmp2, 0, m.mk_join(g->dep(idx1), g->dep(idx2))); - g->update(idx1, m.mk_true(), 0, 0); + g->update(idx2, tmp2, nullptr, m.mk_join(g->dep(idx1), g->dep(idx2))); + g->update(idx1, m.mk_true(), nullptr, nullptr); m_progress = true; //IF_VERBOSE(0, if (!g->inconsistent()) display_annotation(verbose_stream(), g);); } @@ -652,7 +613,7 @@ private: } } } - m_r.set_substitution(0); + m_r.set_substitution(nullptr); } bool subsumes(expr_ref_vector const& args1, diff --git a/src/tactic/core/propagate_values_tactic.cpp b/src/tactic/core/propagate_values_tactic.cpp index 607ecbd79..41e48d864 100644 --- a/src/tactic/core/propagate_values_tactic.cpp +++ b/src/tactic/core/propagate_values_tactic.cpp @@ -23,6 +23,7 @@ Revision History: #include "ast/ast_smt2_pp.h" #include "ast/expr_substitution.h" #include "tactic/goal_shared_occs.h" +#include "ast/pb_decl_plugin.h" class propagate_values_tactic : public tactic { struct imp { @@ -38,7 +39,7 @@ class propagate_values_tactic : public tactic { imp(ast_manager & m, params_ref const & p): m(m), m_r(m, p), - m_goal(0), + m_goal(nullptr), m_occs(m, true /* track atoms */) { updt_params_core(p); } @@ -92,7 +93,7 @@ class propagate_values_tactic : public tactic { if (m_goal->unsat_core_enabled()) { new_d = m_goal->dep(m_idx); expr_dependency * used_d = m_r.get_used_dependencies(); - if (used_d != 0) { + if (used_d != nullptr) { new_d = m.mk_join(new_d, used_d); m_r.reset_used_dependencies(); } @@ -129,19 +130,28 @@ class propagate_values_tactic : public tactic { } TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n";); - push_result(new_curr, new_pr); - - if (new_curr != curr) + if (new_curr != curr) { m_modified = true; + //if (has_pb(curr)) + // IF_VERBOSE(0, verbose_stream() << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n"); + } + push_result(new_curr, new_pr); + } + + bool has_pb(expr* e) { + pb_util pb(m); + if (pb.is_ge(e)) return true; + if (m.is_or(e)) { + for (expr* a : *to_app(e)) { + if (pb.is_ge(a)) return true; + } + } + return false; } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("propagate-values", *g); m_goal = g.get(); @@ -210,7 +220,7 @@ class propagate_values_tactic : public tactic { SASSERT(m_goal->is_well_sorted()); TRACE("propagate_values", tout << "end\n"; m_goal->display(tout);); TRACE("propagate_values_core", m_goal->display_with_dependencies(tout);); - m_goal = 0; + m_goal = nullptr; } }; @@ -222,38 +232,34 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(propagate_values_tactic, m, m_params); } - virtual ~propagate_values_tactic() { + ~propagate_values_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { th_rewriter::get_param_descrs(r); r.insert("max_rounds", CPK_UINT, "(default: 2) maximum number of rounds."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m; params_ref p = std::move(m_params); m_imp->~imp(); diff --git a/src/tactic/core/reduce_args_tactic.cpp b/src/tactic/core/reduce_args_tactic.cpp index 476c21232..24898320d 100644 --- a/src/tactic/core/reduce_args_tactic.cpp +++ b/src/tactic/core/reduce_args_tactic.cpp @@ -22,8 +22,7 @@ Notes: #include "ast/has_free_vars.h" #include "util/map.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" /** \brief Reduce the number of arguments in function applications. @@ -68,14 +67,14 @@ class reduce_args_tactic : public tactic { public: reduce_args_tactic(ast_manager & m); - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(reduce_args_tactic, m); } - virtual ~reduce_args_tactic(); + ~reduce_args_tactic() override; - virtual void operator()(goal_ref const & g, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); - virtual void cleanup(); + void operator()(goal_ref const & g, goal_ref_buffer & result) override; + void cleanup() override; }; tactic * mk_reduce_args_tactic(ast_manager & m, params_ref const & p) { @@ -104,7 +103,7 @@ struct reduce_args_tactic::imp { } static bool may_be_unique(ast_manager& m, bv_util& bv, expr* e, expr*& base) { - base = 0; + base = nullptr; return m.is_unique_value(e) || is_var_plus_offset(m, bv, e, base); } @@ -341,7 +340,7 @@ struct reduce_args_tactic::imp { } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; if (f->get_arity() == 0) return BR_FAILED; // ignore constants if (f->get_family_id() != null_family_id) @@ -357,7 +356,7 @@ struct reduce_args_tactic::imp { } app_ref tmp(m.mk_app(f, num, args), m); - func_decl *& new_f = map->insert_if_not_there2(tmp, 0)->get_data().m_value; + func_decl *& new_f = map->insert_if_not_there2(tmp, nullptr)->get_data().m_value; if (!new_f) { // create fresh symbol ptr_buffer domain; @@ -394,14 +393,11 @@ struct reduce_args_tactic::imp { ptr_buffer new_args; var_ref_vector new_vars(m_manager); ptr_buffer new_eqs; - extension_model_converter * e_mc = alloc(extension_model_converter, m_manager); - filter_model_converter * f_mc = alloc(filter_model_converter, m_manager); - decl2arg2func_map::iterator it = decl2arg2funcs.begin(); - decl2arg2func_map::iterator end = decl2arg2funcs.end(); - for (; it != end; ++it) { - func_decl * f = it->m_key; - arg2func * map = it->m_value; - expr * def = 0; + generic_model_converter * f_mc = alloc(generic_model_converter, m_manager, "reduce_args"); + for (auto const& kv : decl2arg2funcs) { + func_decl * f = kv.m_key; + arg2func * map = kv.m_value; + expr * def = nullptr; SASSERT(decl2args.contains(f)); bit_vector & bv = decl2args.find(f); new_vars.reset(); @@ -416,10 +412,10 @@ struct reduce_args_tactic::imp { for (; it2 != end2; ++it2) { app * t = it2->m_key; func_decl * new_def = it2->m_value; - f_mc->insert(new_def); + f_mc->hide(new_def); SASSERT(new_def->get_arity() == new_args.size()); app * new_t = m_manager.mk_app(new_def, new_args.size(), new_args.c_ptr()); - if (def == 0) { + if (def == nullptr) { def = new_t; } else { @@ -438,12 +434,12 @@ struct reduce_args_tactic::imp { } } SASSERT(def); - e_mc->insert(f, def); + f_mc->add(f, def); } - return concat(f_mc, e_mc); + return f_mc; } - void operator()(goal & g, model_converter_ref & mc) { + void operator()(goal & g) { if (g.inconsistent()) return; TRACE("reduce_args", g.display(tout);); @@ -472,9 +468,9 @@ struct reduce_args_tactic::imp { report_tactic_progress(":reduced-funcs", decl2args.size()); if (g.models_enabled()) - mc = mk_mc(decl2args, ctx.m_decl2arg2funcs); + g.add(mk_mc(decl2args, ctx.m_decl2arg2funcs)); - TRACE("reduce_args", g.display(tout); if (mc) mc->display(tout);); + TRACE("reduce_args", g.display(tout); if (g.mc()) g.mc()->display(tout);); } }; @@ -487,15 +483,12 @@ reduce_args_tactic::~reduce_args_tactic() { } void reduce_args_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); fail_if_proof_generation("reduce-args", g); fail_if_unsat_core_generation("reduce-args", g); - mc = 0; pc = 0; core = 0; result.reset(); - m_imp->operator()(*(g.get()), mc); + result.reset(); + m_imp->operator()(*(g.get())); g->inc_depth(); result.push_back(g.get()); SASSERT(g->is_well_sorted()); diff --git a/src/tactic/core/simplify_tactic.cpp b/src/tactic/core/simplify_tactic.cpp index de1fcc99c..7e729714f 100644 --- a/src/tactic/core/simplify_tactic.cpp +++ b/src/tactic/core/simplify_tactic.cpp @@ -93,15 +93,11 @@ void simplify_tactic::get_param_descrs(param_descrs & r) { } void simplify_tactic::operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { try { (*m_imp)(*(in.get())); in->inc_depth(); result.push_back(in.get()); - mc = 0; pc = 0; core = 0; } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); diff --git a/src/tactic/core/simplify_tactic.h b/src/tactic/core/simplify_tactic.h index 1e8420c62..e64408a29 100644 --- a/src/tactic/core/simplify_tactic.h +++ b/src/tactic/core/simplify_tactic.h @@ -28,23 +28,21 @@ class simplify_tactic : public tactic { params_ref m_params; public: simplify_tactic(ast_manager & m, params_ref const & ref = params_ref()); - virtual ~simplify_tactic(); + ~simplify_tactic() override; + + void updt_params(params_ref const & p) override; - virtual void updt_params(params_ref const & p); static void get_param_descrs(param_descrs & r); - virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core); + void collect_param_descrs(param_descrs & r) override { get_param_descrs(r); } - virtual void cleanup(); + void operator()(goal_ref const & in, goal_ref_buffer & result) override; + + void cleanup() override; unsigned get_num_steps() const; - virtual tactic * translate(ast_manager & m) { return alloc(simplify_tactic, m, m_params); } + tactic * translate(ast_manager & m) override { return alloc(simplify_tactic, m, m_params); } }; diff --git a/src/tactic/core/solve_eqs_tactic.cpp b/src/tactic/core/solve_eqs_tactic.cpp index 65d474182..f665fe509 100644 --- a/src/tactic/core/solve_eqs_tactic.cpp +++ b/src/tactic/core/solve_eqs_tactic.cpp @@ -18,15 +18,16 @@ Revision History: --*/ #include "tactic/tactical.h" #include "ast/rewriter/expr_replacer.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/occurs.h" #include "util/cooperate.h" #include "tactic/goal_shared_occs.h" #include "ast/ast_pp.h" +#include "ast/pb_decl_plugin.h" class solve_eqs_tactic : public tactic { struct imp { - typedef extension_model_converter gmc; + typedef generic_model_converter gmc; ast_manager & m_manager; expr_replacer * m_r; @@ -44,6 +45,7 @@ class solve_eqs_tactic : public tactic { expr_sparse_mark m_candidate_set; ptr_vector m_candidates; ptr_vector m_vars; + expr_sparse_mark m_nonzero; ptr_vector m_ordered_vars; bool m_produce_proofs; bool m_produce_unsat_cores; @@ -52,13 +54,12 @@ class solve_eqs_tactic : public tactic { imp(ast_manager & m, params_ref const & p, expr_replacer * r, bool owner): m_manager(m), m_r(r), - m_r_owner(r == 0 || owner), + m_r_owner(r == nullptr || owner), m_a_util(m), m_num_steps(0), - m_num_eliminated_vars(0) - { + m_num_eliminated_vars(0) { updt_params(p); - if (m_r == 0) + if (m_r == nullptr) m_r = mk_default_expr_replacer(m); } @@ -78,7 +79,7 @@ class solve_eqs_tactic : public tactic { void checkpoint() { if (m().canceled()) throw tactic_exception(m().limit().get_cancel_msg()); - cooperate("solve-eqs"); + cooperate("solve-eqs"); } // Check if the number of occurrences of t is below the specified threshold :solve-eqs-max-occs @@ -98,7 +99,7 @@ class solve_eqs_tactic : public tactic { if (is_uninterp_const(lhs) && !m_candidate_vars.is_marked(lhs) && !occurs(lhs, rhs) && check_occs(lhs)) { var = to_app(lhs); def = rhs; - pr = 0; + pr = nullptr; return true; } else { @@ -106,7 +107,8 @@ class solve_eqs_tactic : public tactic { } } bool trivial_solve(expr * lhs, expr * rhs, app_ref & var, expr_ref & def, proof_ref & pr) { - if (trivial_solve1(lhs, rhs, var, def, pr)) return true; + if (trivial_solve1(lhs, rhs, var, def, pr)) + return true; if (trivial_solve1(rhs, lhs, var, def, pr)) { if (m_produce_proofs) { pr = m().mk_commutativity(m().mk_eq(lhs, rhs)); @@ -187,12 +189,83 @@ class solve_eqs_tactic : public tactic { } return false; } + + void add_pos(expr* f) { + expr* lhs = nullptr, *rhs = nullptr; + rational val; + if (m_a_util.is_le(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && val.is_neg()) { + m_nonzero.mark(lhs); + } + else if (m_a_util.is_ge(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && val.is_pos()) { + m_nonzero.mark(lhs); + } + else if (m().is_not(f, f)) { + if (m_a_util.is_le(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && !val.is_neg()) { + m_nonzero.mark(lhs); + } + else if (m_a_util.is_ge(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && !val.is_pos()) { + m_nonzero.mark(lhs); + } + else if (m().is_eq(f, lhs, rhs) && m_a_util.is_numeral(rhs, val) && val.is_zero()) { + m_nonzero.mark(lhs); + } + } + } + + bool is_nonzero(expr* e) { + return m_nonzero.is_marked(e); + } + + bool isolate_var(app* arg, app_ref& var, expr_ref& div, unsigned i, app* lhs, expr* rhs) { + if (!m_a_util.is_mul(arg)) return false; + unsigned n = arg->get_num_args(); + for (unsigned j = 0; j < n; ++j) { + expr* e = arg->get_arg(j); + bool ok = is_uninterp_const(e) && check_occs(e) && !occurs(e, rhs) && !occurs_except(e, lhs, i); + if (!ok) continue; + var = to_app(e); + for (unsigned k = 0; ok && k < n; ++k) { + expr* arg_k = arg->get_arg(k); + ok = k == j || (!occurs(var, arg_k) && is_nonzero(arg_k)); + } + if (!ok) continue; + ptr_vector args; + for (unsigned k = 0; k < n; ++k) { + if (k != j) args.push_back(arg->get_arg(k)); + } + div = m_a_util.mk_mul(args.size(), args.c_ptr()); + return true; + } + return false; + } + + bool solve_nl(app * lhs, expr * rhs, expr* eq, app_ref& var, expr_ref & def, proof_ref & pr) { + SASSERT(m_a_util.is_add(lhs)); + if (m_a_util.is_int(lhs)) return false; + unsigned num = lhs->get_num_args(); + expr_ref div(m()); + for (unsigned i = 0; i < num; i++) { + expr * arg = lhs->get_arg(i); + if (is_app(arg) && isolate_var(to_app(arg), var, div, i, lhs, rhs)) { + ptr_vector args; + for (unsigned k = 0; k < num; ++k) { + if (k != i) args.push_back(lhs->get_arg(k)); + } + def = m_a_util.mk_sub(rhs, m_a_util.mk_add(args.size(), args.c_ptr())); + def = m_a_util.mk_div(def, div); + if (m_produce_proofs) + pr = m().mk_rewrite(eq, m().mk_eq(var, def)); + return true; + } + } + return false; + } bool solve_arith_core(app * lhs, expr * rhs, expr * eq, app_ref & var, expr_ref & def, proof_ref & pr) { SASSERT(m_a_util.is_add(lhs)); bool is_int = m_a_util.is_int(lhs); - expr * a; - expr * v; + expr * a = nullptr; + expr * v = nullptr; rational a_val; unsigned num = lhs->get_num_args(); unsigned i; @@ -204,7 +277,8 @@ class solve_eqs_tactic : public tactic { break; } else if (m_a_util.is_mul(arg, a, v) && - is_uninterp_const(v) && !m_candidate_vars.is_marked(v) && + is_uninterp_const(v) && + !m_candidate_vars.is_marked(v) && m_a_util.is_numeral(a, a_val) && !a_val.is_zero() && (!is_int || a_val.is_minus_one()) && @@ -252,24 +326,25 @@ class solve_eqs_tactic : public tactic { return (m_a_util.is_add(lhs) && solve_arith_core(to_app(lhs), rhs, eq, var, def, pr)) || (m_a_util.is_add(rhs) && solve_arith_core(to_app(rhs), lhs, eq, var, def, pr)); +#if 0 + // better done inside of nlsat + (m_a_util.is_add(lhs) && solve_nl(to_app(lhs), rhs, eq, var, def, pr)) || + (m_a_util.is_add(rhs) && solve_nl(to_app(rhs), lhs, eq, var, def, pr)); +#endif } bool solve(expr * f, app_ref & var, expr_ref & def, proof_ref & pr) { - if (m().is_eq(f)) { - if (trivial_solve(to_app(f)->get_arg(0), to_app(f)->get_arg(1), var, def, pr)) + expr* arg1 = nullptr, *arg2 = nullptr; + if (m().is_eq(f, arg1, arg2)) { + if (trivial_solve(arg1, arg2, var, def, pr)) return true; if (m_theory_solver) { - expr * lhs = to_app(f)->get_arg(0); - expr * rhs = to_app(f)->get_arg(1); - if (solve_arith(lhs, rhs, f, var, def, pr)) + if (solve_arith(arg1, arg2, f, var, def, pr)) return true; } return false; } - - if (m().is_iff(f)) - return trivial_solve(to_app(f)->get_arg(0), to_app(f)->get_arg(1), var, def, pr); - + #if 0 if (not_bool_eq(f, var, def, pr)) return true; @@ -316,16 +391,19 @@ class solve_eqs_tactic : public tactic { void collect(goal const & g) { m_subst->reset(); m_norm_subst->reset(); - m_r->set_substitution(0); + m_r->set_substitution(nullptr); m_candidate_vars.reset(); m_candidate_set.reset(); m_candidates.reset(); m_vars.reset(); - + m_nonzero.reset(); app_ref var(m()); expr_ref def(m()); proof_ref pr(m()); unsigned size = g.size(); + for (unsigned idx = 0; idx < size; idx++) { + add_pos(g.form(idx)); + } for (unsigned idx = 0; idx < size; idx++) { checkpoint(); expr * f = g.form(idx); @@ -347,10 +425,8 @@ class solve_eqs_tactic : public tactic { TRACE("solve_eqs", tout << "candidate vars:\n"; - ptr_vector::iterator it = m_vars.begin(); - ptr_vector::iterator end = m_vars.end(); - for (; it != end; ++it) { - tout << mk_ismt2_pp(*it, m()) << " "; + for (app* v : m_vars) { + tout << mk_ismt2_pp(v, m()) << " "; } tout << "\n";); } @@ -374,12 +450,9 @@ class solve_eqs_tactic : public tactic { typedef std::pair frame; svector todo; - ptr_vector::const_iterator it = m_vars.begin(); - ptr_vector::const_iterator end = m_vars.end(); - unsigned num; - for (; it != end; ++it) { + unsigned num = 0; + for (app* v : m_vars) { checkpoint(); - app * v = *it; if (!m_candidate_vars.is_marked(v)) continue; todo.push_back(frame(v, 0)); @@ -424,7 +497,7 @@ class solve_eqs_tactic : public tactic { // Must save t and its definition. // See comment in the beginning of the function - expr * def = 0; + expr * def = nullptr; proof * pr; expr_dependency * dep; m_subst->find(to_app(t), def, pr, dep); @@ -438,7 +511,7 @@ class solve_eqs_tactic : public tactic { else { visiting.mark(t); fr.second = 1; - expr * def = 0; + expr * def = nullptr; proof * pr; expr_dependency * dep; m_subst->find(to_app(t), def, pr, dep); @@ -483,20 +556,19 @@ class solve_eqs_tactic : public tactic { } // cleanup - it = m_vars.begin(); - for (unsigned idx = 0; it != end; ++it, ++idx) { - if (!m_candidate_vars.is_marked(*it)) { + unsigned idx = 0; + for (expr* v : m_vars) { + if (!m_candidate_vars.is_marked(v)) { m_candidate_set.mark(m_candidates[idx], false); } + ++idx; } TRACE("solve_eqs", tout << "ordered vars:\n"; - ptr_vector::iterator it = m_ordered_vars.begin(); - ptr_vector::iterator end = m_ordered_vars.end(); - for (; it != end; ++it) { - SASSERT(m_candidate_vars.is_marked(*it)); - tout << mk_ismt2_pp(*it, m()) << " "; + for (app* v : m_ordered_vars) { + SASSERT(m_candidate_vars.is_marked(v)); + tout << mk_ismt2_pp(v, m()) << " "; } tout << "\n";); m_candidate_vars.reset(); @@ -509,13 +581,11 @@ class solve_eqs_tactic : public tactic { expr_ref new_def(m()); proof_ref new_pr(m()); expr_dependency_ref new_dep(m()); - unsigned size = m_ordered_vars.size(); - for (unsigned idx = 0; idx < size; idx++) { + for (app * v : m_ordered_vars) { checkpoint(); - expr * v = m_ordered_vars[idx]; - expr * def = 0; - proof * pr = 0; - expr_dependency * dep = 0; + expr * def = nullptr; + proof * pr = nullptr; + expr_dependency * dep = nullptr; m_subst->find(v, def, pr, dep); SASSERT(def != 0); m_r->operator()(def, new_def, new_pr, new_dep); @@ -531,8 +601,7 @@ class solve_eqs_tactic : public tactic { m_subst->reset(); TRACE("solve_eqs", tout << "after normalizing variables\n"; - for (unsigned i = 0; i < m_ordered_vars.size(); i++) { - expr * v = m_ordered_vars[i]; + for (expr * v : m_ordered_vars) { expr * def = 0; proof * pr = 0; expr_dependency * dep = 0; @@ -541,16 +610,15 @@ class solve_eqs_tactic : public tactic { }); #if 0 DEBUG_CODE({ - for (unsigned i = 0; i < m_ordered_vars.size(); i++) { - expr * v = m_ordered_vars[i]; - expr * def = 0; - proof * pr = 0; - expr_dependency * dep = 0; - m_norm_subst->find(v, def, pr, dep); - SASSERT(def != 0); - CASSERT("solve_eqs_bug", !occurs(v, def)); - } - }); + for (expr * v : m_ordered_vars) { + expr * def = 0; + proof * pr = 0; + expr_dependency * dep = 0; + m_norm_subst->find(v, def, pr, dep); + SASSERT(def != 0); + CASSERT("solve_eqs_bug", !occurs(v, def)); + } + }); #endif } @@ -568,15 +636,22 @@ class solve_eqs_tactic : public tactic { TRACE("gaussian_leak", tout << "processing:\n" << mk_ismt2_pp(f, m()) << "\n";); if (m_candidate_set.is_marked(f)) { // f may be deleted after the following update. - // so, we must remove remove the mark before doing the update + // so, we must remove the mark before doing the update m_candidate_set.mark(f, false); SASSERT(!m_candidate_set.is_marked(f)); - g.update(idx, m().mk_true(), m().mk_true_proof(), 0); + g.update(idx, m().mk_true(), m().mk_true_proof(), nullptr); m_num_steps ++; continue; } m_r->operator()(f, new_f, new_pr, new_dep); +#if 0 + pb_util pb(m()); + if (pb.is_ge(f) && f != new_f) { + IF_VERBOSE(0, verbose_stream() << mk_ismt2_pp(f, m()) << "\n--->\n" << mk_ismt2_pp(new_f, m()) << "\n"); + } +#endif + TRACE("solve_eqs_subst", tout << mk_ismt2_pp(f, m()) << "\n--->\n" << mk_ismt2_pp(new_f, m()) << "\n";); m_num_steps += m_r->get_num_steps() + 1; if (m_produce_proofs) @@ -594,12 +669,11 @@ class solve_eqs_tactic : public tactic { g.display(tout);); #if 0 DEBUG_CODE({ - for (unsigned i = 0; i < m_ordered_vars.size(); i++) { - expr * v = m_ordered_vars[i]; - for (unsigned j = 0; j < g.size(); j++) { - CASSERT("solve_eqs_bug", !occurs(v, g.form(j))); - } - }}); + for (expr* v : m_ordered_vars) { + for (unsigned j = 0; j < g.size(); j++) { + CASSERT("solve_eqs_bug", !occurs(v, g.form(j))); + } + }}); #endif } @@ -607,18 +681,15 @@ class solve_eqs_tactic : public tactic { IF_VERBOSE(100, if (!m_ordered_vars.empty()) verbose_stream() << "num. eliminated vars: " << m_ordered_vars.size() << "\n";); m_num_eliminated_vars += m_ordered_vars.size(); if (m_produce_models) { - if (mc.get() == 0) - mc = alloc(gmc, m()); - ptr_vector::iterator it = m_ordered_vars.begin(); - ptr_vector::iterator end = m_ordered_vars.end(); - for (; it != end; ++it) { - app * v = *it; - expr * def = 0; + if (!mc.get()) + mc = alloc(gmc, m(), "solve-eqs"); + for (app* v : m_ordered_vars) { + expr * def = nullptr; proof * pr; - expr_dependency * dep; + expr_dependency * dep = nullptr; m_norm_subst->find(v, def, pr, dep); - SASSERT(def != 0); - static_cast(mc.get())->insert(v->get_decl(), def); + SASSERT(def); + static_cast(mc.get())->add(v, def); } } } @@ -671,13 +742,9 @@ class solve_eqs_tactic : public tactic { return m_num_eliminated_vars; } - void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; + model_converter_ref mc; tactic_report report("solve_eqs", *g); m_produce_models = g->models_enabled(); m_produce_proofs = g->proofs_enabled(); @@ -697,7 +764,6 @@ class solve_eqs_tactic : public tactic { normalize(); substitute(*(g.get())); if (g->inconsistent()) { - mc = 0; break; } save_elim_vars(mc); @@ -705,6 +771,7 @@ class solve_eqs_tactic : public tactic { } } g->inc_depth(); + g->add(mc.get()); result.push_back(g.get()); TRACE("solve_eqs", g->display(tout);); SASSERT(g->is_well_sorted()); @@ -719,40 +786,37 @@ public: m_imp = alloc(imp, m, p, r, owner); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(solve_eqs_tactic, m, m_params, mk_expr_simp_replacer(m, m_params), true); } - virtual ~solve_eqs_tactic() { + ~solve_eqs_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("solve_eqs_max_occs", CPK_UINT, "(default: infty) maximum number of occurrences for considering a variable for gaussian eliminations."); r.insert("theory_solver", CPK_BOOL, "(default: true) use theory solvers."); r.insert("ite_solver", CPK_BOOL, "(default: true) use if-then-else solver."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); report_tactic_progress(":num-elim-vars", m_imp->get_num_eliminated_vars()); } - virtual void cleanup() { + void cleanup() override { unsigned num_elim_vars = m_imp->m_num_eliminated_vars; ast_manager & m = m_imp->m(); expr_replacer * r = m_imp->m_r; if (r) - r->set_substitution(0); + r->set_substitution(nullptr); bool owner = m_imp->m_r_owner; m_imp->m_r_owner = false; // stole replacer @@ -762,20 +826,19 @@ public: dealloc(d); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.update("eliminated vars", m_imp->get_num_eliminated_vars()); } - virtual void reset_statistics() { + void reset_statistics() override { m_imp->m_num_eliminated_vars = 0; } }; tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p, expr_replacer * r) { - if (r == 0) + if (r == nullptr) return clean(alloc(solve_eqs_tactic, m, p, mk_expr_simp_replacer(m, p), true)); else return clean(alloc(solve_eqs_tactic, m, p, r, false)); } - diff --git a/src/tactic/core/solve_eqs_tactic.h b/src/tactic/core/solve_eqs_tactic.h index 084942ed2..a84ffdd43 100644 --- a/src/tactic/core/solve_eqs_tactic.h +++ b/src/tactic/core/solve_eqs_tactic.h @@ -24,7 +24,7 @@ class ast_manager; class tactic; class expr_replacer; -tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref(), expr_replacer * r = 0); +tactic * mk_solve_eqs_tactic(ast_manager & m, params_ref const & p = params_ref(), expr_replacer * r = nullptr); /* ADD_TACTIC("solve-eqs", "eliminate variables by solving equations.", "mk_solve_eqs_tactic(m, p)") diff --git a/src/tactic/core/split_clause_tactic.cpp b/src/tactic/core/split_clause_tactic.cpp index 7a2df81b5..138bc91ec 100644 --- a/src/tactic/core/split_clause_tactic.cpp +++ b/src/tactic/core/split_clause_tactic.cpp @@ -46,21 +46,15 @@ class split_clause_tactic : public tactic { } class split_pc : public proof_converter { - ast_manager & m_manager; - app * m_clause; - proof * m_clause_pr; + app_ref m_clause; + proof_ref m_clause_pr; public: - split_pc(ast_manager & m, app * cls, proof * pr):m_manager(m), m_clause(cls), m_clause_pr(pr) { - m.inc_ref(cls); - m.inc_ref(pr); + split_pc(ast_manager & m, app * cls, proof * pr):m_clause(cls, m), m_clause_pr(pr, m) { } - ~split_pc() { - m_manager.dec_ref(m_clause); - m_manager.dec_ref(m_clause_pr); - } + ~split_pc() override { } - virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { // Let m_clause be of the form (l_0 or ... or l_{num_source - 1}) // Each source[i] proof is a proof for "false" using l_i as a hypothesis // So, I use lemma for producing a proof for (not l_i) that does not contain the hypothesis, @@ -73,12 +67,14 @@ class split_clause_tactic : public tactic { expr * not_li = m.mk_not(m_clause->get_arg(i)); prs.push_back(m.mk_lemma(pr_i, not_li)); } - result = m.mk_unit_resolution(prs.size(), prs.c_ptr()); + return proof_ref(m.mk_unit_resolution(prs.size(), prs.c_ptr()), m); } - virtual proof_converter * translate(ast_translation & translator) { - return alloc(split_pc, translator.to(), translator(m_clause), translator(m_clause_pr)); + proof_converter * translate(ast_translation & translator) override { + return alloc(split_pc, translator.to(), translator(m_clause.get()), translator(m_clause_pr.get())); } + + void display(std::ostream & out) override { out << "(split-clause-pc)\n"; } }; public: @@ -86,32 +82,28 @@ public: updt_params(ref); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { split_clause_tactic * t = alloc(split_clause_tactic); t->m_largest_clause = m_largest_clause; return t; } - virtual ~split_clause_tactic() { + ~split_clause_tactic() override { } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_largest_clause = p.get_bool("split_largest_clause", false); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { r.insert("split_largest_clause", CPK_BOOL, "(default: false) split the largest clause in the goal."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { SASSERT(in->is_well_sorted()); tactic_report report("split-clause", *in); TRACE("before_split_clause", in->display(tout);); - pc = 0; mc = 0; core = 0; ast_manager & m = in->m(); unsigned cls_pos = select_clause(m, in); if (cls_pos == UINT_MAX) { @@ -121,26 +113,23 @@ public: app * cls = to_app(in->form(cls_pos)); expr_dependency * cls_dep = in->dep(cls_pos); if (produce_proofs) - pc = alloc(split_pc, m, cls, in->pr(cls_pos)); - unsigned cls_sz = cls->get_num_args(); - report_tactic_progress(":num-new-branches", cls_sz); - for (unsigned i = 0; i < cls_sz; i++) { - goal * subgoal_i; - if (i == cls_sz - 1) - subgoal_i = in.get(); - else - subgoal_i = alloc(goal, *in); - expr * lit_i = cls->get_arg(i); - proof * pr_i = 0; + in->set(alloc(split_pc, m, cls, in->pr(cls_pos))); + report_tactic_progress(":num-new-branches", cls->get_num_args()); + for (expr* lit_i : *cls) { + goal * subgoal_i = alloc(goal, *in); + subgoal_i->set(in->mc()); + proof * pr_i = nullptr; if (produce_proofs) pr_i = m.mk_hypothesis(lit_i); subgoal_i->update(cls_pos, lit_i, pr_i, cls_dep); subgoal_i->inc_depth(); result.push_back(subgoal_i); } + in->set(concat(in->pc(), result.size(), result.c_ptr())); + in->add(dependency_converter::concat(result.size(), result.c_ptr())); } - virtual void cleanup() { + void cleanup() override { // do nothing this tactic is too simple } }; diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index 8e87a6741..d288adbc7 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -32,18 +32,15 @@ class symmetry_reduce_tactic : public tactic { public: symmetry_reduce_tactic(ast_manager & m); - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(symmetry_reduce_tactic, m); } - virtual ~symmetry_reduce_tactic(); + ~symmetry_reduce_tactic() override; - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core); - virtual void cleanup(); + void operator()(goal_ref const & g, + goal_ref_buffer & result) override; + virtual void cleanup() override; }; class ac_rewriter { @@ -88,7 +85,7 @@ struct ac_rewriter_cfg : public default_rewriter_cfg { bool rewrite_patterns() const { return false; } bool flat_assoc(func_decl * f) const { return false; } br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { - result_pr = 0; + result_pr = nullptr; return m_r.mk_app_core(f, num, args, result); } ac_rewriter_cfg(ast_manager & m):m_r(m) {} @@ -475,7 +472,7 @@ private: T.reset(); ptr_vector todo; todo.push_back(fml); - app* t = 0; + app* t = nullptr; while (!todo.empty()) { fml = todo.back(); todo.pop_back(); @@ -490,24 +487,24 @@ private: bool is_range_restriction(expr* form, term_set const& C, app*& t) { if (!m().is_or(form)) return false; unsigned sz = to_app(form)->get_num_args(); - t = 0; + t = nullptr; for (unsigned i = 0; i < sz; ++i) { expr* e = to_app(form)->get_arg(i); expr* e1, *e2; if (!m().is_eq(e, e1, e2)) return false; if (!is_app(e1) || !is_app(e2)) return false; app* a1 = to_app(e1), *a2 = to_app(e2); - if (C.contains(a1) && (t == 0 || t == a2)) { + if (C.contains(a1) && (t == nullptr || t == a2)) { t = a2; } - else if (C.contains(a2) && (t == 0 || t == a1)) { + else if (C.contains(a2) && (t == nullptr || t == a1)) { t = a1; } else { return false; } } - return t != 0; + return t != nullptr; } @@ -635,13 +632,10 @@ symmetry_reduce_tactic::~symmetry_reduce_tactic() { } void symmetry_reduce_tactic::operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { fail_if_proof_generation("symmetry_reduce", g); fail_if_unsat_core_generation("symmetry_reduce", g); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); (*m_imp)(*(g.get())); g->inc_depth(); result.push_back(g.get()); diff --git a/src/tactic/core/tseitin_cnf_tactic.cpp b/src/tactic/core/tseitin_cnf_tactic.cpp index dbfc748b0..b29a80927 100644 --- a/src/tactic/core/tseitin_cnf_tactic.cpp +++ b/src/tactic/core/tseitin_cnf_tactic.cpp @@ -51,7 +51,7 @@ Notes: --*/ #include "tactic/tactical.h" #include "tactic/goal_shared_occs.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/rewriter/bool_rewriter.h" #include "tactic/core/simplify_tactic.h" #include "util/cooperate.h" @@ -80,7 +80,7 @@ class tseitin_cnf_tactic : public tactic { frame(app * n):m_t(n), m_first(true) {} }; - typedef filter_model_converter mc; + typedef generic_model_converter mc; ast_manager & m; svector m_frame_stack; @@ -176,8 +176,7 @@ class tseitin_cnf_tactic : public tactic { sign = !sign; goto start; case OP_OR: - case OP_IFF: - l = 0; + l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); mk_lit(l, sign, r); @@ -185,7 +184,7 @@ class tseitin_cnf_tactic : public tactic { case OP_ITE: case OP_EQ: if (m.is_bool(to_app(n)->get_arg(1))) { - l = 0; + l = nullptr; m_cache.find(to_app(n), l); SASSERT(l != 0); mk_lit(l, sign, r); @@ -223,7 +222,6 @@ class tseitin_cnf_tactic : public tactic { goto start; } case OP_OR: - case OP_IFF: visited = false; push_frame(to_app(n)); return; @@ -341,10 +339,10 @@ class tseitin_cnf_tactic : public tactic { app * mk_fresh() { m_num_aux_vars++; - app * v = m.mk_fresh_const(0, m.mk_bool_sort()); + app * v = m.mk_fresh_const(nullptr, m.mk_bool_sort()); m_fresh_vars.push_back(v); if (m_mc) - m_mc->insert(v->get_decl()); + m_mc->hide(v->get_decl()); return v; } @@ -468,6 +466,38 @@ class tseitin_cnf_tactic : public tactic { } return NO; } + + mres match_iff_or(app * t, bool first, bool root) { + expr * a = nullptr, * _b = nullptr; + if (!root) return NO; + if (!is_iff(m, t, a, _b)) return NO; + bool sign = m.is_not(_b, _b); + if (!m.is_or(_b)) return NO; + app* b = to_app(_b); + unsigned num = b->get_num_args(); + if (first) { + bool visited = true; + visit(a, visited); + for (expr* arg : *b) { + visit(arg, visited); + } + if (!visited) + return CONT; + } + expr_ref la(m), nla(m), nlb(m), lb(m); + get_lit(a, sign, la); + inv(la, nla); + expr_ref_buffer lits(m); + lits.push_back(nla); + for (expr* arg : *b) { + get_lit(arg, false, lb); + lits.push_back(lb); + inv(lb, nlb); + mk_clause(la, nlb); + } + mk_clause(lits.size(), lits.c_ptr()); + return DONE; + } mres match_iff(app * t, bool first, bool root) { expr * a, * b; @@ -786,6 +816,7 @@ class tseitin_cnf_tactic : public tactic { TRY(match_or_3and); TRY(match_or); TRY(match_iff3); + // TRY(match_iff_or); TRY(match_iff); TRY(match_ite); TRY(match_not); @@ -799,12 +830,8 @@ class tseitin_cnf_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("tseitin-cnf", *g); fail_if_proof_generation("tseitin-cnf", g); m_produce_models = g->models_enabled(); @@ -817,14 +844,14 @@ class tseitin_cnf_tactic : public tactic { m_frame_stack.reset(); m_clauses.reset(); if (m_produce_models) - m_mc = alloc(filter_model_converter, m); + m_mc = alloc(generic_model_converter, m, "tseitin"); else - m_mc = 0; + m_mc = nullptr; unsigned size = g->size(); for (unsigned idx = 0; idx < size; idx++) { process(g->form(idx), g->dep(idx)); - g->update(idx, m.mk_true(), 0, 0); // to save memory + g->update(idx, m.mk_true(), nullptr, nullptr); // to save memory } SASSERT(!m_produce_unsat_cores || m_clauses.size() == m_deps.size()); @@ -838,14 +865,12 @@ class tseitin_cnf_tactic : public tactic { continue; added.mark(cls); if (m_produce_unsat_cores) - g->assert_expr(cls, 0, m_deps.get(i)); + g->assert_expr(cls, nullptr, m_deps.get(i)); else g->assert_expr(cls); } if (m_produce_models && !m_fresh_vars.empty()) - mc = m_mc.get(); - else - mc = 0; + g->add(m_mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("tseitin_cnf", g->display(tout);); @@ -861,20 +886,20 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(tseitin_cnf_tactic, m, m_params); } - virtual ~tseitin_cnf_tactic() { + ~tseitin_cnf_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); r.insert("common_patterns", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by identifing commonly used patterns"); r.insert("distributivity", CPK_BOOL, "(default: true) minimize the number of auxiliary variables during CNF encoding by applying distributivity over unshared subformulas"); @@ -883,16 +908,12 @@ public: r.insert("ite_extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas"); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer & result) override { + (*m_imp)(in, result); report_tactic_progress(":cnf-aux-vars", m_imp->m_num_aux_vars); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m; imp * d = alloc(imp, m, m_params); d->m_num_aux_vars = m_imp->m_num_aux_vars; @@ -900,11 +921,11 @@ public: dealloc(d); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.update("cnf encoding aux vars", m_imp->m_num_aux_vars); } - virtual void reset_statistics() { + void reset_statistics() override { m_imp->m_num_aux_vars = 0; } }; diff --git a/src/tactic/dependency_converter.cpp b/src/tactic/dependency_converter.cpp new file mode 100644 index 000000000..34c864526 --- /dev/null +++ b/src/tactic/dependency_converter.cpp @@ -0,0 +1,107 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + dependency_converter.cpp + +Abstract: + + Utility for converting dependencies across subgoals. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-11-19 + +Notes: + + +--*/ + +#include "tactic/dependency_converter.h" +#include "tactic/goal.h" + +class unit_dependency_converter : public dependency_converter { + expr_dependency_ref m_dep; +public: + + unit_dependency_converter(expr_dependency_ref& d) : m_dep(d) {} + + virtual expr_dependency_ref operator()() { return m_dep; } + + virtual dependency_converter * translate(ast_translation & translator) { + 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) { + out << m_dep.get() << "\n"; + } +}; + +class concat_dependency_converter : public dependency_converter { + dependency_converter_ref m_dc1; + dependency_converter_ref m_dc2; +public: + + concat_dependency_converter(dependency_converter* c1, dependency_converter* c2) : m_dc1(c1), m_dc2(c2) {} + + virtual expr_dependency_ref operator()() { + 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) { + return alloc(concat_dependency_converter, m_dc1->translate(translator), m_dc2->translate(translator)); + } + + virtual void display(std::ostream& out) { + m_dc1->display(out); + m_dc2->display(out); + } +}; + +class goal_dependency_converter : public dependency_converter { + ast_manager& m; + goal_ref_buffer m_goals; +public: + goal_dependency_converter(unsigned n, goal * const* goals) : + m(goals[0]->m()) { + for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]); + } + + virtual expr_dependency_ref operator()() { + expr_dependency_ref result(m.mk_empty_dependencies(), m); + for (goal_ref g : m_goals) { + dependency_converter_ref dc = g->dc(); + if (dc) result = m.mk_join(result, (*dc)()); + } + return result; + } + virtual dependency_converter * translate(ast_translation & translator) { + 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"; } + +}; + +dependency_converter * dependency_converter::concat(dependency_converter * mc1, dependency_converter * mc2) { + if (!mc1) return mc2; + if (!mc2) return mc1; + return alloc(concat_dependency_converter, mc1, mc2); +} + +dependency_converter* dependency_converter::unit(expr_dependency_ref& d) { + return alloc(unit_dependency_converter, d); +} + +dependency_converter * dependency_converter::concat(unsigned n, goal * const* goals) { + if (n == 0) return nullptr; + return alloc(goal_dependency_converter, n, goals); +} diff --git a/src/tactic/dependency_converter.h b/src/tactic/dependency_converter.h new file mode 100644 index 000000000..4ea0c6672 --- /dev/null +++ b/src/tactic/dependency_converter.h @@ -0,0 +1,47 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + dependency_converter.h + +Abstract: + + Utility for converting dependencies across subgoals. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-11-19 + +Notes: + + +--*/ +#ifndef DEPENDENCY_CONVERTER_H_ +#define DEPENDENCY_CONVERTER_H_ + +#include "util/ref.h" +#include "ast/ast_pp_util.h" +#include "model/model.h" +#include "tactic/converter.h" + +class goal; + +class dependency_converter : public converter { +public: + static dependency_converter* unit(expr_dependency_ref& d); + + static dependency_converter* concat(dependency_converter * dc1, dependency_converter * dc2); + + static dependency_converter* concat(unsigned n, goal * const* goals); + + virtual expr_dependency_ref operator()() = 0; + + virtual dependency_converter * translate(ast_translation & translator) = 0; +}; + +typedef ref dependency_converter_ref; +typedef sref_vector dependency_converter_ref_vector; +typedef sref_buffer dependency_converter_ref_buffer; + +#endif diff --git a/src/tactic/equiv_proof_converter.h b/src/tactic/equiv_proof_converter.h index 79f5142b2..5f72b3b07 100644 --- a/src/tactic/equiv_proof_converter.h +++ b/src/tactic/equiv_proof_converter.h @@ -33,13 +33,13 @@ public: equiv_proof_converter(ast_manager& m): m(m), m_replace(m) {} - virtual ~equiv_proof_converter() {} + ~equiv_proof_converter() override {} - virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { - m_replace(m, num_source, source, result); + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { + return m_replace(m, num_source, source); } - virtual proof_converter * translate(ast_translation & translator) { + proof_converter * translate(ast_translation & translator) override { return m_replace.translate(translator); } @@ -47,6 +47,7 @@ public: ast_manager& get_manager() { return m; } + void display(std::ostream & out) override {} }; #endif diff --git a/src/tactic/extension_model_converter.cpp b/src/tactic/extension_model_converter.cpp deleted file mode 100644 index 38f0100ee..000000000 --- a/src/tactic/extension_model_converter.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - extension_model_converter.cpp - -Abstract: - - Model converter that introduces eliminated variables in a model. - -Author: - - Leonardo (leonardo) 2011-10-21 - -Notes: - ---*/ -#include "tactic/extension_model_converter.h" -#include "model/model_evaluator.h" -#include "ast/ast_smt2_pp.h" -#include "model/model_v2_pp.h" -#include "ast/ast_pp.h" - -extension_model_converter::~extension_model_converter() { -} - -#ifdef _TRACE -static void display_decls_info(std::ostream & out, model_ref & md) { - ast_manager & m = md->get_manager(); - unsigned sz = md->get_num_decls(); - for (unsigned i = 0; i < sz; i++) { - func_decl * d = md->get_decl(i); - out << d->get_name(); - out << " ("; - for (unsigned j = 0; j < d->get_arity(); j++) - out << mk_pp(d->get_domain(j), m); - out << mk_pp(d->get_range(), m); - out << ") "; - if (d->get_info()) - out << *(d->get_info()); - out << " :id " << d->get_id() << "\n"; - } -} -#endif - -void extension_model_converter::operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); - TRACE("extension_mc", model_v2_pp(tout, *md); display_decls_info(tout, md);); - model_evaluator ev(*(md.get())); - ev.set_model_completion(true); - ev.set_expand_array_equalities(false); - expr_ref val(m()); - unsigned i = m_vars.size(); - while (i > 0) { - --i; - expr * def = m_defs.get(i); - ev(def, val); - TRACE("extension_mc", tout << m_vars.get(i)->get_name() << " ->\n" << mk_ismt2_pp(def, m()) << "\n==>\n" << mk_ismt2_pp(val, m()) << "\n";); - func_decl * f = m_vars.get(i); - unsigned arity = f->get_arity(); - if (arity == 0) { - md->register_decl(f, val); - } - else { - func_interp * new_fi = alloc(func_interp, m(), arity); - new_fi->set_else(val); - md->register_decl(f, new_fi); - } - } - TRACE("extension_mc", model_v2_pp(tout, *md); display_decls_info(tout, md);); -} - -void extension_model_converter::insert(func_decl * v, expr * def) { - m_vars.push_back(v); - m_defs.push_back(def); -} - - -void extension_model_converter::display(std::ostream & out) { - out << "(extension-model-converter"; - for (unsigned i = 0; i < m_vars.size(); i++) { - out << "\n (" << m_vars.get(i)->get_name() << " "; - unsigned indent = m_vars.get(i)->get_name().size() + 4; - out << mk_ismt2_pp(m_defs.get(i), m(), indent) << ")"; - } - out << ")" << std::endl; -} - -model_converter * extension_model_converter::translate(ast_translation & translator) { - extension_model_converter * res = alloc(extension_model_converter, translator.to()); - for (unsigned i = 0; i < m_vars.size(); i++) - res->m_vars.push_back(translator(m_vars[i].get())); - for (unsigned i = 0; i < m_defs.size(); i++) - res->m_defs.push_back(translator(m_defs[i].get())); - return res; -} diff --git a/src/tactic/extension_model_converter.h b/src/tactic/extension_model_converter.h index dbd7a7e3b..0816fdd8b 100644 --- a/src/tactic/extension_model_converter.h +++ b/src/tactic/extension_model_converter.h @@ -31,18 +31,18 @@ public: extension_model_converter(ast_manager & m):m_vars(m), m_defs(m) { } - virtual ~extension_model_converter(); + ~extension_model_converter() override; ast_manager & m() const { return m_vars.get_manager(); } - virtual void operator()(model_ref & md, unsigned goal_idx); + void operator()(model_ref & md) override; - virtual void display(std::ostream & out); + void display(std::ostream & out) override; // register a variable that was eliminated void insert(func_decl * v, expr * def); - virtual model_converter * translate(ast_translation & translator); + model_converter * translate(ast_translation & translator) override; }; diff --git a/src/tactic/filter_model_converter.cpp b/src/tactic/filter_model_converter.cpp deleted file mode 100644 index 46328186d..000000000 --- a/src/tactic/filter_model_converter.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - filter_model_converter.cpp - -Abstract: - - Filter decls from a model - -Author: - - Leonardo (leonardo) 2011-05-06 - -Notes: - ---*/ -#include "tactic/filter_model_converter.h" -#include "model/model_v2_pp.h" - -filter_model_converter::~filter_model_converter() { -} - -void filter_model_converter::operator()(model_ref & old_model, unsigned goal_idx) { - TRACE("filter_mc", tout << "before filter_model_converter\n"; model_v2_pp(tout, *old_model); display(tout);); - ast_fast_mark1 fs; - unsigned num = m_decls.size(); - for (unsigned i = 0; i < num; i++) - fs.mark(m_decls.get(i)); - model * new_model = alloc(model, m()); - num = old_model->get_num_constants(); - for (unsigned i = 0; i < num; i++) { - func_decl * f = old_model->get_constant(i); - if (fs.is_marked(f)) - continue; - expr * fi = old_model->get_const_interp(f); - new_model->register_decl(f, fi); - } - num = old_model->get_num_functions(); - for (unsigned i = 0; i < num; i++) { - func_decl * f = old_model->get_function(i); - if (fs.is_marked(f)) - continue; - func_interp * fi = old_model->get_func_interp(f); - SASSERT(fi); - new_model->register_decl(f, fi->copy()); - } - new_model->copy_usort_interps(*old_model); - old_model = new_model; - TRACE("filter_mc", tout << "after filter_model_converter\n"; model_v2_pp(tout, *old_model);); -} - -void filter_model_converter::operator()(svector & labels, unsigned goal_idx) { -} - -void filter_model_converter::display(std::ostream & out) { - out << "(filter-model-converter"; - for (unsigned i = 0; i < m_decls.size(); i++) { - out << " " << m_decls.get(i)->get_name(); - } - out << ")" << std::endl; -} - -model_converter * filter_model_converter::translate(ast_translation & translator) { - filter_model_converter * res = alloc(filter_model_converter, translator.to()); - for (unsigned i = 0; i < m_decls.size(); i++) - res->m_decls.push_back(translator(m_decls[i].get())); - return res; -} diff --git a/src/tactic/filter_model_converter.h b/src/tactic/filter_model_converter.h index b5d6b86f4..012c43fb6 100644 --- a/src/tactic/filter_model_converter.h +++ b/src/tactic/filter_model_converter.h @@ -26,25 +26,25 @@ class filter_model_converter : public model_converter { public: filter_model_converter(ast_manager & m):m_decls(m) {} - virtual ~filter_model_converter(); + ~filter_model_converter() override; ast_manager & m() const { return m_decls.get_manager(); } - virtual void operator()(model_ref & md, unsigned goal_idx); + void operator()(model_ref & md, unsigned goal_idx) override; virtual void operator()(svector & labels, unsigned goal_idx); - virtual void operator()(model_ref & md) { operator()(md, 0); } // TODO: delete + void operator()(model_ref & md) override { operator()(md, 0); } // TODO: delete - virtual void cancel() {} + void cancel() override {} - virtual void display(std::ostream & out); + void display(std::ostream & out) override; void insert(func_decl * d) { m_decls.push_back(d); } - virtual model_converter * translate(ast_translation & translator); + model_converter * translate(ast_translation & translator) override; }; typedef ref filter_model_converter_ref; diff --git a/src/tactic/fpa/CMakeLists.txt b/src/tactic/fpa/CMakeLists.txt index a54212235..c647df7fc 100644 --- a/src/tactic/fpa/CMakeLists.txt +++ b/src/tactic/fpa/CMakeLists.txt @@ -3,6 +3,7 @@ z3_add_component(fpa_tactics fpa2bv_model_converter.cpp fpa2bv_tactic.cpp qffp_tactic.cpp + qffplra_tactic.cpp COMPONENT_DEPENDENCIES arith_tactics bv_tactics @@ -14,4 +15,5 @@ z3_add_component(fpa_tactics TACTIC_HEADERS fpa2bv_tactic.h qffp_tactic.h + qffplra_tactic.h ) diff --git a/src/tactic/fpa/fpa2bv_model_converter.h b/src/tactic/fpa/fpa2bv_model_converter.h index 989caaa58..2020657b7 100644 --- a/src/tactic/fpa/fpa2bv_model_converter.h +++ b/src/tactic/fpa/fpa2bv_model_converter.h @@ -33,29 +33,24 @@ public: m_bv2fp(alloc(bv2fpa_converter, m, conv)) { } - virtual ~fpa2bv_model_converter() { + ~fpa2bv_model_converter() override { dealloc(m_bv2fp); } - virtual void operator()(model_ref & md, unsigned goal_idx) { - SASSERT(goal_idx == 0); + void operator()(model_ref & md) override { model * new_model = alloc(model, m); convert(md.get(), new_model); md = new_model; } - virtual void operator()(model_ref & md) { - operator()(md, 0); - } + void display(std::ostream & out) override; - void display(std::ostream & out); - - virtual model_converter * translate(ast_translation & translator); + model_converter * translate(ast_translation & translator) override; protected: fpa2bv_model_converter(ast_manager & m) : m(m), - m_bv2fp(0) {} + m_bv2fp(nullptr) {} void convert(model_core * mc, model * float_mdl); }; diff --git a/src/tactic/fpa/fpa2bv_tactic.cpp b/src/tactic/fpa/fpa2bv_tactic.cpp index 6d90b46ea..30babdd33 100644 --- a/src/tactic/fpa/fpa2bv_tactic.cpp +++ b/src/tactic/fpa/fpa2bv_tactic.cpp @@ -47,16 +47,13 @@ class fpa2bv_tactic : public tactic { } void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); m_proofs_enabled = g->proofs_enabled(); m_produce_models = g->models_enabled(); m_produce_unsat_cores = g->unsat_core_enabled(); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); tactic_report report("fpa2bv", *g); m_rw.reset(); @@ -93,14 +90,14 @@ class fpa2bv_tactic : public tactic { expr * sgn, *sig, *exp; m_conv.split_fp(new_curr, sgn, exp, sig); result.back()->assert_expr(m.mk_eq(sgn, m_conv.bu().mk_numeral(0, 1))); - result.back()->assert_expr(m.mk_eq(exp, m_conv.bu().mk_numeral(-1, m_conv.bu().get_bv_size(exp)))); + result.back()->assert_expr(m.mk_eq(exp, m_conv.bu().mk_bv_neg(m_conv.bu().mk_numeral(1, m_conv.bu().get_bv_size(exp))))); result.back()->assert_expr(m.mk_eq(sig, m_conv.bu().mk_numeral(1, m_conv.bu().get_bv_size(sig)))); } } } if (g->models_enabled()) - mc = mk_fpa2bv_model_converter(m, m_conv); + g->add(mk_fpa2bv_model_converter(m, m_conv)); g->inc_depth(); result.push_back(g.get()); @@ -110,7 +107,7 @@ class fpa2bv_tactic : public tactic { SASSERT(g->is_well_sorted()); TRACE("fpa2bv", tout << "AFTER: " << std::endl; g->display(tout); - if (mc) mc->display(tout); tout << std::endl; ); + if (g->mc()) g->mc()->display(tout); tout << std::endl; ); } }; @@ -123,36 +120,33 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(fpa2bv_tactic, m, m_params); } - virtual ~fpa2bv_tactic() { + ~fpa2bv_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { try { - (*m_imp)(in, result, mc, pc, core); + (*m_imp)(in, result); } catch (rewriter_exception & ex) { throw tactic_exception(ex.msg()); } } - virtual void cleanup() { + void cleanup() override { imp * d = alloc(imp, m_imp->m, m_params); std::swap(d, m_imp); dealloc(d); diff --git a/src/tactic/fpa/qffp_tactic.cpp b/src/tactic/fpa/qffp_tactic.cpp index cdc363453..1c48fef38 100644 --- a/src/tactic/fpa/qffp_tactic.cpp +++ b/src/tactic/fpa/qffp_tactic.cpp @@ -66,10 +66,10 @@ struct is_non_fp_qfnra_predicate { class is_fp_qfnra_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return !test(g); } - virtual ~is_fp_qfnra_probe() {} + ~is_fp_qfnra_probe() override {} }; probe * mk_is_fp_qfnra_probe() { @@ -141,11 +141,11 @@ struct is_non_qffp_predicate { class is_qffp_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return !test(g); } - virtual ~is_qffp_probe() {} + ~is_qffp_probe() override {} }; probe * mk_is_qffp_probe() { diff --git a/src/tactic/fpa/qffplra_tactic.cpp b/src/tactic/fpa/qffplra_tactic.cpp new file mode 100644 index 000000000..947a41111 --- /dev/null +++ b/src/tactic/fpa/qffplra_tactic.cpp @@ -0,0 +1,72 @@ +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + qffpalra_tactic.cpp + +Abstract: + + Tactic for QF_FPLRA benchmarks. + +Author: + + Christoph (cwinter) 2018-04-24 + +Notes: + +--*/ +#include "tactic/tactical.h" +#include "tactic/fpa/qffp_tactic.h" +#include "tactic/fpa/qffplra_tactic.h" + +tactic * mk_qffplra_tactic(ast_manager & m, params_ref const & p) { + tactic * st = mk_qffp_tactic(m, p); + st->updt_params(p); + return st; +} + +struct is_non_qffplra_predicate { + struct found {}; + ast_manager & m; + bv_util bu; + fpa_util fu; + arith_util au; + + is_non_qffplra_predicate(ast_manager & _m) : m(_m), bu(m), fu(m), au(m) {} + + void operator()(var *) { throw found(); } + + void operator()(quantifier *) { throw found(); } + + void operator()(app * n) { + sort * s = get_sort(n); + if (!m.is_bool(s) && !fu.is_float(s) && !fu.is_rm(s) && !bu.is_bv_sort(s) && !au.is_real(s)) + throw found(); + family_id fid = n->get_family_id(); + if (fid == m.get_basic_family_id() || + fid == fu.get_family_id() || + fid == bu.get_family_id() || + fid == au.get_family_id()) + return; + if (is_uninterp_const(n)) + return; + if (au.is_real(s)) + return; + + throw found(); + } +}; + +class is_qffplra_probe : public probe { +public: + result operator()(goal const & g) override { + return !test(g); + } + + ~is_qffplra_probe() override {} +}; + +probe * mk_is_qffplra_probe() { + return alloc(is_qffplra_probe); +} diff --git a/src/tactic/fpa/qffplra_tactic.h b/src/tactic/fpa/qffplra_tactic.h new file mode 100644 index 000000000..b5a741ac3 --- /dev/null +++ b/src/tactic/fpa/qffplra_tactic.h @@ -0,0 +1,38 @@ +#pragma once +/*++ +Copyright (c) 2012 Microsoft Corporation + +Module Name: + + qffplra_tactic.h + +Abstract: + + Tactic for QF_FPLRA benchmarks. + +Author: + + Christoph (cwinter) 2018-04-24 + +Notes: + + +--*/ +#ifndef QFFPLRA_TACTIC_H_ +#define QFFPLRA_TACTIC_H_ + +#include "util/params.h" +class ast_manager; +class tactic; + +tactic * mk_qffplra_tactic(ast_manager & m, params_ref const & p = params_ref()); +/* +ADD_TACTIC("qffplra", "(try to) solve goal using the tactic for QF_FPLRA.", "mk_qffplra_tactic(m, p)") +*/ + +probe * mk_is_qffplra_probe(); +/* +ADD_PROBE("is-qffplra", "true if the goal is in QF_FPLRA.", "mk_is_qffplra_probe()") +*/ + +#endif diff --git a/src/tactic/generic_model_converter.cpp b/src/tactic/generic_model_converter.cpp new file mode 100644 index 000000000..0790c962a --- /dev/null +++ b/src/tactic/generic_model_converter.cpp @@ -0,0 +1,269 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + generic_model_converter.cpp + +Abstract: + + Generic model converter that hides and adds entries. + It subsumes filter_model_converter and extension_model_converter. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-29 + +Notes: + +--*/ +#include "ast/ast_pp.h" +#include "ast/for_each_expr.h" +#include "ast/ast_util.h" +#include "ast/occurs.h" +#include "ast/rewriter/expr_safe_replace.h" +#include "ast/rewriter/th_rewriter.h" +#include "tactic/generic_model_converter.h" +#include "model/model_v2_pp.h" +#include "model/model_evaluator.h" + + +generic_model_converter::~generic_model_converter() { +} + +void generic_model_converter::add(func_decl * d, expr* e) { + VERIFY(e); + entry et(d, e, m, ADD); + VERIFY(d->get_range() == m.get_sort(e)); + m_first_idx.insert_if_not_there(et.m_f, m_entries.size()); + m_entries.push_back(et); +} + +void generic_model_converter::operator()(model_ref & md) { + TRACE("model_converter", tout << "before generic_model_converter\n"; model_v2_pp(tout, *md); display(tout);); + + model_evaluator ev(*(md.get())); + ev.set_model_completion(true); + ev.set_expand_array_equalities(false); + expr_ref val(m); + unsigned arity; + bool reset_ev = false; + for (unsigned i = m_entries.size(); i-- > 0; ) { + entry const& e = m_entries[i]; + switch (e.m_instruction) { + case instruction::HIDE: + md->unregister_decl(e.m_f); + break; + case instruction::ADD: + ev(e.m_def, val); + TRACE("model_converter", tout << e.m_f->get_name() << " ->\n" << e.m_def << "\n==>\n" << val << "\n";); + arity = e.m_f->get_arity(); + reset_ev = false; + if (arity == 0) { + expr* old_val = md->get_const_interp(e.m_f); + if (old_val && old_val == val) { + // skip + } + else { + reset_ev = old_val != nullptr; + md->register_decl(e.m_f, val); + } + } + else { + func_interp * old_val = md->get_func_interp(e.m_f); + if (old_val && old_val->get_else() == val) { + // skip + } + else { + reset_ev = old_val != nullptr; + func_interp * new_fi = alloc(func_interp, m, arity); + new_fi->set_else(val); + md->register_decl(e.m_f, new_fi); + } + } + if (reset_ev) { + ev.reset(); + ev.set_model_completion(true); + ev.set_expand_array_equalities(false); + } + break; + } + } + TRACE("model_converter", tout << "after generic_model_converter\n"; model_v2_pp(tout, *md);); +} + +void generic_model_converter::display(std::ostream & out) { + for (entry const& e : m_entries) { + switch (e.m_instruction) { + case instruction::HIDE: + display_del(out, e.m_f); + break; + case instruction::ADD: + display_add(out, m, e.m_f, e.m_def); + break; + } + } +} + +model_converter * generic_model_converter::translate(ast_translation & translator) { + ast_manager& to = translator.to(); + generic_model_converter * res = alloc(generic_model_converter, to, m_orig.c_str()); + for (entry const& e : m_entries) { + res->m_entries.push_back(entry(translator(e.m_f.get()), translator(e.m_def.get()), to, e.m_instruction)); + } + return res; +} + +void generic_model_converter::set_env(ast_pp_util* visitor) { + if (!visitor) { + m_env = nullptr; + } + else { + m_env = &visitor->env(); + for (entry const& e : m_entries) { + visitor->coll.visit_func(e.m_f); + if (e.m_def) visitor->coll.visit(e.m_def); + } + } +} + +struct min_app_idx_proc { + unsigned m_min; + obj_map& m_idxs; + min_app_idx_proc(obj_map& idxs) : m_min(UINT_MAX), m_idxs(idxs) {} + void operator()(app * n) { + unsigned idx; + if (m_idxs.find(n->get_decl(), idx)) { + m_min = std::min(m_min, idx); + } + } + void operator()(var * n) {} + void operator()(quantifier * n) {} +}; + +void generic_model_converter::operator()(expr_ref& fml) { + min_app_idx_proc min_proc(m_first_idx); + for_each_expr(min_proc, fml); + unsigned min_idx = min_proc.m_min; + if (min_idx == UINT_MAX) return; + expr_ref_vector fmls(m); + fmls.push_back(fml); + for (unsigned i = m_entries.size(); i-- > min_idx;) { + entry const& e = m_entries[i]; + if (e.m_instruction != instruction::ADD) { + continue; + } + unsigned arity = e.m_f->get_arity(); + if (arity == 0) { + fmls.push_back(simplify_def(e)); + } + else { + expr_ref_vector args(m); + sort_ref_vector sorts(m); + svector names; + for (unsigned i = 0; i < arity; ++i) { + sorts.push_back(e.m_f->get_domain(i)); + names.push_back(symbol(i)); + args.push_back(m.mk_var(i, sorts.back())); + } + // TBD: check if order is correct with respect to quantifier binding ordering + expr_ref lhs(m.mk_app(e.m_f, arity, args.c_ptr()), m); + expr_ref body(m.mk_eq(lhs, e.m_def), m); + fmls.push_back(m.mk_forall(arity, sorts.c_ptr(), names.c_ptr(), body)); + } + if (m_first_idx[e.m_f] == i) { + m_first_idx.remove(e.m_f); + } + } + unsigned j = min_idx; + for (unsigned i = min_idx; i < m_entries.size(); ++i) { + entry& e = m_entries[i]; + if (e.m_instruction == instruction::HIDE) { + if (i != j) { + m_entries[j] = e; + } + ++j; + } + } + m_entries.shrink(j); + fml = mk_and(fmls); +} + +void generic_model_converter::get_units(obj_map& units) { + th_rewriter rw(m); + expr_safe_replace rep(m); + expr_ref tmp(m); + for (auto const& kv : units) { + rep.insert(kv.m_key, kv.m_value ? m.mk_true() : m.mk_false()); + } + for (unsigned i = m_entries.size(); i-- > 0;) { + entry const& e = m_entries[i]; + switch (e.m_instruction) { + case HIDE: + tmp = m.mk_const(e.m_f); + if (units.contains(tmp)) { + m.dec_ref(tmp); + units.remove(tmp); + } + break; + case ADD: + if (e.m_f->get_arity() == 0 && m.is_bool(e.m_f->get_range())) { + tmp = m.mk_const(e.m_f); + if (units.contains(tmp)) { + break; + } + tmp = e.m_def; + rep(tmp); + rw(tmp); + if (m.is_true(tmp)) { + tmp = m.mk_const(e.m_f); + m.inc_ref(tmp); + units.insert(tmp, true); + rep.insert(tmp, m.mk_true()); + } + else if (m.is_false(tmp)) { + tmp = m.mk_const(e.m_f); + m.inc_ref(tmp); + units.insert(tmp, false); + rep.insert(tmp, m.mk_false()); + } + } + break; + } + } +} + + +/* + \brief simplify definition expansion from model converter in the case they come from blocked clauses. + In this case the definitions are of the form: + + x <=> x or not (C) + + or dually, + + x <=> not (not x or not C) + + in either case the definitions simplify to + + x or C + + */ +expr_ref generic_model_converter::simplify_def(entry const& e) { + expr_ref result(m); + expr_ref c(m.mk_const(e.m_f), m); + if (m.is_bool(c) && occurs(c, e.m_def)) { + expr_safe_replace rep(m); + expr_ref result1 = e.m_def; + expr_ref result2 = e.m_def; + rep.apply_substitution(c, m.mk_true(), result1); + rep.apply_substitution(c, m.mk_false(), result2); + th_rewriter rw(m); + expr_ref result(m.mk_and(m.mk_implies(result2, c), m.mk_implies(c, result1)), m); + rw(result); + return result; + } + else { + return expr_ref(m.mk_eq(c, e.m_def), m); + } +} diff --git a/src/tactic/generic_model_converter.h b/src/tactic/generic_model_converter.h new file mode 100644 index 000000000..a190b8525 --- /dev/null +++ b/src/tactic/generic_model_converter.h @@ -0,0 +1,80 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + generic_model_converter.h + +Abstract: + + Generic model converter that hides and adds entries. + It subsumes filter_model_converter and extension_model_converter. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-29 + +Notes: + +--*/ +#ifndef GENERIC_MODEL_CONVERTER_H_ +#define GENERIC_MODEL_CONVERTER_H_ + +#include "tactic/model_converter.h" + +class generic_model_converter : public model_converter { + enum instruction { HIDE, ADD }; + struct entry { + func_decl_ref m_f; + expr_ref m_def; + instruction m_instruction; + entry(func_decl* f, expr* d, ast_manager& m, instruction i): + m_f(f, m), m_def(d, m), m_instruction(i) {} + + entry& operator=(entry const& other) { + m_f = other.m_f; + m_def = other.m_def; + m_instruction = other.m_instruction; + return *this; + } + }; + ast_manager& m; + std::string m_orig; + vector m_entries; + obj_map m_first_idx; + + expr_ref simplify_def(entry const& e); + +public: + generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {} + + virtual ~generic_model_converter(); + + 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 add(func_decl * d, expr* e); + + void add(expr * d, expr* e) { SASSERT(is_app(d) && to_app(d)->get_num_args() == 0); add(to_app(d)->get_decl(), e); } + + void operator()(labels_vec & labels) override {} + + void operator()(model_ref & md) override; + + void cancel() override {} + + void display(std::ostream & out) override; + + model_converter * translate(ast_translation & translator) override; + + void set_env(ast_pp_util* visitor) override; + + void operator()(expr_ref& fml) override; + + void get_units(obj_map& units) override; +}; + +typedef ref generic_model_converter_ref; + +#endif diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index ea8d14bd0..d5d2ee558 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -16,11 +16,11 @@ Author: Revision History: --*/ -#include "tactic/goal.h" #include "ast/ast_ll_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/for_each_expr.h" #include "ast/well_sorted.h" +#include "tactic/goal.h" goal::precision goal::mk_union(precision p1, precision p2) { if (p1 == PRECISE) return p2; @@ -85,6 +85,9 @@ goal::goal(goal const & src, bool): m_core_enabled(src.unsat_core_enabled()), m_inconsistent(false), m_precision(src.m_precision) { + m_mc = src.m_mc.get(); + m_pc = src.m_pc.get(); + m_dc = src.m_dc.get(); } goal::~goal() { @@ -105,6 +108,9 @@ void goal::copy_to(goal & target) const { SASSERT(target.m_core_enabled == m_core_enabled); target.m_inconsistent = m_inconsistent; target.m_precision = mk_union(prec(), target.prec()); + target.m_mc = m_mc.get(); + target.m_pc = m_pc.get(); + target.m_dc = m_dc.get(); } void goal::push_back(expr * f, proof * pr, expr_dependency * d) { @@ -137,10 +143,10 @@ void goal::push_back(expr * f, proof * pr, expr_dependency * d) { } void goal::quick_process(bool save_first, expr_ref& f, expr_dependency * d) { - expr* g = 0; + expr* g = nullptr; if (!m().is_and(f) && !(m().is_not(f, g) && m().is_or(g))) { if (!save_first) { - push_back(f, 0, d); + push_back(f, nullptr, d); } return; } @@ -184,7 +190,7 @@ void goal::quick_process(bool save_first, expr_ref& f, expr_dependency * d) { save_first = false; } else { - push_back(curr, 0, d); + push_back(curr, nullptr, d); } } } @@ -253,7 +259,7 @@ void goal::assert_expr(expr * f, proof * pr, expr_dependency * d) { } void goal::assert_expr(expr * f, expr_dependency * d) { - assert_expr(f, proofs_enabled() ? m().mk_asserted(f) : 0, d); + assert_expr(f, proofs_enabled() ? m().mk_asserted(f) : nullptr, d); } void goal::get_formulas(ptr_vector & result) { @@ -263,6 +269,13 @@ void goal::get_formulas(ptr_vector & result) { } } +void goal::get_formulas(expr_ref_vector & result) { + unsigned sz = size(); + for (unsigned i = 0; i < sz; i++) { + result.push_back(form(i)); + } +} + void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { // KLM: don't know why this assertion is no longer true // SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr))); @@ -289,7 +302,7 @@ void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { quick_process(true, fr, d); if (!m_inconsistent) { if (m().is_false(fr)) { - push_back(f, 0, d); + push_back(f, nullptr, d); } else { m().set(m_forms, i, fr); @@ -337,10 +350,7 @@ void goal::display_with_dependencies(ast_printer & prn, std::ostream & out) cons out << "\n |-"; deps.reset(); m().linearize(dep(i), deps); - ptr_vector::iterator it = deps.begin(); - ptr_vector::iterator end = deps.end(); - for (; it != end; ++it) { - expr * d = *it; + for (expr * d : deps) { if (is_uninterp_const(d)) { out << " " << mk_ismt2_pp(d, m()); } @@ -354,10 +364,7 @@ void goal::display_with_dependencies(ast_printer & prn, std::ostream & out) cons } if (!to_pp.empty()) { out << "\n :dependencies-definitions ("; - obj_hashtable::iterator it = to_pp.begin(); - obj_hashtable::iterator end = to_pp.end(); - for (; it != end; ++it) { - expr * d = *it; + for (expr* d : to_pp) { out << "\n (#" << d->get_id() << "\n "; prn.display(out, d, 2); out << ")"; @@ -375,10 +382,7 @@ void goal::display_with_dependencies(std::ostream & out) const { out << "\n |-"; deps.reset(); m().linearize(dep(i), deps); - ptr_vector::iterator it = deps.begin(); - ptr_vector::iterator end = deps.end(); - for (; it != end; ++it) { - expr * d = *it; + for (expr * d : deps) { if (is_uninterp_const(d)) { out << " " << mk_ismt2_pp(d, m()); } @@ -481,6 +485,11 @@ void goal::display_dimacs(std::ostream & out) const { } out << "0\n"; } + for (auto const& kv : expr2var) { + expr* key = kv.m_key; + if (is_app(key)) + out << "c " << kv.m_value << " " << to_app(key)->get_decl()->get_name() << "\n"; + } } unsigned goal::num_exprs() const { @@ -498,14 +507,12 @@ void goal::shrink(unsigned j) { unsigned sz = size(); for (unsigned i = j; i < sz; i++) m().pop_back(m_forms); - if (proofs_enabled()) { + if (proofs_enabled()) for (unsigned i = j; i < sz; i++) m().pop_back(m_proofs); - } - if (unsat_core_enabled()) { + if (unsat_core_enabled()) for (unsigned i = j; i < sz; i++) m().pop_back(m_dependencies); - } } /** @@ -575,7 +582,7 @@ void goal::elim_redundancies() { if (neg_lits.is_marked(atom)) continue; if (pos_lits.is_marked(atom)) { - proof * p = 0; + proof * p = nullptr; if (proofs_enabled()) { proof * prs[2] = { pr(get_idx(atom)), pr(i) }; p = m().mk_unit_resolution(2, prs); @@ -592,7 +599,7 @@ void goal::elim_redundancies() { if (pos_lits.is_marked(f)) continue; if (neg_lits.is_marked(f)) { - proof * p = 0; + proof * p = nullptr; if (proofs_enabled()) { proof * prs[2] = { pr(get_not_idx(f)), pr(i) }; p = m().mk_unit_resolution(2, prs); @@ -650,6 +657,9 @@ goal * goal::translate(ast_translation & translator) const { res->m_inconsistent = m_inconsistent; res->m_depth = m_depth; res->m_precision = m_precision; + res->m_pc = m_pc ? m_pc->translate(translator) : nullptr; + res->m_mc = m_mc ? m_mc->translate(translator) : nullptr; + res->m_dc = m_dc ? m_dc->translate(translator) : nullptr; return res; } diff --git a/src/tactic/goal.h b/src/tactic/goal.h index c9de9c037..2d91bc67f 100644 --- a/src/tactic/goal.h +++ b/src/tactic/goal.h @@ -35,6 +35,9 @@ Revision History: #include "util/ref.h" #include "util/ref_vector.h" #include "util/ref_buffer.h" +#include "tactic/model_converter.h" +#include "tactic/proof_converter.h" +#include "tactic/dependency_converter.h" class goal { public: @@ -49,6 +52,9 @@ public: protected: ast_manager & m_manager; + model_converter_ref m_mc; + proof_converter_ref m_pc; + dependency_converter_ref m_dc; unsigned m_ref_count; expr_array m_forms; expr_array m_proofs; @@ -71,6 +77,7 @@ protected: unsigned get_not_idx(expr * f) const; void shrink(unsigned j); void reset_core(); + public: goal(ast_manager & m, bool models_enabled = true, bool core_enabled = false); @@ -103,23 +110,24 @@ public: void copy_to(goal & target) const; void copy_from(goal const & src) { src.copy_to(*this); } - + void assert_expr(expr * f, proof * pr, expr_dependency * d); void assert_expr(expr * f, expr_dependency * d); void assert_expr(expr * f, expr * d) { assert_expr(f, m().mk_leaf(d)); } - void assert_expr(expr * f) { assert_expr(f, static_cast(0)); } + void assert_expr(expr * f) { assert_expr(f, static_cast(nullptr)); } unsigned size() const { return m().size(m_forms); } unsigned num_exprs() const; expr * form(unsigned i) const { return m().get(m_forms, i); } - proof * pr(unsigned i) const { return proofs_enabled() ? static_cast(m().get(m_proofs, i)) : 0; } - expr_dependency * dep(unsigned i) const { return unsat_core_enabled() ? m().get(m_dependencies, i) : 0; } + proof * pr(unsigned i) const { return proofs_enabled() ? static_cast(m().get(m_proofs, i)) : nullptr; } + expr_dependency * dep(unsigned i) const { return unsat_core_enabled() ? m().get(m_dependencies, i) : nullptr; } - void update(unsigned i, expr * f, proof * pr = 0, expr_dependency * dep = 0); + void update(unsigned i, expr * f, proof * pr = nullptr, expr_dependency * dep = nullptr); void get_formulas(ptr_vector & result); + void get_formulas(expr_ref_vector & result); void elim_true(); void elim_redundancies(); @@ -141,6 +149,16 @@ public: bool is_decided() const; bool is_well_sorted() const; + dependency_converter* dc() { return m_dc.get(); } + model_converter* mc() const { return m_mc.get(); } + proof_converter* pc() const { return inconsistent() ? proof2proof_converter(m(), pr(0)) : m_pc.get(); } + void add(dependency_converter* d) { m_dc = dependency_converter::concat(m_dc.get(), d); } + void add(model_converter* m) { m_mc = concat(m_mc.get(), m); } + void add(proof_converter* p) { m_pc = concat(m_pc.get(), p); } + void set(dependency_converter* d) { m_dc = d; } + void set(model_converter* m) { m_mc = m; } + void set(proof_converter* p) { m_pc = p; } + goal * translate(ast_translation & translator) const; }; diff --git a/src/tactic/horn_subsume_model_converter.cpp b/src/tactic/horn_subsume_model_converter.cpp index cb56f9b7b..eeb2967f2 100644 --- a/src/tactic/horn_subsume_model_converter.cpp +++ b/src/tactic/horn_subsume_model_converter.cpp @@ -170,6 +170,10 @@ void horn_subsume_model_converter::add_default_false_interpretation(expr* e, mod } +void horn_subsume_model_converter::operator()(expr_ref& fml) { + NOT_IMPLEMENTED_YET(); +} + void horn_subsume_model_converter::operator()(model_ref& mr) { func_decl_ref pred(m); @@ -190,11 +194,10 @@ void horn_subsume_model_converter::operator()(model_ref& mr) { add_default_false_interpretation(body, mr); SASSERT(m.is_bool(body)); - TRACE("mc", tout << "eval: " << h->get_name() << "\n" << mk_pp(body, m) << "\n";); - expr_ref tmp(body); - mr->eval(tmp, body); + TRACE("mc", tout << "eval: " << h->get_name() << "\n" << body << "\n";); + body = (*mr)(body); - TRACE("mc", tout << "to:\n" << mk_pp(body, m) << "\n";); + TRACE("mc", tout << "to:\n" << body << "\n";); if (arity == 0) { expr* e = mr->get_const_interp(h); diff --git a/src/tactic/horn_subsume_model_converter.h b/src/tactic/horn_subsume_model_converter.h index 1c1ae9feb..6d27ceec2 100644 --- a/src/tactic/horn_subsume_model_converter.h +++ b/src/tactic/horn_subsume_model_converter.h @@ -58,9 +58,8 @@ class horn_subsume_model_converter : public model_converter { public: - horn_subsume_model_converter(ast_manager& m): - m(m), m_funcs(m), m_bodies(m), m_rewrite(m), - m_delay_head(m), m_delay_body(m) {} + horn_subsume_model_converter(ast_manager& m): + m(m), m_funcs(m), m_bodies(m), m_rewrite(m), m_delay_head(m), m_delay_body(m) {} bool mk_horn(expr* clause, func_decl_ref& pred, expr_ref& body); @@ -72,12 +71,18 @@ public: void insert(func_decl* p, expr* body) { m_funcs.push_back(p); m_bodies.push_back(body); } - virtual void operator()(model_ref& _m); + void operator()(model_ref& _m) override; - virtual model_converter * translate(ast_translation & translator); + void operator()(expr_ref& fml) override; + + model_converter * translate(ast_translation & translator) override; ast_manager& get_manager() { return m; } + void display(std::ostream & out) override {} + + void get_units(obj_map& units) override { units.reset(); } + }; #endif diff --git a/src/tactic/model_converter.cpp b/src/tactic/model_converter.cpp index 4269946a1..89e2b6602 100644 --- a/src/tactic/model_converter.cpp +++ b/src/tactic/model_converter.cpp @@ -18,171 +18,167 @@ Notes: --*/ #include "tactic/model_converter.h" #include "model/model_v2_pp.h" +#include "ast/ast_smt2_pp.h" + +/* + * Add or overwrite value in model. + */ +void model_converter::display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const { + VERIFY(e); + smt2_pp_environment_dbg env(m); + smt2_pp_environment* _env = m_env ? m_env : &env; + VERIFY(f->get_range() == m.get_sort(e)); + ast_smt2_pp(out, f, e, *_env, params_ref(), 0, "model-add") << "\n"; +} + +/* + * A value is removed from the model. + */ +void model_converter::display_del(std::ostream& out, func_decl* f) const { + if (m_env) { + ast_smt2_pp(out << "(model-del ", f->get_name(), f->is_skolem(), *m_env) << ")\n"; + } + else { + out << "(model-del " << f->get_name() << ")\n"; + } +} + +void model_converter::set_env(ast_pp_util* visitor) { + if (visitor) { + m_env = &visitor->env(); + } + else { + m_env = nullptr; + } +} + + +void model_converter::display_add(std::ostream& out, ast_manager& m) { + // default printer for converter that adds entries + model_ref mdl = alloc(model, m); + (*this)(mdl); + for (unsigned i = 0, sz = mdl->get_num_constants(); i < sz; ++i) { + func_decl* f = mdl->get_constant(i); + display_add(out, m, f, mdl->get_const_interp(f)); + } + for (unsigned i = 0, sz = mdl->get_num_functions(); i < sz; ++i) { + func_decl* f = mdl->get_function(i); + func_interp* fi = mdl->get_func_interp(f); + display_add(out, m, f, fi->get_interp()); + } +} + class concat_model_converter : public concat_converter { public: - concat_model_converter(model_converter * mc1, model_converter * mc2):concat_converter(mc1, mc2) {} + concat_model_converter(model_converter * mc1, model_converter * mc2): concat_converter(mc1, mc2) { + VERIFY(m_c1 && m_c2); + } - virtual void operator()(model_ref & m) { + void operator()(model_ref & m) override { this->m_c2->operator()(m); this->m_c1->operator()(m); } - virtual void operator()(model_ref & m, unsigned goal_idx) { - this->m_c2->operator()(m, goal_idx); - this->m_c1->operator()(m, 0); + void operator()(expr_ref & fml) override { + this->m_c2->operator()(fml); + this->m_c1->operator()(fml); + } + + void operator()(labels_vec & r) override { + this->m_c2->operator()(r); + this->m_c1->operator()(r); } - virtual void operator()(labels_vec & r, unsigned goal_idx) { - this->m_c2->operator()(r, goal_idx); - this->m_c1->operator()(r, 0); + void get_units(obj_map& fmls) override { + m_c2->get_units(fmls); + m_c1->get_units(fmls); } - - virtual char const * get_name() const { return "concat-model-converter"; } + char const * get_name() const override { return "concat-model-converter"; } - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { return this->translate_core(translator); } + + void set_env(ast_pp_util* visitor) override { + this->m_c1->set_env(visitor); + this->m_c2->set_env(visitor); + } }; model_converter * concat(model_converter * mc1, model_converter * mc2) { - if (mc1 == 0) + if (mc1 == nullptr) return mc2; - if (mc2 == 0) + if (mc2 == nullptr) return mc1; return alloc(concat_model_converter, mc1, mc2); } -class concat_star_model_converter : public concat_star_converter { -public: - concat_star_model_converter(model_converter * mc1, unsigned num, model_converter * const * mc2s, unsigned * szs): - concat_star_converter(mc1, num, mc2s, szs) { - } - - virtual void operator()(model_ref & m) { - // TODO: delete method after conversion is complete - UNREACHABLE(); - } - - virtual void operator()(model_ref & m, unsigned goal_idx) { - unsigned num = this->m_c2s.size(); - for (unsigned i = 0; i < num; i++) { - if (goal_idx < this->m_szs[i]) { - // found the model converter that should be used - model_converter * c2 = this->m_c2s[i]; - if (c2) - c2->operator()(m, goal_idx); - if (m_c1) - this->m_c1->operator()(m, i); - return; - } - // invalid goal - goal_idx -= this->m_szs[i]; - } - UNREACHABLE(); - } - - virtual void operator()(labels_vec & r, unsigned goal_idx) { - unsigned num = this->m_c2s.size(); - for (unsigned i = 0; i < num; i++) { - if (goal_idx < this->m_szs[i]) { - // found the model converter that should be used - model_converter * c2 = this->m_c2s[i]; - if (c2) - c2->operator()(r, goal_idx); - if (m_c1) - this->m_c1->operator()(r, i); - return; - } - // invalid goal - goal_idx -= this->m_szs[i]; - } - UNREACHABLE(); - } - - virtual char const * get_name() const { return "concat-star-model-converter"; } - - virtual model_converter * translate(ast_translation & translator) { - return this->translate_core(translator); - } -}; - -model_converter * concat(model_converter * mc1, unsigned num, model_converter * const * mc2s, unsigned * szs) { - SASSERT(num > 0); - if (num == 1) - return concat(mc1, mc2s[0]); - unsigned i; - for (i = 0; i < num; i++) { - if (mc2s[i] != 0) - break; - } - if (i == num) { - // all mc2s are 0 - return mc1; - } - return alloc(concat_star_model_converter, mc1, num, mc2s, szs); -} class model2mc : public model_converter { model_ref m_model; - buffer m_labels; + labels_vec m_labels; public: model2mc(model * m):m_model(m) {} - model2mc(model * m, buffer const & r):m_model(m), m_labels(r) {} + model2mc(model * m, labels_vec const & r):m_model(m), m_labels(r) {} - virtual ~model2mc() {} + ~model2mc() override {} - virtual void operator()(model_ref & m) { + void operator()(model_ref & m) override { m = m_model; } - virtual void operator()(model_ref & m, unsigned goal_idx) { - m = m_model; + void operator()(labels_vec & r) override { + r.append(m_labels.size(), m_labels.c_ptr()); } - virtual void operator()(labels_vec & r, unsigned goal_idx) { - r.append(m_labels.size(), m_labels.c_ptr()); + void operator()(expr_ref& fml) override { + model::scoped_model_completion _scm(m_model, false); + fml = (*m_model)(fml); } - virtual void cancel() { + void get_units(obj_map& fmls) override { + // no-op + } + + void cancel() override { } - virtual void display(std::ostream & out) { - out << "(model->model-converter-wrapper\n"; + void display(std::ostream & out) override { + out << "(rmodel->model-converter-wrapper\n"; model_v2_pp(out, *m_model); out << ")\n"; } - virtual model_converter * translate(ast_translation & translator) { + model_converter * translate(ast_translation & translator) override { model * m = m_model->translate(translator); - return alloc(model2mc, m); + return alloc(model2mc, m, m_labels); } + }; model_converter * model2model_converter(model * m) { - if (m == 0) - return 0; + if (!m) return nullptr; return alloc(model2mc, m); } -model_converter * model_and_labels2model_converter(model * m, buffer & r) { - if (m == 0) - return 0; +model_converter * model_and_labels2model_converter(model * m, labels_vec const & r) { + if (!m) return nullptr; return alloc(model2mc, m, r); } void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m) { if (mc) { m = alloc(model, mng); - (*mc)(m, 0); + (*mc)(m); } } -void apply(model_converter_ref & mc, model_ref & m, unsigned gidx) { +void apply(model_converter_ref & mc, model_ref & m) { if (mc) { - (*mc)(m, gidx); + (*mc)(m); } } diff --git a/src/tactic/model_converter.h b/src/tactic/model_converter.h index 5e549344e..9c5b72830 100644 --- a/src/tactic/model_converter.h +++ b/src/tactic/model_converter.h @@ -15,51 +15,96 @@ Author: Notes: + A model converter, mc, can be used to convert a model for one + of a generated subgoal into a model for an initial goal or solver state. + For a goal or solver state that is decided, a model converter can be + a simple wrapper around a model. + + Logically, given a formula F and subgoal formula F_s a model converter mc + for F_s relative to F has the property: + + m |= F_s iff mc(m) |= F for every model m + + For the evaluator associated with models, m, we expect + + eval(m)(F_s) <=> eval(mc(m))(F) + + This property holds for both eval, that decides on a fixed value + for constants that have no interpretation in m and for 'peval' + (partial eval) that retuns just the constants that are unfixed. + (in the model evaluator one can control this behavior using a + configuration flag) + + and more generally over the eval method have: + + G => F_s iff peval(mc(e))(G) => F for every formula G + + + where e is the empty model (a model that does not evaluate any + + When a model converter supports application to a formula it satisfies + the following property: + + mc(G) & F_s is SAT iff G & F is SAT + + For a model converter that is a sequence of definitions and removals + of functions we can obtain mc(G) by adding back or expanding definitinos + that are required to interpret G fully in the context of F_s. + --*/ #ifndef MODEL_CONVERTER_H_ #define MODEL_CONVERTER_H_ +#include "util/ref.h" +#include "ast/ast_pp_util.h" #include "model/model.h" #include "tactic/converter.h" -#include "util/ref.h" class labels_vec : public svector {}; +class smt2_pp_environment; class model_converter : public converter { +protected: + smt2_pp_environment* m_env; + void display_add(std::ostream& out, ast_manager& m, func_decl* f, expr* e) const; + void display_del(std::ostream& out, func_decl* f) const; + void display_add(std::ostream& out, ast_manager& m); + public: - virtual void operator()(model_ref & m) {} // TODO: delete - virtual void operator()(model_ref & m, unsigned goal_idx) { - // TODO: make it virtual after the transition to goal/tactic/tactical is complete - SASSERT(goal_idx == 0); - operator()(m); - } + model_converter(): m_env(nullptr) {} - virtual void operator()(labels_vec & r, unsigned goal_idx) {} + virtual void operator()(model_ref & m) = 0; + + virtual void operator()(labels_vec & r) {} virtual model_converter * translate(ast_translation & translator) = 0; + + virtual void set_env(ast_pp_util* visitor); + + /** + \brief we are adding a formula to the context of the model converter. + The operator has as side effect of adding definitions as assertions to the + formula and removing these definitions from the model converter. + */ + virtual void operator()(expr_ref& formula) { UNREACHABLE(); } + + virtual void get_units(obj_map& fmls) { UNREACHABLE(); } }; typedef ref model_converter_ref; - -model_converter * concat(model_converter * mc1, model_converter * mc2); - -/** - \brief \c mc1 is the model converter for a sequence of subgoals of size \c num. - Given an i in [0, num), mc2s[i] is the model converter for subgoal i, - and num_subgoals[i] is the number of subgoals of subgoals[i]. -*/ -model_converter * concat(model_converter * mc1, unsigned num, model_converter * const * mc2s, unsigned * num_subgoals); - -model_converter * model2model_converter(model * m); - -model_converter * model_and_labels2model_converter(model * m, buffer &r); - -void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m); - -void apply(model_converter_ref & mc, model_ref & m, unsigned gidx); - typedef sref_vector model_converter_ref_vector; typedef sref_buffer model_converter_ref_buffer; +model_converter * concat(model_converter * mc1, model_converter * mc2); + +model_converter * model2model_converter(model * m); + +model_converter * model_and_labels2model_converter(model * m, labels_vec const &r); + +void model_converter2model(ast_manager & mng, model_converter * mc, model_ref & m); + +void apply(model_converter_ref & mc, model_ref & m); + + #endif diff --git a/src/tactic/nlsat_smt/CMakeLists.txt b/src/tactic/nlsat_smt/CMakeLists.txt deleted file mode 100644 index ccfc0e3ef..000000000 --- a/src/tactic/nlsat_smt/CMakeLists.txt +++ /dev/null @@ -1,9 +0,0 @@ -z3_add_component(nlsat_smt_tactic - SOURCES - nl_purify_tactic.cpp - COMPONENT_DEPENDENCIES - nlsat_tactic - smt_tactic - TACTIC_HEADERS - nl_purify_tactic.h -) diff --git a/src/tactic/nlsat_smt/nl_purify_tactic.cpp b/src/tactic/nlsat_smt/nl_purify_tactic.cpp index c99ea545a..745b0352d 100644 --- a/src/tactic/nlsat_smt/nl_purify_tactic.cpp +++ b/src/tactic/nlsat_smt/nl_purify_tactic.cpp @@ -131,7 +131,7 @@ public: m_interface_cache.insert(arg, arg); return arg; } - r = m.mk_fresh_const(0, u().mk_real()); + r = m.mk_fresh_const(nullptr, u().mk_real()); m_new_reals.push_back(to_app(r)); m_owner.m_fmc->insert(to_app(r)->get_decl()); m_interface_cache.insert(arg, r); @@ -156,7 +156,7 @@ public: void mk_interface_bool(func_decl * f, unsigned num, expr* const* args, expr_ref& result, proof_ref& pr) { expr_ref old_pred(m.mk_app(f, num, args), m); polarity_t pol = m_polarities.find(old_pred); - result = m.mk_fresh_const(0, m.mk_bool_sort()); + result = m.mk_fresh_const(nullptr, m.mk_bool_sort()); m_polarities.insert(result, pol); m_new_preds.push_back(to_app(result)); m_owner.m_fmc->insert(to_app(result)->get_decl()); @@ -425,8 +425,8 @@ private: void core2result(expr_dependency_ref & lcore, goal_ref const& g, goal_ref_buffer& result) { result.reset(); - proof * pr = 0; - lcore = 0; + proof * pr = nullptr; + lcore = nullptr; g->reset(); obj_hashtable::iterator it = m_used_asms.begin(), end = m_used_asms.end(); for (; it != end; ++it) { @@ -488,7 +488,7 @@ private: nums.push_back(r); if (num2var.find(r, v)) { if (!m_eq_pairs.find(v, w, pred)) { - pred = m.mk_fresh_const(0, m.mk_bool_sort()); + pred = m.mk_fresh_const(nullptr, m.mk_bool_sort()); m_eq_preds.push_back(pred); m_eq_values.push_back(l_true); m_fmc->insert(to_app(pred)->get_decl()); @@ -558,7 +558,7 @@ private: } void translate(expr_safe_replace& num2num, expr* e, expr_ref& result) { - result = 0; + result = nullptr; if (e) { num2num(e, result); } @@ -699,9 +699,9 @@ public: m(m), m_util(m), m_params(p), - m_fmc(0), + m_fmc(nullptr), m_nl_tac(mk_nlsat_tactic(m, p)), - m_nl_g(0), + m_nl_g(nullptr), m_solver(mk_smt_solver(m, p, symbol::null)), m_eq_preds(m), m_new_reals(m), @@ -709,27 +709,27 @@ public: m_asms(m) {} - virtual ~nl_purify_tactic() {} + ~nl_purify_tactic() override {} - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; } - virtual tactic * translate(ast_manager& m) { + tactic * translate(ast_manager& m) override { return alloc(nl_purify_tactic, m, m_params); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { m_nl_tac->collect_statistics(st); m_solver->collect_statistics(st); } - virtual void reset_statistics() { + void reset_statistics() override { m_nl_tac->reset_statistics(); } - virtual void cleanup() { + void cleanup() override { m_solver = mk_smt_solver(m, m_params, symbol::null); m_nl_tac->cleanup(); m_eq_preds.reset(); @@ -744,17 +744,17 @@ public: m_bool2dep.reset(); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) override { tactic_report report("qfufnl-purify", *g); TRACE("nlsat_smt", g->display(tout);); m_produce_proofs = g->proofs_enabled(); - mc = 0; pc = 0; core = 0; + mc = nullptr; pc = nullptr; core = nullptr; fail_if_proof_generation("qfufnra-purify", g); // fail_if_unsat_core_generation("qfufnra-purify", g); diff --git a/src/tactic/nlsat_smt/nl_purify_tactic.h b/src/tactic/nlsat_smt/nl_purify_tactic.h deleted file mode 100644 index 85d033921..000000000 --- a/src/tactic/nlsat_smt/nl_purify_tactic.h +++ /dev/null @@ -1,35 +0,0 @@ -/*++ -Copyright (c) 2011 Microsoft Corporation - -Module Name: - - nl_purify_tactic.h - -Abstract: - - Tactic for purifying quantifier-free formulas that mix QF_NRA and other theories. - It is designed to allow cooprating between the nlsat solver and other theories - in a decoubled way. - -Author: - - Nikolaj Bjorner (nbjorner) 2015-5-5. - -Revision History: - ---*/ -#ifndef NL_PURIFY_TACTIC_H_ -#define NL_PURIFY_TACTIC_H_ - -#include "util/params.h" -class ast_manager; -class tactic; - -tactic * mk_nl_purify_tactic(ast_manager & m, params_ref const & p = params_ref()); - -/* - ADD_TACTIC("nl-purify", "Decompose goal into pure NL-sat formula and formula over other theories.", "mk_nl_purify_tactic(m, p)") -*/ - -#endif - diff --git a/src/tactic/portfolio/CMakeLists.txt b/src/tactic/portfolio/CMakeLists.txt index a8a9b2bba..2b714cc2c 100644 --- a/src/tactic/portfolio/CMakeLists.txt +++ b/src/tactic/portfolio/CMakeLists.txt @@ -1,11 +1,12 @@ z3_add_component(portfolio SOURCES + bounded_int2bv_solver.cpp default_tactic.cpp enum2bv_solver.cpp - pb2bv_solver.cpp - bounded_int2bv_solver.cpp fd_solver.cpp + pb2bv_solver.cpp smt_strategic_solver.cpp + solver2lookahead.cpp COMPONENT_DEPENDENCIES aig_tactic fp @@ -18,4 +19,5 @@ z3_add_component(portfolio ufbv_tactic TACTIC_HEADERS default_tactic.h + fd_solver.h ) diff --git a/src/tactic/portfolio/bounded_int2bv_solver.cpp b/src/tactic/portfolio/bounded_int2bv_solver.cpp index 58078e106..6389ed739 100644 --- a/src/tactic/portfolio/bounded_int2bv_solver.cpp +++ b/src/tactic/portfolio/bounded_int2bv_solver.cpp @@ -21,8 +21,7 @@ Notes: #include "solver/solver_na2as.h" #include "tactic/tactic.h" #include "ast/rewriter/pb2bv_rewriter.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" #include "tactic/arith/bound_manager.h" @@ -65,18 +64,32 @@ public: m_bounds.push_back(alloc(bound_manager, m)); } - virtual ~bounded_int2bv_solver() { + ~bounded_int2bv_solver() override { while (!m_bounds.empty()) { dealloc(m_bounds.back()); m_bounds.pop_back(); } } - virtual solver* translate(ast_manager& m, params_ref const& p) { - return alloc(bounded_int2bv_solver, m, p, m_solver->translate(m, p)); + solver* translate(ast_manager& dst_m, params_ref const& p) override { + flush_assertions(); + bounded_int2bv_solver* result = alloc(bounded_int2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); + ast_translation tr(m, dst_m); + for (auto& kv : m_int2bv) result->m_int2bv.insert(tr(kv.m_key), tr(kv.m_value)); + for (auto& kv : m_bv2int) result->m_bv2int.insert(tr(kv.m_key), tr(kv.m_value)); + for (auto& kv : m_bv2offset) result->m_bv2offset.insert(tr(kv.m_key), kv.m_value); + for (func_decl* f : m_bv_fns) result->m_bv_fns.push_back(tr(f)); + for (func_decl* f : m_int_fns) result->m_int_fns.push_back(tr(f)); + for (bound_manager* b : m_bounds) result->m_bounds.push_back(b->translate(dst_m)); + model_converter_ref mc = external_model_converter(); + if (mc) { + ast_translation tr(m, dst_m); + result->set_model_converter(mc->translate(tr)); + } + return result; } - virtual void assert_expr(expr * t) { + void assert_expr_core(expr * t) override { unsigned i = m_assertions.size(); m_assertions.push_back(t); while (i < m_assertions.size()) { @@ -92,14 +105,14 @@ public: } } - virtual void push_core() { + void push_core() override { flush_assertions(); m_solver->push(); m_bv_fns_lim.push_back(m_bv_fns.size()); m_bounds.push_back(alloc(bound_manager, m)); } - virtual void pop_core(unsigned n) { + void pop_core(unsigned n) override { m_assertions.reset(); m_solver->pop(n); @@ -125,31 +138,58 @@ public: } } - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { flush_assertions(); return m_solver->check_sat(num_assumptions, assumptions); } - virtual void updt_params(params_ref const & p) { solver::updt_params(p); m_solver->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } - virtual void set_produce_models(bool f) { m_solver->set_produce_models(f); } - virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } - virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); } - virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } - virtual void get_model(model_ref & mdl) { + void updt_params(params_ref const & p) override { solver::updt_params(p); m_solver->updt_params(p); } + void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); } + void set_produce_models(bool f) override { m_solver->set_produce_models(f); } + void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } + void collect_statistics(statistics & st) const override { m_solver->collect_statistics(st); } + void get_unsat_core(expr_ref_vector & r) override { m_solver->get_unsat_core(r); } + void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { - extend_model(mdl); - filter_model(mdl); + model_converter_ref mc = local_model_converter(); + if (mc) (*mc)(mdl); } } - virtual proof * get_proof() { return m_solver->get_proof(); } - virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } - virtual void set_reason_unknown(char const* msg) { m_solver->set_reason_unknown(msg); } - virtual void get_labels(svector & r) { m_solver->get_labels(r); } - virtual ast_manager& get_manager() const { return m; } - virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } - virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { + model_converter* external_model_converter() const { + return concat(mc0(), local_model_converter()); + } + model_converter* local_model_converter() const { + if (m_int2bv.empty() && m_bv_fns.empty()) return nullptr; + generic_model_converter* mc = alloc(generic_model_converter, m, "bounded_int2bv"); + for (func_decl* f : m_bv_fns) + mc->hide(f); + for (auto const& kv : m_int2bv) { + rational offset; + VERIFY (m_bv2offset.find(kv.m_value, offset)); + expr_ref value(m_bv.mk_bv2int(m.mk_const(kv.m_value)), m); + if (!offset.is_zero()) { + value = m_arith.mk_add(value, m_arith.mk_numeral(offset, true)); + } + TRACE("int2bv", tout << mk_pp(kv.m_key, m) << " " << value << "\n";); + mc->add(kv.m_key, value); + } + return mc; + } + + model_converter_ref get_model_converter() const override { + model_converter_ref mc = external_model_converter(); + mc = concat(mc.get(), m_solver->get_model_converter().get()); + return mc; + } + proof * get_proof() override { return m_solver->get_proof(); } + std::string reason_unknown() const override { return m_solver->reason_unknown(); } + void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } + void get_labels(svector & r) override { m_solver->get_labels(r); } + ast_manager& get_manager() const override { return m; } + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { flush_assertions(); return m_solver->cube(vars, backtrack_level); } + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } + lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { flush_assertions(); expr_ref_vector bvars(m); for (unsigned i = 0; i < vars.size(); ++i) { @@ -167,7 +207,7 @@ public: // translate bit-vector consequences back to integer values for (unsigned i = 0; i < consequences.size(); ++i) { - expr* a = 0, *b = 0, *u = 0, *v = 0; + expr* a = nullptr, *b = nullptr, *u = nullptr, *v = nullptr; func_decl* f; rational num; unsigned bvsize; @@ -184,38 +224,10 @@ public: } } return r; - } private: - void filter_model(model_ref& mdl) const { - if (m_bv_fns.empty()) { - return; - } - filter_model_converter filter(m); - for (unsigned i = 0; i < m_bv_fns.size(); ++i) { - filter.insert(m_bv_fns[i].get()); - } - filter(mdl, 0); - } - - void extend_model(model_ref& mdl) { - extension_model_converter ext(m); - obj_map::iterator it = m_int2bv.begin(), end = m_int2bv.end(); - for (; it != end; ++it) { - rational offset; - VERIFY (m_bv2offset.find(it->m_value, offset)); - expr_ref value(m_bv.mk_bv2int(m.mk_const(it->m_value)), m); - if (!offset.is_zero()) { - value = m_arith.mk_add(value, m_arith.mk_numeral(offset, true)); - } - TRACE("int2bv", tout << mk_pp(it->m_key, m) << " " << value << "\n";); - ext.insert(it->m_key, value); - } - ext(mdl, 0); - } - void accumulate_sub(expr_safe_replace& sub) const { for (unsigned i = 0; i < m_bounds.size(); ++i) { accumulate_sub(sub, *m_bounds[i]); @@ -292,8 +304,8 @@ private: void flush_assertions() const { if (m_assertions.empty()) return; bound_manager& bm = *m_bounds.back(); - for (unsigned i = 0; i < m_assertions.size(); ++i) { - bm(m_assertions[i].get()); + for (expr* a : m_assertions) { + bm(a); } TRACE("int2bv", bm.display(tout);); expr_safe_replace sub(m); @@ -304,8 +316,8 @@ private: m_solver->assert_expr(m_assertions); } else { - for (unsigned i = 0; i < m_assertions.size(); ++i) { - sub(m_assertions[i].get(), fml1); + for (expr* a : m_assertions) { + sub(a, fml1); m_rewriter(fml1, fml2, proof); if (m.canceled()) { m_rewriter.reset(); @@ -319,12 +331,12 @@ private: m_rewriter.reset(); } - virtual unsigned get_num_assertions() const { + unsigned get_num_assertions() const override { flush_assertions(); return m_solver->get_num_assertions(); } - virtual expr * get_assertion(unsigned idx) const { + expr * get_assertion(unsigned idx) const override { flush_assertions(); return m_solver->get_assertion(idx); } diff --git a/src/tactic/portfolio/default_tactic.cpp b/src/tactic/portfolio/default_tactic.cpp index 09052869b..7f71114f4 100644 --- a/src/tactic/portfolio/default_tactic.cpp +++ b/src/tactic/portfolio/default_tactic.cpp @@ -28,24 +28,27 @@ Notes: #include "tactic/arith/probe_arith.h" #include "tactic/smtlogics/quant_tactics.h" #include "tactic/fpa/qffp_tactic.h" +#include "tactic/fpa/qffplra_tactic.h" #include "tactic/smtlogics/qfaufbv_tactic.h" #include "tactic/smtlogics/qfauflia_tactic.h" -#include "tactic/smtlogics/qfufnra_tactic.h" +#include "tactic/portfolio/fd_solver.h" tactic * mk_default_tactic(ast_manager & m, params_ref const & p) { tactic * st = using_params(and_then(mk_simplify_tactic(m), - cond(mk_is_qfbv_probe(), mk_qfbv_tactic(m), - cond(mk_is_qfaufbv_probe(), mk_qfaufbv_tactic(m), + cond(mk_is_propositional_probe(), if_no_proofs(mk_fd_tactic(m, p)), + cond(mk_is_qfbv_probe(), mk_qfbv_tactic(m), + cond(mk_is_qfaufbv_probe(), mk_qfaufbv_tactic(m), cond(mk_is_qflia_probe(), mk_qflia_tactic(m), cond(mk_is_qfauflia_probe(), mk_qfauflia_tactic(m), cond(mk_is_qflra_probe(), mk_qflra_tactic(m), cond(mk_is_qfnra_probe(), mk_qfnra_tactic(m), cond(mk_is_qfnia_probe(), mk_qfnia_tactic(m), - cond(mk_is_lira_probe(), mk_lira_tactic(m, p), - cond(mk_is_nra_probe(), mk_nra_tactic(m), + cond(mk_is_lira_probe(), mk_lira_tactic(m, p), + cond(mk_is_nra_probe(), mk_nra_tactic(m), cond(mk_is_qffp_probe(), mk_qffp_tactic(m, p), + cond(mk_is_qffplra_probe(), mk_qffplra_tactic(m, p), //cond(mk_is_qfufnra_probe(), mk_qfufnra_tactic(m, p), - mk_smt_tactic()))))))))))), + mk_smt_tactic()))))))))))))), p); return st; } diff --git a/src/tactic/portfolio/enum2bv_solver.cpp b/src/tactic/portfolio/enum2bv_solver.cpp index dd0ee7c4b..29c6aeb39 100644 --- a/src/tactic/portfolio/enum2bv_solver.cpp +++ b/src/tactic/portfolio/enum2bv_solver.cpp @@ -20,21 +20,20 @@ Notes: --*/ -#include "solver/solver_na2as.h" -#include "tactic/tactic.h" #include "ast/bv_decl_plugin.h" #include "ast/datatype_decl_plugin.h" -#include "ast/rewriter/enum2bv_rewriter.h" -#include "tactic/extension_model_converter.h" -#include "tactic/filter_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" +#include "tactic/tactic.h" +#include "tactic/generic_model_converter.h" #include "tactic/portfolio/enum2bv_solver.h" +#include "solver/solver_na2as.h" +#include "ast/rewriter/enum2bv_rewriter.h" class enum2bv_solver : public solver_na2as { - ast_manager& m; - ref m_solver; - enum2bv_rewriter m_rewriter; + ast_manager& m; + ref m_solver; + enum2bv_rewriter m_rewriter; public: @@ -47,13 +46,19 @@ public: solver::updt_params(p); } - virtual ~enum2bv_solver() {} + ~enum2bv_solver() override {} - virtual solver* translate(ast_manager& m, params_ref const& p) { - return alloc(enum2bv_solver, m, p, m_solver->translate(m, p)); + solver* translate(ast_manager& dst_m, params_ref const& p) override { + solver* result = alloc(enum2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); + model_converter_ref mc = external_model_converter(); + if (mc) { + ast_translation tr(m, dst_m); + result->set_model_converter(mc->translate(tr)); + } + return result; } - virtual void assert_expr(expr * t) { + void assert_expr_core(expr * t) override { expr_ref tmp(t, m); expr_ref_vector bounds(m); proof_ref tmp_proof(m); @@ -63,40 +68,69 @@ public: m_solver->assert_expr(bounds); } - virtual void push_core() { + void push_core() override { m_rewriter.push(); m_solver->push(); } - virtual void pop_core(unsigned n) { + void pop_core(unsigned n) override { m_solver->pop(n); m_rewriter.pop(n); } - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { + m_solver->updt_params(get_params()); return m_solver->check_sat(num_assumptions, assumptions); } - virtual void updt_params(params_ref const & p) { solver::updt_params(p); m_solver->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } - virtual void set_produce_models(bool f) { m_solver->set_produce_models(f); } - virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } - virtual void collect_statistics(statistics & st) const { m_solver->collect_statistics(st); } - virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } - virtual void get_model(model_ref & mdl) { + void updt_params(params_ref const & p) override { solver::updt_params(p); m_solver->updt_params(p); } + void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); } + void set_produce_models(bool f) override { m_solver->set_produce_models(f); } + void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } + void collect_statistics(statistics & st) const override { m_solver->collect_statistics(st); } + void get_unsat_core(expr_ref_vector & r) override { m_solver->get_unsat_core(r); } + void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { - extend_model(mdl); - filter_model(mdl); + model_converter_ref mc = local_model_converter(); + if (mc) (*mc)(mdl); } } - virtual proof * get_proof() { return m_solver->get_proof(); } - virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } - virtual void set_reason_unknown(char const* msg) { m_solver->set_reason_unknown(msg); } - virtual void get_labels(svector & r) { m_solver->get_labels(r); } - virtual ast_manager& get_manager() const { return m; } - virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } - virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { + model_converter* local_model_converter() const { + if (m_rewriter.enum2def().empty() && + m_rewriter.enum2bv().empty()) { + return nullptr; + } + generic_model_converter* mc = alloc(generic_model_converter, m, "enum2bv"); + for (auto const& kv : m_rewriter.enum2bv()) + mc->hide(kv.m_value); + for (auto const& kv : m_rewriter.enum2def()) + mc->add(kv.m_key, kv.m_value); + return mc; + } + + model_converter* external_model_converter() const { + return concat(mc0(), local_model_converter()); + } + + model_converter_ref get_model_converter() const override { + model_converter_ref mc = external_model_converter(); + mc = concat(mc.get(), m_solver->get_model_converter().get()); + return mc; + } + proof * get_proof() override { return m_solver->get_proof(); } + std::string reason_unknown() const override { return m_solver->reason_unknown(); } + void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } + void get_labels(svector & r) override { m_solver->get_labels(r); } + ast_manager& get_manager() const override { return m; } + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { + return m_solver->find_mutexes(vars, mutexes); + } + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { + return m_solver->cube(vars, backtrack_level); + } + + lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { datatype_util dt(m); bv_util bv(m); expr_ref_vector bvars(m), conseq(m), bounds(m); @@ -104,8 +138,8 @@ public: // ensure that enumeration variables that // don't occur in the constraints // are also internalized. - for (unsigned i = 0; i < vars.size(); ++i) { - expr_ref tmp(m.mk_eq(vars[i], vars[i]), m); + for (expr* v : vars) { + expr_ref tmp(m.mk_eq(v, v), m); proof_ref proof(m); m_rewriter(tmp, tmp, proof); } @@ -113,20 +147,20 @@ public: m_solver->assert_expr(bounds); // translate enumeration constants to bit-vectors. - for (unsigned i = 0; i < vars.size(); ++i) { + for (expr* v : vars) { func_decl* f = 0; - if (is_app(vars[i]) && is_uninterp_const(vars[i]) && m_rewriter.enum2bv().find(to_app(vars[i])->get_decl(), f)) { + 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)); } else { - bvars.push_back(vars[i]); + bvars.push_back(v); } } lbool r = m_solver->get_consequences(asms, bvars, consequences); // translate bit-vector consequences back to enumeration types for (unsigned i = 0; i < consequences.size(); ++i) { - expr* a = 0, *b = 0, *u = 0, *v = 0; + expr* a = nullptr, *b = nullptr, *u = nullptr, *v = nullptr; func_decl* f; rational num; unsigned bvsize; @@ -144,30 +178,13 @@ public: return r; } - void filter_model(model_ref& mdl) { - filter_model_converter filter(m); - obj_map::iterator it = m_rewriter.enum2bv().begin(), end = m_rewriter.enum2bv().end(); - for (; it != end; ++it) { - filter.insert(it->m_value); - } - filter(mdl, 0); - } - void extend_model(model_ref& mdl) { - extension_model_converter ext(m); - obj_map::iterator it = m_rewriter.enum2def().begin(), end = m_rewriter.enum2def().end(); - for (; it != end; ++it) { - ext.insert(it->m_key, it->m_value); - - } - ext(mdl, 0); - } - virtual unsigned get_num_assertions() const { + unsigned get_num_assertions() const override { return m_solver->get_num_assertions(); } - virtual expr * get_assertion(unsigned idx) const { + expr * get_assertion(unsigned idx) const override { return m_solver->get_assertion(idx); } diff --git a/src/tactic/portfolio/fd_solver.cpp b/src/tactic/portfolio/fd_solver.cpp index b29fb20c1..b0d95baee 100644 --- a/src/tactic/portfolio/fd_solver.cpp +++ b/src/tactic/portfolio/fd_solver.cpp @@ -23,11 +23,23 @@ Notes: #include "tactic/portfolio/enum2bv_solver.h" #include "tactic/portfolio/pb2bv_solver.h" #include "tactic/portfolio/bounded_int2bv_solver.h" +#include "solver/solver2tactic.h" +#include "solver/parallel_tactic.h" +#include "solver/parallel_params.hpp" -solver * mk_fd_solver(ast_manager & m, params_ref const & p) { - solver* s = mk_inc_sat_solver(m, p); +solver * mk_fd_solver(ast_manager & m, params_ref const & p, bool incremental_mode) { + solver* s = mk_inc_sat_solver(m, p, incremental_mode); s = mk_enum2bv_solver(m, p, s); s = mk_pb2bv_solver(m, p, s); s = mk_bounded_int2bv_solver(m, p, s); return s; } + +tactic * mk_fd_tactic(ast_manager & m, params_ref const& p) { + return mk_solver2tactic(mk_fd_solver(m, p, false)); +} + +tactic * mk_parallel_qffd_tactic(ast_manager& m, params_ref const& p) { + solver* s = mk_fd_solver(m, p); + return mk_parallel_tactic(s, p); +} diff --git a/src/tactic/portfolio/fd_solver.h b/src/tactic/portfolio/fd_solver.h index 53cae16d1..e1c5b909c 100644 --- a/src/tactic/portfolio/fd_solver.h +++ b/src/tactic/portfolio/fd_solver.h @@ -23,7 +23,15 @@ Notes: #include "util/params.h" class solver; +class tactic; -solver * mk_fd_solver(ast_manager & m, params_ref const & p); +solver * mk_fd_solver(ast_manager & m, params_ref const & p, bool incremental_mode = true); +tactic * mk_fd_tactic(ast_manager & m, params_ref const & p); +tactic * mk_parallel_qffd_tactic(ast_manager& m, params_ref const& p); + +/* + ADD_TACTIC("qffd", "builtin strategy for solving QF_FD problems.", "mk_fd_tactic(m, p)") + ADD_TACTIC("pqffd", "builtin strategy for solving QF_FD problems in parallel.", "mk_parallel_qffd_tactic(m, p)") +*/ #endif diff --git a/src/tactic/portfolio/pb2bv_solver.cpp b/src/tactic/portfolio/pb2bv_solver.cpp index a06ff77c0..60ca6a5dc 100644 --- a/src/tactic/portfolio/pb2bv_solver.cpp +++ b/src/tactic/portfolio/pb2bv_solver.cpp @@ -16,18 +16,20 @@ Notes: --*/ -#include "tactic/portfolio/pb2bv_solver.h" -#include "solver/solver_na2as.h" -#include "tactic/tactic.h" -#include "ast/rewriter/pb2bv_rewriter.h" -#include "tactic/filter_model_converter.h" #include "ast/ast_pp.h" #include "model/model_smt2_pp.h" +#include "tactic/portfolio/pb2bv_solver.h" +#include "tactic/tactic.h" +#include "tactic/generic_model_converter.h" +#include "solver/solver_na2as.h" +#include "ast/rewriter/pb2bv_rewriter.h" +#include "ast/rewriter/th_rewriter.h" class pb2bv_solver : public solver_na2as { ast_manager& m; mutable expr_ref_vector m_assertions; mutable ref m_solver; + mutable th_rewriter m_th_rewriter; mutable pb2bv_rewriter m_rewriter; public: @@ -37,81 +39,100 @@ public: m(m), m_assertions(m), m_solver(s), + m_th_rewriter(m, p), m_rewriter(m, p) { solver::updt_params(p); } - virtual ~pb2bv_solver() {} + ~pb2bv_solver() override {} - virtual solver* translate(ast_manager& m, params_ref const& p) { - return alloc(pb2bv_solver, m, p, m_solver->translate(m, p)); + solver* translate(ast_manager& dst_m, params_ref const& p) override { + flush_assertions(); + solver* result = alloc(pb2bv_solver, dst_m, p, m_solver->translate(dst_m, p)); + model_converter_ref mc = external_model_converter(); + if (mc.get()) { + ast_translation tr(m, dst_m); + result->set_model_converter(mc->translate(tr)); + } + return result; } - virtual void assert_expr(expr * t) { + void assert_expr_core(expr * t) override { m_assertions.push_back(t); } - virtual void push_core() { + void push_core() override { flush_assertions(); m_rewriter.push(); m_solver->push(); } - virtual void pop_core(unsigned n) { + void pop_core(unsigned n) override { m_assertions.reset(); m_solver->pop(n); m_rewriter.pop(n); } - virtual lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) { + lbool check_sat_core(unsigned num_assumptions, expr * const * assumptions) override { flush_assertions(); return m_solver->check_sat(num_assumptions, assumptions); } - virtual void updt_params(params_ref const & p) { solver::updt_params(p); m_solver->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { m_solver->collect_param_descrs(r); } - virtual void set_produce_models(bool f) { m_solver->set_produce_models(f); } - virtual void set_progress_callback(progress_callback * callback) { m_solver->set_progress_callback(callback); } - virtual void collect_statistics(statistics & st) const { + void updt_params(params_ref const & p) override { solver::updt_params(p); m_rewriter.updt_params(p); m_solver->updt_params(p); } + void collect_param_descrs(param_descrs & r) override { m_solver->collect_param_descrs(r); m_rewriter.collect_param_descrs(r);} + void set_produce_models(bool f) override { m_solver->set_produce_models(f); } + void set_progress_callback(progress_callback * callback) override { m_solver->set_progress_callback(callback); } + void collect_statistics(statistics & st) const override { m_rewriter.collect_statistics(st); m_solver->collect_statistics(st); } - virtual void get_unsat_core(ptr_vector & r) { m_solver->get_unsat_core(r); } - virtual void get_model(model_ref & mdl) { + void get_unsat_core(expr_ref_vector & r) override { m_solver->get_unsat_core(r); } + void get_model_core(model_ref & mdl) override { m_solver->get_model(mdl); if (mdl) { - filter_model(mdl); + model_converter_ref mc = local_model_converter(); + if (mc) (*mc)(mdl); } } - virtual proof * get_proof() { return m_solver->get_proof(); } - virtual std::string reason_unknown() const { return m_solver->reason_unknown(); } - virtual void set_reason_unknown(char const* msg) { m_solver->set_reason_unknown(msg); } - virtual void get_labels(svector & r) { m_solver->get_labels(r); } - virtual ast_manager& get_manager() const { return m; } - virtual lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) { return m_solver->find_mutexes(vars, mutexes); } - virtual lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) { + + model_converter* external_model_converter() const{ + return concat(mc0(), local_model_converter()); + } + model_converter_ref get_model_converter() const override { + model_converter_ref mc = external_model_converter(); + mc = concat(mc.get(), m_solver->get_model_converter().get()); + return mc; + } + proof * get_proof() override { return m_solver->get_proof(); } + std::string reason_unknown() const override { return m_solver->reason_unknown(); } + void set_reason_unknown(char const* msg) override { m_solver->set_reason_unknown(msg); } + void get_labels(svector & r) override { m_solver->get_labels(r); } + ast_manager& get_manager() const override { return m; } + expr_ref_vector cube(expr_ref_vector& vars, unsigned backtrack_level) override { flush_assertions(); return m_solver->cube(vars, backtrack_level); } + lbool find_mutexes(expr_ref_vector const& vars, vector& mutexes) override { return m_solver->find_mutexes(vars, mutexes); } + lbool get_consequences_core(expr_ref_vector const& asms, expr_ref_vector const& vars, expr_ref_vector& consequences) override { flush_assertions(); return m_solver->get_consequences(asms, vars, consequences); } - void filter_model(model_ref& mdl) { + model_converter* local_model_converter() const { if (m_rewriter.fresh_constants().empty()) { - return; + return nullptr; } - filter_model_converter filter(m); + generic_model_converter* filter = alloc(generic_model_converter, m, "pb2bv"); func_decl_ref_vector const& fns = m_rewriter.fresh_constants(); - for (unsigned i = 0; i < fns.size(); ++i) { - filter.insert(fns[i]); + for (func_decl* f : fns) { + filter->hide(f); } - filter(mdl, 0); + return filter; } - virtual unsigned get_num_assertions() const { + unsigned get_num_assertions() const override { flush_assertions(); return m_solver->get_num_assertions(); } - virtual expr * get_assertion(unsigned idx) const { + expr * get_assertion(unsigned idx) const override { flush_assertions(); return m_solver->get_assertion(idx); } @@ -119,11 +140,14 @@ public: private: void flush_assertions() const { + if (m_assertions.empty()) return; + m_rewriter.updt_params(get_params()); proof_ref proof(m); - expr_ref fml(m); + expr_ref fml1(m), fml(m); expr_ref_vector fmls(m); - for (unsigned i = 0; i < m_assertions.size(); ++i) { - m_rewriter(m_assertions[i].get(), fml, proof); + for (expr* a : m_assertions) { + m_th_rewriter(a, fml1, proof); + m_rewriter(false, fml1, fml, proof); m_solver->assert_expr(fml); } m_rewriter.flush_side_constraints(fmls); diff --git a/src/tactic/portfolio/smt_strategic_solver.cpp b/src/tactic/portfolio/smt_strategic_solver.cpp index 3e77b7abc..b8ba2f59d 100644 --- a/src/tactic/portfolio/smt_strategic_solver.cpp +++ b/src/tactic/portfolio/smt_strategic_solver.cpp @@ -36,7 +36,6 @@ Notes: #include "tactic/portfolio/fd_solver.h" #include "tactic/ufbv/ufbv_tactic.h" #include "tactic/fpa/qffp_tactic.h" -#include "tactic/smtlogics/qfufnra_tactic.h" #include "muz/fp/horn_tactic.h" #include "smt/smt_solver.h" #include "sat/sat_solver/inc_sat_solver.h" @@ -94,9 +93,7 @@ tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const else if (logic=="HORN") return mk_horn_tactic(m, p); else if ((logic == "QF_FD" || logic == "SAT") && !m.proofs_enabled()) - return mk_solver2tactic(mk_fd_solver(m, p)); - //else if (logic=="QF_UFNRA") - // return mk_qfufnra_tactic(m, p); + return mk_fd_tactic(m, p); else return mk_default_tactic(m, p); } @@ -104,7 +101,7 @@ tactic * mk_tactic_for_logic(ast_manager & m, params_ref const & p, symbol const static solver* mk_special_solver_for_logic(ast_manager & m, params_ref const & p, symbol const& logic) { if ((logic == "QF_FD" || logic == "SAT") && !m.proofs_enabled()) return mk_fd_solver(m, p); - return 0; + return nullptr; } static solver* mk_solver_for_logic(ast_manager & m, params_ref const & p, symbol const& logic) { @@ -122,8 +119,8 @@ class smt_strategic_solver_factory : public solver_factory { public: smt_strategic_solver_factory(symbol const & logic):m_logic(logic) {} - virtual ~smt_strategic_solver_factory() {} - virtual solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) { + ~smt_strategic_solver_factory() override {} + solver * operator()(ast_manager & m, params_ref const & p, bool proofs_enabled, bool models_enabled, bool unsat_core_enabled, symbol const & logic) override { symbol l; if (m_logic != symbol::null) l = m_logic; diff --git a/src/tactic/portfolio/solver2lookahead.cpp b/src/tactic/portfolio/solver2lookahead.cpp new file mode 100644 index 000000000..0c18ab079 --- /dev/null +++ b/src/tactic/portfolio/solver2lookahead.cpp @@ -0,0 +1,24 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + solver2lookahead.cpp + +Abstract: + + Lookahead wrapper for arbitrary solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-9 + +Notes: + +--*/ +#include "sat/sat_solver/inc_sat_solver.h" +#include "solver/solver.h" + +solver * mk_solver2lookahead(solver* s) { + return 0; +} diff --git a/src/tactic/portfolio/solver2lookahead.h b/src/tactic/portfolio/solver2lookahead.h new file mode 100644 index 000000000..80d73ddf3 --- /dev/null +++ b/src/tactic/portfolio/solver2lookahead.h @@ -0,0 +1,26 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + solver2lookahead.h + +Abstract: + + Lookahead wrapper for arbitrary solver. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-10-9 + +Notes: + +--*/ +#ifndef SOLVER2LOOKAHEAD_H_ +#define SOLVER2LOOKAHEAD_H_ + +class solver; + +solver * mk_solver2lookahead(solver* s); + +#endif diff --git a/src/tactic/probe.cpp b/src/tactic/probe.cpp index 072b866b1..dcd1dc500 100644 --- a/src/tactic/probe.cpp +++ b/src/tactic/probe.cpp @@ -27,7 +27,7 @@ Revision History: class memory_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(static_cast(memory::get_allocation_size())/static_cast(1024*1024)); } }; @@ -38,21 +38,21 @@ probe * mk_memory_probe() { class depth_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(g.depth()); } }; class size_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(g.size()); } }; class num_exprs_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(g.num_exprs()); } }; @@ -79,7 +79,7 @@ public: p->inc_ref(); } - ~unary_probe() { + ~unary_probe() override { m_p->dec_ref(); } @@ -99,7 +99,7 @@ public: p2->inc_ref(); } - ~bin_probe() { + ~bin_probe() override { m_p1->dec_ref(); m_p2->dec_ref(); } @@ -108,7 +108,7 @@ public: class not_probe : public unary_probe { public: not_probe(probe * p):unary_probe(p) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(!m_p->operator()(g).is_true()); } }; @@ -116,7 +116,7 @@ public: class and_probe : public bin_probe { public: and_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(m_p1->operator()(g).is_true() && m_p2->operator()(g).is_true()); } }; @@ -124,7 +124,7 @@ public: class or_probe : public bin_probe { public: or_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(m_p1->operator()(g).is_true() || m_p2->operator()(g).is_true()); } }; @@ -132,7 +132,7 @@ public: class eq_probe : public bin_probe { public: eq_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() == m_p2->operator()(g).get_value()); } }; @@ -140,7 +140,7 @@ public: class le_probe : public bin_probe { public: le_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() <= m_p2->operator()(g).get_value()); } }; @@ -148,7 +148,7 @@ public: class add_probe : public bin_probe { public: add_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() + m_p2->operator()(g).get_value()); } }; @@ -156,7 +156,7 @@ public: class sub_probe : public bin_probe { public: sub_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() - m_p2->operator()(g).get_value()); } }; @@ -164,7 +164,7 @@ public: class mul_probe : public bin_probe { public: mul_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() * m_p2->operator()(g).get_value()); } }; @@ -172,7 +172,7 @@ public: class div_probe : public bin_probe { public: div_probe(probe * p1, probe * p2):bin_probe(p1, p2) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(m_p1->operator()(g).get_value() / m_p2->operator()(g).get_value()); } }; @@ -182,7 +182,7 @@ class const_probe : public probe { public: const_probe(double v):m_val(v) {} - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return result(m_val); } }; @@ -303,7 +303,7 @@ struct is_non_qfbv_predicate { class is_propositional_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return !test(g); } }; @@ -311,7 +311,7 @@ public: class is_qfbv_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return !test(g); } }; @@ -353,7 +353,7 @@ struct is_non_qfaufbv_predicate { class is_qfaufbv_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return !test(g); } }; @@ -391,7 +391,7 @@ struct is_non_qfufbv_predicate { class is_qfufbv_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return !test(g); } }; @@ -411,7 +411,7 @@ class num_consts_probe : public probe { family_id m_fid; unsigned m_counter; proc(ast_manager & _m, bool b, char const * family):m(_m), m_bool(b), m_counter(0) { - if (family != 0) + if (family != nullptr) m_fid = m.mk_family_id(family); else m_fid = null_family_id; @@ -442,7 +442,7 @@ public: num_consts_probe(bool b, char const * f): m_bool(b), m_family(f) { } - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { proc p(g.m(), m_bool, m_family); unsigned sz = g.size(); expr_fast_mark1 visited; @@ -454,11 +454,11 @@ public: }; probe * mk_num_consts_probe() { - return alloc(num_consts_probe, false, 0); + return alloc(num_consts_probe, false, nullptr); } probe * mk_num_bool_consts_probe() { - return alloc(num_consts_probe, true, 0); + return alloc(num_consts_probe, true, nullptr); } probe * mk_num_arith_consts_probe() { @@ -471,21 +471,21 @@ probe * mk_num_bv_consts_probe() { class produce_proofs_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return g.proofs_enabled(); } }; class produce_models_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return g.models_enabled(); } }; class produce_unsat_cores_probe : public probe { public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { return g.unsat_core_enabled(); } }; @@ -514,7 +514,7 @@ struct has_pattern_probe : public probe { } }; public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { try { expr_fast_mark1 visited; proc p; @@ -544,7 +544,7 @@ struct has_quantifier_probe : public probe { void operator()(quantifier * n) { throw found(); } }; public: - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { try { expr_fast_mark1 visited; proc p; diff --git a/src/tactic/proof_converter.cpp b/src/tactic/proof_converter.cpp index 095488415..f1a209487 100644 --- a/src/tactic/proof_converter.cpp +++ b/src/tactic/proof_converter.cpp @@ -17,126 +17,101 @@ Notes: --*/ #include "tactic/proof_converter.h" +#include "tactic/goal.h" #include "ast/ast_smt2_pp.h" class concat_proof_converter : public concat_converter { public: concat_proof_converter(proof_converter * pc1, proof_converter * pc2):concat_converter(pc1, pc2) {} - virtual char const * get_name() const { return "concat-proof-converter"; } + char const * get_name() const override { return "concat-proof-converter"; } - virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { proof_ref tmp(m); - this->m_c2->operator()(m, num_source, source, tmp); + tmp = this->m_c2->operator()(m, num_source, source); proof * new_source = tmp.get(); - this->m_c1->operator()(m, 1, &new_source, result); + return this->m_c1->operator()(m, 1, &new_source); } - virtual proof_converter * translate(ast_translation & translator) { + proof_converter * translate(ast_translation & translator) override { return this->translate_core(translator); } }; proof_converter * concat(proof_converter * pc1, proof_converter * pc2) { - if (pc1 == 0) + if (pc1 == nullptr) return pc2; - if (pc2 == 0) + if (pc2 == nullptr) return pc1; return alloc(concat_proof_converter, pc1, pc2); } -class concat_star_proof_converter : public concat_star_converter { +class subgoal_proof_converter : public proof_converter { + proof_converter_ref m_pc; + goal_ref_buffer m_goals; public: - concat_star_proof_converter(proof_converter * pc1, unsigned num, proof_converter * const * pc2s, unsigned * szs): - concat_star_converter(pc1, num, pc2s, szs) { + subgoal_proof_converter(proof_converter* pc, unsigned n, goal * const* goals): + m_pc(pc) + { + for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]); } - virtual char const * get_name() const { return "concat-star-proof-converter"; } + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { + // ignore the proofs from the arguments, instead obtain the proofs fromt he subgoals. + SASSERT(num_source == 0); + proof_converter_ref_buffer pc_buffer; + for (goal_ref g : m_goals) { + pc_buffer.push_back(g->pc()); - virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { - unsigned num = this->m_szs.size(); -#ifdef Z3DEBUG - unsigned sum = 0; - for (unsigned i = 0; i < num; i++) { - sum += this->m_szs[i]; - } - SASSERT(sum == num_source); -#endif - proof_ref_buffer tmp_prs(m); - for (unsigned i = 0; i < num; i++) { - unsigned sz = m_szs[i]; - proof_converter * c2 = m_c2s[i]; - proof_ref pr(m); - if (c2) { - (*c2)(m, sz, source, pr); - } - else { - SASSERT(sz == 1); - pr = *source; - } - source += sz; - tmp_prs.push_back(pr.get()); - } - if (m_c1) { - (*m_c1)(m, tmp_prs.size(), tmp_prs.c_ptr(), result); - } - else { - SASSERT(tmp_prs.size() == 1); - result = tmp_prs[0]; } + return apply(m, m_pc, pc_buffer); } - virtual proof_converter * translate(ast_translation & translator) { - return this->translate_core(translator); + proof_converter* translate(ast_translation& tr) override { + proof_converter_ref pc1 = m_pc->translate(tr); + goal_ref_buffer goals; + for (goal_ref g : m_goals) goals.push_back(g->translate(tr)); + return alloc(subgoal_proof_converter, pc1.get(), goals.size(), goals.c_ptr()); } + + void display(std::ostream& out) override {} + }; -proof_converter * concat(proof_converter * pc1, unsigned num, proof_converter * const * pc2s, unsigned * szs) { - SASSERT(num > 0); - if (num == 1) - return concat(pc1, pc2s[0]); - unsigned i; - for (i = 0; i < num; i++) { - if (pc2s[i] != 0) - break; - } - if (i == num) { - // all pc2s are 0 - return pc1; - } - return alloc(concat_star_proof_converter, pc1, num, pc2s, szs); +proof_converter * concat(proof_converter *pc, unsigned n, goal* const* goals) { + return alloc(subgoal_proof_converter, pc, n, goals); } class proof2pc : public proof_converter { proof_ref m_pr; public: proof2pc(ast_manager & m, proof * pr):m_pr(pr, m) {} - virtual ~proof2pc() {} + ~proof2pc() override {} - virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) { + proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) override { SASSERT(num_source == 0); - result = m_pr; + return m_pr; } - virtual proof_converter * translate(ast_translation & translator) { + proof_converter * translate(ast_translation & translator) override { return alloc(proof2pc, translator.to(), translator(m_pr.get())); } - virtual void display(std::ostream & out) { + void display(std::ostream & out) override { out << "(proof->proof-converter-wrapper\n" << mk_ismt2_pp(m_pr.get(), m_pr.get_manager()) << ")\n"; } }; proof_converter * proof2proof_converter(ast_manager & m, proof * pr) { - if (pr == 0) - return 0; + if (pr == nullptr) + return nullptr; return alloc(proof2pc, m, pr); } void apply(ast_manager & m, proof_converter * pc, proof_ref & pr) { if (pc) { proof * _pr = pr.get(); - (*pc)(m, 1, &_pr, pr); + pr = (*pc)(m, 1, &_pr); } } @@ -148,15 +123,15 @@ void apply(ast_manager & m, proof_converter * pc, proof_ref & pr) { pc1 and pc2s must be different from 0. */ -void apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s, proof_ref & result) { +proof_ref apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s) { SASSERT(pc1); proof_ref_buffer prs(m); unsigned sz = pc2s.size(); for (unsigned i = 0; i < sz; i++) { proof_ref pr(m); SASSERT(pc2s[i]); // proof production is enabled - pc2s[i]->operator()(m, 0, 0, pr); + pr = pc2s[i]->operator()(m, 0, 0); prs.push_back(pr); } - (*pc1)(m, sz, prs.c_ptr(), result); + return (*pc1)(m, sz, prs.c_ptr()); } diff --git a/src/tactic/proof_converter.h b/src/tactic/proof_converter.h index e925436d2..df8462de7 100644 --- a/src/tactic/proof_converter.h +++ b/src/tactic/proof_converter.h @@ -20,33 +20,34 @@ Notes: #define PROOF_CONVERTER_H_ #include "ast/ast.h" -#include "tactic/converter.h" #include "util/ref.h" +#include "tactic/converter.h" +class goal; class proof_converter : public converter { public: - virtual ~proof_converter() { } - virtual void operator()(ast_manager & m, unsigned num_source, proof * const * source, proof_ref & result) = 0; + ~proof_converter() override { } + virtual proof_ref operator()(ast_manager & m, unsigned num_source, proof * const * source) = 0; virtual proof_converter * translate(ast_translation & translator) = 0; }; typedef ref proof_converter_ref; +typedef sref_vector proof_converter_ref_vector; +typedef sref_buffer proof_converter_ref_buffer; + proof_converter * concat(proof_converter * pc1, proof_converter * pc2); /** - \brief \c pc1 is the proof converter for a sequence of subgoals of size \c num. - Given an i in [0, num), pc2s[i] is the proof converter for subgoal i, - and num_subgoals[i] is the number of subgoals of subgoals[i]. -*/ -proof_converter * concat(proof_converter * pc1, unsigned num, proof_converter * const * pc2s, unsigned * num_subgoals); + \brief create a proof converter that takes a set of subgoals and converts their proofs to a proof of + the goal they were derived from. + */ +proof_converter * concat(proof_converter *pc1, unsigned n, goal* const* goals); proof_converter * proof2proof_converter(ast_manager & m, proof * pr); -typedef sref_vector proof_converter_ref_vector; -typedef sref_buffer proof_converter_ref_buffer; - -void apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s, proof_ref & result); void apply(ast_manager & m, proof_converter * pc, proof_ref & pr); +proof_ref apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_buffer & pc2s); + #endif diff --git a/src/tactic/replace_proof_converter.cpp b/src/tactic/replace_proof_converter.cpp index 98f28bb1b..4a98110eb 100644 --- a/src/tactic/replace_proof_converter.cpp +++ b/src/tactic/replace_proof_converter.cpp @@ -39,7 +39,7 @@ public: replace_map(ast_manager& m): map_proc(m) {} void insert(expr* src, expr* dst) { - m_map.insert(src, dst, 0); + m_map.insert(src, dst, nullptr); } void operator()(var* v) { visit(v); } @@ -53,8 +53,7 @@ public: }; -void replace_proof_converter::operator()(ast_manager & m, unsigned num_source, - proof * const * source, proof_ref & result) { +proof_ref replace_proof_converter::operator()(ast_manager & m, unsigned num_source, proof * const * source) { SASSERT(num_source == 1); replace_map replace(m); proof_ref p(m); @@ -73,14 +72,12 @@ void replace_proof_converter::operator()(ast_manager & m, unsigned num_source, replace.apply(tmp); TRACE("proof_converter", tout << mk_pp(source[0], m) << "\n"; tout << mk_pp(tmp.get(), m) << "\n";); - result = to_app(tmp); + return proof_ref(to_app(tmp), m); } proof_converter * replace_proof_converter::translate(ast_translation & translator) { replace_proof_converter* rp = alloc(replace_proof_converter, m); - for (unsigned i = 0; i < m_proofs.size(); ++i) { - rp->insert(translator(m_proofs[i].get())); - } + for (proof* p : m_proofs) rp->insert(translator(p)); return rp; } diff --git a/src/tactic/replace_proof_converter.h b/src/tactic/replace_proof_converter.h index b768a18a0..67cd77a0f 100644 --- a/src/tactic/replace_proof_converter.h +++ b/src/tactic/replace_proof_converter.h @@ -32,11 +32,11 @@ public: replace_proof_converter(ast_manager& _m): m(_m), m_proofs(m) {} - virtual ~replace_proof_converter() {} + ~replace_proof_converter() override {} - virtual void operator()(ast_manager & _m, unsigned num_source, proof * const * source, proof_ref & result); + proof_ref operator()(ast_manager & _m, unsigned num_source, proof * const * source) override; - virtual proof_converter * translate(ast_translation & translator); + proof_converter * translate(ast_translation & translator) override; void insert(proof* p) { m_proofs.push_back(p); } @@ -45,6 +45,8 @@ public: // run the replacements the inverse direction. void invert() { m_proofs.reverse(); } + void display(std::ostream & out) override {} + }; #endif diff --git a/src/tactic/sine_filter.cpp b/src/tactic/sine_filter.cpp index ba35bac84..647791784 100644 --- a/src/tactic/sine_filter.cpp +++ b/src/tactic/sine_filter.cpp @@ -18,11 +18,9 @@ Revision History: #include "tactic/sine_filter.h" #include "tactic/tactical.h" -#include "tactic/filter_model_converter.h" +#include "tactic/generic_model_converter.h" #include "ast/datatype_decl_plugin.h" #include "ast/rewriter/rewriter_def.h" -#include "tactic/filter_model_converter.h" -#include "tactic/extension_model_converter.h" #include "ast/rewriter/var_subst.h" #include "ast/ast_util.h" #include "util/obj_pair_hashtable.h" @@ -35,26 +33,20 @@ class sine_tactic : public tactic { public: - sine_tactic(ast_manager& m, params_ref const& p): + sine_tactic(ast_manager& m, params_ref const& p): m(m), m_params(p) {} - - virtual tactic * translate(ast_manager & m) { + + tactic * translate(ast_manager & m) override { return alloc(sine_tactic, m, m_params); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; pc = 0; core = 0; - + void operator()(goal_ref const & g, goal_ref_buffer& result) override { TRACE("sine", g->display(tout);); TRACE("sine", tout << g->size();); ptr_vector new_forms; @@ -69,118 +61,116 @@ public: result.push_back(g.get()); TRACE("sine", result[0]->display(tout);); SASSERT(g->is_well_sorted()); - filter_model_converter * fmc = alloc(filter_model_converter, m); - mc = fmc; } - - virtual void cleanup() { + + void cleanup() override { } private: typedef std::pair t_work_item; - t_work_item work_item(expr *e, expr *root) { + t_work_item work_item(expr * e, expr * root) { return std::pair(e, root); } - void find_constants(expr *e, obj_hashtable &consts) { + void find_constants(expr * e, obj_hashtable &consts) { ptr_vector stack; stack.push_back(e); - expr *curr; + expr * curr; while (!stack.empty()) { curr = stack.back(); stack.pop_back(); - if (is_app(curr)) { + if (is_app(curr) && is_uninterp(curr)) { app *a = to_app(curr); - if (is_uninterp(a)) { - func_decl *f = a->get_decl(); - consts.insert_if_not_there(f); - } + func_decl *f = a->get_decl(); + consts.insert_if_not_there(f); } } } - bool quantifier_matches(quantifier *q, + bool quantifier_matches(quantifier * q, obj_hashtable const & consts, ptr_vector & next_consts) { - TRACE("sine", tout << "size of consts is "; tout << consts.size(); tout << "\n";); - for (obj_hashtable::iterator constit = consts.begin(), constend = consts.end(); constit != constend; constit++) { - TRACE("sine", tout << *constit; tout << "\n";); - } + TRACE("sine", + tout << "size of consts is "; tout << consts.size(); tout << "\n"; + for (func_decl* f : consts) tout << f->get_name() << "\n";); + bool matched = false; for (unsigned i = 0; i < q->get_num_patterns(); i++) { bool p_matched = true; ptr_vector stack; - expr *curr; + expr * curr; + // patterns are wrapped with "pattern" - if (!m.is_pattern(q->get_pattern(i), stack)) { + if (!m.is_pattern(q->get_pattern(i), stack)) continue; - } + while (!stack.empty()) { curr = stack.back(); stack.pop_back(); + if (is_app(curr)) { - app *a = to_app(curr); - func_decl *f = a->get_decl(); + app * a = to_app(curr); + func_decl * f = a->get_decl(); if (!consts.contains(f)) { TRACE("sine", tout << mk_pp(f, m) << "\n";); p_matched = false; next_consts.push_back(f); break; } - for (unsigned j = 0; j < a->get_num_args(); j++) { + for (unsigned j = 0; j < a->get_num_args(); j++) stack.push_back(a->get_arg(j)); - } } } + if (p_matched) { matched = true; break; } } + return matched; } - + void filter_expressions(goal_ref const & g, ptr_vector & new_exprs) { obj_map > const2exp; obj_map > exp2const; obj_map > const2quantifier; obj_hashtable consts; vector stack; - for (unsigned i = 0; i < g->size(); i++) { - stack.push_back(work_item(g->form(i), g->form(i))); - } t_work_item curr; + + for (unsigned i = 0; i < g->size(); i++) + stack.push_back(work_item(g->form(i), g->form(i))); + while (!stack.empty()) { curr = stack.back(); stack.pop_back(); - if (is_app(curr.first)) { - app *a = to_app(curr.first); - if (is_uninterp(a)) { - func_decl *f = a->get_decl(); - if (!consts.contains(f)) { - consts.insert(f); - if (const2quantifier.contains(f)) { - for (obj_pair_hashtable::iterator it = const2quantifier[f].begin(), end = const2quantifier[f].end(); it != end; it++) { - stack.push_back(*it); - } - const2quantifier.remove(f); - } - } - if (!const2exp.contains(f)) { - const2exp.insert(f, obj_hashtable()); - } - if (!const2exp[f].contains(curr.second)) { - const2exp[f].insert(curr.second); - } - if (!exp2const.contains(curr.second)) { - exp2const.insert(curr.second, obj_hashtable()); - } - if (!exp2const[curr.second].contains(f)) { - exp2const[curr.second].insert(f); + if (is_app(curr.first) && is_uninterp(curr.first)) { + app * a = to_app(curr.first); + func_decl * f = a->get_decl(); + if (!consts.contains(f)) { + consts.insert(f); + if (const2quantifier.contains(f)) { + for (auto const& p : const2quantifier[f]) + stack.push_back(p); + const2quantifier.remove(f); } } + if (!const2exp.contains(f)) { + const2exp.insert(f, obj_hashtable()); + } + if (!const2exp[f].contains(curr.second)) { + const2exp[f].insert(curr.second); + } + if (!exp2const.contains(curr.second)) { + exp2const.insert(curr.second, obj_hashtable()); + } + if (!exp2const[curr.second].contains(f)) { + exp2const[curr.second].insert(f); + } + for (unsigned i = 0; i < a->get_num_args(); i++) { stack.push_back(work_item(a->get_arg(i), curr.second)); } @@ -214,28 +204,27 @@ private: } } } + // ok, now we just need to find the connected component of the last term - obj_hashtable visited; ptr_vector to_visit; to_visit.push_back(g->form(g->size() - 1)); - expr *visiting; + expr * visiting; + while (!to_visit.empty()) { visiting = to_visit.back(); to_visit.pop_back(); visited.insert(visiting); - for (obj_hashtable::iterator constit = exp2const[visiting].begin(), constend = exp2const[visiting].end(); constit != constend; constit++) { - for (obj_hashtable::iterator exprit = const2exp[*constit].begin(), exprend = const2exp[*constit].end(); exprit != exprend; exprit++) { - if (!visited.contains(*exprit)) { - to_visit.push_back(*exprit); - } + for (func_decl* f : exp2const[visiting]) + for (expr* e : const2exp[f]) { + if (!visited.contains(e)) + to_visit.push_back(e); } - } } + for (unsigned i = 0; i < g->size(); i++) { - if (visited.contains(g->form(i))) { + if (visited.contains(g->form(i))) new_exprs.push_back(g->form(i)); - } } } }; diff --git a/src/tactic/sls/bvsls_opt_engine.cpp b/src/tactic/sls/bvsls_opt_engine.cpp index 502bcbde6..37454ca72 100644 --- a/src/tactic/sls/bvsls_opt_engine.cpp +++ b/src/tactic/sls/bvsls_opt_engine.cpp @@ -43,7 +43,7 @@ bvsls_opt_engine::optimization_result bvsls_opt_engine::optimize( m_hard_tracker.initialize(m_assertions); setup_opt_tracker(objective, _maximize); - if (initial_model.get() != 0) { + if (initial_model.get() != nullptr) { TRACE("sls_opt", tout << "Initial model provided: " << std::endl; for (unsigned i = 0; i < initial_model->get_num_constants(); i++) { func_decl * fd = initial_model->get_constant(i); diff --git a/src/tactic/sls/bvsls_opt_engine.h b/src/tactic/sls/bvsls_opt_engine.h index 9487130d3..3acce8f0a 100644 --- a/src/tactic/sls/bvsls_opt_engine.h +++ b/src/tactic/sls/bvsls_opt_engine.h @@ -65,7 +65,7 @@ protected: unsigned & best_const, mpz & best_value, unsigned & new_bit, move_type & move, mpz const & max_score, expr * objective); - mpz top_score(void) { + mpz top_score() { mpz res(0); obj_hashtable const & top_exprs = m_obj_tracker.get_top_exprs(); for (obj_hashtable::iterator it = top_exprs.begin(); diff --git a/src/tactic/sls/sls_engine.cpp b/src/tactic/sls/sls_engine.cpp index 4e1c6a693..f5b5ec1b2 100644 --- a/src/tactic/sls/sls_engine.cpp +++ b/src/tactic/sls/sls_engine.cpp @@ -100,36 +100,27 @@ void sls_engine::checkpoint() { } bool sls_engine::full_eval(model & mdl) { - bool res = true; - - unsigned sz = m_assertions.size(); - for (unsigned i = 0; i < sz && res; i++) { - checkpoint(); - expr_ref o(m_manager); - - if (!mdl.eval(m_assertions[i], o, true)) - exit(ERR_INTERNAL_FATAL); - - res = m_manager.is_true(o.get()); - } - - TRACE("sls", tout << "Evaluation: " << res << std::endl;); - - return res; + model::scoped_model_completion _scm(mdl, true); + for (expr* a : m_assertions) { + checkpoint(); + if (!mdl.is_true(a)) { + TRACE("sls", tout << "Evaluation: false\n";); + return false; + } + } + return true; } double sls_engine::top_score() { double top_sum = 0.0; - unsigned sz = m_assertions.size(); - for (unsigned i = 0; i < sz; i++) { - expr * e = m_assertions[i]; + for (expr* e : m_assertions) { top_sum += m_tracker.get_score(e); } TRACE("sls_top", tout << "Score distribution:"; - for (unsigned i = 0; i < sz; i++) - tout << " " << m_tracker.get_score(m_assertions[i]); - tout << " AVG: " << top_sum / (double)sz << std::endl;); + for (expr* e : m_assertions) + tout << " " << m_tracker.get_score(e); + tout << " AVG: " << top_sum / (double)m_assertions.size() << std::endl;); m_tracker.set_top_sum(top_sum); @@ -537,7 +528,7 @@ bailout: void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { if (g->inconsistent()) { - mc = 0; + mc = nullptr; return; } @@ -565,7 +556,7 @@ void sls_engine::operator()(goal_ref const & g, model_converter_ref & mc) { g->reset(); } else - mc = 0; + mc = nullptr; } lbool sls_engine::operator()() { diff --git a/src/tactic/sls/sls_engine.h b/src/tactic/sls/sls_engine.h index 0c4fb4d39..da9a8a2c8 100644 --- a/src/tactic/sls/sls_engine.h +++ b/src/tactic/sls/sls_engine.h @@ -99,7 +99,7 @@ public: // stats const & get_stats(void) { return m_stats; } void collect_statistics(statistics & st) const; - void reset_statistics(void) { m_stats.reset(); } + void reset_statistics() { m_stats.reset(); } bool full_eval(model & mdl); @@ -109,7 +109,7 @@ public: void mk_inv(unsigned bv_sz, const mpz & old_value, mpz & inverted); void mk_flip(sort * s, const mpz & old_value, unsigned bit, mpz & flipped); - lbool search(void); + lbool search(); lbool operator()(); void operator()(goal_ref const & g, model_converter_ref & mc); diff --git a/src/tactic/sls/sls_tactic.cpp b/src/tactic/sls/sls_tactic.cpp index 19937e8b0..4a26a3716 100644 --- a/src/tactic/sls/sls_tactic.cpp +++ b/src/tactic/sls/sls_tactic.cpp @@ -42,53 +42,51 @@ public: m_engine = alloc(sls_engine, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(sls_tactic, m, m_params); } - virtual ~sls_tactic() { + ~sls_tactic() override { dealloc(m_engine); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_engine->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { sls_params::collect_param_descrs(r); } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & g, + goal_ref_buffer & result) override { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; result.reset(); + result.reset(); TRACE("sls", g->display(tout);); tactic_report report("sls", *g); + model_converter_ref mc; m_engine->operator()(g, mc); - + g->add(mc.get()); g->inc_depth(); result.push_back(g.get()); TRACE("sls", g->display(tout);); SASSERT(g->is_well_sorted()); } - virtual void cleanup() { + void cleanup() override { sls_engine * d = alloc(sls_engine, m, m_params); std::swap(d, m_engine); dealloc(d); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { m_engine->collect_statistics(st); } - virtual void reset_statistics() { + void reset_statistics() override { m_engine->reset_statistics(); } diff --git a/src/tactic/sls/sls_tracker.h b/src/tactic/sls/sls_tracker.h index 15f06f096..b730e78da 100644 --- a/src/tactic/sls/sls_tracker.h +++ b/src/tactic/sls/sls_tracker.h @@ -40,7 +40,7 @@ class sls_tracker { mpz m_zero, m_one, m_two; struct value_score { - value_score() : m(0), value(unsynch_mpz_manager::mk_z(0)), score(0.0), score_prune(0.0), has_pos_occ(0), has_neg_occ(0), distance(0), touched(1) {}; + value_score() : m(nullptr), value(unsynch_mpz_manager::mk_z(0)), score(0.0), score_prune(0.0), has_pos_occ(0), has_neg_occ(0), distance(0), touched(1) {}; value_score(value_score && other) : m(other.m), value(std::move(other.value)), @@ -1038,7 +1038,7 @@ public: if (m_mpz_manager.neq(get_value(as[0]), m_one)) return as[0]; else - return 0; + return nullptr; } m_temp_constants.reset(); @@ -1061,7 +1061,7 @@ public: } } if (pos == static_cast(-1)) - return 0; + return nullptr; m_touched++; m_scores.find(as[pos]).touched++; @@ -1082,7 +1082,7 @@ public: for (unsigned i = 0; i < sz; i++) if (m_mpz_manager.neq(get_value(as[i]), m_one) && (get_random_uint(16) % ++cnt_unsat == 0)) pos = i; if (pos == static_cast(-1)) - return 0; + return nullptr; } m_last_pos = pos; @@ -1092,7 +1092,7 @@ public: expr * get_new_unsat_assertion(ptr_vector const & as) { unsigned sz = as.size(); if (sz == 1) - return 0; + return nullptr; m_temp_constants.reset(); unsigned cnt_unsat = 0, pos = -1; @@ -1100,7 +1100,7 @@ public: if ((i != m_last_pos) && m_mpz_manager.neq(get_value(as[i]), m_one) && (get_random_uint(16) % ++cnt_unsat == 0)) pos = i; if (pos == static_cast(-1)) - return 0; + return nullptr; return as[pos]; } }; diff --git a/src/tactic/smtlogics/CMakeLists.txt b/src/tactic/smtlogics/CMakeLists.txt index c90fd7468..2741334b4 100644 --- a/src/tactic/smtlogics/CMakeLists.txt +++ b/src/tactic/smtlogics/CMakeLists.txt @@ -11,7 +11,6 @@ z3_add_component(smtlogic_tactics qfnra_tactic.cpp qfufbv_ackr_model_converter.cpp qfufbv_tactic.cpp - qfufnra_tactic.cpp qfuf_tactic.cpp quant_tactics.cpp COMPONENT_DEPENDENCIES @@ -22,7 +21,6 @@ z3_add_component(smtlogic_tactics fp muz nlsat_tactic - nlsat_smt_tactic qe sat_solver smt_tactic @@ -40,6 +38,5 @@ z3_add_component(smtlogic_tactics qfnra_tactic.h qfuf_tactic.h qfufbv_tactic.h - qfufnra_tactic.h quant_tactics.h ) diff --git a/src/tactic/smtlogics/nra_tactic.cpp b/src/tactic/smtlogics/nra_tactic.cpp index 381bc4bb6..a9b32e5a8 100644 --- a/src/tactic/smtlogics/nra_tactic.cpp +++ b/src/tactic/smtlogics/nra_tactic.cpp @@ -19,13 +19,13 @@ Notes: #include "tactic/tactical.h" #include "tactic/core/simplify_tactic.h" #include "tactic/core/propagate_values_tactic.h" -#include "smt/tactic/smt_tactic.h" #include "tactic/core/nnf_tactic.h" +#include "tactic/arith/probe_arith.h" +#include "smt/tactic/smt_tactic.h" #include "qe/qe_tactic.h" #include "qe/nlqsat.h" -#include "nlsat/tactic/qfnra_nlsat_tactic.h" #include "qe/qe_lite.h" -#include "tactic/arith/probe_arith.h" +#include "nlsat/tactic/qfnra_nlsat_tactic.h" tactic * mk_nra_tactic(ast_manager & m, params_ref const& p) { params_ref p1 = p; diff --git a/src/tactic/smtlogics/qfbv_tactic.cpp b/src/tactic/smtlogics/qfbv_tactic.cpp index b69069864..bc93b4e7b 100644 --- a/src/tactic/smtlogics/qfbv_tactic.cpp +++ b/src/tactic/smtlogics/qfbv_tactic.cpp @@ -28,6 +28,7 @@ Notes: #include "tactic/bv/bv_size_reduction_tactic.h" #include "tactic/aig/aig_tactic.h" #include "sat/tactic/sat_tactic.h" +#include "sat/sat_solver/inc_sat_solver.h" #include "ackermannization/ackermannize_bv_tactic.h" #define MEMLIMIT 300 @@ -127,11 +128,10 @@ static tactic * mk_qfbv_tactic(ast_manager& m, params_ref const & p, tactic* sat tactic * mk_qfbv_tactic(ast_manager & m, params_ref const & p) { - tactic * new_sat = cond(mk_produce_proofs_probe(), and_then(mk_simplify_tactic(m), mk_smt_tactic()), - mk_sat_tactic(m)); + mk_psat_tactic(m, p)); - return mk_qfbv_tactic(m, p, new_sat, mk_smt_tactic()); + return mk_qfbv_tactic(m, p, new_sat, mk_psmt_tactic(m, p)); } diff --git a/src/tactic/smtlogics/qflia_tactic.cpp b/src/tactic/smtlogics/qflia_tactic.cpp index de542f3c4..541a81682 100644 --- a/src/tactic/smtlogics/qflia_tactic.cpp +++ b/src/tactic/smtlogics/qflia_tactic.cpp @@ -24,7 +24,6 @@ Notes: #include "tactic/core/solve_eqs_tactic.h" #include "tactic/core/elim_uncnstr_tactic.h" #include "smt/tactic/smt_tactic.h" -// include"mip_tactic.h" #include "tactic/arith/add_bounds_tactic.h" #include "tactic/arith/pb2bv_tactic.h" #include "tactic/arith/lia2pb_tactic.h" @@ -37,7 +36,7 @@ Notes: #include "tactic/arith/probe_arith.h" struct quasi_pb_probe : public probe { - virtual result operator()(goal const & g) { + result operator()(goal const & g) override { bool found_non_01 = false; bound_manager bm(g.m()); bm(g); @@ -90,7 +89,7 @@ static tactic * mk_bv2sat_tactic(ast_manager & m) { mk_max_bv_sharing_tactic(m), mk_bit_blaster_tactic(m), mk_aig_tactic(), - mk_sat_tactic(m)), + mk_sat_tactic(m, solver_p)), solver_p); } @@ -221,7 +220,7 @@ tactic * mk_qflia_tactic(ast_manager & m, params_ref const & p) { using_params(mk_lia2sat_tactic(m), quasi_pb_p), mk_fail_if_undecided_tactic()), mk_bounded_tactic(m), - mk_smt_tactic())), + mk_psmt_tactic(m, p))), main_p); st->updt_params(p); diff --git a/src/tactic/smtlogics/qfnia_tactic.cpp b/src/tactic/smtlogics/qfnia_tactic.cpp index 5374ba1c1..2ef49229a 100644 --- a/src/tactic/smtlogics/qfnia_tactic.cpp +++ b/src/tactic/smtlogics/qfnia_tactic.cpp @@ -26,7 +26,7 @@ Notes: #include "tactic/bv/max_bv_sharing_tactic.h" #include "sat/tactic/sat_tactic.h" #include "tactic/arith/nla2bv_tactic.h" -#include "tactic/arith/elim01_tactic.h" +#include "tactic/arith/lia2card_tactic.h" #include "tactic/core/ctx_simplify_tactic.h" #include "tactic/core/cofactor_term_ite_tactic.h" #include "nlsat/tactic/qfnra_nlsat_tactic.h" @@ -73,7 +73,7 @@ static tactic * mk_qfnia_premable(ast_manager & m, params_ref const & p_ref) { using_params(mk_ctx_simplify_tactic(m), ctx_simp_p), using_params(mk_simplify_tactic(m), pull_ite_p), mk_elim_uncnstr_tactic(m), - mk_elim01_tactic(m), + mk_lia2card_tactic(m), skip_if_failed(using_params(mk_cofactor_term_ite_tactic(m), elim_p))); } @@ -102,13 +102,17 @@ static tactic * mk_qfnia_nlsat_solver(ast_manager & m, params_ref const & p) { mk_fail_if_undecided_tactic()); } -tactic * mk_qfnia_tactic(ast_manager & m, params_ref const & p) { +static tactic * mk_qfnia_smt_solver(ast_manager& m, params_ref const& p) { params_ref simp_p = p; simp_p.set_bool("som", true); // expand into sums of monomials + return and_then(using_params(mk_simplify_tactic(m), simp_p), mk_smt_tactic()); +} + +tactic * mk_qfnia_tactic(ast_manager & m, params_ref const & p) { return and_then(mk_qfnia_premable(m, p), - or_else(mk_qfnia_nlsat_solver(m, p), - mk_qfnia_sat_solver(m, p), - and_then(using_params(mk_simplify_tactic(m), simp_p), - mk_smt_tactic()))); + or_else(mk_qfnia_sat_solver(m, p), + try_for(mk_qfnia_smt_solver(m, p), 2000), + mk_qfnia_nlsat_solver(m, p), + mk_qfnia_smt_solver(m, p))); } diff --git a/src/tactic/smtlogics/qfnra_tactic.cpp b/src/tactic/smtlogics/qfnra_tactic.cpp index 63c09c19c..cc01950a2 100644 --- a/src/tactic/smtlogics/qfnra_tactic.cpp +++ b/src/tactic/smtlogics/qfnra_tactic.cpp @@ -33,7 +33,9 @@ static tactic * mk_qfnra_sat_solver(ast_manager& m, params_ref const& p, unsigne } tactic * mk_qfnra_tactic(ast_manager & m, params_ref const& p) { - params_ref p1 = p; + params_ref p0 = p; + p0.set_bool("inline_vars", true); + params_ref p1 = p; p1.set_uint("seed", 11); p1.set_bool("factor", false); params_ref p2 = p; @@ -42,7 +44,7 @@ tactic * mk_qfnra_tactic(ast_manager & m, params_ref const& p) { return and_then(mk_simplify_tactic(m, p), mk_propagate_values_tactic(m, p), - or_else(try_for(mk_qfnra_nlsat_tactic(m, p), 5000), + or_else(try_for(mk_qfnra_nlsat_tactic(m, p0), 5000), try_for(mk_qfnra_nlsat_tactic(m, p1), 10000), mk_qfnra_sat_solver(m, p, 4), and_then(try_for(mk_smt_tactic(), 5000), mk_fail_if_undecided_tactic()), diff --git a/src/tactic/smtlogics/qfufbv_tactic.cpp b/src/tactic/smtlogics/qfufbv_tactic.cpp index df2791a8f..3bd28ad6d 100644 --- a/src/tactic/smtlogics/qfufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfufbv_tactic.cpp @@ -40,6 +40,7 @@ Notes: #include "tactic/smtlogics/qfbv_tactic.h" #include "solver/tactic2solver.h" #include "tactic/bv/bv_bound_chk_tactic.h" +#include "ackermannization/ackermannize_bv_tactic.h" /////////////// class qfufbv_ackr_tactic : public tactic { @@ -51,14 +52,9 @@ public: , m_inc_use_sat(false) {} - virtual ~qfufbv_ackr_tactic() { } + ~qfufbv_ackr_tactic() override { } - virtual void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; + void operator()(goal_ref const & g, goal_ref_buffer & result) override { ast_manager& m(g->m()); tactic_report report("qfufbv_ackr", *g); fail_if_unsat_core_generation("qfufbv_ackr", g); @@ -80,27 +76,27 @@ public: // report model if (g->models_enabled() && (o == l_true)) { model_ref abstr_model = imp.get_model(); - mc = mk_qfufbv_ackr_model_converter(m, imp.get_info(), abstr_model); + g->add(mk_qfufbv_ackr_model_converter(m, imp.get_info(), abstr_model)); } } - void updt_params(params_ref const & _p) { + void updt_params(params_ref const & _p) override { qfufbv_tactic_params p(_p); m_use_sat = p.sat_backend(); m_inc_use_sat = p.inc_sat_backend(); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { ackermannization_params p(m_p); if (!p.eager()) st.update("lackr-its", m_st.m_it); st.update("ackr-constraints", m_st.m_ackrs_sz); } - virtual void reset_statistics() { m_st.reset(); } + void reset_statistics() override { m_st.reset(); } - virtual void cleanup() { } + void cleanup() override { } - virtual tactic* translate(ast_manager& m) { + tactic* translate(ast_manager& m) override { return alloc(qfufbv_ackr_tactic, m, m_p); } private: @@ -111,7 +107,7 @@ private: bool m_inc_use_sat; solver* setup_sat() { - solver * sat(NULL); + solver * sat(nullptr); if (m_use_sat) { if (m_inc_use_sat) { sat = mk_inc_sat_solver(m_m, m_p); @@ -162,13 +158,14 @@ static tactic * mk_qfufbv_preamble1(ast_manager & m, params_ref const & p) { static tactic * mk_qfufbv_preamble(ast_manager & m, params_ref const & p) { return and_then(mk_simplify_tactic(m), - mk_propagate_values_tactic(m), - mk_solve_eqs_tactic(m), - mk_elim_uncnstr_tactic(m), - if_no_proofs(if_no_unsat_cores(mk_reduce_args_tactic(m))), - if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), - mk_max_bv_sharing_tactic(m) - ); + mk_propagate_values_tactic(m), + mk_solve_eqs_tactic(m), + mk_elim_uncnstr_tactic(m), + if_no_proofs(if_no_unsat_cores(mk_reduce_args_tactic(m))), + if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))), + mk_max_bv_sharing_tactic(m), + if_no_proofs(if_no_unsat_cores(mk_ackermannize_bv_tactic(m,p))) + ); } tactic * mk_qfufbv_tactic(ast_manager & m, params_ref const & p) { diff --git a/src/tactic/smtlogics/qfufnra_tactic.cpp b/src/tactic/smtlogics/qfufnra_tactic.cpp deleted file mode 100644 index e031b0f52..000000000 --- a/src/tactic/smtlogics/qfufnra_tactic.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/*++ -Copyright (c) 2015 Microsoft Corporation - -Module Name: - - qfufnra_tactic.cpp - -Abstract: - - Tactic for QF_UFNRA - -Author: - - Nikolaj (nbjorner) 2015-05-05 - -Notes: - ---*/ -#include "tactic/tactical.h" -#include "tactic/core/simplify_tactic.h" -#include "tactic/core/propagate_values_tactic.h" -#include "tactic/nlsat_smt/nl_purify_tactic.h" -#include "tactic/smtlogics/qfufnra_tactic.h" -#include "tactic/arith/purify_arith_tactic.h" -#include "tactic/core/solve_eqs_tactic.h" -#include "tactic/core/elim_term_ite_tactic.h" -#include "tactic/core/elim_uncnstr_tactic.h" -#include "tactic/core/simplify_tactic.h" -#include "tactic/core/nnf_tactic.h" -#include "tactic/core/tseitin_cnf_tactic.h" - -tactic * mk_qfufnra_tactic(ast_manager & m, params_ref const& p) { - params_ref main_p = p; - main_p.set_bool("elim_and", true); - main_p.set_bool("blast_distinct", true); - - return and_then(and_then(using_params(mk_simplify_tactic(m, p), main_p), - mk_purify_arith_tactic(m, p), - mk_propagate_values_tactic(m, p), - mk_solve_eqs_tactic(m, p), - mk_elim_uncnstr_tactic(m, p)), - and_then(mk_elim_term_ite_tactic(m, p), - mk_solve_eqs_tactic(m, p), - using_params(mk_simplify_tactic(m, p), main_p), - mk_tseitin_cnf_core_tactic(m, p), - using_params(mk_simplify_tactic(m, p), main_p), - mk_nl_purify_tactic(m, p))); -} - - diff --git a/src/tactic/smtlogics/qfufnra_tactic.h b/src/tactic/smtlogics/qfufnra_tactic.h deleted file mode 100644 index 026ab5c5c..000000000 --- a/src/tactic/smtlogics/qfufnra_tactic.h +++ /dev/null @@ -1,31 +0,0 @@ -/*++ -Copyright (c) 2012 Microsoft Corporation - -Module Name: - - qfufnra_tactic.h - -Abstract: - - Tactic for QF_UFNRA - -Author: - - Leonardo (leonardo) 2012-02-28 - -Notes: - ---*/ -#ifndef QFUFNRA_TACTIC_H_ -#define QFUFNRA_TACTIC_H_ - -#include "util/params.h" -class ast_manager; -class tactic; - -tactic * mk_qfufnra_tactic(ast_manager & m, params_ref const & p = params_ref()); -/* - ADD_TACTIC("qfufnra", "builtin strategy for solving QF_UNFRA problems.", "mk_qfufnra_tactic(m, p)") -*/ - -#endif diff --git a/src/tactic/tactic.cpp b/src/tactic/tactic.cpp index 6c4ca7476..4fa9ca43f 100644 --- a/src/tactic/tactic.cpp +++ b/src/tactic/tactic.cpp @@ -53,7 +53,7 @@ tactic_report::tactic_report(char const * id, goal const & g) { if (get_verbosity_level() >= TACTIC_VERBOSITY_LVL) m_imp = alloc(imp, id, g); else - m_imp = 0; + m_imp = nullptr; } tactic_report::~tactic_report() { @@ -67,16 +67,8 @@ void report_tactic_progress(char const * id, unsigned val) { } } -void skip_tactic::operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - result.reset(); +void skip_tactic::operator()(goal_ref const & in, goal_ref_buffer& result) { result.push_back(in.get()); - mc = 0; - pc = 0; - core = 0; } tactic * mk_skip_tactic() { @@ -85,17 +77,13 @@ tactic * mk_skip_tactic() { class fail_tactic : public tactic { public: - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer & result) override { throw tactic_exception("fail tactic"); } - virtual void cleanup() {} + void cleanup() override {} - virtual tactic * translate(ast_manager & m) { return this; } + tactic * translate(ast_manager & m) override { return this; } }; tactic * mk_fail_tactic() { @@ -108,13 +96,9 @@ class report_verbose_tactic : public skip_tactic { public: report_verbose_tactic(char const * msg, unsigned lvl) : m_msg(msg), m_lvl(lvl) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { IF_VERBOSE(m_lvl, verbose_stream() << m_msg << "\n";); - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; @@ -127,14 +111,10 @@ class trace_tactic : public skip_tactic { public: trace_tactic(char const * tag): m_tag(tag) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { TRACE(m_tag, in->display(tout);); (void)m_tag; - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; @@ -146,14 +126,10 @@ class fail_if_undecided_tactic : public skip_tactic { public: fail_if_undecided_tactic() {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (!in->is_decided()) throw tactic_exception("undecided"); - skip_tactic::operator()(in, result, mc, pc, core); + skip_tactic::operator()(in, result); } }; @@ -161,10 +137,10 @@ tactic * mk_fail_if_undecided_tactic() { return alloc(fail_if_undecided_tactic); } -void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core) { +void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result) { t.reset_statistics(); try { - t(in, result, mc, pc, core); + t(in, result); t.cleanup(); } catch (tactic_exception & ex) { @@ -179,34 +155,31 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, p bool models_enabled = g->models_enabled(); bool proofs_enabled = g->proofs_enabled(); bool cores_enabled = g->unsat_core_enabled(); - md = 0; - pr = 0; - core = 0; + md = nullptr; + pr = nullptr; + core = nullptr; ast_manager & m = g->m(); goal_ref_buffer r; - model_converter_ref mc; - proof_converter_ref pc; try { - exec(t, g, r, mc, pc, core); + exec(t, g, r); } catch (tactic_exception & ex) { reason_unknown = ex.msg(); return l_undef; } - TRACE("tactic_mc", mc->display(tout);); TRACE("tactic_check_sat", tout << "r.size(): " << r.size() << "\n"; for (unsigned i = 0; i < r.size(); i++) r[i]->display(tout);); if (is_decided_sat(r)) { - if (models_enabled) { - if (mc) - (*mc)(labels, 0); + model_converter_ref mc = r[0]->mc(); + if (mc.get()) { + (*mc)(labels); model_converter2model(m, mc.get(), md); - if (!md) { - // create empty model. - md = alloc(model, m); - } + } + if (!md) { + // create empty model. + md = alloc(model, m); } return l_true; } @@ -218,10 +191,11 @@ lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, p return l_false; } else { - if (models_enabled) { - model_converter2model(m, mc.get(), md); - if (mc) - (*mc)(labels, 0); + if (models_enabled && r.size() >= 1) { + model_converter_ref mc = r[0]->mc(); + model_converter2model(m, mc.get(), md); + if (mc) + (*mc)(labels); } reason_unknown = "incomplete"; return l_undef; diff --git a/src/tactic/tactic.h b/src/tactic/tactic.h index 0e4c61611..c9b5a23fd 100644 --- a/src/tactic/tactic.h +++ b/src/tactic/tactic.h @@ -24,8 +24,6 @@ Notes: #include "tactic/goal.h" #include "util/params.h" #include "util/statistics.h" -#include "tactic/model_converter.h" -#include "tactic/proof_converter.h" #include "tactic/tactic_exception.h" #include "util/lbool.h" @@ -50,19 +48,7 @@ public: The list of resultant subgoals is stored in \c result. The content of \c in may be destroyed during the operation. - - The resultant model converter \c mc can be used to convert a model for one of the returned subgoals - into a model for \in. If mc == 0, then model construction is disabled or any model for a subgoal - of \c in is also a model for \c in. - If \c result is decided_sat (i.e., it contains a single empty subgoal), then - the model converter is just wrapping the model. - - The resultant proof converter \c pc can be used to convert proofs for each subgoal in \c result - into a proof for \c in. If pc == 0, then one of the following conditions should hold: - 1- proof construction is disabled, - 2- result contains a single subgoal, and any proof of unsatisfiability for this subgoal is a proof for \c in. - 3- result is an decided_unsat (i.e., it contains a single unsat subgoal). The actual proof can be extracted from this goal. - + The output parameter \c core is used to accumulate the unsat core of closed subgoals. It must be 0 if dependency tracking is disabled, or the result is decided unsat, or no tagged assertions were used to close any subgoal. @@ -75,11 +61,7 @@ public: Therefore, in most cases, pc == 0 and core == 0 for non-branching tactics. */ - virtual void operator()(/* in */ goal_ref const & in, - /* out */ goal_ref_buffer & result, - /* out */ model_converter_ref & mc, - /* out */ proof_converter_ref & pc, - /* out */ expr_dependency_ref & core) = 0; + virtual void operator()(goal_ref const & in, goal_ref_buffer& result) = 0; virtual void collect_statistics(statistics & st) const {} virtual void reset_statistics() {} @@ -119,9 +101,9 @@ void report_tactic_progress(char const * id, unsigned val); class skip_tactic : public tactic { public: - virtual void operator()(goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); - virtual void cleanup() {} - virtual tactic * translate(ast_manager & m) { return this; } + void operator()(goal_ref const & in, goal_ref_buffer& result) override; + void cleanup() override {} + tactic * translate(ast_manager & m) override { return this; } }; tactic * mk_skip_tactic(); @@ -152,7 +134,7 @@ public: #define MK_SIMPLE_TACTIC_FACTORY(NAME, ST) MK_TACTIC_FACTORY(NAME, return ST;) -void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result, model_converter_ref & mc, proof_converter_ref & pc, expr_dependency_ref & core); +void exec(tactic & t, goal_ref const & in, goal_ref_buffer & result); lbool check_sat(tactic & t, goal_ref & g, model_ref & md, labels_vec & labels, proof_ref & pr, expr_dependency_ref & core, std::string & reason_unknown); // Throws an exception if goal \c in requires proof generation. diff --git a/src/tactic/tactic_exception.h b/src/tactic/tactic_exception.h index e989ed2bf..bdf2636a9 100644 --- a/src/tactic/tactic_exception.h +++ b/src/tactic/tactic_exception.h @@ -7,7 +7,7 @@ Module Name: Abstract: - Tactic expection object. + Tactic exception object. Author: @@ -27,8 +27,8 @@ protected: std::string m_msg; public: tactic_exception(char const * msg):m_msg(msg) {} - virtual ~tactic_exception() {} - virtual char const * msg() const { return m_msg.c_str(); } + ~tactic_exception() override {} + char const * msg() const override { return m_msg.c_str(); } }; #define TACTIC_CANCELED_MSG Z3_CANCELED_MSG diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index c5a70c0db..bb04a2be7 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -16,70 +16,65 @@ Author: Notes: --*/ -#include "tactic/tactical.h" #include "util/scoped_timer.h" #include "util/cancel_eh.h" #include "util/cooperate.h" #include "util/scoped_ptr_vector.h" #include "util/z3_omp.h" +#include "tactic/tactical.h" class binary_tactical : public tactic { protected: - tactic * m_t1; - tactic * m_t2; - + tactic_ref m_t1; + tactic_ref m_t2; public: + binary_tactical(tactic * t1, tactic * t2): m_t1(t1), m_t2(t2) { SASSERT(m_t1); SASSERT(m_t2); - m_t1->inc_ref(); - m_t2->inc_ref(); } - virtual ~binary_tactical() { - m_t1->dec_ref(); - m_t2->dec_ref(); - } + ~binary_tactical() override { } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_t1->updt_params(p); m_t2->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { m_t1->collect_param_descrs(r); m_t2->collect_param_descrs(r); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { m_t1->collect_statistics(st); m_t2->collect_statistics(st); } - virtual void reset_statistics() { + void reset_statistics() override { m_t1->reset_statistics(); m_t2->reset_statistics(); } - virtual void cleanup() { + void cleanup() override { m_t1->cleanup(); m_t2->cleanup(); } - virtual void reset() { + void reset() override { m_t1->reset(); m_t2->reset(); } - virtual void set_logic(symbol const & l) { + void set_logic(symbol const & l) override { m_t1->set_logic(l); m_t2->set_logic(l); } - virtual void set_progress_callback(progress_callback * callback) { + void set_progress_callback(progress_callback * callback) override { m_t1->set_progress_callback(callback); m_t2->set_progress_callback(callback); } @@ -104,115 +99,69 @@ struct false_pred { class and_then_tactical : public binary_tactical { public: and_then_tactical(tactic * t1, tactic * t2):binary_tactical(t1, t2) {} - virtual ~and_then_tactical() {} + ~and_then_tactical() override {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { - bool models_enabled = in->models_enabled(); bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); - ast_manager & m = in->m(); - goal_ref_buffer r1; - model_converter_ref mc1; - proof_converter_ref pc1; - expr_dependency_ref core1(m); - result.reset(); - mc = 0; - pc = 0; - core = 0; - m_t1->operator()(in, r1, mc1, pc1, core1); - SASSERT(!is_decided(r1) || (!pc1 && !core1)); // the pc and core of decided goals is 0 + ast_manager & m = in->m(); + goal_ref_buffer r1; + m_t1->operator()(in, r1); unsigned r1_size = r1.size(); - SASSERT(r1_size > 0); + SASSERT(r1_size > 0); if (r1_size == 1) { if (r1[0]->is_decided()) { - result.push_back(r1[0]); - if (models_enabled) mc = mc1; - return; + result.push_back(r1[0]); + return; } goal_ref r1_0 = r1[0]; - m_t2->operator()(r1_0, result, mc, pc, core); - if (models_enabled) mc = concat(mc1.get(), mc.get()); - if (proofs_enabled) pc = concat(pc1.get(), pc.get()); - if (cores_enabled) core = m.mk_join(core1.get(), core); + m_t2->operator()(r1_0, result); } else { - if (cores_enabled) core = core1; - proof_converter_ref_buffer pc_buffer; - model_converter_ref_buffer mc_buffer; - sbuffer sz_buffer; - goal_ref_buffer r2; + goal_ref_buffer r2; for (unsigned i = 0; i < r1_size; i++) { - goal_ref g = r1[i]; - r2.reset(); - model_converter_ref mc2; - proof_converter_ref pc2; - expr_dependency_ref core2(m); - m_t2->operator()(g, r2, mc2, pc2, core2); + goal_ref g = r1[i]; + r2.reset(); + m_t2->operator()(g, r2); if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { - // found solution... + // found solution... + result.reset(); result.push_back(r2[0]); - if (models_enabled) { - // mc2 contains the actual model - model_ref md; - md = alloc(model, m); - apply(mc2, md, 0); - apply(mc1, md, i); - mc = model2model_converter(md.get()); - } - SASSERT(!pc); SASSERT(!core); - return; + return; } else { SASSERT(is_decided_unsat(r2)); - // the proof and unsat core of a decided_unsat goal are stored in the node itself. - // pc2 and core2 must be 0. - SASSERT(!pc2); - SASSERT(!core2); - if (models_enabled) mc_buffer.push_back(0); - if (proofs_enabled) pc_buffer.push_back(proof2proof_converter(m, r2[0]->pr(0))); - if (models_enabled || proofs_enabled) sz_buffer.push_back(0); - if (cores_enabled) core = m.mk_join(core.get(), r2[0]->dep(0)); } } else { result.append(r2.size(), r2.c_ptr()); - if (models_enabled) mc_buffer.push_back(mc2.get()); - if (proofs_enabled) pc_buffer.push_back(pc2.get()); - if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size()); - if (cores_enabled) core = m.mk_join(core.get(), core2.get()); } } - + if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); - if (proofs_enabled) - apply(m, pc1, pc_buffer, pr); - SASSERT(cores_enabled || core == 0); + expr_dependency_ref core(m); + if (proofs_enabled) { + apply(m, in->pc(), pr); + } + dependency_converter* dc = in->dc(); + if (cores_enabled && dc) { + core = (*dc)(); + } in->assert_expr(m.mk_false(), pr, core); - core = 0; result.push_back(in.get()); - SASSERT(!mc); SASSERT(!pc); SASSERT(!core); - } - else { - if (models_enabled) mc = concat(mc1.get(), mc_buffer.size(), mc_buffer.c_ptr(), sz_buffer.c_ptr()); - if (proofs_enabled) pc = concat(pc1.get(), pc_buffer.size(), pc_buffer.c_ptr(), sz_buffer.c_ptr()); - SASSERT(cores_enabled || core == 0); } } } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return translate_core(m); } @@ -271,92 +220,58 @@ tactic * and_then(unsigned num, tactic * const * ts) { class nary_tactical : public tactic { protected: - ptr_vector m_ts; + sref_vector m_ts; public: nary_tactical(unsigned num, tactic * const * ts) { for (unsigned i = 0; i < num; i++) { SASSERT(ts[i]); m_ts.push_back(ts[i]); - ts[i]->inc_ref(); } } - virtual ~nary_tactical() { - unsigned sz = m_ts.size(); - for (unsigned i = 0; i < sz; i++) { - m_ts[i]->dec_ref(); - } - } + ~nary_tactical() override { } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { TRACE("nary_tactical_updt_params", tout << "updt_params: " << p << "\n";); - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->updt_params(p); + for (tactic* t : m_ts) t->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->collect_param_descrs(r); + void collect_param_descrs(param_descrs & r) override { + for (tactic* t : m_ts) t->collect_param_descrs(r); } - virtual void collect_statistics(statistics & st) const { - ptr_vector::const_iterator it = m_ts.begin(); - ptr_vector::const_iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->collect_statistics(st); + void collect_statistics(statistics & st) const override { + for (tactic const* t : m_ts) t->collect_statistics(st); } - virtual void reset_statistics() { - ptr_vector::const_iterator it = m_ts.begin(); - ptr_vector::const_iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->reset_statistics(); + void reset_statistics() override { + for (tactic* t : m_ts) t->reset_statistics(); } - virtual void cleanup() { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->cleanup(); + void cleanup() override { + for (tactic* t : m_ts) t->cleanup(); } - virtual void reset() { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->reset(); + void reset() override { + for (tactic* t : m_ts) t->reset(); } - virtual void set_logic(symbol const & l) { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->set_logic(l); + void set_logic(symbol const & l) override { + for (tactic* t : m_ts) t->set_logic(l); } - virtual void set_progress_callback(progress_callback * callback) { - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) - (*it)->set_progress_callback(callback); + void set_progress_callback(progress_callback * callback) override { + for (tactic* t : m_ts) t->set_progress_callback(callback); } protected: template tactic * translate_core(ast_manager & m) { - ptr_buffer new_ts; - ptr_vector::iterator it = m_ts.begin(); - ptr_vector::iterator end = m_ts.end(); - for (; it != end; ++it) { - tactic * curr = *it; - tactic * new_curr = curr->translate(m); - new_ts.push_back(new_curr); + sref_vector new_ts; + for (tactic* curr : m_ts) { + new_ts.push_back(curr->translate(m)); } return alloc(T, new_ts.size(), new_ts.c_ptr()); } @@ -367,33 +282,26 @@ class or_else_tactical : public nary_tactical { public: or_else_tactical(unsigned num, tactic * const * ts):nary_tactical(num, ts) { SASSERT(num > 0); } - virtual ~or_else_tactical() {} + ~or_else_tactical() override {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { goal orig(*(in.get())); unsigned sz = m_ts.size(); unsigned i; for (i = 0; i < sz; i++) { tactic * t = m_ts[i]; - result.reset(); - mc = 0; - pc = 0; - core = 0; SASSERT(sz > 0); if (i < sz - 1) { try { - t->operator()(in, result, mc, pc, core); + t->operator()(in, result); return; } catch (tactic_exception &) { + result.reset(); } } else { - t->operator()(in, result, mc, pc, core); + t->operator()(in, result); return; } in->reset_all(); @@ -401,7 +309,7 @@ public: } } - virtual tactic * translate(ast_manager & m) { return translate_core(m); } + tactic * translate(ast_manager & m) override { return translate_core(m); } }; tactic * or_else(unsigned num, tactic * const * ts) { @@ -464,15 +372,11 @@ class par_tactical : public or_else_tactical { public: par_tactical(unsigned num, tactic * const * ts):or_else_tactical(num, ts) {} - virtual ~par_tactical() {} + ~par_tactical() override {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { bool use_seq; #ifdef _NO_OMP_ use_seq = true; @@ -481,7 +385,7 @@ public: #endif if (use_seq) { // execute tasks sequentially - or_else_tactical::operator()(in, result, mc, pc, core); + or_else_tactical::operator()(in, result); return; } @@ -509,15 +413,12 @@ public: #pragma omp parallel for for (int i = 0; i < static_cast(sz); i++) { goal_ref_buffer _result; - model_converter_ref _mc; - proof_converter_ref _pc; - expr_dependency_ref _core(*(managers[i])); goal_ref in_copy = in_copies[i]; tactic & t = *(ts.get(i)); try { - t(in_copy, _result, _mc, _pc, _core); + t(in_copy, _result); bool first = false; #pragma omp critical (par_tactical) { @@ -533,13 +434,11 @@ public: } } ast_translation translator(*(managers[i]), m, false); - for (unsigned k = 0; k < _result.size(); k++) { - result.push_back(_result[k]->translate(translator)); + for (goal* g : _result) { + result.push_back(g->translate(translator)); } - mc = _mc ? _mc->translate(translator) : 0; - pc = _pc ? _pc->translate(translator) : 0; - expr_dependency_translation td(translator); - core = td(_core); + goal_ref in2(in_copy->translate(translator)); + in->copy_from(*(in2.get())); } } catch (tactic_exception & ex) { @@ -562,7 +461,6 @@ public: } } if (finished_id == UINT_MAX) { - mc = 0; switch (ex_kind) { case ERROR_EX: throw z3_error(error_code); case TACTIC_EX: throw tactic_exception(ex_msg.c_str()); @@ -572,7 +470,7 @@ public: } } - virtual tactic * translate(ast_manager & m) { return translate_core(m); } + tactic * translate(ast_manager & m) override { return translate_core(m); } }; tactic * par(unsigned num, tactic * const * ts) { @@ -597,13 +495,9 @@ tactic * par(tactic * t1, tactic * t2, tactic * t3, tactic * t4) { class par_and_then_tactical : public and_then_tactical { public: par_and_then_tactical(tactic * t1, tactic * t2):and_then_tactical(t1, t2) {} - virtual ~par_and_then_tactical() {} + ~par_and_then_tactical() override {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { bool use_seq; #ifdef _NO_OMP_ use_seq = true; @@ -612,43 +506,30 @@ public: #endif if (use_seq) { // execute tasks sequentially - and_then_tactical::operator()(in, result, mc, pc, core); + and_then_tactical::operator()(in, result); return; } - bool models_enabled = in->models_enabled(); + // enabling proofs is possible, but requires translating subgoals back. + fail_if_proof_generation("par_and_then", in); bool proofs_enabled = in->proofs_enabled(); bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); - goal_ref_buffer r1; - model_converter_ref mc1; - proof_converter_ref pc1; - expr_dependency_ref core1(m); - result.reset(); - mc = 0; - pc = 0; - core = 0; - m_t1->operator()(in, r1, mc1, pc1, core1); - SASSERT(!is_decided(r1) || (!pc1 && !core1)); // the pc and core of decided goals is 0 - unsigned r1_size = r1.size(); + goal_ref_buffer r1; + m_t1->operator()(in, r1); + unsigned r1_size = r1.size(); SASSERT(r1_size > 0); if (r1_size == 1) { // Only one subgoal created... no need for parallelism if (r1[0]->is_decided()) { - result.push_back(r1[0]); - if (models_enabled) mc = mc1; - SASSERT(!pc); SASSERT(!core); - return; + result.push_back(r1[0]); + return; } goal_ref r1_0 = r1[0]; - m_t2->operator()(r1_0, result, mc, pc, core); - if (models_enabled) mc = concat(mc1.get(), mc.get()); - if (proofs_enabled) pc = concat(pc1.get(), pc.get()); - if (cores_enabled) core = m.mk_join(core1.get(), core); + m_t2->operator()(r1_0, result); } else { - if (cores_enabled) core = core1; scoped_ptr_vector managers; tactic_ref_vector ts2; @@ -662,13 +543,9 @@ public: ts2.push_back(m_t2->translate(*new_m)); } - proof_converter_ref_buffer pc_buffer; - model_converter_ref_buffer mc_buffer; scoped_ptr_vector core_buffer; scoped_ptr_vector goals_vect; - pc_buffer.resize(r1_size); - mc_buffer.resize(r1_size); core_buffer.resize(r1_size); goals_vect.resize(r1_size); @@ -684,14 +561,11 @@ public: goal_ref new_g = g_copies[i]; goal_ref_buffer r2; - model_converter_ref mc2; - proof_converter_ref pc2; - expr_dependency_ref core2(new_m); bool curr_failed = false; try { - ts2[i]->operator()(new_g, r2, mc2, pc2, core2); + ts2[i]->operator()(new_g, r2); } catch (tactic_exception & ex) { #pragma omp critical (par_and_then_tactical) @@ -756,31 +630,12 @@ public: } ast_translation translator(new_m, m, false); SASSERT(r2.size() == 1); - result.push_back(r2[0]->translate(translator)); - if (models_enabled) { - // mc2 contains the actual model - mc2 = mc2 ? mc2->translate(translator) : 0; - model_ref md; - md = alloc(model, m); - apply(mc2, md, 0); - apply(mc1, md, i); - mc = model2model_converter(md.get()); - } - SASSERT(!pc); SASSERT(!core); + result.push_back(r2[0]->translate(translator)); } } else { SASSERT(is_decided_unsat(r2)); - // the proof and unsat core of a decided_unsat goal are stored in the node itself. - // pc2 and core2 must be 0. - SASSERT(!pc2); - SASSERT(!core2); - if (models_enabled) mc_buffer.set(i, 0); - if (proofs_enabled) { - proof * pr = r2[0]->pr(0); - pc_buffer.push_back(proof2proof_converter(m, pr)); - } if (cores_enabled && r2[0]->dep(0) != 0) { expr_dependency_ref * new_dep = alloc(expr_dependency_ref, new_m); *new_dep = r2[0]->dep(0); @@ -792,11 +647,10 @@ public: goal_ref_buffer * new_r2 = alloc(goal_ref_buffer); goals_vect.set(i, new_r2); new_r2->append(r2.size(), r2.c_ptr()); - mc_buffer.set(i, mc2.get()); - pc_buffer.set(i, pc2.get()); - if (cores_enabled && core2 != 0) { + dependency_converter* dc = r1[i]->dc(); + if (cores_enabled && dc) { expr_dependency_ref * new_dep = alloc(expr_dependency_ref, new_m); - *new_dep = core2; + *new_dep = (*dc)(); core_buffer.set(i, new_dep); } } @@ -814,55 +668,51 @@ public: if (found_solution) return; - - core = 0; - sbuffer sz_buffer; + + expr_dependency_ref core(m); for (unsigned i = 0; i < r1_size; i++) { ast_translation translator(*(managers[i]), m, false); goal_ref_buffer * r = goals_vect[i]; + unsigned j = result.size(); if (r != 0) { for (unsigned k = 0; k < r->size(); k++) { result.push_back((*r)[k]->translate(translator)); } - sz_buffer.push_back(r->size()); } - else { - sz_buffer.push_back(0); + if (proofs_enabled) { + // update proof converter of r1[i] + r1[i]->set(concat(r1[i]->pc(), result.size() - j, result.c_ptr() + j)); } - if (mc_buffer[i] != 0) - mc_buffer.set(i, mc_buffer[i]->translate(translator)); - if (pc_buffer[i] != 0) - pc_buffer.set(i, pc_buffer[i]->translate(translator)); expr_dependency_translation td(translator); - if (core_buffer[i] != 0) { + if (core_buffer[i] != nullptr) { expr_dependency_ref curr_core(m); curr_core = td(*(core_buffer[i])); core = m.mk_join(curr_core, core); } } + if (core) { + in->add(dependency_converter::unit(core)); + } if (result.empty()) { // all subgoals were shown to be unsat. // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); - if (proofs_enabled) - apply(m, pc1, pc_buffer, pr); - SASSERT(cores_enabled || core == 0); + if (proofs_enabled) { + apply(m, in->pc(), pr); + } + dependency_converter* dc = in->dc(); + if (cores_enabled && dc) { + core = (*dc)(); + } in->assert_expr(m.mk_false(), pr, core); - core = 0; result.push_back(in.get()); - SASSERT(!mc); SASSERT(!pc); SASSERT(!core); - } - else { - if (models_enabled) mc = concat(mc1.get(), mc_buffer.size(), mc_buffer.c_ptr(), sz_buffer.c_ptr()); - if (proofs_enabled) pc = concat(pc1.get(), pc_buffer.size(), pc_buffer.c_ptr(), sz_buffer.c_ptr()); - SASSERT(cores_enabled || core == 0); } } } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return translate_core(m); } @@ -885,36 +735,29 @@ tactic * par_and_then(unsigned num, tactic * const * ts) { class unary_tactical : public tactic { protected: - tactic * m_t; + tactic_ref m_t; public: unary_tactical(tactic * t): m_t(t) { - SASSERT(t); - t->inc_ref(); + SASSERT(t); } - virtual ~unary_tactical() { - m_t->dec_ref(); - } + virtual ~unary_tactical() { } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - m_t->operator()(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer& result) override { + m_t->operator()(in, result); } - virtual void cleanup(void) { m_t->cleanup(); } - virtual void collect_statistics(statistics & st) const { m_t->collect_statistics(st); } - virtual void reset_statistics() { m_t->reset_statistics(); } - virtual void updt_params(params_ref const & p) { m_t->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { m_t->collect_param_descrs(r); } - virtual void reset() { m_t->reset(); } - virtual void set_logic(symbol const& l) { m_t->set_logic(l); } - virtual void set_progress_callback(progress_callback * callback) { m_t->set_progress_callback(callback); } + void cleanup(void) override { m_t->cleanup(); } + void collect_statistics(statistics & st) const override { m_t->collect_statistics(st); } + void reset_statistics() override { m_t->reset_statistics(); } + void updt_params(params_ref const & p) override { m_t->updt_params(p); } + void collect_param_descrs(param_descrs & r) override { m_t->collect_param_descrs(r); } + void reset() override { m_t->reset(); } + void set_logic(symbol const& l) override { m_t->set_logic(l); } + void set_progress_callback(progress_callback * callback) override { m_t->set_progress_callback(callback); } protected: template @@ -929,16 +772,10 @@ class repeat_tactical : public unary_tactical { void operator()(unsigned depth, goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer& result) { // TODO: implement a non-recursive version. if (depth > m_max_depth) { result.push_back(in.get()); - mc = 0; - pc = 0; - core = 0; return; } @@ -947,23 +784,14 @@ class repeat_tactical : public unary_tactical { bool cores_enabled = in->unsat_core_enabled(); ast_manager & m = in->m(); - goal_ref_buffer r1; - model_converter_ref mc1; - proof_converter_ref pc1; - expr_dependency_ref core1(m); - result.reset(); - mc = 0; - pc = 0; - core = 0; + goal_ref_buffer r1; + result.reset(); { goal orig_in(in->m(), proofs_enabled, models_enabled, cores_enabled); orig_in.copy_from(*(in.get())); - m_t->operator()(in, r1, mc1, pc1, core1); - if (is_equal(orig_in, *(in.get()))) { - result.push_back(r1[0]); - if (models_enabled) mc = mc1; - if (proofs_enabled) pc = pc1; - if (cores_enabled) core = core1; + m_t->operator()(in, r1); + if (r1.size() == 1 && is_equal(orig_in, *(r1[0]))) { + result.push_back(r1[0]); return; } } @@ -971,61 +799,31 @@ class repeat_tactical : public unary_tactical { SASSERT(r1_size > 0); if (r1_size == 1) { if (r1[0]->is_decided()) { - result.push_back(r1[0]); - if (models_enabled) mc = mc1; - SASSERT(!pc); SASSERT(!core); + result.push_back(r1[0]); return; } goal_ref r1_0 = r1[0]; - operator()(depth+1, r1_0, result, mc, pc, core); - if (models_enabled) mc = concat(mc.get(), mc1.get()); - if (proofs_enabled) pc = concat(pc.get(), pc1.get()); - if (cores_enabled) core = m.mk_join(core1.get(), core); + operator()(depth+1, r1_0, result); } else { - if (cores_enabled) core = core1; - proof_converter_ref_buffer pc_buffer; - model_converter_ref_buffer mc_buffer; - sbuffer sz_buffer; goal_ref_buffer r2; for (unsigned i = 0; i < r1_size; i++) { goal_ref g = r1[i]; - r2.reset(); - model_converter_ref mc2; - proof_converter_ref pc2; - expr_dependency_ref core2(m); - operator()(depth+1, g, r2, mc2, pc2, core2); + r2.reset(); + operator()(depth+1, g, r2); if (is_decided(r2)) { SASSERT(r2.size() == 1); if (is_decided_sat(r2)) { // found solution... result.push_back(r2[0]); - if (models_enabled) { - // mc2 contains the actual model - model_ref md; - if (mc2) (*mc2)(md, 0); - if (mc1) (*mc1)(md, i); - mc = model2model_converter(md.get()); - } - SASSERT(!pc); SASSERT(!core); return; } else { SASSERT(is_decided_unsat(r2)); - SASSERT(!pc2); - SASSERT(!core2); - if (models_enabled) mc_buffer.push_back(0); - if (proofs_enabled) pc_buffer.push_back(proof2proof_converter(m, r2[0]->pr(0))); - if (models_enabled || proofs_enabled) sz_buffer.push_back(0); - if (cores_enabled) core = m.mk_join(core.get(), r2[0]->dep(0)); } } else { - result.append(r2.size(), r2.c_ptr()); - if (models_enabled) mc_buffer.push_back(mc2.get()); - if (proofs_enabled) pc_buffer.push_back(pc2.get()); - if (models_enabled || proofs_enabled) sz_buffer.push_back(r2.size()); - if (cores_enabled) core = m.mk_join(core.get(), core2.get()); + result.append(r2.size(), r2.c_ptr()); } } @@ -1034,18 +832,15 @@ class repeat_tactical : public unary_tactical { // create an decided_unsat goal with the proof in->reset_all(); proof_ref pr(m); - if (proofs_enabled) - apply(m, pc1, pc_buffer, pr); - SASSERT(cores_enabled || core == 0); + expr_dependency_ref core(m); + if (proofs_enabled) { + apply(m, in->pc(), pr); + } + if (cores_enabled && in->dc()) { + core = (*in->dc())(); + } in->assert_expr(m.mk_false(), pr, core); - core = 0; result.push_back(in.get()); - SASSERT(!mc); SASSERT(!pc); SASSERT(!core); - } - else { - if (models_enabled) mc = concat(mc1.get(), mc_buffer.size(), mc_buffer.c_ptr(), sz_buffer.c_ptr()); - if (proofs_enabled) pc = concat(pc1.get(), pc_buffer.size(), pc_buffer.c_ptr(), sz_buffer.c_ptr()); - SASSERT(cores_enabled || core == 0); } } } @@ -1056,15 +851,11 @@ public: m_max_depth(max_depth) { } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - operator()(0, in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer& result) override { + operator()(0, in, result); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(repeat_tactical, new_t, m_max_depth); } @@ -1079,22 +870,15 @@ class fail_if_branching_tactical : public unary_tactical { public: fail_if_branching_tactical(tactic * t, unsigned threshold):unary_tactical(t), m_threshold(threshold) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - m_t->operator()(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer& result) override { + m_t->operator()(in, result); if (result.size() > m_threshold) { - result.reset(); - mc = 0; - pc = 0; - core = 0; + result.reset(); // assumes in is not strenthened to one of the branches throw tactic_exception("failed-if-branching tactical"); } }; - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(fail_if_branching_tactical, new_t, m_threshold); } @@ -1108,16 +892,12 @@ class cleanup_tactical : public unary_tactical { public: cleanup_tactical(tactic * t):unary_tactical(t) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - m_t->operator()(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer& result) override { + m_t->operator()(in, result); m_t->cleanup(); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(cleanup_tactical, new_t); } @@ -1132,20 +912,16 @@ class try_for_tactical : public unary_tactical { public: try_for_tactical(tactic * t, unsigned ts):unary_tactical(t), m_timeout(ts) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { cancel_eh eh(in->m().limit()); { // Warning: scoped_timer is not thread safe in Linux. scoped_timer timer(m_timeout, &eh); - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(try_for_tactical, new_t, m_timeout); } @@ -1162,7 +938,7 @@ public: t->updt_params(p); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { TRACE("using_params", tout << "before p: " << p << "\n"; tout << "m_params: " << m_params << "\n";); @@ -1177,7 +953,7 @@ public: tout << "new_p: " << new_p << "\n";); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(using_params_tactical, new_t, m_params); } @@ -1202,16 +978,12 @@ public: annotate_tactical(char const* name, tactic* t): unary_tactical(t), m_name(name) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { scope _scope(m_name); - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { tactic * new_t = m_t->translate(m); return alloc(annotate_tactical, m_name.c_str(), new_t); } @@ -1223,34 +995,27 @@ tactic * annotate_tactic(char const* name, tactic * t) { } class cond_tactical : public binary_tactical { - probe * m_p; + probe_ref m_p; public: cond_tactical(probe * p, tactic * t1, tactic * t2): binary_tactical(t1, t2), m_p(p) { SASSERT(m_p); - m_p->inc_ref(); } - ~cond_tactical() { - m_p->dec_ref(); - } + virtual ~cond_tactical() {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer & result) override { if (m_p->operator()(*(in.get())).is_true()) - m_t1->operator()(in, result, mc, pc, core); + m_t1->operator()(in, result); else - m_t2->operator()(in, result, mc, pc, core); + m_t2->operator()(in, result); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { tactic * new_t1 = m_t1->translate(m); tactic * new_t2 = m_t2->translate(m); - return alloc(cond_tactical, m_p, new_t1, new_t2); + return alloc(cond_tactical, m_p.get(), new_t1, new_t2); } }; @@ -1263,35 +1028,25 @@ tactic * when(probe * p, tactic * t) { } class fail_if_tactic : public tactic { - probe * m_p; + probe_ref m_p; public: fail_if_tactic(probe * p): m_p(p) { SASSERT(m_p); - m_p->inc_ref(); } - ~fail_if_tactic() { - m_p->dec_ref(); - } + virtual ~fail_if_tactic() {} - void cleanup() {} + void cleanup() override {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - mc = 0; - pc = 0; - core = 0; + void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (m_p->operator()(*(in.get())).is_true()) { throw tactic_exception("fail-if tactic"); } result.push_back(in.get()); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return this; } }; @@ -1308,66 +1063,51 @@ class if_no_proofs_tactical : public unary_tactical { public: if_no_proofs_tactical(tactic * t):unary_tactical(t) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer & result) override { if (in->proofs_enabled()) { - mc = 0; pc = 0; core = 0; - result.reset(); result.push_back(in.get()); } else { - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } } - virtual tactic * translate(ast_manager & m) { return translate_core(m); } + tactic * translate(ast_manager & m) override { return translate_core(m); } }; class if_no_unsat_cores_tactical : public unary_tactical { public: if_no_unsat_cores_tactical(tactic * t):unary_tactical(t) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (in->unsat_core_enabled()) { - mc = 0; pc = 0; core = 0; - result.reset(); result.push_back(in.get()); } else { - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } } - virtual tactic * translate(ast_manager & m) { return translate_core(m); } + tactic * translate(ast_manager & m) override { return translate_core(m); } }; class if_no_models_tactical : public unary_tactical { public: if_no_models_tactical(tactic * t):unary_tactical(t) {} - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + void operator()(goal_ref const & in, goal_ref_buffer& result) override { if (in->models_enabled()) { - mc = 0; pc = 0; core = 0; - result.reset(); result.push_back(in.get()); } else { - m_t->operator()(in, result, mc, pc, core); + m_t->operator()(in, result); } } - virtual tactic * translate(ast_manager & m) { return translate_core(m); } + tactic * translate(ast_manager & m) override { + return translate_core(m); + } + }; tactic * if_no_proofs(tactic * t) { @@ -1386,4 +1126,3 @@ tactic * skip_if_failed(tactic * t) { return or_else(t, mk_skip_tactic()); } - diff --git a/src/tactic/tactical.h b/src/tactic/tactical.h index 169566f39..9ec2f901f 100644 --- a/src/tactic/tactical.h +++ b/src/tactic/tactical.h @@ -47,7 +47,7 @@ tactic * or_else(tactic * t1, tactic * t2, tactic * t3, tactic * t4, tactic * t5 tactic * repeat(tactic * t, unsigned max = UINT_MAX); /** - \brief Fails if \c t produeces more than \c threshold subgoals. + \brief Fails if \c t produces more than \c threshold subgoals. Otherwise, it behaves like \c t. */ tactic * fail_if_branching(tactic * t, unsigned threshold = 1); diff --git a/src/tactic/ufbv/macro_finder_tactic.cpp b/src/tactic/ufbv/macro_finder_tactic.cpp index 3a482f37c..3c981302e 100644 --- a/src/tactic/ufbv/macro_finder_tactic.cpp +++ b/src/tactic/ufbv/macro_finder_tactic.cpp @@ -19,7 +19,7 @@ Notes: #include "tactic/tactical.h" #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" -#include "tactic/extension_model_converter.h" +#include "tactic/generic_model_converter.h" #include "tactic/ufbv/macro_finder_tactic.h" class macro_finder_tactic : public tactic { @@ -38,12 +38,8 @@ class macro_finder_tactic : public tactic { void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("macro-finder", *g); bool produce_proofs = g->proofs_enabled(); @@ -66,18 +62,17 @@ class macro_finder_tactic : public tactic { g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) g->assert_expr(new_forms.get(i), - produce_proofs ? new_proofs.get(i) : 0, - unsat_core_enabled ? new_deps.get(i) : 0); + produce_proofs ? new_proofs.get(i) : nullptr, + unsat_core_enabled ? new_deps.get(i) : nullptr); - extension_model_converter * evmc = alloc(extension_model_converter, mm.get_manager()); + generic_model_converter * evmc = alloc(generic_model_converter, mm.get_manager(), "macro_finder"); unsigned num = mm.get_num_macros(); for (unsigned i = 0; i < num; i++) { expr_ref f_interp(mm.get_manager()); func_decl * f = mm.get_macro_interpretation(i, f_interp); - evmc->insert(f, f_interp); + evmc->add(f, f_interp); } - mc = evmc; - + g->add(evmc); g->inc_depth(); result.push_back(g.get()); TRACE("macro-finder", g->display(tout);); @@ -97,35 +92,32 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(macro_finder_tactic, m, m_params); } - virtual ~macro_finder_tactic() { + ~macro_finder_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_produce_models(r); insert_produce_proofs(r); r.insert("elim_and", CPK_BOOL, "(default: false) eliminate conjunctions during (internal) calls to the simplifier."); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); diff --git a/src/tactic/ufbv/quasi_macros_tactic.cpp b/src/tactic/ufbv/quasi_macros_tactic.cpp index 925b5a5e3..9b39308f3 100644 --- a/src/tactic/ufbv/quasi_macros_tactic.cpp +++ b/src/tactic/ufbv/quasi_macros_tactic.cpp @@ -17,9 +17,9 @@ Notes: --*/ #include "tactic/tactical.h" +#include "tactic/generic_model_converter.h" #include "ast/macros/macro_manager.h" #include "ast/macros/macro_finder.h" -#include "tactic/extension_model_converter.h" #include "ast/macros/quasi_macros.h" #include "tactic/ufbv/quasi_macros_tactic.h" @@ -36,12 +36,8 @@ class quasi_macros_tactic : public tactic { void operator()(goal_ref const & g, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { + goal_ref_buffer & result) { SASSERT(g->is_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("quasi-macros", *g); bool produce_proofs = g->proofs_enabled(); @@ -78,18 +74,17 @@ class quasi_macros_tactic : public tactic { g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) g->assert_expr(forms.get(i), - produce_proofs ? proofs.get(i) : 0, - produce_unsat_cores ? deps.get(i) : 0); + produce_proofs ? proofs.get(i) : nullptr, + produce_unsat_cores ? deps.get(i) : nullptr); - extension_model_converter * evmc = alloc(extension_model_converter, mm.get_manager()); + generic_model_converter * evmc = alloc(generic_model_converter, mm.get_manager(), "quasi_macros"); unsigned num = mm.get_num_macros(); for (unsigned i = 0; i < num; i++) { expr_ref f_interp(mm.get_manager()); func_decl * f = mm.get_macro_interpretation(i, f_interp); - evmc->insert(f, f_interp); + evmc->add(f, f_interp); } - mc = evmc; - + g->add(evmc); g->inc_depth(); result.push_back(g.get()); TRACE("quasi-macros", g->display(tout);); @@ -109,34 +104,31 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(quasi_macros_tactic, m, m_params); } - virtual ~quasi_macros_tactic() { + ~quasi_macros_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_produce_models(r); insert_produce_proofs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, + goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); diff --git a/src/tactic/ufbv/ufbv_rewriter.cpp b/src/tactic/ufbv/ufbv_rewriter.cpp index 446d1a49c..875ef2edb 100644 --- a/src/tactic/ufbv/ufbv_rewriter.cpp +++ b/src/tactic/ufbv/ufbv_rewriter.cpp @@ -196,14 +196,14 @@ int ufbv_rewriter::is_smaller(expr * e1, expr * e2) const { class max_var_id_proc { unsigned m_max_var_id; public: - max_var_id_proc(void):m_max_var_id(0) {} + max_var_id_proc():m_max_var_id(0) {} void operator()(var * n) { if(n->get_idx() > m_max_var_id) m_max_var_id = n->get_idx(); } void operator()(quantifier * n) {} void operator()(app * n) {} - unsigned get_max(void) { return m_max_var_id; } + unsigned get_max() { return m_max_var_id; } }; unsigned ufbv_rewriter::max_var_id(expr * e) @@ -253,7 +253,7 @@ void ufbv_rewriter::remove_fwd_idx(func_decl * f, quantifier * demodulator) { } } -bool ufbv_rewriter::check_fwd_idx_consistency(void) { +bool ufbv_rewriter::check_fwd_idx_consistency() { for (fwd_idx_map::iterator it = m_fwd_idx.begin(); it != m_fwd_idx.end() ; it++ ) { quantifier_set * set = it->m_value; SASSERT(set); @@ -322,8 +322,27 @@ bool ufbv_rewriter::rewrite_visit_children(app * a) { while (j > 0) { expr * e = a->get_arg(--j); if (!m_rewrite_cache.contains(e) || !m_rewrite_cache.get(e).second) { - m_rewrite_todo.push_back(e); - res = false; + bool recursive = false; + unsigned sz = m_rewrite_todo.size(); + expr * v = e; + if (m_rewrite_cache.contains(e)) { + expr_bool_pair const & ebp = m_rewrite_cache.get(e); + if (ebp.second) + v = ebp.first; + } + for (unsigned i = sz; i > 0; i--) { + if (m_rewrite_todo[i - 1] == v) { + recursive = true; + TRACE("demodulator", tout << "Detected demodulator cycle: " << + mk_pp(a, m_manager) << " --> " << mk_pp(v, m_manager) << std::endl;); + m_rewrite_cache.insert(e, expr_bool_pair(v, true)); + break; + } + } + if (!recursive) { + m_rewrite_todo.push_back(e); + res = false; + } } } return res; @@ -347,7 +366,8 @@ expr * ufbv_rewriter::rewrite(expr * n) { while (!m_rewrite_todo.empty()) { TRACE("demodulator_stack", tout << "STACK: " << std::endl; for ( unsigned i = 0; iis_well_sorted()); - mc = 0; pc = 0; core = 0; tactic_report report("ufbv-rewriter", *g); fail_if_unsat_core_generation("ufbv-rewriter", g); @@ -58,9 +54,10 @@ class ufbv_rewriter_tactic : public tactic { g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) - g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : 0, 0); + g->assert_expr(new_forms.get(i), produce_proofs ? new_proofs.get(i) : nullptr, nullptr); - mc = 0; // CMW: Remark: The demodulator could potentially remove all references to a variable. + // CMW: Remark: The demodulator could potentially + // remove all references to a variable. g->inc_depth(); result.push_back(g.get()); @@ -81,34 +78,30 @@ public: m_imp = alloc(imp, m, p); } - virtual tactic * translate(ast_manager & m) { + tactic * translate(ast_manager & m) override { return alloc(ufbv_rewriter_tactic, m, m_params); } - virtual ~ufbv_rewriter_tactic() { + ~ufbv_rewriter_tactic() override { dealloc(m_imp); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params = p; m_imp->updt_params(p); } - virtual void collect_param_descrs(param_descrs & r) { + void collect_param_descrs(param_descrs & r) override { insert_max_memory(r); insert_produce_models(r); insert_produce_proofs(r); } - virtual void operator()(goal_ref const & in, - goal_ref_buffer & result, - model_converter_ref & mc, - proof_converter_ref & pc, - expr_dependency_ref & core) { - (*m_imp)(in, result, mc, pc, core); + void operator()(goal_ref const & in, goal_ref_buffer & result) override { + (*m_imp)(in, result); } - virtual void cleanup() { + void cleanup() override { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); std::swap(d, m_imp); diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index fbcaec5ef..7367a52ff 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(test-z3 arith_rewriter.cpp arith_simplifier_plugin.cpp ast.cpp + bdd.cpp bit_blaster.cpp bits.cpp bit_vector.cpp @@ -23,6 +24,7 @@ add_executable(test-z3 chashtable.cpp check_assumptions.cpp cnf_backbones.cpp + cube_clause.cpp datalog_parser.cpp ddnf.cpp diff_logic.cpp @@ -79,7 +81,6 @@ add_executable(test-z3 optional.cpp parray.cpp pb2bv.cpp - pdr.cpp permutation.cpp polynomial.cpp polynorm.cpp @@ -92,6 +93,8 @@ add_executable(test-z3 rational.cpp rcf.cpp region.cpp + sat_local_search.cpp + sat_lookahead.cpp sat_user_scope.cpp simple_parser.cpp simplex.cpp @@ -99,6 +102,7 @@ add_executable(test-z3 small_object_allocator.cpp smt2print_parse.cpp smt_context.cpp + solver_pool.cpp sorting_network.cpp stack.cpp string_buffer.cpp @@ -116,7 +120,6 @@ add_executable(test-z3 upolynomial.cpp var_subst.cpp vector.cpp - lp.cpp ${z3_test_extra_object_files} ) z3_add_install_tactic_rule(${z3_test_deps}) diff --git a/src/test/algebraic.cpp b/src/test/algebraic.cpp index b893b5ee2..38948c0d7 100644 --- a/src/test/algebraic.cpp +++ b/src/test/algebraic.cpp @@ -278,7 +278,7 @@ static void tst_select_small(mpbq_manager & m, scoped_mpbq const & l, scoped_mpb std::cout << "choice: " << r << " as decimal: "; m.display_decimal(std::cout, r); std::cout << std::endl; } -static void tst_select_small(mpbq_manager & m, int64 n1, unsigned k1, int64 n2, unsigned k2, bool expected) { +static void tst_select_small(mpbq_manager & m, int64_t n1, unsigned k1, int64_t n2, unsigned k2, bool expected) { scoped_mpbq l(m); scoped_mpbq u(m); m.set(l, n1, k1); diff --git a/src/test/arith_rewriter.cpp b/src/test/arith_rewriter.cpp index a279d58b2..d0a110c4f 100644 --- a/src/test/arith_rewriter.cpp +++ b/src/test/arith_rewriter.cpp @@ -10,7 +10,6 @@ Copyright (c) 2015 Microsoft Corporation #include "ast/reg_decl_plugins.h" #include "ast/rewriter/th_rewriter.h" #include "model/model.h" -#include "muz/pdr/pdr_util.h" #include "parsers/smt2/smt2parser.h" @@ -53,13 +52,9 @@ void tst_arith_rewriter() { expr_ref fml = parse_fml(m, example1); rw(fml); std::cout << mk_pp(fml, m) << "\n"; - pdr::normalize_arithmetic(fml); - std::cout << mk_pp(fml, m) << "\n"; fml = parse_fml(m, example2); rw(fml); std::cout << mk_pp(fml, m) << "\n"; - pdr::normalize_arithmetic(fml); - std::cout << mk_pp(fml, m) << "\n"; } diff --git a/src/test/bdd.cpp b/src/test/bdd.cpp new file mode 100644 index 000000000..ea5a0bc34 --- /dev/null +++ b/src/test/bdd.cpp @@ -0,0 +1,83 @@ +#include "sat/sat_bdd.h" + +namespace sat { + static void test1() { + bdd_manager m(20); + bdd v0 = m.mk_var(0); + bdd v1 = m.mk_var(1); + bdd v2 = m.mk_var(2); + bdd c1 = v0 && v1 && v2; + bdd c2 = v2 && v0 && v1; + std::cout << c1 << "\n"; + SASSERT(c1 == c2); + std::cout << "cnf size: " << c1.cnf_size() << "\n"; + + c1 = v0 || v1 || v2; + c2 = v2 || v1 || v0; + std::cout << c1 << "\n"; + SASSERT(c1 == c2); + std::cout << "cnf size: " << c1.cnf_size() << "\n"; + } + + static void test2() { + bdd_manager m(20); + bdd v0 = m.mk_var(0); + bdd v1 = m.mk_var(1); + bdd v2 = m.mk_var(2); + SASSERT(m.mk_ite(v0,v0,v1) == (v0 || v1)); + SASSERT(m.mk_ite(v0,v1,v1) == v1); + SASSERT(m.mk_ite(v1,v0,v1) == (v0 && v1)); + SASSERT(m.mk_ite(v1,v0,v0) == v0); + SASSERT(m.mk_ite(v0,!v0,v1) == (!v0 && v1)); + SASSERT(m.mk_ite(v0,v1,!v0) == (!v0 || v1)); + SASSERT(!(v0 && v1) == (!v0 || !v1)); + SASSERT(!(v0 || v1) == (!v0 && !v1)); + } + + static void test3() { + bdd_manager m(20); + bdd v0 = m.mk_var(0); + bdd v1 = m.mk_var(1); + bdd v2 = m.mk_var(2); + bdd c1 = (v0 && v1) || v2; + bdd c2 = m.mk_exists(0, c1); + std::cout << c1 << "\n"; + std::cout << c2 << "\n"; + SASSERT(c2 == (v1 || v2)); + c2 = m.mk_exists(1, c1); + SASSERT(c2 == (v0 || v2)); + c2 = m.mk_exists(2, c1); + SASSERT(c2.is_true()); + SASSERT(m.mk_exists(3, c1) == c1); + c1 = (v0 && v1) || (v1 && v2) || (!v0 && !v2); + c2 = m.mk_exists(0, c1); + SASSERT(c2 == (v1 || (v1 && v2) || !v2)); + c2 = m.mk_exists(1, c1); + SASSERT(c2 == (v0 || v2 || (!v0 && !v2))); + c2 = m.mk_exists(2, c1); + SASSERT(c2 == ((v0 && v1) || v1 || !v0)); + } + + void test4() { + bdd_manager m(3); + bdd v0 = m.mk_var(0); + bdd v1 = m.mk_var(1); + bdd v2 = m.mk_var(2); + bdd c1 = (v0 && v2) || v1; + std::cout << "before reorder:\n"; + std::cout << c1 << "\n"; + std::cout << c1.bdd_size() << "\n"; + m.gc(); + m.try_reorder(); + std::cout << "after reorder:\n"; + std::cout << c1 << "\n"; + std::cout << c1.bdd_size() << "\n"; + } +} + +void tst_bdd() { + sat::test1(); + sat::test2(); + sat::test3(); + sat::test4(); +} diff --git a/src/test/check_assumptions.cpp b/src/test/check_assumptions.cpp index 020b3e277..d1d81245e 100644 --- a/src/test/check_assumptions.cpp +++ b/src/test/check_assumptions.cpp @@ -20,13 +20,13 @@ void tst_check_assumptions() reg_decl_plugins(mgr); sort_ref b(mgr.mk_bool_sort(), mgr); - func_decl_ref pPred(mgr.mk_func_decl(symbol("p"), 0, static_cast(0), b), mgr); - func_decl_ref qPred(mgr.mk_func_decl(symbol("q"), 0, static_cast(0), b), mgr); - func_decl_ref rPred(mgr.mk_func_decl(symbol("r"), 0, static_cast(0), b), mgr); + func_decl_ref pPred(mgr.mk_func_decl(symbol("p"), 0, static_cast(nullptr), b), mgr); + func_decl_ref qPred(mgr.mk_func_decl(symbol("q"), 0, static_cast(nullptr), b), mgr); + func_decl_ref rPred(mgr.mk_func_decl(symbol("r"), 0, static_cast(nullptr), b), mgr); - app_ref p(mgr.mk_app(pPred,0,static_cast(0)), mgr); - app_ref q(mgr.mk_app(qPred,0,static_cast(0)), mgr); - app_ref r(mgr.mk_app(rPred,0,static_cast(0)), mgr); + app_ref p(mgr.mk_app(pPred,0,static_cast(nullptr)), mgr); + app_ref q(mgr.mk_app(qPred,0,static_cast(nullptr)), mgr); + app_ref r(mgr.mk_app(rPred,0,static_cast(nullptr)), mgr); app_ref pOqOr(mgr.mk_or(p,q,r), mgr); app_ref np(mgr.mk_not(p), mgr); diff --git a/src/test/cnf_backbones.cpp b/src/test/cnf_backbones.cpp index 3e9a106b5..50584c90c 100644 --- a/src/test/cnf_backbones.cpp +++ b/src/test/cnf_backbones.cpp @@ -11,7 +11,7 @@ Copyright (c) 2017 Microsoft Corporation #include "sat/sat_solver.h" #include "util/gparams.h" -static sat::solver * g_solver = 0; +static sat::solver * g_solver = nullptr; static clock_t g_start_time; static void display_statistics() { @@ -228,8 +228,8 @@ static void cnf_backbones(bool use_chunk, char const* file_name) { params_ref p = gparams::get_module("sat"); p.set_bool("produce_models", true); reslimit limit; - sat::solver solver(p, limit, 0); - sat::solver solver2(p, limit, 0); + sat::solver solver(p, limit); + sat::solver solver2(p, limit); g_solver = &solver; if (file_name) { diff --git a/src/test/cube_clause.cpp b/src/test/cube_clause.cpp new file mode 100644 index 000000000..0c783277b --- /dev/null +++ b/src/test/cube_clause.cpp @@ -0,0 +1,66 @@ +#include "ast/reg_decl_plugins.h" +#include "solver/solver_pool.h" +#include "smt/smt_solver.h" + + +void tst_cube_clause() { + ast_manager m; + reg_decl_plugins(m); + params_ref p; + lbool r; + ref solver = mk_smt_solver(m, p, symbol::null); + + expr_ref a(m.mk_const(symbol("a"), m.mk_bool_sort()), m); + expr_ref b(m.mk_const(symbol("b"), m.mk_bool_sort()), m); + expr_ref c(m.mk_const(symbol("c"), m.mk_bool_sort()), m); + expr_ref d(m.mk_const(symbol("d"), m.mk_bool_sort()), m); + expr_ref e(m.mk_const(symbol("e"), m.mk_bool_sort()), m); + expr_ref f(m.mk_const(symbol("f"), m.mk_bool_sort()), m); + expr_ref g(m.mk_const(symbol("g"), m.mk_bool_sort()), m); + expr_ref fml(m); + fml = m.mk_not(m.mk_and(a, b)); + solver->assert_expr(fml); + fml = m.mk_not(m.mk_and(c, d)); + solver->assert_expr(fml); + fml = m.mk_not(m.mk_and(e, f)); + solver->assert_expr(fml); + expr_ref_vector cube(m), clause(m), core(m); + r = solver->check_sat(cube); + std::cout << r << "\n"; + cube.push_back(a); + r = solver->check_sat(cube); + std::cout << r << "\n"; + cube.push_back(c); + cube.push_back(e); + r = solver->check_sat(cube); + std::cout << r << "\n"; + clause.push_back(b); + vector clauses; + clauses.push_back(clause); + r = solver->check_sat_cc(cube, clauses); + std::cout << r << "\n"; + core.reset(); + solver->get_unsat_core(core); + std::cout << core << "\n"; + clause.push_back(d); + clauses.reset(); + clauses.push_back(clause); + r = solver->check_sat_cc(cube, clauses); + std::cout << r << "\n"; + core.reset(); + solver->get_unsat_core(core); + std::cout << core << "\n"; + clause.push_back(f); + clauses.reset(); + clauses.push_back(clause); + r = solver->check_sat_cc(cube, clauses); + std::cout << r << "\n"; + core.reset(); + solver->get_unsat_core(core); + std::cout << core << "\n"; + clause.push_back(g); + clauses.reset(); + clauses.push_back(clause); + r = solver->check_sat_cc(cube, clauses); + std::cout << r << "\n"; +} diff --git a/src/test/ddnf.cpp b/src/test/ddnf.cpp index c9eb6aa08..003e5bdb8 100644 --- a/src/test/ddnf.cpp +++ b/src/test/ddnf.cpp @@ -26,7 +26,7 @@ void read_nums(std::istream& is, unsigned & x, unsigned& y) { std::getline(is, line); } -static char const* g_file = 0; +static char const* g_file = nullptr; void create_forwarding( diff --git a/src/test/dl_query.cpp b/src/test/dl_query.cpp index c5765bd6b..3dc99e33c 100644 --- a/src/test/dl_query.cpp +++ b/src/test/dl_query.cpp @@ -95,12 +95,12 @@ void dl_query_test(ast_manager & m, smt_params & fparams, params_ref& params, f_b.reset(); f_q.reset(); for(unsigned col=0; colget_domain(0); - uint64 var_sz; + uint64_t var_sz; TRUSTME( ctx.try_get_sort_constant_count(var_sort, var_sz) ); for(unsigned attempt=0; attemptm_key; sort* ites[3] = { bools[0], s, s }; - add_func_decl(m_manager.mk_func_decl(bfid, OP_ITE, 0, 0, 3, ites)); + add_func_decl(m_manager.mk_func_decl(bfid, OP_ITE, 0, nullptr, 3, ites)); } } diff --git a/src/test/get_consequences.cpp b/src/test/get_consequences.cpp index 6d600f594..767b07b6d 100644 --- a/src/test/get_consequences.cpp +++ b/src/test/get_consequences.cpp @@ -61,12 +61,12 @@ void test2() { datatype_decl_plugin & dt = *(static_cast(m.get_plugin(m.get_family_id("datatype")))); sort_ref_vector new_sorts(m); - constructor_decl* R = mk_constructor_decl(symbol("R"), symbol("is-R"), 0, 0); - constructor_decl* G = mk_constructor_decl(symbol("G"), symbol("is-G"), 0, 0); - constructor_decl* B = mk_constructor_decl(symbol("B"), symbol("is-B"), 0, 0); + constructor_decl* R = mk_constructor_decl(symbol("R"), symbol("is-R"), 0, nullptr); + constructor_decl* G = mk_constructor_decl(symbol("G"), symbol("is-G"), 0, nullptr); + constructor_decl* B = mk_constructor_decl(symbol("B"), symbol("is-B"), 0, nullptr); constructor_decl* constrs[3] = { R, G, B }; datatype_decl * enum_sort = mk_datatype_decl(dtutil, symbol("RGB"), 0, nullptr, 3, constrs); - VERIFY(dt.mk_datatypes(1, &enum_sort, 0, nullptr, new_sorts)); + VERIFY(dt.mk_datatypes(1, &enum_sort, 0, nullptr, new_sorts)); sort* rgb = new_sorts[0].get(); expr_ref x = mk_const(m, "x", rgb), y = mk_const(m, "y", rgb), z = mk_const(m, "z", rgb); @@ -89,7 +89,7 @@ void test2() { fd_solver->push(); fd_solver->assert_expr(m.mk_not(m.mk_eq(x, g))); - VERIFY(l_false == fd_solver->check_sat(0,0)); + VERIFY(l_false == fd_solver->check_sat(0,nullptr)); fd_solver->pop(1); VERIFY(l_true == fd_solver->get_consequences(asms, vars, conseq)); @@ -101,7 +101,7 @@ void test2() { fd_solver->get_model(mr); model_smt2_pp(std::cout << "model:\n", m, *mr.get(), 0); - VERIFY(l_true == fd_solver->check_sat(0,0)); + VERIFY(l_true == fd_solver->check_sat(0,nullptr)); fd_solver->get_model(mr); ENSURE(mr.get()); model_smt2_pp(std::cout, m, *mr.get(), 0); diff --git a/src/test/get_implied_equalities.cpp b/src/test/get_implied_equalities.cpp index 952fb121a..dae182a34 100644 --- a/src/test/get_implied_equalities.cpp +++ b/src/test/get_implied_equalities.cpp @@ -75,9 +75,9 @@ static void tst_get_implied_equalities1() { } static void tst_get_implied_equalities2() { - enable_trace("after_search"); - enable_trace("get_implied_equalities"); - enable_trace("implied_equalities"); + //enable_trace("after_search"); + //enable_trace("get_implied_equalities"); + //enable_trace("implied_equalities"); Z3_config cfg = Z3_mk_config(); Z3_context ctx = Z3_mk_context(cfg); Z3_del_config(cfg); diff --git a/src/test/heap.cpp b/src/test/heap.cpp index 39353af76..2c77b939e 100644 --- a/src/test/heap.cpp +++ b/src/test/heap.cpp @@ -19,20 +19,22 @@ Revision History: #include #include "util/util.h" #include "util/heap.h" -#include "util/hashtable.h" #include "util/trace.h" +#include "util/uint_set.h" +// include "util/hashtable.h" struct lt_proc { bool operator()(int v1, int v2) const { return v1 < v2; } }; +//struct int_hash_proc { unsigned operator()(int v) const { std::cout << "hash " << v << "\n"; VERIFY(v >= 0); return v; }}; +//typedef int_hashtable > int_set; typedef heap int_heap; -struct int_hash_proc { unsigned operator()(int v) const { return v * 17; }}; -typedef int_hashtable > int_set; #define N 10000 static random_gen heap_rand(1); static void tst1() { int_heap h(N); - int_set t; +// int_set t; + uint_set t; for (int i = 0; i < N * 3; i++) { int val = heap_rand() % N; if (!h.contains(val)) { @@ -41,14 +43,15 @@ static void tst1() { t.insert(val); } else { + if (!t.contains(val)) { + for (int v : t) std::cout << v << "\n"; + } ENSURE(t.contains(val)); } } ENSURE(h.check_invariant()); - int_set::iterator it = t.begin(); - int_set::iterator end = t.end(); - for (; it != end; ++it) { - ENSURE(h.contains(*it)); + for (int v : t) { + ENSURE(h.contains(v)); } while (!h.empty()) { int m1 = h.min_value(); @@ -84,6 +87,8 @@ static void dump_heap(const int_heap2 & h, std::ostream & out) { static void tst2() { int_heap2 h(N); for (int i = 0; i < N * 10; i++) { + + if (i % 1 == 0) std::cout << "i: " << i << std::endl; if (i % 1000 == 0) std::cout << "i: " << i << std::endl; int cmd = heap_rand() % 10; if (cmd <= 3) { @@ -134,6 +139,7 @@ void tst_heap() { enable_trace("heap"); unsigned i = 0; while (i < 3) { + IF_VERBOSE(1, verbose_stream() << "test\n";); heap_rand.set_seed(i++); tst1(); init_values(); diff --git a/src/test/hilbert_basis.cpp b/src/test/hilbert_basis.cpp index 7329b1d3d..bf059ee10 100644 --- a/src/test/hilbert_basis.cpp +++ b/src/test/hilbert_basis.cpp @@ -210,7 +210,7 @@ expr_ref hilbert_basis_validate::mk_validate(hilbert_basis& hb) { } -hilbert_basis* g_hb = 0; +hilbert_basis* g_hb = nullptr; static double g_start_time; static void display_statistics(hilbert_basis& hb) { diff --git a/src/test/horn_subsume_model_converter.cpp b/src/test/horn_subsume_model_converter.cpp index a361cfb2a..3e493d408 100644 --- a/src/test/horn_subsume_model_converter.cpp +++ b/src/test/horn_subsume_model_converter.cpp @@ -31,17 +31,17 @@ void tst_horn_subsume_model_converter() { mc->insert(p, m.mk_app(q, a.mk_numeral(rational(1), true), a.mk_numeral(rational(2), true))); model_converter_ref mcr = mc.get(); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); mc->insert(p, m.mk_app(q, a.mk_numeral(rational(3), true), a.mk_numeral(rational(5), true))); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); mc->insert(p, m.mk_app(r, m.mk_var(0,a.mk_int()), m.mk_var(1, a.mk_int()))); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); @@ -52,7 +52,7 @@ void tst_horn_subsume_model_converter() { body1 = m.mk_app(q, m.mk_var(1, a.mk_int()), m.mk_var(2, a.mk_int())); VERIFY(mc->mk_horn(head1, body1, pred, body2)); mc->insert(pred, body2); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); mr = alloc(model, m); @@ -60,7 +60,7 @@ void tst_horn_subsume_model_converter() { body1 = m.mk_app(q, m.mk_var(1, a.mk_int()), m.mk_var(0, a.mk_int())); VERIFY(mc->mk_horn(head1, body1, pred, body2)); mc->insert(pred, body2); - apply(mcr, mr, 0); + apply(mcr, mr); model_smt2_pp(std::cout, m, *mr.get(), 0); diff --git a/src/test/list.cpp b/src/test/list.cpp index 4cfa16801..956477237 100644 --- a/src/test/list.cpp +++ b/src/test/list.cpp @@ -27,10 +27,10 @@ static void tst1() { list * l2 = new (r) list(20, l1); list * l3 = new (r) list(30); list * l4 = new (r) list(40, l3); - ENSURE(append(r, l1, static_cast *>(0)) == l1); - ENSURE(append(r, l2, static_cast *>(0)) == l2); - ENSURE(append(r, static_cast *>(0), l2) == l2); - ENSURE(append(r, static_cast *>(0), static_cast *>(0)) == 0); + ENSURE(append(r, l1, static_cast *>(nullptr)) == l1); + ENSURE(append(r, l2, static_cast *>(nullptr)) == l2); + ENSURE(append(r, static_cast *>(nullptr), l2) == l2); + ENSURE(append(r, static_cast *>(nullptr), static_cast *>(nullptr)) == nullptr); TRACE("list", display(tout, l2->begin(), l2->end()); tout << "\n";); list * l5 = append(r, l4, l2); TRACE("list", display(tout, l5->begin(), l5->end()); tout << "\n";); diff --git a/src/test/main.cpp b/src/test/main.cpp index 98be722e3..d330610d9 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -77,49 +77,50 @@ void display_usage() { void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage, bool& test_all) { int i = 1; while (i < argc) { - char * arg = argv[i], *eq_pos = 0; - - if (arg[0] == '-' || arg[0] == '/') { - char * opt_name = arg + 1; - char * opt_arg = 0; - char * colon = strchr(arg, ':'); - if (colon) { - opt_arg = colon + 1; - *colon = 0; - } - if (strcmp(opt_name, "h") == 0 || + char * arg = argv[i]; + char * eq_pos = 0; + + if (arg[0] == '-' || arg[0] == '/') { + char * opt_name = arg + 1; + char * opt_arg = 0; + char * colon = strchr(arg, ':'); + if (colon) { + opt_arg = colon + 1; + *colon = 0; + } + if (strcmp(opt_name, "h") == 0 || strcmp(opt_name, "?") == 0) { - display_usage(); + display_usage(); do_display_usage = true; return; - } - else if (strcmp(opt_name, "v") == 0) { - if (!opt_arg) - error("option argument (/v:level) is missing."); - long lvl = strtol(opt_arg, 0, 10); - set_verbosity_level(lvl); - } - else if (strcmp(opt_name, "w") == 0) { + } + else if (strcmp(opt_name, "v") == 0) { + if (!opt_arg) + error("option argument (/v:level) is missing."); + long lvl = strtol(opt_arg, 0, 10); + set_verbosity_level(lvl); + } + else if (strcmp(opt_name, "w") == 0) { enable_warning_messages(true); - } - else if (strcmp(opt_name, "a") == 0) { + } + else if (strcmp(opt_name, "a") == 0) { test_all = true; - } + } #ifdef _TRACE - else if (strcmp(opt_name, "tr") == 0) { - if (!opt_arg) - error("option argument (/tr:tag) is missing."); - enable_trace(opt_arg); - } + else if (strcmp(opt_name, "tr") == 0) { + if (!opt_arg) + error("option argument (/tr:tag) is missing."); + enable_trace(opt_arg); + } #endif #ifdef Z3DEBUG - else if (strcmp(opt_name, "dbg") == 0) { - if (!opt_arg) - error("option argument (/dbg:tag) is missing."); - enable_debug(opt_arg); - } + else if (strcmp(opt_name, "dbg") == 0) { + if (!opt_arg) + error("option argument (/dbg:tag) is missing."); + enable_debug(opt_arg); + } #endif - } + } else if (arg[0] != '"' && (eq_pos = strchr(arg, '='))) { char * key = arg; *eq_pos = 0; @@ -171,6 +172,7 @@ int main(int argc, char ** argv) { TST(var_subst); TST(simple_parser); TST(api); + TST(cube_clause); TST(old_interval); TST(get_implied_equalities); TST(arith_simplifier_plugin); @@ -237,14 +239,16 @@ int main(int argc, char ** argv) { TST(theory_pb); TST(simplex); TST(sat_user_scope); - TST(pdr); TST_ARGV(ddnf); TST(ddnf1); TST(model_evaluator); - TST_ARGV(lp); TST(get_consequences); TST(pb2bv); + TST_ARGV(sat_lookahead); + TST_ARGV(sat_local_search); TST_ARGV(cnf_backbones); + TST(bdd); + TST(solver_pool); //TST_ARGV(hs); } diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp index 7b3b95afd..1bae7435f 100644 --- a/src/test/model_based_opt.cpp +++ b/src/test/model_based_opt.cpp @@ -218,6 +218,12 @@ static void test4() { std::cout << "u: " << mbo.get_value(u) << "\n"; } +static void display_project(vector const& defs) { + for (auto const& d : defs) { + std::cout << d << "\n"; + } +} + static void test5() { opt::model_based_opt mbo; unsigned x = mbo.add_var(rational(2)); @@ -231,13 +237,13 @@ static void test5() { add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); unsigned vars[2] = { y, z }; - mbo.project(1, vars); + display_project(mbo.project(1, vars, true)); mbo.display(std::cout); - mbo.project(1, vars); + display_project(mbo.project(1, vars, true)); mbo.display(std::cout); - mbo.project(1, vars+1); + display_project(mbo.project(1, vars+1, true)); mbo.display(std::cout); vector rows; @@ -263,7 +269,7 @@ static void test6() { add_ineq(mbo, y, 1, w, -1, 1, opt::t_le); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } @@ -285,7 +291,7 @@ static void test7() { add_ineq(mbo, y, 1, w, -1, 1, opt::t_lt); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } @@ -306,7 +312,7 @@ static void test8() { add_ineq(mbo, y, 1, v, -1, 1, opt::t_le); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } @@ -327,7 +333,7 @@ static void test9() { add_ineq(mbo, y, 1, v, -1, 1, opt::t_le); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); } @@ -348,17 +354,47 @@ static void test10() { add_ineq(mbo, y, 3, v, -6, 1, opt::t_le); mbo.display(std::cout); - mbo.project(1, &y); + display_project(mbo.project(1, &y, true)); mbo.display(std::cout); - mbo.project(1, &x0); + display_project(mbo.project(1, &x0, true)); mbo.display(std::cout); } +static void test11() { + { + opt::model_based_opt mbo; + unsigned s = mbo.add_var(rational(2), true); + unsigned a = mbo.add_var(rational(1), true); + unsigned t = mbo.add_var(rational(3), true); + + add_ineq(mbo, s, 1, a, -2, 0, opt::t_le); // s - 2a <= 0 + add_ineq(mbo, a, 2, t, -1, 0, opt::t_le); // 2a - t <= 0 + + mbo.display(std::cout); + display_project(mbo.project(1, &a, true)); + mbo.display(std::cout); + } + + { + opt::model_based_opt mbo; + unsigned s = mbo.add_var(rational(3), true); + unsigned a = mbo.add_var(rational(2), true); + unsigned t = mbo.add_var(rational(4), true); + + add_ineq(mbo, s, 1, a, -2, 0, opt::t_le); // s - 2a <= 0 + add_ineq(mbo, a, 2, t, -1, 0, opt::t_le); // 2a - t <= 0 + + mbo.display(std::cout); + display_project(mbo.project(1, &a, true)); + mbo.display(std::cout); + } + +} + // test with mix of upper and lower bounds void tst_model_based_opt() { test10(); - return; check_random_ineqs(); test1(); test2(); @@ -369,4 +405,5 @@ void tst_model_based_opt() { test7(); test8(); test9(); + test11(); } diff --git a/src/test/mpff.cpp b/src/test/mpff.cpp index 73ed114a7..c78489f21 100644 --- a/src/test/mpff.cpp +++ b/src/test/mpff.cpp @@ -35,7 +35,7 @@ static void tst1() { std::cout << i << ": " << a << "\n"; } } - catch (z3_exception & ex) { + catch (const z3_exception & ex) { std::cout << ex.msg() << "\n"; } } @@ -43,8 +43,8 @@ static void tst1() { static void tst2() { mpff_manager m; scoped_mpff a(m), b(m); - m.set(a, static_cast(100)); - m.set(b, static_cast(-100)); + m.set(a, static_cast(100)); + m.set(b, static_cast(-100)); std::cout << "[test2], a: " << a << ", b: " << b << "\n"; } @@ -75,7 +75,7 @@ static void tst4() { static void tst5() { mpff_manager m; scoped_mpff a(m), b(m); - m.set(a, static_cast(1) << 63); + m.set(a, static_cast(1) << 63); m.display_raw(std::cout, a); std::cout << "\n"; ENSURE(m.is_zero(b)); ENSURE(m.lt(b, a)); @@ -117,7 +117,7 @@ static void tst7() { #define MK_BIN_OP(OP) \ -static void tst_ ## OP ## _core(int64 n1, uint64 d1, int64 n2, uint64 d2, unsigned precision = 2, unsigned exp = 0) { \ +static void tst_ ## OP ## _core(int64_t n1, uint64_t d1, int64_t n2, uint64_t d2, unsigned precision = 2, unsigned exp = 0) { \ TRACE("mpff_bug", tout << n1 << "/" << d1 << ", " << n2 << "/" << d2 << "\n";); \ unsynch_mpq_manager qm; \ scoped_mpq qa(qm), qb(qm), qc(qm), qt(qm); \ @@ -207,7 +207,7 @@ static void tst_set64(unsigned N, unsigned prec) { mpff_manager fm(prec); scoped_mpff a(fm); - fm.set(a, static_cast(INT64_MAX)); + fm.set(a, static_cast(INT64_MAX)); ENSURE(fm.is_int64(a)); ENSURE(fm.is_uint64(a)); fm.inc(a); @@ -221,7 +221,7 @@ static void tst_set64(unsigned N, unsigned prec) { ENSURE(fm.is_int64(a)); ENSURE(fm.is_uint64(a)); - fm.set(a, static_cast(INT64_MIN)); + fm.set(a, static_cast(INT64_MIN)); ENSURE(fm.is_int64(a)); ENSURE(!fm.is_uint64(a)); fm.dec(a); @@ -235,7 +235,7 @@ static void tst_set64(unsigned N, unsigned prec) { ENSURE(fm.is_int64(a)); ENSURE(!fm.is_uint64(a)); - fm.set(a, static_cast(UINT64_MAX)); + fm.set(a, static_cast(UINT64_MAX)); ENSURE(fm.is_uint64(a)); ENSURE(!fm.is_int64(a)); fm.inc(a); @@ -250,23 +250,23 @@ static void tst_set64(unsigned N, unsigned prec) { for (unsigned i = 0; i < N; i++) { { - uint64 v = (static_cast(rand()) << 32) + static_cast(rand()); + uint64_t v = (static_cast(rand()) << 32) + static_cast(rand()); fm.set(a, v); ENSURE(fm.is_uint64(a)); - v = (static_cast(rand() % 3) << 32) + static_cast(rand()); + v = (static_cast(rand() % 3) << 32) + static_cast(rand()); fm.set(a, v); ENSURE(fm.is_uint64(a)); } { - int64 v = (static_cast(rand() % INT_MAX) << 32) + static_cast(rand()); + int64_t v = (static_cast(rand() % INT_MAX) << 32) + static_cast(rand()); if (rand()%2 == 0) v = -v; fm.set(a, v); ENSURE(fm.is_int64(a)); - v = (static_cast(rand() % 3) << 32) + static_cast(rand()); + v = (static_cast(rand() % 3) << 32) + static_cast(rand()); if (rand()%2 == 0) v = -v; fm.set(a, v); @@ -336,7 +336,7 @@ static void tst_power(unsigned prec = 2) { m.set(a, UINT_MAX); m.inc(a); ENSURE(m.is_power_of_two(a, k) && k == 32); - ENSURE(m.get_uint64(a) == static_cast(UINT_MAX) + 1); + ENSURE(m.get_uint64(a) == static_cast(UINT_MAX) + 1); m.power(a, 2, a); ENSURE(m.is_power_of_two(a, k) && k == 64); m.power(a, 4, a); @@ -432,7 +432,7 @@ static void tst_limits(unsigned prec) { m.round_to_plus_inf(); bool overflow = false; try { m.inc(a); } - catch (mpff_manager::overflow_exception) { overflow = true; } + catch (const mpff_manager::overflow_exception &) { overflow = true; } VERIFY(overflow); m.set_max(a); m.dec(a); @@ -446,7 +446,7 @@ static void tst_limits(unsigned prec) { ENSURE(m.eq(a, b)); overflow = true; try { m.dec(a); } - catch (mpff_manager::overflow_exception) { overflow = true; } + catch (const mpff_manager::overflow_exception &) { overflow = true; } ENSURE(overflow); m.round_to_plus_inf(); m.set_min(a); @@ -538,7 +538,7 @@ static void tst_add_corner(unsigned prec) { } #endif -static void tst_decimal(int64 n, uint64 d, bool to_plus_inf, unsigned prec, char const * expected, unsigned decimal_places = UINT_MAX) { +static void tst_decimal(int64_t n, uint64_t d, bool to_plus_inf, unsigned prec, char const * expected, unsigned decimal_places = UINT_MAX) { mpff_manager m(prec); scoped_mpff a(m); m.set_rounding(to_plus_inf); @@ -567,7 +567,7 @@ static void tst_decimal() { tst_decimal(-32, 5, true, 2, "-6.39999999999999999965305530480463858111761510372161865234375"); } -static void tst_prev_power_2(int64 n, uint64 d, unsigned expected) { +static void tst_prev_power_2(int64_t n, uint64_t d, unsigned expected) { mpff_manager m; scoped_mpff a(m); m.set(a, n, d); @@ -598,7 +598,7 @@ static void tst_div(unsigned prec) { scoped_mpff a(m), b(m), c(m); m.round_to_plus_inf(); m.set(a, 1); - m.set(b, static_cast(UINT64_MAX)); + m.set(b, static_cast(UINT64_MAX)); m.div(a, b, c); m.display_raw(std::cout, a); std::cout << "\n"; m.display_raw(std::cout, b); std::cout << "\n"; diff --git a/src/test/mpfx.cpp b/src/test/mpfx.cpp index f9dc123f5..f5cf7e2fb 100644 --- a/src/test/mpfx.cpp +++ b/src/test/mpfx.cpp @@ -35,7 +35,7 @@ static void tst1() { m.display_decimal(std::cout, a); std::cout << "\n"; } -static void tst_prev_power_2(int64 n, uint64 d, unsigned expected) { +static void tst_prev_power_2(int64_t n, uint64_t d, unsigned expected) { mpfx_manager m; scoped_mpfx a(m); m.set(a, n, d); diff --git a/src/test/mpq.cpp b/src/test/mpq.cpp index 7b60d9dcb..6294a97f7 100644 --- a/src/test/mpq.cpp +++ b/src/test/mpq.cpp @@ -133,7 +133,7 @@ static void set_str_bug() { ENSURE(a == b); } -static void tst_prev_power_2(int64 n, uint64 d, unsigned expected) { +static void tst_prev_power_2(int64_t n, uint64_t d, unsigned expected) { unsynch_mpq_manager m; scoped_mpq a(m); m.set(a, n, d); diff --git a/src/test/mpz.cpp b/src/test/mpz.cpp index 7926388df..a9f9ca757 100644 --- a/src/test/mpz.cpp +++ b/src/test/mpz.cpp @@ -50,7 +50,7 @@ static void tst1() { static void tst2() { synch_mpz_manager m; mpz v1, v2, v3; - m.set(v1, static_cast(UINT_MAX)); + m.set(v1, static_cast(UINT_MAX)); m.add(v1, m.mk_z(1), v2); m.mul(v2, v2, v3); std::cout << "v2:\n" << m.to_string(v2) << "\n"; @@ -63,7 +63,7 @@ static void tst2() { static void tst2b() { synch_mpz_manager m; mpz v1, v2, v3; - m.set(v1, static_cast(UINT_MAX)); + m.set(v1, static_cast(UINT_MAX)); m.add(v1, m.mk_z(1), v2); m.mul(v2, v2, v3); std::cout << "v2:\n" << m.to_string(v2) << "\n"; @@ -127,8 +127,8 @@ static void bug3() { static void bug4() { synch_mpz_manager m; mpz x, y; - m.set(y, 4294967295ull); - m.set(x, 4026531839ull); + m.set(y, static_cast(4294967295ull)); + m.set(x, static_cast(4026531839ull)); mpz result1; m.bitwise_or(x, y, result1); @@ -282,7 +282,7 @@ void tst_int_min_bug() { mpz big; mpz expected; mpz r; - m.set(big, static_cast(UINT64_MAX)); + m.set(big, static_cast(UINT64_MAX)); m.set(expected, "18446744075857035263"); m.sub(big, intmin, r); std::cout << "r: " << m.to_string(r) << "\nexpected: " << m.to_string(expected) << "\n"; diff --git a/src/test/nlsat.cpp b/src/test/nlsat.cpp index ecf73843f..1d69c8078 100644 --- a/src/test/nlsat.cpp +++ b/src/test/nlsat.cpp @@ -272,7 +272,7 @@ static void tst4() { static void tst5() { params_ref ps; reslimit rlim; - nlsat::solver s(rlim, ps); + nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); @@ -293,7 +293,7 @@ static void tst5() { bool is_even[1] = { false }; nlsat::bool_var b = s.mk_ineq_atom(nlsat::atom::GT, 1, _p, is_even); nlsat::atom * a = s.bool_var2atom(b); - ENSURE(a != 0); + ENSURE(a != nullptr); scoped_anum zero(am); am.set(zero, 0); as.set(0, zero); @@ -330,7 +330,7 @@ static nlsat::literal mk_eq(nlsat::solver& s, nlsat::poly* p) { static void tst6() { params_ref ps; reslimit rlim; - nlsat::solver s(rlim, ps); + nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); @@ -390,7 +390,7 @@ static void tst6() { static void tst7() { params_ref ps; reslimit rlim; - nlsat::solver s(rlim, ps); + nlsat::solver s(rlim, ps, false); nlsat::pmanager & pm = s.pm(); nlsat::var x0, x1, x2, a, b, c, d; a = s.mk_var(false); @@ -443,7 +443,7 @@ static void tst7() { static void tst8() { params_ref ps; reslimit rlim; - nlsat::solver s(rlim, ps); + nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); @@ -492,7 +492,7 @@ static void tst8() { static void tst9() { params_ref ps; reslimit rlim; - nlsat::solver s(rlim, ps); + nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); @@ -624,7 +624,7 @@ static bool satisfies_root(nlsat::solver& s, nlsat::atom::kind k, nlsat::poly* p static void tst10() { params_ref ps; reslimit rlim; - nlsat::solver s(rlim, ps); + nlsat::solver s(rlim, ps, false); anum_manager & am = s.am(); nlsat::pmanager & pm = s.pm(); nlsat::assignment as(am); diff --git a/src/test/old_interval.cpp b/src/test/old_interval.cpp index 4ab8b0dd0..7ef9cdc87 100644 --- a/src/test/old_interval.cpp +++ b/src/test/old_interval.cpp @@ -120,10 +120,10 @@ class interval_tester { interval singleton(int i) { return interval(m, rational(i)); } interval all() { return interval(m); } - interval l(int i, bool o = false, size_t idx = 0) { return interval(m, rational(i), o, true, idx == 0 ? 0 : m.mk_leaf(reinterpret_cast(idx))); } - interval r(int i, bool o = false, size_t idx = 0) { return interval(m, rational(i), o, false, idx == 0 ? 0 : m.mk_leaf(reinterpret_cast(idx))); } + interval l(int i, bool o = false, size_t idx = 0) { return interval(m, rational(i), o, true, idx == 0 ? nullptr : m.mk_leaf(reinterpret_cast(idx))); } + interval r(int i, bool o = false, size_t idx = 0) { return interval(m, rational(i), o, false, idx == 0 ? nullptr : m.mk_leaf(reinterpret_cast(idx))); } interval b(int l, int u, bool lo = false, bool uo = false, size_t idx_l = 0, size_t idx_u = 0) { - return interval(m, rational(l), lo, idx_l == 0 ? 0 : m.mk_leaf(reinterpret_cast(idx_l)), rational(u), uo, idx_u == 0 ? 0 : m.mk_leaf(reinterpret_cast(idx_u))); + return interval(m, rational(l), lo, idx_l == 0 ? nullptr : m.mk_leaf(reinterpret_cast(idx_l)), rational(u), uo, idx_u == 0 ? nullptr : m.mk_leaf(reinterpret_cast(idx_u))); } void bugs() { diff --git a/src/test/pb2bv.cpp b/src/test/pb2bv.cpp index 632b2d642..d58bf61ee 100644 --- a/src/test/pb2bv.cpp +++ b/src/test/pb2bv.cpp @@ -37,7 +37,7 @@ static void test1() { expr_ref fml(m), result(m); proof_ref proof(m); fml = pb.mk_at_least_k(vars.size(), vars.c_ptr(), k); - rw(fml, result, proof); + rw(true, fml, result, proof); std::cout << fml << " |-> " << result << "\n"; } expr_ref_vector lemmas(m); @@ -60,9 +60,10 @@ static void test_semantics(ast_manager& m, expr_ref_vector const& vars, vector(1 << N); ++values) { smt_params fp; smt::kernel solver(m, fp); @@ -86,6 +87,12 @@ static void test_semantics(ast_manager& m, expr_ref_vector const& vars, vectorcheck_sat(0,0); + lbool res = slv->check_sat(0,nullptr); VERIFY(res == l_true); slv->assert_expr(m.is_true(result2) ? m.mk_not(result1) : result1.get()); - res = slv->check_sat(0,0); + res = slv->check_sat(0,nullptr); VERIFY(res == l_false); } } diff --git a/src/test/pdr.cpp b/src/test/pdr.cpp deleted file mode 100644 index 7e2f1a5ad..000000000 --- a/src/test/pdr.cpp +++ /dev/null @@ -1,128 +0,0 @@ - -/*++ -Copyright (c) 2015 Microsoft Corporation - ---*/ - -#include "muz/pdr/pdr_context.h" -#include "ast/reg_decl_plugins.h" - - -using namespace pdr; - -static expr_ref mk_state(expr_ref_vector const& states, random_gen& rand) { - expr_ref result(states.get_manager()); - result = states[rand(states.size())]; - return result; -} - - -struct test_model_search { - struct init_test { - init_test(func_decl_ref& fn) { - ast_manager& m = fn.get_manager(); - reg_decl_plugins(m); - fn = m.mk_const_decl(symbol("f"), m.mk_bool_sort()); - } - }; - - ast_manager m; - smt_params m_smt_params; - fixedpoint_params fp_params; - context ctx; - manager pm; - func_decl_ref fn; - init_test initt; - pred_transformer pt; - random_gen rand; - model_search search; - expr_ref_vector states; - - - test_model_search(): - ctx(m_smt_params, fp_params, m), - pm(m_smt_params, 10, m), - fn(m), - initt(fn), - pt(ctx, pm, fn), - rand(10), - search(true), - states(m) { - } - - void add_tree(model_node* parent, bool force_goal) { - unsigned level = parent->level(); - search.add_leaf(*parent); - expr_ref state(m); - if (level > 0 && (force_goal || parent->is_goal())) { - search.remove_goal(*parent); - - state = mk_state(states, rand); - add_tree(alloc(model_node, parent, state, pt, level-1), false); - - state = mk_state(states, rand); - add_tree(alloc(model_node, parent, state, pt, level-1), false); - parent->check_pre_closed(); - } - } - - bool mutate() { - model_node* leaf = search.next(); - if (!leaf) return false; - unsigned level = leaf->level(); - if (level == 0) { - if (rand(2) == 0) { - leaf->display(std::cout << "backtrack to grandparent\n", 1); - search.backtrack_level(false, *leaf->parent()); - } - else { - leaf->display(std::cout << "backtrack to parent\n", 1); - search.backtrack_level(false, *leaf); - } - } - else { - leaf->display(std::cout << "grow tree\n", 1); - add_tree(leaf, true); - } - return true; - } - - void init() { - std::cout << "pdr state-hash search tree\n"; - - expr_ref state(m); - func_decl_ref fn(m); - for (unsigned i = 0; i < 10; ++i) { - std::ostringstream strm; - strm << "s" << i; - state = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); - fn = to_app(state)->get_decl(); - states.push_back(state); - } - state = states[0].get(); - unsigned level = 4; - for(unsigned n = 0; n < 100; ++n) { - state = mk_state(states, rand); - model_node* root = alloc(model_node, 0, state, pt, level); - search.set_root(root); - add_tree(root, false); - search.display(std::cout); - - while (true) { - search.well_formed(); - if (!mutate()) break; - search.display(std::cout); - } - search.reset(); - //++level; - } - search.reset(); - } - -}; - -void tst_pdr() { - test_model_search test; - - test.init(); -} diff --git a/src/test/polynorm.cpp b/src/test/polynorm.cpp index 22df91b45..ef01bd27d 100644 --- a/src/test/polynorm.cpp +++ b/src/test/polynorm.cpp @@ -172,7 +172,7 @@ static expr_ref mk_mul(arith_util& arith, unsigned num_args, expr* const* args) static void nf(expr_ref& term) { ast_manager& m = term.get_manager(); - expr *e1 = 0, *e2 = 0; + expr *e1 = nullptr, *e2 = nullptr; th_rewriter rw(m); arith_util arith(m); diff --git a/src/test/prime_generator.cpp b/src/test/prime_generator.cpp index 12c38ef78..3820959d9 100644 --- a/src/test/prime_generator.cpp +++ b/src/test/prime_generator.cpp @@ -25,7 +25,7 @@ void tst_prime_generator() { prime_generator gen; for (unsigned i = 0; i < 10000; i++) { - uint64 p = gen(i); + uint64_t p = gen(i); std::cout << p << ", "; if (i % 11 == 0) std::cout << "\n"; std::cout.flush(); @@ -33,8 +33,8 @@ void tst_prime_generator() { continue; m.set(sqrt_p, p); m.root(sqrt_p, 2); - uint64 k = m.get_uint64(sqrt_p); - for (uint64 i = 2; i <= k; i++) { + uint64_t k = m.get_uint64(sqrt_p); + for (uint64_t i = 2; i <= k; i++) { ENSURE(p % i != 0); } } diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index d18e0f717..031912c46 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -88,8 +88,7 @@ static void test(app* var, expr_ref& fml) { std::cout << "projected: " << mk_pp(pr, m) << "\n"; // projection is consistent with model. - expr_ref tmp(m); - VERIFY(md->eval(pr, tmp) && m.is_true(tmp)); + VERIFY(md->is_true(pr)); // projection implies E x. fml { diff --git a/src/test/quant_elim.cpp b/src/test/quant_elim.cpp index 12fca706d..f376e2c1d 100644 --- a/src/test/quant_elim.cpp +++ b/src/test/quant_elim.cpp @@ -13,6 +13,7 @@ Copyright (c) 2015 Microsoft Corporation #include "ast/reg_decl_plugins.h" +#if 0 static void test_qe(ast_manager& m, lbool expected_outcome, expr* fml, char const* option) { // enable_trace("bit2int"); @@ -48,6 +49,7 @@ static void test_qe(ast_manager& m, lbool expected_outcome, expr* fml, char cons //exit(-1); } } +#endif static void test_formula(lbool expected_outcome, char const* fml) { ast_manager m; diff --git a/src/test/rational.cpp b/src/test/rational.cpp index 7b5e474f0..ac477a02f 100644 --- a/src/test/rational.cpp +++ b/src/test/rational.cpp @@ -194,8 +194,8 @@ static void tst2() { ENSURE(uint64_max.is_uint64()); // get_int64, get_uint64 - uint64 u1 = uint64_max.get_uint64(); - uint64 u2 = UINT64_MAX; + uint64_t u1 = uint64_max.get_uint64(); + uint64_t u2 = UINT64_MAX; VERIFY(u1 == u2); std::cout << "int64_max: " << int64_max << ", INT64_MAX: " << INT64_MAX << ", int64_max.get_int64(): " << int64_max.get_int64() << ", int64_max.get_uint64(): " << int64_max.get_uint64() << "\n"; ENSURE(int64_max.get_int64() == INT64_MAX); @@ -387,9 +387,9 @@ static void tst9() { static void tst10(bool use_ints) { if (use_ints) - std::cout << "Testing multiplication performace using small ints\n"; + std::cout << "Testing multiplication performance using small ints\n"; else - std::cout << "Testing multiplication performace using small rationals\n"; + std::cout << "Testing multiplication performance using small rationals\n"; vector vals; vector vals2; vector fvals; diff --git a/src/test/sat_local_search.cpp b/src/test/sat_local_search.cpp new file mode 100644 index 000000000..1116c5420 --- /dev/null +++ b/src/test/sat_local_search.cpp @@ -0,0 +1,132 @@ +#include "sat/sat_local_search.h" +#include "sat/sat_solver.h" +#include "util/cancel_eh.h" +#include "util/scoped_ctrl_c.h" +#include "util/scoped_timer.h" + +static bool build_instance(char const * filename, sat::solver& s, sat::local_search& local_search) +{ + char line[16383]; + // for temperally storage + + std::ifstream infile(filename); + //if (infile == NULL) //linux + if (!infile) { + std::cout << "File not found " << filename << "\n"; + return false; + } + infile.getline(line, 16383); +#ifdef _WINDOWS + int cur_term; + int num_vars = 0, num_constraints = 0; + sscanf_s(line, "%d %d", &num_vars, &num_constraints); + //std::cout << "number of variables: " << num_vars << '\n'; + //std::cout << "number of constraints: " << num_constraints << '\n'; + + + unsigned_vector coefficients; + sat::literal_vector lits; + + // process objective function: + // read coefficents + infile >> cur_term; + while (cur_term != 0) { + coefficients.push_back(cur_term); + infile >> cur_term; + } + + // read variables + infile >> cur_term; + while (cur_term != 0) { + lits.push_back(sat::literal(abs(cur_term), cur_term < 0)); + infile >> cur_term; + } + + if (lits.size() != coefficients.size()) { + std::cout << "Objective function format error. They have different lenghts.\n"; + return false; + } + + for (unsigned i = 0; i < lits.size(); ++i) { + local_search.add_soft(lits[i].var(), coefficients[i]); + } + + // read the constraints, one at a time + int k; + for (int c = 0; c < num_constraints; ++c) { + lits.reset(); + infile >> cur_term; + while (cur_term != 0) { + lits.push_back(sat::literal(abs(cur_term), cur_term > 0)); + infile >> cur_term; + } + infile >> k; + //local_search.add_cardinality(lits.size(), lits.c_ptr(), static_cast(lits.size() - k)); + local_search.add_cardinality(lits.size(), lits.c_ptr(), static_cast(k)); + } + + infile.close(); + return true; +#else + return false; +#endif +} + +void tst_sat_local_search(char ** argv, int argc, int& i) { + if (argc < i + 2) { + std::cout << "require dimacs file name\n"; + return; + } + reslimit limit; + params_ref params; + sat::solver solver(params, limit); + sat::local_search local_search; + + local_search.import(solver, true); + char const* file_name = argv[i + 1]; + ++i; + + int cutoff_time = 1; + + int v; + while (i + 1 < argc) { + std::cout << argv[i + 1] << "\n"; + // set other ad hoc parameters. + if (argv[i + 1][0] == '-' && i + 2 < argc) { + switch (argv[i + 1][1]) { + case 's': // seed + v = atoi(argv[i + 2]); + local_search.config().set_random_seed(v); + break; + case 't': // cutoff_time + v = atoi(argv[i + 2]); + cutoff_time = v; + break; + case 'b': // best_known_value + v = atoi(argv[i + 2]); + local_search.config().set_best_known_value(v); + break; + default: + ++i; + v = -1; + break; + } + } + ++i; + } + + if (!build_instance(file_name, solver, local_search)) { + return; + } + + //std::cout << "local instance built\n"; + + + // set up cancellation/timeout environment. + + cancel_eh eh(local_search.rlimit()); + scoped_ctrl_c ctrlc(eh, false, true); + scoped_timer timer(cutoff_time*1000, &eh); + local_search.check(); + +} diff --git a/src/test/sat_lookahead.cpp b/src/test/sat_lookahead.cpp new file mode 100644 index 000000000..fccbe8eed --- /dev/null +++ b/src/test/sat_lookahead.cpp @@ -0,0 +1,53 @@ +#include "sat/sat_solver.h" +#include "sat/sat_watched.h" +#include "util/statistics.h" +#include "sat/sat_lookahead.h" +#include "sat/dimacs.h" + +static void display_model(sat::model const & m) { + for (unsigned i = 1; i < m.size(); i++) { + switch (m[i]) { + case l_false: std::cout << "-" << i << " "; break; + case l_undef: break; + case l_true: std::cout << i << " "; break; + } + } + std::cout << "\n"; +} + + +void tst_sat_lookahead(char ** argv, int argc, int& i) { + if (argc != i + 2) { + std::cout << "require dimacs file name\n"; + return; + } +// enable_trace("sat"); + reslimit limit; + params_ref params; + sat::solver solver(params, limit); + char const* file_name = argv[i + 1]; + ++i; + + { + std::ifstream in(file_name); + if (in.bad() || in.fail()) { + std::cerr << "(error \"failed to open file '" << file_name << "'\")" << std::endl; + exit(ERR_OPEN_FILE); + } + parse_dimacs(in, solver); + } + + sat::lookahead lh(solver); + + IF_VERBOSE(20, solver.display_status(verbose_stream());); + + lbool is_sat = lh.check(); + std::cout << is_sat << "\n"; + + statistics st; + lh.collect_statistics(st); + st.display(std::cout); + if (is_sat == l_true) { + display_model(lh.get_model()); + } +} diff --git a/src/test/sat_user_scope.cpp b/src/test/sat_user_scope.cpp index 10abb44c3..51af0f7c3 100644 --- a/src/test/sat_user_scope.cpp +++ b/src/test/sat_user_scope.cpp @@ -55,7 +55,7 @@ static void init_vars(sat::solver& s) { static void check_coherence(sat::solver& s1, trail_t& t) { params_ref p; reslimit rlim; - sat::solver s2(p, rlim, 0); + sat::solver s2(p, rlim); init_vars(s2); sat::literal_vector cls; for (unsigned i = 0; i < t.size(); ++i) { @@ -81,7 +81,7 @@ void tst_sat_user_scope() { trail_t trail; params_ref p; reslimit rlim; - sat::solver s(p, rlim, 0); // incremental solver + sat::solver s(p, rlim); // incremental solver init_vars(s); while (true) { for (unsigned i = 0; i < s_num_frames; ++i) { diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index 80a57c7d5..99ffb726c 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -10,46 +10,57 @@ Copyright (c) 2015 Microsoft Corporation #include "api/z3.h" #include -void test_print(Z3_context ctx, Z3_ast a) { +void test_print(Z3_context ctx, Z3_ast_vector av) { Z3_set_ast_print_mode(ctx, Z3_PRINT_SMTLIB2_COMPLIANT); + Z3_ast* args = new Z3_ast[Z3_ast_vector_size(ctx, av)]; + for (unsigned i = 0; i < Z3_ast_vector_size(ctx, av); ++i) { + args[i] = Z3_ast_vector_get(ctx, av, i); + } + 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); + Z3_dec_ref(ctx, a); std::cout << "spec1: benchmark->string\n" << spec1 << "\n"; std::cout << "attempting to parse spec1...\n"; - Z3_ast b = + Z3_ast_vector b = Z3_parse_smtlib2_string(ctx, spec1, 0, + nullptr, + nullptr, 0, - 0, - 0, - 0, - 0); + nullptr, + nullptr); std::cout << "parse successful, converting ast->string\n"; - char const* spec2 = Z3_ast_to_string(ctx, b); + Z3_ast_vector_inc_ref(ctx, b); + char const* spec2 = Z3_ast_vector_to_string(ctx, b); std::cout << "spec2: string->ast->string\n" << spec2 << "\n"; + Z3_ast_vector_dec_ref(ctx, b); } void test_parseprint(char const* spec) { - Z3_context ctx = Z3_mk_context(0); + Z3_context ctx = Z3_mk_context(nullptr); std::cout << "spec:\n" << spec << "\n"; - Z3_ast a = + Z3_ast_vector a = Z3_parse_smtlib2_string(ctx, spec, 0, + nullptr, + nullptr, 0, - 0, - 0, - 0, - 0); + nullptr, + nullptr); std::cout << "done parsing\n"; - + Z3_ast_vector_inc_ref(ctx, a); test_print(ctx, a); std::cout << "done printing\n"; + Z3_ast_vector_dec_ref(ctx, a); Z3_del_context(ctx); } diff --git a/src/test/solver_pool.cpp b/src/test/solver_pool.cpp new file mode 100644 index 000000000..4a2e533bf --- /dev/null +++ b/src/test/solver_pool.cpp @@ -0,0 +1,41 @@ +#include "ast/reg_decl_plugins.h" +#include "solver/solver_pool.h" +#include "smt/smt_solver.h" + +void tst_solver_pool() { + ast_manager m; + reg_decl_plugins(m); + params_ref p; + ref base = mk_smt_solver(m, p, symbol::null); + + expr_ref a(m.mk_const(symbol("a"), m.mk_bool_sort()), m); + expr_ref b(m.mk_const(symbol("b"), m.mk_bool_sort()), m); + expr_ref c(m.mk_const(symbol("c"), m.mk_bool_sort()), m); + expr_ref d(m.mk_const(symbol("d"), m.mk_bool_sort()), m); + expr_ref fml(m); + fml = m.mk_or(a, b); + base->assert_expr(fml); + + solver_pool pool(base.get(), 3); + + // base solver is cloned so any assertions + // added to base after mk_solver() are ignored. + ref s1 = pool.mk_solver(); + ref s2 = pool.mk_solver(); + ref s3 = pool.mk_solver(); + ref s4 = pool.mk_solver(); + + fml = m.mk_and(b, c); + s1->assert_expr(fml); + fml = m.mk_and(a, b); + s2->assert_expr(fml); + expr_ref_vector asms(m); + asms.push_back(m.mk_not(a)); + std::cout << base->check_sat(asms) << "\n"; + std::cout << s1->check_sat(asms) << "\n"; + std::cout << s2->check_sat(asms) << "\n"; + std::cout << s3->check_sat(asms) << "\n"; + std::cout << *s1; + std::cout << *s2; + std::cout << *base; +} diff --git a/src/test/sorting_network.cpp b/src/test/sorting_network.cpp index 1062057f6..f5c415c04 100644 --- a/src/test/sorting_network.cpp +++ b/src/test/sorting_network.cpp @@ -28,7 +28,7 @@ struct ast_ext { return m.mk_implies(a, b); } UNREACHABLE(); - return 0; + return nullptr; } T mk_default() { return m.mk_false(); @@ -152,36 +152,81 @@ struct ast_ext2 { expr_ref_vector m_clauses; expr_ref_vector m_trail; ast_ext2(ast_manager& m):m(m), m_clauses(m), m_trail(m) {} - typedef expr* literal; - typedef ptr_vector literal_vector; + typedef expr* pliteral; + typedef ptr_vector pliteral_vector; expr* trail(expr* e) { m_trail.push_back(e); return e; } - literal mk_false() { return m.mk_false(); } - literal mk_true() { return m.mk_true(); } - literal mk_max(literal a, literal b) { + pliteral mk_false() { return m.mk_false(); } + pliteral mk_true() { return m.mk_true(); } + pliteral mk_max(pliteral a, pliteral b) { return trail(m.mk_or(a, b)); } - literal mk_min(literal a, literal b) { return trail(m.mk_and(a, b)); } - literal mk_not(literal a) { if (m.is_not(a,a)) return a; + pliteral mk_min(pliteral a, pliteral b) { return trail(m.mk_and(a, b)); } + pliteral mk_not(pliteral a) { if (m.is_not(a,a)) return a; return trail(m.mk_not(a)); } - std::ostream& pp(std::ostream& out, literal lit) { + std::ostream& pp(std::ostream& out, pliteral lit) { return out << mk_pp(lit, m); } - literal fresh() { - return trail(m.mk_fresh_const("x", m.mk_bool_sort())); + pliteral fresh(char const* n) { + return trail(m.mk_fresh_const(n, m.mk_bool_sort())); } - void mk_clause(unsigned n, literal const* lits) { + void mk_clause(unsigned n, pliteral const* lits) { m_clauses.push_back(mk_or(m, n, lits)); } }; +static void test_eq1(unsigned n, sorting_network_encoding enc) { + //std::cout << "test eq1 " << n << " for encoding: " << enc << "\n"; + ast_manager m; + reg_decl_plugins(m); + ast_ext2 ext(m); + expr_ref_vector in(m), out(m); + for (unsigned i = 0; i < n; ++i) { + in.push_back(m.mk_fresh_const("a",m.mk_bool_sort())); + } + smt_params fp; + smt::kernel solver(m, fp); + psort_nw sn(ext); + sn.cfg().m_encoding = enc; -static void test_sorting_eq(unsigned n, unsigned k) { + expr_ref result1(m), result2(m); + + // equality: + solver.push(); + result1 = sn.eq(true, 1, in.size(), in.c_ptr()); + for (expr* cl : ext.m_clauses) { + solver.assert_expr(cl); + } + expr_ref_vector ors(m); + for (unsigned i = 0; i < n; ++i) { + expr_ref_vector ands(m); + for (unsigned j = 0; j < n; ++j) { + ands.push_back(j == i ? in[j].get() : m.mk_not(in[j].get())); + } + ors.push_back(mk_and(ands)); + } + result2 = mk_or(ors); + solver.assert_expr(m.mk_not(m.mk_eq(result1, result2))); + //std::cout << ext.m_clauses << "\n"; + //std::cout << result1 << "\n"; + //std::cout << result2 << "\n"; + lbool res = solver.check(); + if (res == l_true) { + model_ref model; + solver.get_model(model); + model_smt2_pp(std::cout, m, *model, 0); + TRACE("pb", model_smt2_pp(tout, m, *model, 0);); + } + ENSURE(l_false == res); + ext.m_clauses.reset(); +} + +static void test_sorting_eq(unsigned n, unsigned k, sorting_network_encoding enc) { ENSURE(k < n); ast_manager m; reg_decl_plugins(m); @@ -193,15 +238,17 @@ static void test_sorting_eq(unsigned n, unsigned k) { smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result(m); // equality: - std::cout << "eq " << k << "\n"; + std::cout << "eq " << k << " out of " << n << " for encoding " << enc << "\n"; solver.push(); - result = sn.eq(true, k, in.size(), in.c_ptr()); + result = sn.eq(false, k, in.size(), in.c_ptr()); + std::cout << result << "\n" << ext.m_clauses << "\n"; solver.assert_expr(result); - for (unsigned i = 0; i < ext.m_clauses.size(); ++i) { - solver.assert_expr(ext.m_clauses[i].get()); + for (expr* cl : ext.m_clauses) { + solver.assert_expr(cl); } lbool res = solver.check(); ENSURE(res == l_true); @@ -230,7 +277,7 @@ static void test_sorting_eq(unsigned n, unsigned k) { ext.m_clauses.reset(); } -static void test_sorting_le(unsigned n, unsigned k) { +static void test_sorting_le(unsigned n, unsigned k, sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); @@ -241,6 +288,7 @@ static void test_sorting_le(unsigned n, unsigned k) { smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result(m); // B <= k std::cout << "le " << k << "\n"; @@ -277,7 +325,7 @@ static void test_sorting_le(unsigned n, unsigned k) { } -void test_sorting_ge(unsigned n, unsigned k) { +void test_sorting_ge(unsigned n, unsigned k, sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); ast_ext2 ext(m); @@ -288,6 +336,7 @@ void test_sorting_ge(unsigned n, unsigned k) { smt_params fp; smt::kernel solver(m, fp); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result(m); // k <= B std::cout << "ge " << k << "\n"; @@ -323,11 +372,11 @@ void test_sorting_ge(unsigned n, unsigned k) { solver.pop(1); } -void test_sorting5(unsigned n, unsigned k) { +void test_sorting5(unsigned n, unsigned k, sorting_network_encoding enc) { std::cout << "n: " << n << " k: " << k << "\n"; - test_sorting_le(n, k); - test_sorting_eq(n, k); - test_sorting_ge(n, k); + test_sorting_le(n, k, enc); + test_sorting_eq(n, k, enc); + test_sorting_ge(n, k, enc); } expr_ref naive_at_most1(expr_ref_vector const& xs) { @@ -341,7 +390,7 @@ expr_ref naive_at_most1(expr_ref_vector const& xs) { return mk_and(clauses); } -void test_at_most_1(unsigned n, bool full) { +void test_at_most_1(unsigned n, bool full, sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); expr_ref_vector in(m), out(m); @@ -351,11 +400,15 @@ void test_at_most_1(unsigned n, bool full) { ast_ext2 ext(m); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result1(m), result2(m); result1 = sn.le(full, 1, in.size(), in.c_ptr()); result2 = naive_at_most1(in); + std::cout << "clauses: " << ext.m_clauses << "\n-----\n"; + std::cout << "encoded: " << result1 << "\n"; + std::cout << "naive: " << result2 << "\n"; smt_params fp; smt::kernel solver(m, fp); @@ -367,15 +420,27 @@ void test_at_most_1(unsigned n, bool full) { solver.assert_expr(m.mk_not(m.mk_eq(result1, result2))); std::cout << result1 << "\n"; + lbool res = solver.check(); + if (res == l_true) { + model_ref model; + solver.get_model(model); + model_smt2_pp(std::cout, m, *model, 0); + } - VERIFY(l_false == solver.check()); + VERIFY(l_false == res); solver.pop(1); } if (n >= 9) return; + if (n <= 1) return; for (unsigned i = 0; i < static_cast(1 << n); ++i) { - std::cout << "checking: " << n << ": " << i << "\n"; + std::cout << "checking n: " << n << " bits: "; + for (unsigned j = 0; j < n; ++j) { + bool is_true = (i & (1 << j)) != 0; + std::cout << (is_true?"1":"0"); + } + std::cout << "\n"; solver.push(); unsigned k = 0; for (unsigned j = 0; j < n; ++j) { @@ -402,7 +467,7 @@ void test_at_most_1(unsigned n, bool full) { } -static void test_at_most1() { +static void test_at_most1(sorting_network_encoding enc) { ast_manager m; reg_decl_plugins(m); expr_ref_vector in(m), out(m); @@ -413,33 +478,43 @@ static void test_at_most1() { ast_ext2 ext(m); psort_nw sn(ext); + sn.cfg().m_encoding = enc; expr_ref result(m); result = sn.le(true, 1, in.size(), in.c_ptr()); std::cout << result << "\n"; std::cout << ext.m_clauses << "\n"; } -void tst_sorting_network() { - for (unsigned i = 1; i < 17; ++i) { - test_at_most_1(i, true); - test_at_most_1(i, false); - } - - for (unsigned n = 2; n < 20; ++n) { - std::cout << "verify eq-1 out of " << n << "\n"; - test_sorting_eq(n, 1); - } - - test_at_most1(); - - test_sorting_eq(11,7); +static void test_sorting5(sorting_network_encoding enc) { + test_sorting_eq(11,7, enc); for (unsigned n = 3; n < 20; n += 2) { for (unsigned k = 1; k < n; ++k) { - test_sorting5(n, k); + test_sorting5(n, k, enc); } } +} + +static void tst_sorting_network(sorting_network_encoding enc) { + for (unsigned i = 1; i < 17; ++i) { + test_at_most_1(i, true, enc); + test_at_most_1(i, false, enc); + } + for (unsigned n = 2; n < 20; ++n) { + std::cout << "verify eq-1 out of " << n << "\n"; + test_sorting_eq(n, 1, enc); + test_eq1(n, enc); + } + test_at_most1(enc); + test_sorting5(enc); +} + +void tst_sorting_network() { + tst_sorting_network(sorting_network_encoding::ordered_at_most_1); + tst_sorting_network(sorting_network_encoding::grouped_at_most_1); + tst_sorting_network(sorting_network_encoding::bimander_at_most_1); test_sorting1(); test_sorting2(); test_sorting3(); test_sorting4(); } + diff --git a/src/test/theory_pb.cpp b/src/test/theory_pb.cpp index 8fcbde706..7010b96d0 100644 --- a/src/test/theory_pb.cpp +++ b/src/test/theory_pb.cpp @@ -148,7 +148,7 @@ void tst_theory_pb() { ctx.push(); smt::literal l = smt::theory_pb::assert_ge(ctx, k+1, lits.size(), lits.c_ptr()); if (l != smt::false_literal) { - ctx.assign(l, 0, false); + ctx.assign(l, nullptr, false); TRACE("pb", tout << "assign: " << l << "\n"; ctx.display(tout);); VERIFY(l_false == ctx.check()); @@ -160,7 +160,7 @@ void tst_theory_pb() { ctx.push(); smt::literal l = smt::theory_pb::assert_ge(ctx, k, lits.size(), lits.c_ptr()); ENSURE(l != smt::false_literal); - ctx.assign(l, 0, false); + ctx.assign(l, nullptr, false); TRACE("pb", ctx.display(tout);); VERIFY(l_true == ctx.check()); ctx.pop(1); diff --git a/src/test/udoc_relation.cpp b/src/test/udoc_relation.cpp index 5f1bd7b36..4555d4ef0 100644 --- a/src/test/udoc_relation.cpp +++ b/src/test/udoc_relation.cpp @@ -89,7 +89,7 @@ class udoc_tester { for (unsigned i = 0; i < num_diff; ++i) { t = mk_rand_tbv(dm, result->pos()); if (dm.tbvm().equals(*t, result->pos())) { - return 0; + return nullptr; } if (!result->neg().is_empty() && dm.tbvm().equals(*t, result->neg()[0])) { @@ -278,7 +278,7 @@ public: t1->to_formula(t10); expr_ref delta0(m); delta->to_formula(delta0); - rel_union union_fn = p.mk_union_fn(*t1, *t2, 0); + rel_union union_fn = p.mk_union_fn(*t1, *t2, nullptr); t1->display(std::cout << "t1 before:"); std::cout << "\n"; (*union_fn)(*t1, *t2, delta); @@ -840,7 +840,7 @@ public: void set_random(udoc_relation& r, unsigned num_vals) { unsigned num_bits = r.get_dm().num_tbits(); udoc_relation* full = mk_full(r.get_signature()); - rel_union union_fn = p.mk_union_fn(r, r, 0); + rel_union union_fn = p.mk_union_fn(r, r, nullptr); (*union_fn)(r, *full); doc_manager& dm = r.get_dm(); ENSURE(r.get_udoc().size() == 1); @@ -905,8 +905,8 @@ public: void apply_filter(udoc_relation& t, app* cond) { udoc_relation* full = mk_full(t.get_signature()); - rel_union union_fn = p.mk_union_fn(t, *full, 0); - (*union_fn)(t, *full, 0); + rel_union union_fn = p.mk_union_fn(t, *full, nullptr); + (*union_fn)(t, *full, nullptr); expr_ref fml0(m); t.to_formula(fml0); rel_mut fint = p.mk_filter_interpreted_fn(t, cond); diff --git a/src/test/var_subst.cpp b/src/test/var_subst.cpp index 169b1e131..09535ae8c 100644 --- a/src/test/var_subst.cpp +++ b/src/test/var_subst.cpp @@ -27,7 +27,7 @@ Revision History: namespace find_q { struct proc { quantifier * m_q; - proc():m_q(0) {} + proc():m_q(nullptr) {} void operator()(var * n) {} void operator()(app * n) {} void operator()(quantifier * n) { m_q = n; } diff --git a/src/util/array.h b/src/util/array.h index f0c084b30..cf82e123c 100644 --- a/src/util/array.h +++ b/src/util/array.h @@ -74,7 +74,7 @@ public: typedef T * iterator; typedef const T * const_iterator; - array():m_data(0) {} + array():m_data(nullptr) {} /** \brief Store the array in the given chunk of memory (mem). @@ -123,7 +123,7 @@ public: if (CallDestructors) destroy_elements(); a.deallocate(space(size()), raw_ptr()); - m_data = 0; + m_data = nullptr; } } @@ -148,7 +148,7 @@ public: } unsigned size() const { - if (m_data == 0) { + if (m_data == nullptr) { return 0; } return static_cast(reinterpret_cast(m_data)[SIZE_IDX]); diff --git a/src/util/bit_vector.h b/src/util/bit_vector.h index 2d42e35a2..5f21d2e59 100644 --- a/src/util/bit_vector.h +++ b/src/util/bit_vector.h @@ -62,7 +62,7 @@ public: bit_vector(): m_num_bits(0), m_capacity(0), - m_data(0) { + m_data(nullptr) { } bit_vector(unsigned reserve_num_bits) : @@ -75,7 +75,7 @@ public: bit_vector(bit_vector const & source): m_num_bits(source.m_num_bits), m_capacity(source.m_capacity), - m_data(0) { + m_data(nullptr) { if (source.m_data) { m_data = alloc_svect(unsigned, m_capacity); memcpy(m_data, source.m_data, m_capacity * sizeof(unsigned)); diff --git a/src/util/cancel_eh.h b/src/util/cancel_eh.h index 59f11b3f3..09b996582 100644 --- a/src/util/cancel_eh.h +++ b/src/util/cancel_eh.h @@ -30,8 +30,8 @@ class cancel_eh : public event_handler { T & m_obj; public: cancel_eh(T & o): m_canceled(false), m_obj(o) {} - ~cancel_eh() { if (m_canceled) m_obj.dec_cancel(); } - virtual void operator()(event_handler_caller_t caller_id) { + ~cancel_eh() override { if (m_canceled) m_obj.dec_cancel(); } + void operator()(event_handler_caller_t caller_id) override { if (!m_canceled) { m_caller_id = caller_id; m_canceled = true; diff --git a/src/util/chashtable.h b/src/util/chashtable.h index 69c48207a..9bc5988e5 100644 --- a/src/util/chashtable.h +++ b/src/util/chashtable.h @@ -106,13 +106,13 @@ protected: SASSERT(target_it < target + target_slots); if (target_it->is_free()) { target_it->m_data = list_it->m_data; - target_it->m_next = 0; + target_it->m_next = nullptr; used_slots++; } else { SASSERT((get_hash(target_it->m_data) & target_mask) == idx); if (target_cellar == target_end) - return 0; // the cellar is too small... + return nullptr; // the cellar is too small... SASSERT(target_cellar >= target + target_slots); SASSERT(target_cellar < target_end); *target_cellar = *target_it; @@ -123,7 +123,7 @@ protected: SASSERT(!target_it->is_free()); list_it = list_it->m_next; } - while (list_it != 0); + while (list_it != nullptr); } } #if 0 @@ -164,13 +164,13 @@ protected: cell * next_cell = copy_table(m_table, m_slots, m_capacity, new_table, new_slots, new_capacity, m_used_slots); - if (next_cell != 0) { + if (next_cell != nullptr) { delete_table(); m_table = new_table; m_capacity = new_capacity; m_slots = new_slots; m_next_cell = next_cell; - m_free_cell = 0; + m_free_cell = nullptr; CASSERT("chashtable", check_invariant()); return; } @@ -180,11 +180,11 @@ protected: } bool has_free_cells() const { - return m_free_cell != 0 || m_next_cell < m_table + m_capacity; + return m_free_cell != nullptr || m_next_cell < m_table + m_capacity; } cell * get_free_cell() { - if (m_free_cell != 0) { + if (m_free_cell != nullptr) { cell * c = m_free_cell; m_free_cell = c->m_next; return c; @@ -211,7 +211,7 @@ protected: m_used_slots = 0; m_size = 0; m_next_cell = m_table + slots; - m_free_cell = 0; + m_free_cell = nullptr; } public: @@ -281,7 +281,7 @@ public: m_size++; m_used_slots++; c->m_data = d; - c->m_next = 0; + c->m_next = nullptr; CASSERT("chashtable_bug", check_invariant()); return; } @@ -297,7 +297,7 @@ public: CHS_CODE(m_collisions++;); it = it->m_next; } - while (it != 0); + while (it != nullptr); // d is not in the table. m_size++; cell * new_c = get_free_cell(); @@ -320,7 +320,7 @@ public: m_size++; m_used_slots++; c->m_data = d; - c->m_next = 0; + c->m_next = nullptr; CASSERT("chashtable_bug", check_invariant()); return c->m_data; } @@ -335,7 +335,7 @@ public: CHS_CODE(m_collisions++;); it = it->m_next; } - while (it != 0); + while (it != nullptr); // d is not in the table. m_size++; cell * new_c = get_free_cell(); @@ -358,7 +358,7 @@ public: m_size++; m_used_slots++; c->m_data = d; - c->m_next = 0; + c->m_next = nullptr; CASSERT("chashtable_bug", check_invariant()); return true; } @@ -373,7 +373,7 @@ public: CHS_CODE(m_collisions++;); it = it->m_next; } - while (it != 0); + while (it != nullptr); // d is not in the table. m_size++; cell * new_c = get_free_cell(); @@ -399,7 +399,7 @@ public: CHS_CODE(const_cast(this)->m_collisions++;); c = c->m_next; } - while (c != 0); + while (c != nullptr); return false; } @@ -409,7 +409,7 @@ public: unsigned idx = h & mask; cell * c = m_table + idx; if (c->is_free()) - return 0; + return nullptr; do { if (equals(c->m_data, d)) { return &(c->m_data); @@ -417,8 +417,8 @@ public: CHS_CODE(const_cast(this)->m_collisions++;); c = c->m_next; } - while (c != 0); - return 0; + while (c != nullptr); + return nullptr; } bool find(T const & d, T & r) { @@ -436,7 +436,7 @@ public: CHS_CODE(const_cast(this)->m_collisions++;); c = c->m_next; } - while (c != 0); + while (c != nullptr); return false; } @@ -447,13 +447,13 @@ public: cell * c = m_table + idx; if (c->is_free()) return; - cell * prev = 0; + cell * prev = nullptr; do { if (equals(c->m_data, d)) { m_size--; - if (prev == 0) { + if (prev == nullptr) { cell * next = c->m_next; - if (next == 0) { + if (next == nullptr) { m_used_slots--; c->mark_free(); SASSERT(c->is_free()); @@ -474,7 +474,7 @@ public: prev = c; c = c->m_next; } - while (c != 0); + while (c != nullptr); } class iterator { @@ -490,12 +490,12 @@ public: } m_it++; } - m_list_it = 0; + m_list_it = nullptr; } public: iterator(cell * start, cell * end): m_it(start), m_end(end) { move_to_used(); } - iterator():m_it(0), m_end(0), m_list_it(0) {} + iterator():m_it(nullptr), m_end(nullptr), m_list_it(nullptr) {} T & operator*() { return m_list_it->m_data; } @@ -506,7 +506,7 @@ public: T * operator->() { return &(operator*()); } iterator & operator++() { m_list_it = m_list_it->m_next; - if (m_list_it == 0) { + if (m_list_it == nullptr) { m_it++; move_to_used(); } @@ -658,7 +658,7 @@ public: bool find(Key const & k, Value & v) const { key_value * e = m_table.find_core(key_value(k)); - if (e == 0) + if (e == nullptr) return false; v = e->m_value; return true; diff --git a/src/util/checked_int64.h b/src/util/checked_int64.h index 8a2eb124d..507564a2e 100644 --- a/src/util/checked_int64.h +++ b/src/util/checked_int64.h @@ -7,7 +7,7 @@ Module Name: Abstract: - A class for wrapping checked (and unchecked) int64 operations. + A class for wrapping checked (and unchecked) int64_t operations. Note: the mpfx class defines a more general class of fixed-point operations. A tradeoff is that it relies on a manager. This class several of the most common operations from rational, so @@ -29,19 +29,19 @@ Revision History: template class checked_int64 { - int64 m_value; + int64_t m_value; typedef checked_int64 ci; - rational r64(int64 i) { return rational(i, rational::i64()); } + rational r64(int64_t i) { return rational(i, rational::i64()); } public: checked_int64(): m_value(0) {} - checked_int64(int64 v): m_value(v) {} + checked_int64(int64_t v): m_value(v) {} checked_int64(checked_int64 const& other) { m_value = other.m_value; } class overflow_exception : public z3_exception { - virtual char const * msg() const { return "checked_int64 overflow/underflow";} + char const * msg() const override { return "checked_int64 overflow/underflow";} }; bool is_zero() const { return m_value == 0; } @@ -57,7 +57,7 @@ public: static checked_int64 one() { return ci(1); } static checked_int64 minus_one() { return ci(-1);} - int64 get_int64() const { return m_value; } + int64_t get_int64() const { return m_value; } checked_int64 abs() const { if (m_value >= 0) { @@ -117,9 +117,9 @@ public: checked_int64& operator+=(checked_int64 const& other) { if (CHECK) { - uint64 x = static_cast(m_value); - uint64 y = static_cast(other.m_value); - int64 r = static_cast(x + y); + uint64_t x = static_cast(m_value); + uint64_t y = static_cast(other.m_value); + int64_t r = static_cast(x + y); if (m_value > 0 && other.m_value > 0 && r <= 0) throw overflow_exception(); if (m_value < 0 && other.m_value < 0 && r >= 0) throw overflow_exception(); m_value = r; @@ -132,9 +132,9 @@ public: checked_int64& operator-=(checked_int64 const& other) { if (CHECK) { - uint64 x = static_cast(m_value); - uint64 y = static_cast(other.m_value); - int64 r = static_cast(x - y); + uint64_t x = static_cast(m_value); + uint64_t y = static_cast(other.m_value); + int64_t r = static_cast(x - y); if (m_value > 0 && other.m_value < 0 && r <= 0) throw overflow_exception(); if (m_value < 0 && other.m_value > 0 && r >= 0) throw overflow_exception(); m_value = r; diff --git a/src/util/cmd_context_types.h b/src/util/cmd_context_types.h index ae64e2de6..e82536704 100644 --- a/src/util/cmd_context_types.h +++ b/src/util/cmd_context_types.h @@ -91,8 +91,8 @@ public: virtual void reset(cmd_context & ctx) {} virtual void finalize(cmd_context & ctx) {} virtual symbol get_name() const { return m_name; } - virtual char const * get_usage() const { return 0; } - virtual char const * get_descr(cmd_context & ctx) const { return 0; } + virtual char const * get_usage() const { return nullptr; } + virtual char const * get_descr(cmd_context & ctx) const { return nullptr; } virtual unsigned get_arity() const { return 0; } // command invocation diff --git a/src/util/container_util.h b/src/util/container_util.h index e114c87b9..e68ab7199 100644 --- a/src/util/container_util.h +++ b/src/util/container_util.h @@ -29,7 +29,7 @@ Revision History: // ----------------------------------- template - void set_intersection(Set1 & tgt, const Set2 & src) { +void set_intersection(Set1 & tgt, const Set2 & src) { svector to_remove; for (auto const& itm : tgt) if (!src.contains(itm)) diff --git a/src/util/cooperate.cpp b/src/util/cooperate.cpp index 2ae250206..2b2e7958e 100644 --- a/src/util/cooperate.cpp +++ b/src/util/cooperate.cpp @@ -28,7 +28,7 @@ struct cooperation_lock { cooperation_lock() { omp_set_nested(1); omp_init_nest_lock(&m_lock); - m_task = 0; + m_task = nullptr; m_owner_thread = -1; } ~cooperation_lock() { diff --git a/src/util/debug.cpp b/src/util/debug.cpp index 5d39e7c02..4434cb19f 100644 --- a/src/util/debug.cpp +++ b/src/util/debug.cpp @@ -41,7 +41,7 @@ void notify_assertion_violation(const char * fileName, int line, const char * co std::cerr << condition << "\n"; } -static str_hashtable* g_enabled_debug_tags = 0; +static str_hashtable* g_enabled_debug_tags = nullptr; static void init_debug_table() { if (!g_enabled_debug_tags) { @@ -51,7 +51,7 @@ static void init_debug_table() { void finalize_debug() { dealloc(g_enabled_debug_tags); - g_enabled_debug_tags = 0; + g_enabled_debug_tags = nullptr; } void enable_debug(const char * tag) { @@ -72,7 +72,7 @@ bool is_debug_enabled(const char * tag) { #ifndef _WINDOWS void invoke_gdb() { char buffer[1024]; - int * x = 0; + int * x = nullptr; for (;;) { std::cerr << "(C)ontinue, (A)bort, (S)top, (T)hrow exception, Invoke (G)DB\n"; char result; @@ -103,7 +103,7 @@ void invoke_gdb() { else { std::cerr << "error starting GDB...\n"; // forcing seg fault. - int * x = 0; + int * x = nullptr; *x = 0; } return; diff --git a/src/util/dependency.h b/src/util/dependency.h index 5055399bc..0b6772852 100644 --- a/src/util/dependency.h +++ b/src/util/dependency.h @@ -138,7 +138,7 @@ public: } dependency * mk_empty() { - return 0; + return nullptr; } dependency * mk_leaf(value const & v) { @@ -148,10 +148,10 @@ public: } dependency * mk_join(dependency * d1, dependency * d2) { - if (d1 == 0) { + if (d1 == nullptr) { return d2; } - else if (d2 == 0) { + else if (d2 == nullptr) { return d1; } else if (d1 == d2) { diff --git a/src/util/double_manager.h b/src/util/double_manager.h index 7532a3b8b..481701f42 100644 --- a/src/util/double_manager.h +++ b/src/util/double_manager.h @@ -75,8 +75,8 @@ public: static void set(double & a, char const * val) { a = atof(val); } static void set(double & a, int val) { a = static_cast(val); } static void set(double & a, unsigned val) { a = static_cast(val); } - static void set(double & a, int64 val) { a = static_cast(val); } - static void set(double & a, uint64 val) { a = static_cast(val); } + static void set(double & a, int64_t val) { a = static_cast(val); } + static void set(double & a, uint64_t val) { a = static_cast(val); } static void swap(double & a, double & b) { std::swap(a, b); } bool is_pos(double a) const { return a > m_zero_tolerance; } bool is_neg(double a) const { return a < m_zero_tolerance; } @@ -93,11 +93,11 @@ public: } static unsigned hash(double a) { - return hash_ull(static_cast(a)); + return hash_ull(static_cast(a)); } }; -static_assert(sizeof(uint64) == sizeof(double), ""); +static_assert(sizeof(uint64_t) == sizeof(double), ""); #endif /* DOUBLE_MANAGER_H_ */ diff --git a/src/util/ema.h b/src/util/ema.h new file mode 100644 index 000000000..5a32e021c --- /dev/null +++ b/src/util/ema.h @@ -0,0 +1,56 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + ema.h + +Abstract: + + Exponential moving average based on CaDiCal. + The exponential scheme used to adjust beta to alpha is + described in Biere & Froelich, POS (Pragmatics of SAT) 2016. + +Author: + + Nikolaj Bjorner (nbjorner) 2018-05-03 + +Revision History: + +--*/ +#ifndef EMA_H_ +#define EMA_H_ + +class ema { + double m_alpha, m_beta, m_value; + unsigned m_period, m_wait; + bool invariant() const { return 0 <= m_alpha && m_alpha <= m_beta && m_beta <= 1; } + public: + ema(): m_alpha(0), m_beta(1), m_value(0), m_period(0), m_wait(0) { + SASSERT(invariant()); + } + + ema(double alpha): + m_alpha(alpha), m_beta(1), m_value(0), + m_period(0), m_wait(0) { + SASSERT(invariant()); + } + + void set_alpha(double alpha) { + m_alpha = alpha; + SASSERT(invariant()); + } + + operator double () const { return m_value; } + + void update(double x) { + SASSERT(invariant()); + m_value += m_beta * (x - m_value); + if (m_beta <= m_alpha || m_wait--) return; + m_wait = m_period = 2*(m_period + 1) - 1; + m_beta *= 0.5; + if (m_beta < m_alpha) m_beta = m_alpha; + } +}; + +#endif diff --git a/src/util/env_params.cpp b/src/util/env_params.cpp index c2b5f7974..3ba6df735 100644 --- a/src/util/env_params.cpp +++ b/src/util/env_params.cpp @@ -23,7 +23,7 @@ Notes: #include "util/memory_manager.h" void env_params::updt_params() { - params_ref p = gparams::get(); + params_ref const& p = gparams::get_ref(); set_verbosity_level(p.get_uint("verbose", get_verbosity_level())); enable_warning_messages(p.get_bool("warning", true)); memory::set_max_size(megabytes_to_bytes(p.get_uint("memory_max_size", 0))); diff --git a/src/util/file_path.h b/src/util/file_path.h index c34c8b408..dbd8130c7 100644 --- a/src/util/file_path.h +++ b/src/util/file_path.h @@ -21,12 +21,12 @@ Revision History: #include inline char const * get_extension(char const * file_name) { - if (file_name == 0) - return 0; - char const * last_dot = 0; + if (file_name == nullptr) + return nullptr; + char const * last_dot = nullptr; for (;;) { char const * tmp = strchr(file_name, '.'); - if (tmp == 0) { + if (tmp == nullptr) { return last_dot; } last_dot = tmp + 1; diff --git a/src/util/gparams.cpp b/src/util/gparams.cpp index e71594810..5ee49ef16 100644 --- a/src/util/gparams.cpp +++ b/src/util/gparams.cpp @@ -23,7 +23,7 @@ Notes: extern void gparams_register_modules(); static char const * g_old_params_names[] = { - "arith_adaptive","arith_adaptive_assertion_threshold","arith_adaptive_gcd","arith_adaptive_propagation_threshold","arith_add_binary_bounds","arith_blands_rule_threshold","arith_branch_cut_ratio","arith_dump_lemmas","arith_eager_eq_axioms","arith_eager_gcd","arith_eq_bounds","arith_euclidean_solver","arith_expand_eqs","arith_force_simplex","arith_gcd_test","arith_ignore_int","arith_lazy_adapter","arith_lazy_pivoting","arith_max_lemma_size","arith_process_all_eqs","arith_propagate_eqs","arith_propagation_mode","arith_propagation_threshold","arith_prop_strategy","arith_random_initial_value","arith_random_lower","arith_random_seed","arith_random_upper","arith_reflect","arith_skip_big_coeffs","arith_small_lemma_size","arith_solver","arith_stronger_lemmas","array_always_prop_upward","array_canonize","array_cg","array_delay_exp_axiom","array_extensional","array_laziness","array_lazy_ieq","array_lazy_ieq_delay","array_solver","array_weak","async_commands","at_labels_cex","auto_config","bb_eager","bb_ext_gates","bb_quantifiers","bin_clauses","bit2int","bv2int_distribute","bv_blast_max_size","bv_cc","bv_enable_int2bv_propagation","bv_lazy_le","bv_max_sharing","bv_reflect","bv_solver","case_split","check_at_labels","check_proof","cnf_factor","cnf_mode","context_simplifier","dack","dack_eq","dack_factor","dack_gc","dack_gc_inv_decay","dack_threshold","default_qid","default_table","default_table_checked","delay_units","delay_units_threshold","der","display_config","display_dot_proof","display_error_for_visual_studio","display_features","display_proof","display_unsat_core","distribute_forall","dt_lazy_splits","dump_goal_as_smt","elim_and","elim_bounds","elim_nlarith_quantifiers","elim_quantifiers","elim_term_ite","ematching","engine","eq_propagation","hi_div0","ignore_bad_patterns","ignore_setparameter","instruction_max","inst_gen","interactive","internalizer_nnf","lemma_gc_factor","lemma_gc_half","lemma_gc_initial","lemma_gc_new_clause_activity","lemma_gc_new_clause_relevancy","lemma_gc_new_old_ratio","lemma_gc_old_clause_activity","lemma_gc_old_clause_relevancy","lemma_gc_strategy","lift_ite","lookahead_diseq","macro_finder","max_conflicts","max_counterexamples","mbqi","mbqi_force_template","mbqi_max_cexs","mbqi_max_cexs_incr","mbqi_max_iterations","mbqi_trace","minimize_lemmas","model","model_compact","model_completion","model_display_arg_sort","model_hide_unused_partitions","model_on_final_check","model_on_timeout","model_partial","model_v1","model_v2","model_validate","new_core2th_eq","ng_lift_ite","nl_arith","nl_arith_branching","nl_arith_gb","nl_arith_gb_eqs","nl_arith_gb_perturbate","nl_arith_gb_threshold","nl_arith_max_degree","nl_arith_rounds","nnf_factor","nnf_ignore_labels","nnf_mode","nnf_sk_hack","order","order_var_weight","order_weights","phase_selection","pi_arith","pi_arith_weight","pi_avoid_skolems","pi_block_looop_patterns","pi_max_multi_patterns","pi_non_nested_arith_weight","pi_nopat_weight","pi_pull_quantifiers","pi_use_database","pi_warnings","pp_bounded","pp_bv_literals","pp_bv_neg","pp_decimal","pp_decimal_precision","pp_fixed_indent","pp_flat_assoc","pp_max_depth","pp_max_indent","pp_max_num_lines","pp_max_ribbon","pp_max_width","pp_min_alias_size","pp_simplify_implies","pp_single_line","precedence","precedence_gen","pre_demodulator","pre_simplifier","pre_simplify_expr","profile_res_sub","progress_sampling_freq","proof_mode","propagate_booleans","propagate_values","pull_cheap_ite_trees","pull_nested_quantifiers","qi_conservative_final_check","qi_cost","qi_eager_threshold","qi_lazy_instantiation","qi_lazy_quick_checker","qi_lazy_threshold","qi_max_eager_multi_patterns","qi_max_instances","qi_max_lazy_multi_pattern_matching","qi_new_gen","qi_profile","qi_profile_freq","qi_promote_unsat","qi_quick_checker","quasi_macros","random_case_split_freq","random_initial_activity","random_seed","recent_lemma_threshold","reduce_args","refine_inj_axiom","relevancy","relevancy_lemma","rel_case_split_order","restart_adaptive","restart_agility_threshold","restart_factor","restart_initial","restart_strategy","restricted_quasi_macros","simplify_clauses","smtlib2_compliant","smtlib_category","smtlib_dump_lemmas","smtlib_logic","smtlib_source_info","smtlib_trace_path","soft_timeout","solver","spc_bs","spc_es","spc_factor_subsumption_index_opt","spc_initial_subsumption_index_opt","spc_max_subsumption_index_features","spc_min_func_freq_subsumption_index","spc_num_iterations","spc_trace","statistics","strong_context_simplifier","tick","trace","trace_file_name","type_check","user_theory_persist_axioms","user_theory_preprocess_axioms","verbose","warning","well_sorted_check","z3_solver_ll_pp","z3_solver_smt_pp", 0 }; + "arith_adaptive","arith_adaptive_assertion_threshold","arith_adaptive_gcd","arith_adaptive_propagation_threshold","arith_add_binary_bounds","arith_blands_rule_threshold","arith_branch_cut_ratio","arith_dump_lemmas","arith_eager_eq_axioms","arith_eager_gcd","arith_eq_bounds","arith_euclidean_solver","arith_expand_eqs","arith_force_simplex","arith_gcd_test","arith_ignore_int","arith_lazy_adapter","arith_lazy_pivoting","arith_max_lemma_size","arith_process_all_eqs","arith_propagate_eqs","arith_propagation_mode","arith_propagation_threshold","arith_prop_strategy","arith_random_initial_value","arith_random_lower","arith_random_seed","arith_random_upper","arith_reflect","arith_skip_big_coeffs","arith_small_lemma_size","arith_solver","arith_stronger_lemmas","array_always_prop_upward","array_canonize","array_cg","array_delay_exp_axiom","array_extensional","array_laziness","array_lazy_ieq","array_lazy_ieq_delay","array_solver","array_weak","async_commands","at_labels_cex","auto_config","bb_eager","bb_ext_gates","bb_quantifiers","bin_clauses","bit2int","bv2int_distribute","bv_blast_max_size","bv_cc","bv_enable_int2bv_propagation","bv_lazy_le","bv_max_sharing","bv_reflect","bv_solver","case_split","check_at_labels","check_proof","cnf_factor","cnf_mode","context_simplifier","dack","dack_eq","dack_factor","dack_gc","dack_gc_inv_decay","dack_threshold","default_qid","default_table","default_table_checked","delay_units","delay_units_threshold","der","display_config","display_dot_proof","display_error_for_visual_studio","display_features","display_proof","display_unsat_core","distribute_forall","dt_lazy_splits","dump_goal_as_smt","elim_and","elim_bounds","elim_nlarith_quantifiers","elim_quantifiers","elim_term_ite","ematching","engine","eq_propagation","hi_div0","ignore_bad_patterns","ignore_setparameter","instruction_max","inst_gen","interactive","internalizer_nnf","lemma_gc_factor","lemma_gc_half","lemma_gc_initial","lemma_gc_new_clause_activity","lemma_gc_new_clause_relevancy","lemma_gc_new_old_ratio","lemma_gc_old_clause_activity","lemma_gc_old_clause_relevancy","lemma_gc_strategy","lift_ite","lookahead_diseq","macro_finder","max_conflicts","max_counterexamples","mbqi","mbqi_force_template","mbqi_max_cexs","mbqi_max_cexs_incr","mbqi_max_iterations","mbqi_trace","minimize_lemmas","model","model_compact","model_completion","model_display_arg_sort","model_hide_unused_partitions","model_on_final_check","model_on_timeout","model_partial","model_v1","model_v2","model_validate","new_core2th_eq","ng_lift_ite","nl_arith","nl_arith_branching","nl_arith_gb","nl_arith_gb_eqs","nl_arith_gb_perturbate","nl_arith_gb_threshold","nl_arith_max_degree","nl_arith_rounds","nnf_factor","nnf_ignore_labels","nnf_mode","nnf_sk_hack","order","order_var_weight","order_weights","phase_selection","pi_arith","pi_arith_weight","pi_avoid_skolems","pi_block_looop_patterns","pi_max_multi_patterns","pi_non_nested_arith_weight","pi_nopat_weight","pi_pull_quantifiers","pi_use_database","pi_warnings","pp_bounded","pp_bv_literals","pp_bv_neg","pp_decimal","pp_decimal_precision","pp_fixed_indent","pp_flat_assoc","pp_max_depth","pp_max_indent","pp_max_num_lines","pp_max_ribbon","pp_max_width","pp_min_alias_size","pp_simplify_implies","pp_single_line","precedence","precedence_gen","pre_demodulator","pre_simplifier","pre_simplify_expr","profile_res_sub","progress_sampling_freq","proof_mode","propagate_booleans","propagate_values","pull_cheap_ite_trees","pull_nested_quantifiers","qi_conservative_final_check","qi_cost","qi_eager_threshold","qi_lazy_instantiation","qi_lazy_quick_checker","qi_lazy_threshold","qi_max_eager_multi_patterns","qi_max_instances","qi_max_lazy_multi_pattern_matching","qi_new_gen","qi_profile","qi_profile_freq","qi_promote_unsat","qi_quick_checker","quasi_macros","random_case_split_freq","random_initial_activity","random_seed","recent_lemma_threshold","reduce_args","refine_inj_axiom","relevancy","relevancy_lemma","rel_case_split_order","restart_adaptive","restart_agility_threshold","restart_factor","restart_initial","restart_strategy","restricted_quasi_macros","simplify_clauses","smtlib2_compliant","smtlib_category","smtlib_dump_lemmas","smtlib_logic","smtlib_source_info","smtlib_trace_path","soft_timeout","solver","spc_bs","spc_es","spc_factor_subsumption_index_opt","spc_initial_subsumption_index_opt","spc_max_subsumption_index_features","spc_min_func_freq_subsumption_index","spc_num_iterations","spc_trace","statistics","strong_context_simplifier","tick","trace","trace_file_name","type_check","user_theory_persist_axioms","user_theory_preprocess_axioms","verbose","warning","well_sorted_check","z3_solver_ll_pp","z3_solver_smt_pp", nullptr }; bool is_old_param_name(symbol const & name) { char const * const * it = g_old_params_names; @@ -64,7 +64,7 @@ static char const * g_params_renames[] = { "pp_bv_neg", "pp.bv_neg", "pp_max_depth", "pp.max_depth", "pp_min_alias_size", "pp.min_alias_size", - 0 }; + nullptr }; char const * get_new_param_name(symbol const & p) { char const * const * it = g_params_renames; @@ -75,7 +75,7 @@ char const * get_new_param_name(symbol const & p) { } it += 2; } - return 0; + return nullptr; } struct gparams::imp { @@ -104,10 +104,8 @@ public: ~imp() { reset(); - dictionary::iterator it = m_module_param_descrs.begin(); - dictionary::iterator end = m_module_param_descrs.end(); - for (; it != end; ++it) { - dealloc(it->m_value); + for (auto & kv : m_module_param_descrs) { + dealloc(kv.m_value); } } @@ -115,10 +113,8 @@ public: #pragma omp critical (gparams) { m_params.reset(); - dictionary::iterator it = m_module_params.begin(); - dictionary::iterator end = m_module_params.end(); - for (; it != end; ++it) { - dealloc(it->m_value); + for (auto & kv : m_module_params) { + dealloc(kv.m_value); } m_module_params.reset(); } @@ -191,7 +187,7 @@ public: return m_params; } else { - params_ref * p = 0; + params_ref * p = nullptr; if (!m_module_params.find(mod_name, p)) { p = alloc(params_ref); m_module_params.insert(mod_name, p); @@ -279,7 +275,7 @@ public: throw_unknown_parameter(param_name, d, mod_name); } else if (k == CPK_UINT) { - long val = strtol(value, 0, 10); + long val = strtol(value, nullptr, 10); ps.set_uint(param_name, static_cast(val)); } else if (k == CPK_DOUBLE) { @@ -374,7 +370,7 @@ public: throw_unknown_parameter(p, d, m); } char const * r = d.get_default(p); - if (r == 0) + if (r == nullptr) return "default"; return r; } @@ -397,7 +393,7 @@ public: } } else { - params_ref * ps = 0; + params_ref * ps = nullptr; if (m_module_params.find(m, ps) && ps->contains(p)) { r = get_value(*ps, p); } @@ -425,26 +421,22 @@ public: return r; } + // unfortunately, params_ref is not thread safe + // so better create a local copy of the parameters. params_ref get_module(symbol const & module_name) { params_ref result; - params_ref * ps = 0; + params_ref * ps = nullptr; #pragma omp critical (gparams) { if (m_module_params.find(module_name, ps)) { - result = *ps; + result.copy(*ps); } } return result; } - params_ref get() { - params_ref result; - TRACE("gparams", tout << "get() m_params: " << m_params << "\n";); - #pragma omp critical (gparams) - { - result = m_params; - } - return result; + params_ref const& get_ref() { + return m_params; } // ----------------------------------------------- @@ -464,16 +456,14 @@ public: out << "Example: pp.decimal=true\n"; out << "\n"; } - dictionary::iterator it = get_module_param_descrs().begin(); - dictionary::iterator end = get_module_param_descrs().end(); - for (; it != end; ++it) { - out << "[module] " << it->m_key; - char const * descr = 0; - if (get_module_descrs().find(it->m_key, descr)) { + for (auto & kv : get_module_param_descrs()) { + out << "[module] " << kv.m_key; + char const * descr = nullptr; + if (get_module_descrs().find(kv.m_key, descr)) { out << ", description: " << descr; } out << "\n"; - it->m_value->display(out, indent + 4, smt2_style, include_descr); + kv.m_value->display(out, indent + 4, smt2_style, include_descr); } } } @@ -481,12 +471,10 @@ public: void display_modules(std::ostream & out) { #pragma omp critical (gparams) { - dictionary::iterator it = get_module_param_descrs().begin(); - dictionary::iterator end = get_module_param_descrs().end(); - for (; it != end; ++it) { - out << "[module] " << it->m_key; - char const * descr = 0; - if (get_module_descrs().find(it->m_key, descr)) { + for (auto & kv : get_module_param_descrs()) { + out << "[module] " << kv.m_key; + char const * descr = nullptr; + if (get_module_descrs().find(kv.m_key, descr)) { out << ", description: " << descr; } out << "\n"; @@ -500,14 +488,14 @@ public: #pragma omp critical (gparams) { try { - param_descrs * d = 0; + param_descrs * d = nullptr; if (!get_module_param_descrs().find(module_name, d)) { std::stringstream strm; strm << "unknown module '" << module_name << "'"; throw exception(strm.str()); } out << "[module] " << module_name; - char const * descr = 0; + char const * descr = nullptr; if (get_module_descrs().find(module_name, descr)) { out << ", description: " << descr; } @@ -566,7 +554,7 @@ public: } }; -gparams::imp * gparams::g_imp = 0; +gparams::imp * gparams::g_imp = nullptr; void gparams::reset() { SASSERT(g_imp != 0); @@ -618,10 +606,10 @@ params_ref gparams::get_module(symbol const & module_name) { return g_imp->get_module(module_name); } -params_ref gparams::get() { - TRACE("gparams", tout << "gparams::get()\n";); +params_ref const& gparams::get_ref() { + TRACE("gparams", tout << "gparams::get_ref()\n";); SASSERT(g_imp != 0); - return g_imp->get(); + return g_imp->get_ref(); } void gparams::display(std::ostream & out, unsigned indent, bool smt2_style, bool include_descr) { @@ -651,9 +639,9 @@ void gparams::init() { void gparams::finalize() { TRACE("gparams", tout << "gparams::finalize()\n";); - if (g_imp != 0) { + if (g_imp != nullptr) { dealloc(g_imp); - g_imp = 0; + g_imp = nullptr; } } diff --git a/src/util/gparams.h b/src/util/gparams.h index 894732890..5334b28d0 100644 --- a/src/util/gparams.h +++ b/src/util/gparams.h @@ -47,7 +47,7 @@ public: set_global_param('pp.decimal', 'true') will set the parameter "decimal" in the module "pp" to true. - An exception is thrown if the the parameter name is unknown, or if the value is incorrect. + An exception is thrown if the parameter name is unknown, or if the value is incorrect. */ static void set(char const * name, char const * value); static void set(symbol const & name, char const * value); @@ -57,7 +57,7 @@ public: If the parameter is not set, then it just returns 'default'. - An exception is thrown if the the parameter name is unknown. + An exception is thrown if the parameter name is unknown. */ static std::string get_value(char const * name); static std::string get_value(symbol const & name); @@ -106,7 +106,8 @@ public: /** \brief Return the global parameter set (i.e., parameters that are not associated with any particular module). */ - static params_ref get(); + + static params_ref const& get_ref(); /** \brief Dump information about available parameters in the given output stream. diff --git a/src/util/hash.h b/src/util/hash.h index bc6117cac..a2af8253f 100644 --- a/src/util/hash.h +++ b/src/util/hash.h @@ -140,8 +140,8 @@ struct size_t_hash { }; struct uint64_hash { - typedef uint64 data; - unsigned operator()(uint64 x) const { return static_cast(x); } + typedef uint64_t data; + unsigned operator()(uint64_t x) const { return static_cast(x); } }; struct bool_hash { diff --git a/src/util/hashtable.h b/src/util/hashtable.h index fa9fef180..420e48949 100644 --- a/src/util/hashtable.h +++ b/src/util/hashtable.h @@ -24,11 +24,12 @@ Revision History: #include #include "util/memory_manager.h" #include "util/hash.h" +#include "util/vector.h" #define DEFAULT_HASHTABLE_INITIAL_CAPACITY 8 #define SMALL_TABLE_CAPACITY 64 -// #define HASHTABLE_STATISTICS +// #define HASHTABLE_STATISTICS #ifdef HASHTABLE_STATISTICS #define HS_CODE(CODE) { CODE } @@ -91,9 +92,9 @@ class ptr_hash_entry { T * m_ptr; public: typedef T * data; - ptr_hash_entry():m_ptr(0) {} + ptr_hash_entry():m_ptr(nullptr) {} unsigned get_hash() const { return m_hash; } - bool is_free() const { return m_ptr == 0; } + bool is_free() const { return m_ptr == nullptr; } bool is_deleted() const { return m_ptr == reinterpret_cast(1); } bool is_used() const { return m_ptr != reinterpret_cast(0) && m_ptr != reinterpret_cast(1); } T * get_data() const { return m_ptr; } @@ -101,7 +102,7 @@ public: void set_data(T * d) { m_ptr = d; } void set_hash(unsigned h) { m_hash = h; } void mark_as_deleted() { m_ptr = reinterpret_cast(1); } - void mark_as_free() { m_ptr = 0; } + void mark_as_free() { m_ptr = nullptr; } }; @@ -114,9 +115,9 @@ class ptr_addr_hash_entry : public ptr_hash_entry { T * m_ptr; public: typedef T * data; - ptr_addr_hash_entry():m_ptr(0) {} + ptr_addr_hash_entry():m_ptr(nullptr) {} unsigned get_hash() const { return get_ptr_hash(m_ptr); } - bool is_free() const { return m_ptr == 0; } + bool is_free() const { return m_ptr == nullptr; } bool is_deleted() const { return m_ptr == reinterpret_cast(1); } bool is_used() const { return m_ptr != reinterpret_cast(0) && m_ptr != reinterpret_cast(1); } T * get_data() const { return m_ptr; } @@ -145,7 +146,7 @@ protected: void delete_table() { dealloc_vect(m_table, m_capacity); - m_table = 0; + m_table = nullptr; } public: @@ -375,8 +376,7 @@ public: } ((void) 0) void insert(data && e) { - if ((m_size + m_num_deleted) << 2 > (m_capacity * 3)) { - // if ((m_size + m_num_deleted) * 2 > (m_capacity)) { + if (((m_size + m_num_deleted) << 2) > (m_capacity * 3)) { expand_table(); } unsigned hash = get_hash(e); @@ -385,7 +385,7 @@ public: entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; - entry * del_entry = 0; + entry * del_entry = nullptr; for (; curr != end; ++curr) { INSERT_LOOP_BODY(); } @@ -441,7 +441,7 @@ public: entry * begin = m_table + idx; entry * end = m_table + m_capacity; entry * curr = begin; - entry * del_entry = 0; + entry * del_entry = nullptr; for (; curr != end; ++curr) { INSERT_LOOP_CORE_BODY(); } @@ -449,7 +449,7 @@ public: INSERT_LOOP_CORE_BODY(); } UNREACHABLE(); - return 0; + return false; } bool insert_if_not_there_core(const data & e, entry * & et) { @@ -488,7 +488,9 @@ public: else if (curr->is_free()) { \ return 0; \ } \ - HS_CODE(const_cast(this)->m_st_collision++;); \ + else { \ + HS_CODE(const_cast(this)->m_st_collision++;); \ + } \ } ((void) 0) entry * find_core(data const & e) const { @@ -504,12 +506,12 @@ public: for (curr = m_table; curr != begin; ++curr) { FIND_LOOP_BODY(); } - return 0; + return nullptr; } bool find(data const & k, data & r) const { entry * e = find_core(k); - if (e != 0) { + if (e != nullptr) { r = e->get_data(); return true; } @@ -517,7 +519,7 @@ public: } bool contains(data const & e) const { - return find_core(e) != 0; + return find_core(e) != nullptr; } iterator find(data const & e) const { @@ -600,9 +602,8 @@ public: core_hashtable& operator|=(core_hashtable const& other) { if (this == &other) return *this; - iterator i = other.begin(), e = other.end(); - for (; i != e; ++i) { - insert(*i); + for (const data& d : other) { + insert(d); } return *this; } @@ -610,10 +611,9 @@ public: core_hashtable& operator&=(core_hashtable const& other) { if (this == &other) return *this; core_hashtable copy(*this); - iterator i = copy.begin(), e = copy.end(); - for (; i != e; ++i) { - if (!other.contains(*i)) { - remove(*i); + for (const data& d : copy) { + if (!other.contains(d)) { + remove(d); } } return *this; @@ -622,9 +622,8 @@ public: core_hashtable& operator=(core_hashtable const& other) { if (this == &other) return *this; reset(); - iterator i = other.begin(), e = other.end(); - for (; i != e; ++i) { - insert(*i); + for (const data& d : other) { + insert(d); } return *this; } @@ -655,7 +654,33 @@ public: unsigned long long get_num_collision() const { return 0; } #endif - +#define COLL_LOOP_BODY() { \ + if (curr->is_used()) { \ + if (curr->get_hash() == hash && equals(curr->get_data(), e)) return; \ + collisions.push_back(curr->get_data()); \ + continue; \ + } \ + else if (curr->is_free()) { \ + continue; \ + } \ + collisions.push_back(curr->get_data()); \ + } ((void) 0); + + void get_collisions(data const& e, vector& collisions) { + unsigned hash = get_hash(e); + unsigned mask = m_capacity - 1; + unsigned idx = hash & mask; + entry * begin = m_table + idx; + entry * end = m_table + m_capacity; + entry * curr = begin; + for (; curr != end; ++curr) { + COLL_LOOP_BODY(); + } + for (curr = m_table; curr != begin; ++curr) { + COLL_LOOP_BODY(); + } + + } }; template diff --git a/src/util/heap.h b/src/util/heap.h index 02e30bf04..182932fd0 100644 --- a/src/util/heap.h +++ b/src/util/heap.h @@ -27,10 +27,6 @@ class heap : private LT { int_vector m_values; int_vector m_value2indices; - bool less_than(int v1, int v2) const { - return LT::operator()(v1, v2); - } - static int left(int i) { return i << 1; } @@ -126,6 +122,10 @@ public: CASSERT("heap", check_invariant()); } + bool less_than(int v1, int v2) const { + return LT::operator()(v1, v2); + } + bool empty() const { return m_values.size() == 1; } diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 014e62625..b8f481329 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -91,8 +91,8 @@ hwf_manager::~hwf_manager() { } -uint64 RAW(double X) { uint64 tmp; memcpy(&tmp, &(X), sizeof(uint64)); return tmp; } -double DBL(uint64 X) { double tmp; memcpy(&tmp, &(X), sizeof(double)); return tmp; } +uint64_t RAW(double X) { uint64_t tmp; memcpy(&tmp, &(X), sizeof(uint64_t)); return tmp; } +double DBL(uint64_t X) { double tmp; memcpy(&tmp, &(X), sizeof(double)); return tmp; } void hwf_manager::set(hwf & o, int value) { o.value = (double) value; @@ -147,7 +147,7 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mp mpq sig; m_mpq_manager.set(sig, significand); - int64 exp = m_mpz_manager.get_int64(exponent); + int64_t exp = m_mpz_manager.get_int64(exponent); if (m_mpq_manager.is_zero(significand)) o.value = 0.0; @@ -160,17 +160,17 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mp } hwf s; s.value = m_mpq_manager.get_double(sig); - uint64 r = (RAW(s.value) & 0x800FFFFFFFFFFFFFull) | ((exp + 1023) << 52); + uint64_t r = (RAW(s.value) & 0x800FFFFFFFFFFFFFull) | ((exp + 1023) << 52); o.value = DBL(r); } } -void hwf_manager::set(hwf & o, bool sign, uint64 significand, int exponent) { +void hwf_manager::set(hwf & o, bool sign, uint64_t significand, int exponent) { // Assumption: this represents (sign * -1) * (significand/2^sbits) * 2^exponent. SASSERT(significand <= 0x000FFFFFFFFFFFFFull); SASSERT(-1022 <= exponent && exponent <= 1023); - uint64 raw = (sign?0x8000000000000000ull:0); - raw |= (((uint64)exponent) + 1023) << 52; + uint64_t raw = (sign?0x8000000000000000ull:0); + raw |= (((uint64_t)exponent) + 1023) << 52; raw |= significand; memcpy(&o.value, &raw, sizeof(double)); } @@ -413,12 +413,12 @@ void hwf_manager::to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o) scoped_mpz n(qm), d(qm); if (is_normal(x)) - qm.set(n, sig(x) | 0x0010000000000000ull); + qm.set(n, (uint64_t)(sig(x) | 0x0010000000000000ull)); else qm.set(n, sig(x)); if (sgn(x)) qm.neg(n); - qm.set(d, 0x0010000000000000ull); + qm.set(d, (uint64_t)0x0010000000000000ull); int e = exp(x); if (e >= 0) qm.mul2k(n, (unsigned)e); @@ -428,7 +428,7 @@ void hwf_manager::to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o) } bool hwf_manager::is_zero(hwf const & x) { - uint64 t = RAW(x.value) & 0x7FFFFFFFFFFFFFFFull; + uint64_t t = RAW(x.value) & 0x7FFFFFFFFFFFFFFFull; return (t == 0x0ull); // CMW: I tried, and these are slower: // return (t != 0x0ull) ? false : true; @@ -483,12 +483,12 @@ bool hwf_manager::is_ninf(hwf const & x) { } bool hwf_manager::is_normal(hwf const & x) { - uint64 t = RAW(x.value) & 0x7FF0000000000000ull; + uint64_t t = RAW(x.value) & 0x7FF0000000000000ull; return (t != 0x0ull && t != 0x7FF0000000000000ull); } bool hwf_manager::is_denormal(hwf const & x) { - uint64 t = RAW(x.value); + uint64_t t = RAW(x.value); return ((t & 0x7FF0000000000000ull) == 0x0 && (t & 0x000FFFFFFFFFFFFFull) != 0x0); } @@ -498,7 +498,7 @@ bool hwf_manager::is_regular(hwf const & x) { // Note that +-0.0 and denormal numbers have exponent==0; these are regular. // All normal numbers are also regular. What remains is +-Inf and NaN, they are // not regular and they are the only numbers that have exponent 7FF. - uint64 e = RAW(x.value) & 0x7FF0000000000000ull; // the exponent + uint64_t e = RAW(x.value) & 0x7FF0000000000000ull; // the exponent return (e != 0x7FF0000000000000ull); } @@ -513,15 +513,15 @@ bool hwf_manager::is_int(hwf const & x) { return false; else { - uint64 t = sig(x); + uint64_t t = sig(x); unsigned shift = 52 - ((unsigned)e); - uint64 mask = (0x1ull << shift) - 1; + uint64_t mask = (0x1ull << shift) - 1; return (t & mask) == 0; } } void hwf_manager::mk_nzero(hwf & o) { - uint64 raw = 0x8000000000000000ull; + uint64_t raw = 0x8000000000000000ull; o.value = DBL(raw); } @@ -537,22 +537,22 @@ void hwf_manager::mk_zero(bool sign, hwf & o) { } void hwf_manager::mk_nan(hwf & o) { - uint64 raw = 0x7FF0000000000001ull; + uint64_t raw = 0x7FF0000000000001ull; o.value = DBL(raw); } void hwf_manager::mk_inf(bool sign, hwf & o) { - uint64 raw = (sign) ? 0xFFF0000000000000ull : 0x7FF0000000000000ull; + uint64_t raw = (sign) ? 0xFFF0000000000000ull : 0x7FF0000000000000ull; o.value = DBL(raw); } void hwf_manager::mk_pinf(hwf & o) { - uint64 raw = 0x7FF0000000000000ull; + uint64_t raw = 0x7FF0000000000000ull; o.value = DBL(raw); } void hwf_manager::mk_ninf(hwf & o) { - uint64 raw = 0xFFF0000000000000ull; + uint64_t raw = 0xFFF0000000000000ull; o.value = DBL(raw); } diff --git a/src/util/hwf.h b/src/util/hwf.h index 5f7692204..21e7655ea 100644 --- a/src/util/hwf.h +++ b/src/util/hwf.h @@ -28,8 +28,8 @@ class hwf { friend class hwf_manager; double value; hwf & operator=(hwf const & other) { UNREACHABLE(); return *this; } - uint64 get_raw() const { - uint64 n; + uint64_t get_raw() const { + uint64_t n; SASSERT(sizeof(n) == sizeof(value)); memcpy(&n, &value, sizeof(value)); return n; @@ -60,7 +60,7 @@ public: void set(hwf & o, mpf_rounding_mode rm, mpq const & value); void set(hwf & o, mpf_rounding_mode rm, char const * value); void set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mpz const & exponent); - void set(hwf & o, bool sign, uint64 significand, int exponent); + void set(hwf & o, bool sign, uint64_t significand, int exponent); void set(hwf & o, hwf const & x); // auxiliary methods to make the interface compatible with mpf @@ -128,7 +128,7 @@ public: return (x.get_raw() & 0x8000000000000000ull) != 0; } - uint64 sig(hwf const & x) const { + uint64_t sig(hwf const & x) const { return x.get_raw() & 0x000FFFFFFFFFFFFFull; } diff --git a/src/util/inf_eps_rational.h b/src/util/inf_eps_rational.h index c184623ca..d0b6f2d99 100644 --- a/src/util/inf_eps_rational.h +++ b/src/util/inf_eps_rational.h @@ -118,12 +118,12 @@ class inf_eps_rational { bool is_rational() const { return m_infty.is_zero() && m_r.is_rational(); } - int64 get_int64() const { + int64_t get_int64() const { SASSERT(is_int64()); return m_r.get_int64(); } - uint64 get_uint64() const { + uint64_t get_uint64() const { SASSERT(is_uint64()); return m_r.get_uint64(); } diff --git a/src/util/inf_int_rational.h b/src/util/inf_int_rational.h index c9c82052e..44ed76ebd 100644 --- a/src/util/inf_int_rational.h +++ b/src/util/inf_int_rational.h @@ -109,12 +109,12 @@ class inf_int_rational { bool is_rational() const { return m_second == 0; } - int64 get_int64() const { + int64_t get_int64() const { SASSERT(is_int64()); return m_first.get_int64(); } - uint64 get_uint64() const { + uint64_t get_uint64() const { SASSERT(is_uint64()); return m_first.get_uint64(); } diff --git a/src/util/inf_rational.h b/src/util/inf_rational.h index d49e45f50..c3f4160ba 100644 --- a/src/util/inf_rational.h +++ b/src/util/inf_rational.h @@ -122,12 +122,12 @@ class inf_rational { bool is_rational() const { return m_second.is_zero(); } - int64 get_int64() const { + int64_t get_int64() const { SASSERT(is_int64()); return m_first.get_int64(); } - uint64 get_uint64() const { + uint64_t get_uint64() const { SASSERT(is_uint64()); return m_first.get_uint64(); } diff --git a/src/util/inf_s_integer.h b/src/util/inf_s_integer.h index 067000202..dd136d1b9 100644 --- a/src/util/inf_s_integer.h +++ b/src/util/inf_s_integer.h @@ -60,8 +60,8 @@ class inf_s_integer { bool is_int64() const { return m_second == 0; } bool is_uint64() const { return m_second == 0; } bool is_rational() const { return m_second == 0; } - int64 get_int64() const { return m_first; } - uint64 get_uint64() const { return m_first; } + int64_t get_int64() const { return m_first; } + uint64_t get_uint64() const { return m_first; } s_integer get_rational() const { return s_integer(m_first); } s_integer get_infinitesimal() const { return s_integer(m_second); } inf_s_integer & operator=(const inf_s_integer & r) { diff --git a/src/util/list.h b/src/util/list.h index efde5ada1..075d5a0e1 100644 --- a/src/util/list.h +++ b/src/util/list.h @@ -28,7 +28,7 @@ class list { list * m_tail; public: - list(T const & h, list * t = 0): + list(T const & h, list * t = nullptr): m_head(h), m_tail(t) { } @@ -73,7 +73,7 @@ unsigned length(list * l) { */ template list * append(region & r, list * l1, list * l2) { - if (l2 == 0) { + if (l2 == nullptr) { return l1; } ptr_buffer > buffer; diff --git a/src/util/lp/core_solver_pretty_printer.h b/src/util/lp/core_solver_pretty_printer.h index 20b2c1cbe..87c528792 100644 --- a/src/util/lp/core_solver_pretty_printer.h +++ b/src/util/lp/core_solver_pretty_printer.h @@ -86,7 +86,7 @@ public: unsigned get_column_width(unsigned column); - unsigned regular_cell_width(unsigned row, unsigned column, std::string name) { + unsigned regular_cell_width(unsigned row, unsigned column, const std::string & name) { return regular_cell_string(row, column, name).size(); } diff --git a/src/util/lp/dense_matrix.h b/src/util/lp/dense_matrix.h index 6b157ffd4..e2ee54058 100644 --- a/src/util/lp/dense_matrix.h +++ b/src/util/lp/dense_matrix.h @@ -79,13 +79,14 @@ public: void apply_from_left_to_X(vector & w, lp_settings & ); - virtual void set_number_of_rows(unsigned /*m*/) {} - virtual void set_number_of_columns(unsigned /*n*/) { } + void set_number_of_rows(unsigned /*m*/) override {} + void set_number_of_columns(unsigned /*n*/) override {} +#ifdef Z3DEBUG + T get_elem(unsigned i, unsigned j) const override { return m_values[i * m_n + j]; } +#endif - T get_elem(unsigned i, unsigned j) const { return m_values[i * m_n + j]; } - - unsigned row_count() const { return m_m; } - unsigned column_count() const { return m_n; } + unsigned row_count() const override { return m_m; } + unsigned column_count() const override { return m_n; } void set_elem(unsigned i, unsigned j, const T& val) { m_values[i * m_n + j] = val; } diff --git a/src/util/lp/disjoint_intervals.h b/src/util/lp/disjoint_intervals.h new file mode 100644 index 000000000..5f4f31af6 --- /dev/null +++ b/src/util/lp/disjoint_intervals.h @@ -0,0 +1,334 @@ +/* + Copyright (c) 2017 Microsoft Corporation + Author: Lev Nachmanson +*/ +#pragma once +#include +namespace lp { +// represents the set of disjoint intervals of integer number +struct disjoint_intervals { + std::map m_endpoints; // 0 means start, 1 means end, 2 means both - for a point interval + bool m_empty; + // constructors create an interval containing all integer numbers or an empty interval + disjoint_intervals() : m_empty(false) {} + disjoint_intervals(bool is_empty) : m_empty(is_empty) {} + + bool is_start(short x) const { return x == 0 || x == 2; } + bool is_start(const std::map::iterator & it) const { + return is_start(it->second); + } + bool is_start(const std::map::reverse_iterator & it) const { + return is_start(it->second); + } + bool is_end(short x) const { return x == 1 || x == 2; } + bool is_end(const std::map::iterator & it) const { + return is_end(it->second); + } + bool is_end(const std::map::reverse_iterator & it) const { + return is_end(it->second); + } + + int pos(const std::map::iterator & it) const { + return it->first; + } + int pos(const std::map::reverse_iterator & it) const { + return it->first; + } + + int bound_kind(const std::map::iterator & it) const { + return it->second; + } + + int bound_kind(const std::map::reverse_iterator & it) const { + return it->second; + } + + bool is_proper_start(short x) const { return x == 0; } + bool is_proper_end(short x) const { return x == 1; } + bool is_proper_end(const std::map::iterator & it) const { + return is_proper_end(it->second); + } + bool is_proper_end(const std::map::reverse_iterator & it) const { + return is_proper_end(it->second); + } + + bool is_one_point_interval(short x) const { return x == 2; } + bool is_one_point_interval(const std::map::iterator & it) const { + return is_one_point_interval(it->second); + } + bool is_one_point_interval(const std::map::reverse_iterator & it) const { + return is_one_point_interval(it->second); + } + + + void erase(int x) { + m_endpoints.erase(x); + } + + void set_one_point_segment(int x) { + m_endpoints[x] = 2; + } + + void set_start(int x) { + m_endpoints[x] = 0; + } + + void set_end(int x) { + m_endpoints[x] = 1; + } + + void remove_all_endpoints_below(int x) { + while (m_endpoints.begin() != m_endpoints.end() && m_endpoints.begin()->first < x) + m_endpoints.erase(m_endpoints.begin()); + } + // we intersect the existing set with the half open to the right interval + void intersect_with_lower_bound(int x) { + if (m_empty) + return; + if (m_endpoints.empty()) { + set_start(x); + return; + } + bool pos_inf = has_pos_inf(); + auto it = m_endpoints.begin(); + while (it != m_endpoints.end() && pos(it) < x) { + m_endpoints.erase(it); + it = m_endpoints.begin(); + } + if (m_endpoints.empty()) { + if (!pos_inf) { + m_empty = true; + return; + } + set_start(x); + return; + } + lp_assert(pos(it) >= x); + if (pos(it) == x) { + if (is_proper_end(it)) + set_one_point_segment(x); + } + else { // x(it) > x + if (is_proper_end(it)) { + set_start(x); + } + } + + lp_assert(is_correct()); + } + + // we intersect the existing set with the half open interval + void intersect_with_upper_bound(int x) { + if (m_empty) + return; + if (m_endpoints.empty()) { + set_end(x); + return; + } + bool neg_inf = has_neg_inf(); + auto it = m_endpoints.rbegin(); + + while (!m_endpoints.empty() && pos(it) > x) { + m_endpoints.erase(std::prev(m_endpoints.end())); + it = m_endpoints.rbegin(); + } + if (m_endpoints.empty()) { + if (!neg_inf) { + m_empty = true; + return; + } + set_end(x); + } + lp_assert(pos(it) <= x); + if (pos(it) == x) { + if (is_one_point_interval(it)) {} + else if (is_proper_end(it)) {} + else {// is_proper_start(it->second) + set_one_point_segment(x); + } + } + else { // pos(it) < x} + if (is_start(it)) + set_end(x); + } + lp_assert(is_correct()); + } + + bool has_pos_inf() const { + if (m_empty) + return false; + + if (m_endpoints.empty()) + return true; + + lp_assert(m_endpoints.rbegin() != m_endpoints.rend()); + return m_endpoints.rbegin()->second == 0; + } + + bool has_neg_inf() const { + if (m_empty) + return false; + + if (m_endpoints.empty()) + return true; + auto it = m_endpoints.begin(); + return is_proper_end(it->second);//m_endpoints.begin()); + } + + // we are intersecting + void intersect_with_interval(int x, int y) { + if (m_empty) + return; + lp_assert(x <= y); + intersect_with_lower_bound(x); + intersect_with_upper_bound(y); + } + + // add an intervar [x, inf] + void unite_with_interval_x_pos_inf(int x) { + if (m_empty) { + set_start(x); + m_empty = false; + return; + } + + while (!m_endpoints.empty() && pos(m_endpoints.rbegin()) > x) { + m_endpoints.erase(std::prev(m_endpoints.end())); + } + + if (m_endpoints.empty()) { + set_start(x); + return; + } + auto it = m_endpoints.rbegin(); + lp_assert(pos(it) <= x); + if (pos(it) == x) { + if (is_end(it)) { + m_endpoints.erase(x); + } else { + set_start(x); + } + } else if (pos(it) == x - 1 && is_end(it)) { + m_endpoints.erase(x - 1); // closing the gap + } else { + if (!has_pos_inf()) + set_start(x); + } + } + + // add an interval [-inf, x] + void unite_with_interval_neg_inf_x(int x) { + if (m_empty) { + set_end(x); + m_empty = false; + return; + } + auto it = m_endpoints.upper_bound(x); + + if (it == m_endpoints.end()) { + bool pos_inf = has_pos_inf(); + m_endpoints.clear(); + // it could be the case where x is inside of the last infinite interval with pos inf + if (!pos_inf) + set_end(x); + return; + } + lp_assert(pos(it) > x); + if (is_one_point_interval(pos(it))) { + set_end(it->second); + } else { + if (is_start(it->second)) { + set_end(x); + } + } + + while (!m_endpoints.empty() && m_endpoints.begin()->first < x) { + m_endpoints.erase(m_endpoints.begin()); + } + lp_assert(is_correct()); + } + + void unite_with_interval(int x, int y) { + lp_assert(false); // not implemented + } + + bool is_correct() const { + if (m_empty) { + if (m_endpoints.size() > 0) { + std::cout << "is empty is true but m_endpoints.size() = " << m_endpoints.size() << std::endl; + return false; + } + return true; + } + bool expect_end; + bool prev = false; + int prev_x; + for (auto t : m_endpoints) { + if (prev && (expect_end != t.second > 0)) { + std::cout << "x = " << t.first << "\n"; + if (expect_end) { + std::cout << "expecting an interval end\n"; + } else { + std::cout << "expecting an interval start\n"; + } + return false; + } + + if (t.second == 2) { + expect_end = false; // swallow a point interval + } else { + if (prev) + expect_end = !expect_end; + else + expect_end = is_start(t.second); + } + if (prev) { + if (t.first - prev_x <= 1) { + std::cout << "the sequence is not increasing or the gap is too small: " << prev_x << ", " << t.first << std::endl; + return false; + } + } + prev = true; + prev_x = t.first; + } + + return true; + } + + void print(std::ostream & out) const { + if (m_empty) { + out << "empty\n"; + return; + } + if (m_endpoints.empty()){ + out << "[-oo,oo]\n"; + return; + } + bool first = true; + for (auto t : m_endpoints) { + if (first) { + if (t.second == 1) { + out << "[-oo," << t.first << "]"; + } + else if (t.second == 0) + out << "[" << t.first << ","; + else if (t.second == 2) + out << "[" << t.first << "]"; + first = false; + } else { + if (t.second==0) + out << "[" << t.first << ","; + else if (t.second == 1) + out << t.first << "]"; + else if (t.second == 2) + out << "[" << t.first << "]"; + } + } + if (has_pos_inf()) + out << "oo]"; + out << "\n"; + } + + +}; +} diff --git a/src/util/lp/eta_matrix.h b/src/util/lp/eta_matrix.h index 6c30e2146..abed6d06b 100644 --- a/src/util/lp/eta_matrix.h +++ b/src/util/lp/eta_matrix.h @@ -46,7 +46,7 @@ public: #endif m_column_index(column_index) {} - bool is_dense() const { return false; } + bool is_dense() const override { return false; } void print(std::ostream & out) { print_matrix(*this, out); @@ -65,12 +65,12 @@ public: return m_diagonal_element; } - void apply_from_left(vector & w, lp_settings & ); + void apply_from_left(vector & w, lp_settings & ) override; template void apply_from_left_local(indexed_vector & w, lp_settings & settings); - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) { + void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { apply_from_left_local(w, settings); } @@ -80,15 +80,15 @@ public: m_column_vector.push_back(row_index, val); } - void apply_from_right(vector & w); - void apply_from_right(indexed_vector & w); + void apply_from_right(vector & w) override; + void apply_from_right(indexed_vector & w) override; - T get_elem(unsigned i, unsigned j) const; #ifdef Z3DEBUG - unsigned row_count() const { return m_length; } - unsigned column_count() const { return m_length; } - void set_number_of_rows(unsigned m) { m_length = m; } - void set_number_of_columns(unsigned n) { m_length = n; } + T get_elem(unsigned i, unsigned j) const override; + unsigned row_count() const override { return m_length; } + unsigned column_count() const override { return m_length; } + void set_number_of_rows(unsigned m) override { m_length = m; } + void set_number_of_columns(unsigned n) override { m_length = n; } #endif void divide_by_diagonal_element() { m_column_vector.divide(m_diagonal_element); diff --git a/src/util/lp/iterator_on_column.h b/src/util/lp/iterator_on_column.h index 5bb43f4c6..c9112a064 100644 --- a/src/util/lp/iterator_on_column.h +++ b/src/util/lp/iterator_on_column.h @@ -27,14 +27,14 @@ struct iterator_on_column:linear_combination_iterator { const vector& m_column; // the offset in term coeffs const static_matrix & m_A; int m_i; // the initial offset in the column - unsigned size() const { return m_column.size(); } + unsigned size() const override { return m_column.size(); } iterator_on_column(const vector& column, const static_matrix & A) // the offset in term coeffs : m_column(column), m_A(A), m_i(-1) {} - bool next(mpq & a, unsigned & i) { + bool next(mpq & a, unsigned & i) override { if (++m_i >= static_cast(m_column.size())) return false; @@ -44,7 +44,7 @@ struct iterator_on_column:linear_combination_iterator { return true; } - bool next(unsigned & i) { + bool next(unsigned & i) override { if (++m_i >= static_cast(m_column.size())) return false; @@ -53,11 +53,11 @@ struct iterator_on_column:linear_combination_iterator { return true; } - void reset() { + void reset() override { m_i = -1; } - linear_combination_iterator * clone() { + linear_combination_iterator * clone() override { iterator_on_column * r = new iterator_on_column(m_column, m_A); return r; } diff --git a/src/util/lp/iterator_on_indexed_vector.h b/src/util/lp/iterator_on_indexed_vector.h index 2c8daf83b..6cb98b8f2 100644 --- a/src/util/lp/iterator_on_indexed_vector.h +++ b/src/util/lp/iterator_on_indexed_vector.h @@ -28,8 +28,8 @@ struct iterator_on_indexed_vector:linear_combination_iterator { m_v(v), m_offset(0) {} - unsigned size() const { return m_v.m_index.size(); } - bool next(T & a, unsigned & i) { + unsigned size() const override { return m_v.m_index.size(); } + bool next(T & a, unsigned & i) override { if (m_offset >= m_v.m_index.size()) return false; i = m_v.m_index[m_offset++]; @@ -37,16 +37,16 @@ struct iterator_on_indexed_vector:linear_combination_iterator { return true; } - bool next(unsigned & i) { + bool next(unsigned & i) override { if (m_offset >= m_v.m_index.size()) return false; i = m_v.m_index[m_offset++]; return true; } - void reset() { + void reset() override { m_offset = 0; } - linear_combination_iterator* clone() { + linear_combination_iterator* clone() override { return new iterator_on_indexed_vector(m_v); } }; diff --git a/src/util/lp/iterator_on_pivot_row.h b/src/util/lp/iterator_on_pivot_row.h index 8aa498477..721502bc2 100644 --- a/src/util/lp/iterator_on_pivot_row.h +++ b/src/util/lp/iterator_on_pivot_row.h @@ -26,11 +26,11 @@ struct iterator_on_pivot_row:linear_combination_iterator { const indexed_vector & m_v; unsigned m_basis_j; iterator_on_indexed_vector m_it; - unsigned size() const { return m_it.size(); } + unsigned size() const override { return m_it.size(); } iterator_on_pivot_row(const indexed_vector & v, unsigned basis_j) : m_basis_returned(false), m_v(v), m_basis_j(basis_j), m_it(v) {} - bool next(T & a, unsigned & i) { + bool next(T & a, unsigned & i) override { if (m_basis_returned == false) { m_basis_returned = true; a = one_of_type(); @@ -39,7 +39,7 @@ struct iterator_on_pivot_row:linear_combination_iterator { } return m_it.next(a, i); } - bool next(unsigned & i) { + bool next(unsigned & i) override { if (m_basis_returned == false) { m_basis_returned = true; i = m_basis_j; @@ -47,11 +47,11 @@ struct iterator_on_pivot_row:linear_combination_iterator { } return m_it.next(i); } - void reset() { + void reset() override { m_basis_returned = false; m_it.reset(); } - linear_combination_iterator * clone() { + linear_combination_iterator * clone() override { iterator_on_pivot_row * r = new iterator_on_pivot_row(m_v, m_basis_j); return r; } diff --git a/src/util/lp/iterator_on_row.h b/src/util/lp/iterator_on_row.h index 1ac5b66bc..55fbda907 100644 --- a/src/util/lp/iterator_on_row.h +++ b/src/util/lp/iterator_on_row.h @@ -26,8 +26,8 @@ struct iterator_on_row:linear_combination_iterator { unsigned m_i; // offset iterator_on_row(const vector> & row) : m_row(row), m_i(0) {} - unsigned size() const { return m_row.size(); } - bool next(T & a, unsigned & i) { + unsigned size() const override { return m_row.size(); } + bool next(T & a, unsigned & i) override { if (m_i == m_row.size()) return false; auto &c = m_row[m_i++]; @@ -35,17 +35,17 @@ struct iterator_on_row:linear_combination_iterator { a = c.get_val(); return true; } - bool next(unsigned & i) { + bool next(unsigned & i) override { if (m_i == m_row.size()) return false; auto &c = m_row[m_i++]; i = c.m_j; return true; } - void reset() { + void reset() override { m_i = 0; } - linear_combination_iterator* clone() { + linear_combination_iterator* clone() override { return new iterator_on_row(m_row); } }; diff --git a/src/util/lp/iterator_on_term_with_basis_var.h b/src/util/lp/iterator_on_term_with_basis_var.h index e566b92b5..85d11cf36 100644 --- a/src/util/lp/iterator_on_term_with_basis_var.h +++ b/src/util/lp/iterator_on_term_with_basis_var.h @@ -27,14 +27,14 @@ struct iterator_on_term_with_basis_var:linear_combination_iterator { std::unordered_map::const_iterator m_i; // the offset in term coeffs bool m_term_j_returned; unsigned m_term_j; - unsigned size() const {return static_cast(m_term.m_coeffs.size() + 1);} + unsigned size() const override {return static_cast(m_term.m_coeffs.size() + 1);} iterator_on_term_with_basis_var(const lar_term & t, unsigned term_j) : m_term(t), m_i(t.m_coeffs.begin()), m_term_j_returned(false), m_term_j(term_j) {} - bool next(mpq & a, unsigned & i) { + bool next(mpq & a, unsigned & i) override { if (m_term_j_returned == false) { m_term_j_returned = true; a = - one_of_type(); @@ -48,7 +48,7 @@ struct iterator_on_term_with_basis_var:linear_combination_iterator { m_i++; return true; } - bool next(unsigned & i) { + bool next(unsigned & i) override { if (m_term_j_returned == false) { m_term_j_returned = true; i = m_term_j; @@ -60,11 +60,11 @@ struct iterator_on_term_with_basis_var:linear_combination_iterator { m_i++; return true; } - void reset() { + void reset() override { m_term_j_returned = false; m_i = m_term.m_coeffs.begin(); } - linear_combination_iterator * clone() { + linear_combination_iterator * clone() override { iterator_on_term_with_basis_var * r = new iterator_on_term_with_basis_var(m_term, m_term_j); return r; } diff --git a/src/util/lp/lar_constraints.h b/src/util/lp/lar_constraints.h index 7b573bab7..5c33db8c6 100644 --- a/src/util/lp/lar_constraints.h +++ b/src/util/lp/lar_constraints.h @@ -59,24 +59,24 @@ public: struct lar_var_constraint: public lar_base_constraint { unsigned m_j; - vector> get_left_side_coefficients() const { + vector> get_left_side_coefficients() const override { vector> ret; ret.push_back(std::make_pair(one_of_type(), m_j)); return ret; } - unsigned size() const { return 1;} + unsigned size() const override { return 1;} lar_var_constraint(unsigned j, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_j(j) { } }; struct lar_term_constraint: public lar_base_constraint { const lar_term * m_term; - vector> get_left_side_coefficients() const { + vector> get_left_side_coefficients() const override { return m_term->coeffs_as_vector(); } - unsigned size() const { return m_term->size();} + 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) { } - virtual mpq get_free_coeff_of_left_side() const { return m_term->m_v;} + mpq get_free_coeff_of_left_side() const override { return m_term->m_v;} }; @@ -84,7 +84,6 @@ struct lar_term_constraint: public lar_base_constraint { class lar_constraint : public lar_base_constraint { public: vector> m_coeffs; - lar_constraint() {} lar_constraint(const vector> & left_side, lconstraint_kind kind, const mpq & right_side) : lar_base_constraint(kind, right_side), m_coeffs(left_side) {} @@ -92,10 +91,10 @@ public: SASSERT(false); // should not be called : todo! } - unsigned size() const { + unsigned size() const override { return static_cast(m_coeffs.size()); } - vector> get_left_side_coefficients() const { return m_coeffs; } + vector> get_left_side_coefficients() const override { return m_coeffs; } }; } diff --git a/src/util/lp/lar_core_solver.h b/src/util/lp/lar_core_solver.h index 61b0d9b38..32229cf27 100644 --- a/src/util/lp/lar_core_solver.h +++ b/src/util/lp/lar_core_solver.h @@ -481,7 +481,7 @@ public: } bool no_r_lu() const { - return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated; + return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated; } void solve_on_signature_tableau(const lar_solution_signature & signature, const vector & changes_of_basis) { diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 1f1054bb6..c26333edb 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -1011,7 +1011,7 @@ public: return ret; } - std::string get_column_name(unsigned j) const { + std::string get_column_name(unsigned j) const override { if (j >= m_terms_start_index) return std::string("_t") + T_to_string(j); if (j >= m_columns_to_ext_vars_or_term_indices.size()) diff --git a/src/util/lp/lar_solver.hpp b/src/util/lp/lar_solver.hpp new file mode 100644 index 000000000..6846717af --- /dev/null +++ b/src/util/lp/lar_solver.hpp @@ -0,0 +1,2089 @@ +#include "util/lp/lar_solver.h" +/* + Copyright (c) 2017 Microsoft Corporation + Author: Nikolaj Bjorner, Lev Nachmanson +*/ + +namespace lp { + +unsigned lar_solver::constraint_count() const { + return m_constraints.size(); +} +const lar_base_constraint& lar_solver::get_constraint(unsigned ci) const { + return *(m_constraints[ci]); +} + +////////////////// methods //////////////////////////////// +static_matrix> & lar_solver::A_r() { return m_mpq_lar_core_solver.m_r_A;} +static_matrix> const & lar_solver::A_r() const { return m_mpq_lar_core_solver.m_r_A;} +static_matrix & lar_solver::A_d() { return m_mpq_lar_core_solver.m_d_A;} +static_matrix const & lar_solver::A_d() const { return m_mpq_lar_core_solver.m_d_A;} + +lp_settings & lar_solver::settings() { return m_settings;} + +lp_settings const & lar_solver::settings() const { return m_settings;} + +void clear() {lp_assert(false); // not implemented +} + + +lar_solver::lar_solver() : m_status(lp_status::OPTIMAL), + m_infeasible_column_index(-1), + m_terms_start_index(1000000), + m_mpq_lar_core_solver(m_settings, *this), + m_tracker_of_x_change([&](unsigned j) { + call_assignment_tracker(j); + } + ), + m_int_solver(nullptr) +{} + +void lar_solver::set_track_pivoted_rows(bool v) { + m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows = v? (& m_rows_with_changed_bounds) : nullptr; +} + +bool lar_solver::get_track_pivoted_rows() const { + return m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows != nullptr; +} + + +lar_solver::~lar_solver(){ + for (auto c : m_constraints) + delete c; + for (auto t : m_terms) + delete t; +} + +bool lar_solver::is_term(var_index j) const { + return j >= m_terms_start_index && j - m_terms_start_index < m_terms.size(); +} + +unsigned lar_solver::adjust_term_index(unsigned j) const { + lp_assert(is_term(j)); + return j - m_terms_start_index; +} + + +bool lar_solver::use_lu() const { return m_settings.simplex_strategy() == simplex_strategy_enum::lu; } + +bool lar_solver::sizes_are_correct() const { + lp_assert(strategy_is_undecided() || !m_mpq_lar_core_solver.need_to_presolve_with_double_solver() || A_r().column_count() == A_d().column_count()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_column_types.size()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_x.size()); + return true; +} + + +void 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)) { + out << "it is a term number " << be.m_j << std::endl; + print_term(*m_terms[be.m_j - m_terms_start_index], out); + } + else { + out << get_column_name(v); + } + out << " " << lconstraint_kind_string(be.kind()) << " " << be.m_bound << std::endl; + out << "end of implied bound" << std::endl; +} + +bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const { + std::unordered_map coeff_map; + auto rs_of_evidence = zero_of_type(); + unsigned n_of_G = 0, n_of_L = 0; + bool strict = false; + for (auto & it : explanation) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + const auto & constr = *m_constraints[con_ind]; + lconstraint_kind kind = coeff.is_pos() ? constr.m_kind : flip_kind(constr.m_kind); + register_in_map(coeff_map, constr, coeff); + if (kind == GT || kind == LT) + strict = true; + if (kind == GE || kind == GT) n_of_G++; + else if (kind == LE || kind == LT) n_of_L++; + rs_of_evidence += coeff*constr.m_right_side; + } + lp_assert(n_of_G == 0 || n_of_L == 0); + lconstraint_kind kind = n_of_G ? GE : (n_of_L ? LE : EQ); + if (strict) + kind = static_cast((static_cast(kind) / 2)); + + if (!is_term(be.m_j)) { + if (coeff_map.size() != 1) + return false; + auto it = coeff_map.find(be.m_j); + if (it == coeff_map.end()) return false; + mpq ratio = it->second; + if (ratio < zero_of_type()) { + kind = static_cast(-kind); + } + rs_of_evidence /= ratio; + } else { + const lar_term * t = m_terms[adjust_term_index(be.m_j)]; + const auto first_coeff = *t->m_coeffs.begin(); + unsigned j = first_coeff.first; + auto it = coeff_map.find(j); + if (it == coeff_map.end()) + return false; + mpq ratio = it->second / first_coeff.second; + for (auto & p : t->m_coeffs) { + it = coeff_map.find(p.first); + if (it == coeff_map.end()) + return false; + if (p.second * ratio != it->second) + return false; + } + if (ratio < zero_of_type()) { + 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; +} + + +void lar_solver::analyze_new_bounds_on_row( + unsigned row_index, + bound_propagator & bp) { + lp_assert(!use_tableau()); + iterator_on_pivot_row it(m_mpq_lar_core_solver.get_pivot_row(), m_mpq_lar_core_solver.m_r_basis[row_index]); + + bound_analyzer_on_row ra_pos(it, + zero_of_type>(), + row_index, + bp + ); + ra_pos.analyze(); +} + +void lar_solver::analyze_new_bounds_on_row_tableau( + unsigned row_index, + bound_propagator & bp + ) { + + if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation) + return; + iterator_on_row it(A_r().m_rows[row_index]); + lp_assert(use_tableau()); + bound_analyzer_on_row::analyze_row(it, + zero_of_type>(), + row_index, + bp + ); +} + + +void lar_solver::substitute_basis_var_in_terms_for_row(unsigned i) { + // todo : create a map from term basic vars to the rows where they are used + unsigned basis_j = m_mpq_lar_core_solver.m_r_solver.m_basis[i]; + for (unsigned k = 0; k < m_terms.size(); k++) { + if (term_is_used_as_row(k)) + continue; + if (!m_terms[k]->contains(basis_j)) + continue; + m_terms[k]->subst(basis_j, m_mpq_lar_core_solver.m_r_solver.m_pivot_row); + } +} + +void lar_solver::calculate_implied_bounds_for_row(unsigned i, bound_propagator & bp) { + if(use_tableau()) { + analyze_new_bounds_on_row_tableau(i, bp); + } else { + m_mpq_lar_core_solver.calculate_pivot_row(i); + substitute_basis_var_in_terms_for_row(i); + analyze_new_bounds_on_row(i, bp); + } +} + + +linear_combination_iterator * lar_solver::create_new_iter_from_term(unsigned term_index) const { + lp_assert(false); // not implemented + return nullptr; + // new linear_combination_iterator_on_vector(m_terms[adjust_term_index(term_index)]->coeffs_as_vector()); +} + +unsigned lar_solver::adjust_column_index_to_term_index(unsigned j) const { + unsigned ext_var_or_term = m_columns_to_ext_vars_or_term_indices[j]; + return ext_var_or_term < m_terms_start_index ? j : ext_var_or_term; +} + +void lar_solver::propagate_bounds_on_a_term(const lar_term& t, bound_propagator & bp, unsigned term_offset) { + lp_assert(false); // not implemented +} + + +void lar_solver::explain_implied_bound(implied_bound & ib, bound_propagator & bp) { + unsigned i = ib.m_row_or_term_index; + int bound_sign = ib.m_is_low_bound? 1: -1; + int j_sign = (ib.m_coeff_before_j_is_pos ? 1 :-1) * bound_sign; + unsigned m_j = ib.m_j; + if (is_term(m_j)) { + auto it = m_ext_vars_to_columns.find(m_j); + lp_assert(it != m_ext_vars_to_columns.end()); + m_j = it->second.ext_j(); + } + for (auto const& r : A_r().m_rows[i]) { + unsigned j = r.m_j; + mpq const& a = r.get_val(); + if (j == m_j) continue; + if (is_term(j)) { + auto it = m_ext_vars_to_columns.find(j); + lp_assert(it != m_ext_vars_to_columns.end()); + j = it->second.ext_j(); + } + int a_sign = is_pos(a)? 1: -1; + int sign = j_sign * a_sign; + const ul_pair & ul = m_columns_to_ul_pairs[j]; + auto witness = sign > 0? ul.upper_bound_witness(): ul.low_bound_witness(); + lp_assert(is_valid(witness)); + bp.consume(a, witness); + } + // lp_assert(implied_bound_is_correctly_explained(ib, explanation)); +} + + +bool lar_solver::term_is_used_as_row(unsigned term) const { + lp_assert(is_term(term)); + return contains(m_ext_vars_to_columns, term); +} + +void lar_solver::propagate_bounds_on_terms(bound_propagator & bp) { + for (unsigned i = 0; i < m_terms.size(); i++) { + if (term_is_used_as_row(i + m_terms_start_index)) + continue; // this term is used a left side of a constraint, + // it was processed as a touched row if needed + propagate_bounds_on_a_term(*m_terms[i], bp, i); + } +} + + +// goes over touched rows and tries to induce bounds +void lar_solver::propagate_bounds_for_touched_rows(bound_propagator & bp) { + if (!use_tableau()) + return; // todo: consider to remove the restriction + + for (unsigned i : m_rows_with_changed_bounds.m_index) { + calculate_implied_bounds_for_row(i, bp); + } + m_rows_with_changed_bounds.clear(); + if (!use_tableau()) { + propagate_bounds_on_terms(bp); + } +} + +lp_status lar_solver::get_status() const { return m_status;} + +void lar_solver::set_status(lp_status s) {m_status = s;} + +bool lar_solver::has_int_var() const { + return m_mpq_lar_core_solver.m_r_solver.m_tracker_of_x_change != nullptr; +} + +lp_status lar_solver::find_feasible_solution() { + lp_assert(inf_int_set_is_correct()); + m_settings.st().m_make_feasible++; + if (A_r().column_count() > m_settings.st().m_max_cols) + m_settings.st().m_max_cols = A_r().column_count(); + if (A_r().row_count() > m_settings.st().m_max_rows) + m_settings.st().m_max_rows = A_r().row_count(); + if (strategy_is_undecided()) + decide_on_strategy_and_adjust_initial_state(); + + if (has_int_var()) { + m_inf_int_set.resize(A_r().column_count()); + } + + m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = true; + auto ret = solve(); + TRACE("intinf", m_int_solver->display_inf_or_int_inf_columns(tout);); + lp_assert(inf_int_set_is_correct()); + return ret; +} + +lp_status lar_solver::solve() { + lp_assert(inf_int_set_is_correct()); + if (m_status == lp_status::INFEASIBLE) { + return m_status; + } + solve_with_core_solver(); + if (m_status != lp_status::INFEASIBLE) { + if (m_settings.bound_propagation()) + detect_rows_with_changed_bounds(); + } + + m_columns_with_changed_bound.clear(); + lp_assert(inf_int_set_is_correct()); + return m_status; +} + +void lar_solver::fill_explanation_from_infeasible_column(vector> & evidence) const{ + + // this is the case when the lower bound is in conflict with the upper one + const ul_pair & ul = m_columns_to_ul_pairs[m_infeasible_column_index]; + evidence.push_back(std::make_pair(numeric_traits::one(), ul.upper_bound_witness())); + evidence.push_back(std::make_pair(-numeric_traits::one(), ul.low_bound_witness())); +} + + +unsigned lar_solver::get_total_iterations() const { return m_mpq_lar_core_solver.m_r_solver.total_iterations(); } +vector lar_solver::get_list_of_all_var_indices() const { + vector ret; + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_heading.size(); j++) + ret.push_back(j); + return ret; +} +void lar_solver::push() { + m_simplex_strategy = m_settings.simplex_strategy(); + m_simplex_strategy.push(); + m_columns_to_ul_pairs.push(); + m_infeasible_column_index.push(); + m_mpq_lar_core_solver.push(); + m_term_count = m_terms.size(); + m_term_count.push(); + m_constraint_count = m_constraints.size(); + m_constraint_count.push(); +} + +void lar_solver::clean_popped_elements(unsigned n, int_set& set) { + vector to_remove; + for (unsigned j: set.m_index) + if (j >= n) + to_remove.push_back(j); + for (unsigned j : to_remove) + set.erase(j); +} + +void lar_solver::shrink_inf_set_after_pop(unsigned n, int_set & set) { + clean_popped_elements(n, set); + set.resize(n); +} + + +void lar_solver::pop(unsigned k) { + TRACE("arith_int", tout << "pop" << std::endl;); + lp_assert(inf_int_set_is_correct()); + TRACE("lar_solver", tout << "k = " << k << std::endl;); + + int n_was = static_cast(m_ext_vars_to_columns.size()); + m_infeasible_column_index.pop(k); + unsigned n = m_columns_to_ul_pairs.peek_size(k); + for (unsigned j = n_was; j-- > n;) + m_ext_vars_to_columns.erase(m_columns_to_ext_vars_or_term_indices[j]); + m_columns_to_ext_vars_or_term_indices.resize(n); + TRACE("arith_int", tout << "pop" << std::endl;); + if (m_settings.use_tableau()) { + pop_tableau(); + } + lp_assert(A_r().column_count() == n); + m_columns_to_ul_pairs.pop(k); + + m_mpq_lar_core_solver.pop(k); + clean_popped_elements(n, m_columns_with_changed_bound); + clean_popped_elements(n, m_inf_int_set); + unsigned m = A_r().row_count(); + lp_assert(inf_int_set_is_correct()); + clean_popped_elements(m, m_rows_with_changed_bounds); + lp_assert(inf_int_set_is_correct()); + clean_inf_set_of_r_solver_after_pop(); + lp_assert(m_settings.simplex_strategy() == simplex_strategy_enum::undecided || + (!use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + + + lp_assert(ax_is_correct()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.inf_set_is_correct()); + m_constraint_count.pop(k); + for (unsigned i = m_constraint_count; i < m_constraints.size(); i++) + delete m_constraints[i]; + + m_constraints.resize(m_constraint_count); + m_term_count.pop(k); + for (unsigned i = m_term_count; i < m_terms.size(); i++) { + delete m_terms[i]; + } + m_terms.resize(m_term_count); + m_simplex_strategy.pop(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; + +} + +vector lar_solver::get_all_constraint_indices() const { + vector ret; + constraint_index i = 0; + while ( i < m_constraints.size()) + ret.push_back(i++); + return ret; +} + +bool lar_solver::maximize_term_on_tableau(const vector> & term, + impq &term_max) { + if (settings().simplex_strategy() == simplex_strategy_enum::undecided) + decide_on_strategy_and_adjust_initial_state(); + + m_mpq_lar_core_solver.solve(); + if (m_mpq_lar_core_solver.m_r_solver.get_status() == lp_status::UNBOUNDED) + return false; + + term_max = 0; + for (auto & p : term) + term_max += p.first * m_mpq_lar_core_solver.m_r_x[p.second]; + + return true; +} + +bool lar_solver::costs_are_zeros_for_r_solver() const { + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_costs.size(); j++) { + lp_assert(is_zero(m_mpq_lar_core_solver.m_r_solver.m_costs[j])); + } + return true; +} +bool lar_solver::reduced_costs_are_zeroes_for_r_solver() const { + for (unsigned j = 0; j < m_mpq_lar_core_solver.m_r_solver.m_d.size(); j++) { + lp_assert(is_zero(m_mpq_lar_core_solver.m_r_solver.m_d[j])); + } + return true; +} + +void lar_solver::set_costs_to_zero(const vector> & term) { + auto & rslv = m_mpq_lar_core_solver.m_r_solver; + auto & jset = m_mpq_lar_core_solver.m_r_solver.m_inf_set; // hijack this set that should be empty right now + lp_assert(jset.m_index.size()==0); + + for (auto & p : term) { + unsigned j = p.second; + rslv.m_costs[j] = zero_of_type(); + int i = rslv.m_basis_heading[j]; + if (i < 0) + jset.insert(j); + else { + for (auto & rc : A_r().m_rows[i]) + jset.insert(rc.m_j); + } + } + + for (unsigned j : jset.m_index) + rslv.m_d[j] = zero_of_type(); + + jset.clear(); + + lp_assert(reduced_costs_are_zeroes_for_r_solver()); + lp_assert(costs_are_zeros_for_r_solver()); +} + +void lar_solver::prepare_costs_for_r_solver(const vector> & term) { + + auto & rslv = m_mpq_lar_core_solver.m_r_solver; + rslv.m_using_infeas_costs = false; + lp_assert(costs_are_zeros_for_r_solver()); + lp_assert(reduced_costs_are_zeroes_for_r_solver()); + rslv.m_costs.resize(A_r().column_count(), zero_of_type()); + for (auto & p : term) { + unsigned j = p.second; + rslv.m_costs[j] = p.first; + if (rslv.m_basis_heading[j] < 0) + rslv.m_d[j] += p.first; + else + rslv.update_reduced_cost_for_basic_column_cost_change(- p.first, j); + } + lp_assert(rslv.reduced_costs_are_correct_tableau()); +} + +bool lar_solver::maximize_term_on_corrected_r_solver(const vector> & term, + impq &term_max) { + settings().backup_costs = false; + switch (settings().simplex_strategy()) { + case simplex_strategy_enum::tableau_rows: + prepare_costs_for_r_solver(term); + settings().simplex_strategy() = simplex_strategy_enum::tableau_costs; + { + bool ret = maximize_term_on_tableau(term, term_max); + settings().simplex_strategy() = simplex_strategy_enum::tableau_rows; + set_costs_to_zero(term); + m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); + return ret; + } + case simplex_strategy_enum::tableau_costs: + prepare_costs_for_r_solver(term); + { + bool ret = maximize_term_on_tableau(term, term_max); + set_costs_to_zero(term); + m_mpq_lar_core_solver.m_r_solver.set_status(lp_status::OPTIMAL); + return ret; + } + + case simplex_strategy_enum::lu: + lp_assert(false); // not implemented + return false; + default: + lp_unreachable(); // wrong mode + } + return false; +} +// starting from a given feasible state look for the maximum of the term +// return true if found and false if unbounded +bool lar_solver::maximize_term(const vector> & term, + impq &term_max) { + lp_assert(m_mpq_lar_core_solver.m_r_solver.current_x_is_feasible()); + m_mpq_lar_core_solver.m_r_solver.m_look_for_feasible_solution_only = false; + return maximize_term_on_corrected_r_solver(term, term_max); +} + + + +const lar_term & lar_solver::get_term(unsigned j) const { + lp_assert(j >= m_terms_start_index); + return *m_terms[j - m_terms_start_index]; +} + +void lar_solver::pop_core_solver_params() { + pop_core_solver_params(1); +} + +void lar_solver::pop_core_solver_params(unsigned k) { + A_r().pop(k); + A_d().pop(k); +} + + +void lar_solver::set_upper_bound_witness(var_index j, constraint_index ci) { + ul_pair ul = m_columns_to_ul_pairs[j]; + ul.upper_bound_witness() = ci; + m_columns_to_ul_pairs[j] = ul; +} + +void lar_solver::set_low_bound_witness(var_index j, constraint_index ci) { + ul_pair ul = m_columns_to_ul_pairs[j]; + ul.low_bound_witness() = ci; + m_columns_to_ul_pairs[j] = ul; +} + +void lar_solver::register_monoid_in_map(std::unordered_map & coeffs, const mpq & a, unsigned j) { + auto it = coeffs.find(j); + if (it == coeffs.end()) { + coeffs[j] = a; + } else { + it->second += a; + } +} + + +void lar_solver::substitute_terms_in_linear_expression(const vector>& left_side_with_terms, + vector> &left_side, mpq & free_coeff) const { + std::unordered_map coeffs; + for (auto & t : left_side_with_terms) { + unsigned j = t.second; + if (!is_term(j)) { + register_monoid_in_map(coeffs, t.first, j); + } else { + const lar_term & term = * m_terms[adjust_term_index(t.second)]; + for (auto & p : term.coeffs()){ + register_monoid_in_map(coeffs, t.first * p.second , p.first); + } + free_coeff += t.first * term.m_v; + } + } + + for (auto & p : coeffs) + left_side.push_back(std::make_pair(p.second, p.first)); +} + + +void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column(unsigned j) { + if (A_r().row_count() != m_column_buffer.data_size()) + m_column_buffer.resize(A_r().row_count()); + else + m_column_buffer.clear(); + lp_assert(m_column_buffer.size() == 0 && m_column_buffer.is_OK()); + + m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); + for (unsigned i : m_column_buffer.m_index) + m_rows_with_changed_bounds.insert(i); +} + + + +void lar_solver::detect_rows_of_bound_change_column_for_nbasic_column_tableau(unsigned j) { + for (auto & rc : m_mpq_lar_core_solver.m_r_A.m_columns[j]) + m_rows_with_changed_bounds.insert(rc.m_i); +} + +bool lar_solver::use_tableau() const { return m_settings.use_tableau(); } + +bool lar_solver::use_tableau_costs() const { + return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; +} + +void lar_solver::detect_rows_of_column_with_bound_change(unsigned j) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { // it is a basic column + // just mark the row at touched and exit + m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); + return; + } + + if (use_tableau()) + detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); + else + detect_rows_of_bound_change_column_for_nbasic_column(j); +} + +void lar_solver::adjust_x_of_column(unsigned j) { + lp_assert(false); +} + +bool lar_solver::row_is_correct(unsigned i) const { + numeric_pair r = zero_of_type>(); + for (const auto & c : A_r().m_rows[i]) + r += c.m_value * m_mpq_lar_core_solver.m_r_x[c.m_j]; + return is_zero(r); +} + +bool lar_solver::ax_is_correct() const { + for (unsigned i = 0; i < A_r().row_count(); i++) { + if (!row_is_correct(i)) + return false; + } + return true; +} + +bool lar_solver::tableau_with_costs() const { + return m_settings.simplex_strategy() == simplex_strategy_enum::tableau_costs; +} + +bool lar_solver::costs_are_used() const { + return m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows; +} + +void lar_solver::change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair & delta) { + lp_assert(inf_int_set_is_correct()); + if (use_tableau()) { + for (const auto & c : A_r().m_columns[j]) { + unsigned bj = m_mpq_lar_core_solver.m_r_basis[c.m_i]; + if (tableau_with_costs()) { + m_basic_columns_with_changed_cost.insert(bj); + } + m_mpq_lar_core_solver.m_r_solver.update_x_with_delta_and_track_feasibility(bj, - A_r().get_val(c) * delta); + TRACE("change_x_del", + tout << "changed basis column " << bj << ", it is " << + ( m_mpq_lar_core_solver.m_r_solver.column_is_feasible(bj)? "feas":"inf") << std::endl;); + + } + } else { + m_column_buffer.clear(); + m_column_buffer.resize(A_r().row_count()); + m_mpq_lar_core_solver.m_r_solver.solve_Bd(j, m_column_buffer); + for (unsigned i : m_column_buffer.m_index) { + unsigned bj = m_mpq_lar_core_solver.m_r_basis[i]; + m_mpq_lar_core_solver.m_r_solver.update_x_with_delta_and_track_feasibility(bj, -m_column_buffer[i] * delta); + } + } + lp_assert(inf_int_set_is_correct()); +} + +void lar_solver::update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + if (costs_are_used()) { + bool was_infeas = m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j); + m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); + if (was_infeas != m_mpq_lar_core_solver.m_r_solver.m_inf_set.contains(j)) + m_basic_columns_with_changed_cost.insert(j); + } else { + m_mpq_lar_core_solver.m_r_solver.track_column_feasibility(j); + } + } else { + numeric_pair delta; + if (m_mpq_lar_core_solver.m_r_solver.make_column_feasible(j, delta)) + change_basic_columns_dependend_on_a_given_nb_column(j, delta); + } +} + + +void lar_solver::detect_rows_with_changed_bounds_for_column(unsigned j) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + m_rows_with_changed_bounds.insert(m_mpq_lar_core_solver.m_r_heading[j]); + return; + } + + if (use_tableau()) + detect_rows_of_bound_change_column_for_nbasic_column_tableau(j); + else + detect_rows_of_bound_change_column_for_nbasic_column(j); +} + +void lar_solver::detect_rows_with_changed_bounds() { + for (auto j : m_columns_with_changed_bound.m_index) + detect_rows_with_changed_bounds_for_column(j); +} + +void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds() { + for (auto j : m_columns_with_changed_bound.m_index) + update_x_and_inf_costs_for_column_with_changed_bounds(j); +} + +void lar_solver::update_x_and_inf_costs_for_columns_with_changed_bounds_tableau() { + for (auto j : m_columns_with_changed_bound.m_index) + update_x_and_inf_costs_for_column_with_changed_bounds(j); + + if (tableau_with_costs()) { + for (unsigned j : m_basic_columns_with_changed_cost.m_index) + m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); + lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + } +} + + +void lar_solver::solve_with_core_solver() { + if (!use_tableau()) + add_last_rows_to_lu(m_mpq_lar_core_solver.m_r_solver); + if (m_mpq_lar_core_solver.need_to_presolve_with_double_solver()) { + add_last_rows_to_lu(m_mpq_lar_core_solver.m_d_solver); + } + m_mpq_lar_core_solver.prefix_r(); + if (costs_are_used()) { + m_basic_columns_with_changed_cost.clear(); + m_basic_columns_with_changed_cost.resize(m_mpq_lar_core_solver.m_r_x.size()); + } + if (use_tableau()) + update_x_and_inf_costs_for_columns_with_changed_bounds_tableau(); + else + update_x_and_inf_costs_for_columns_with_changed_bounds(); + TRACE("intinf", m_int_solver->display_inf_or_int_inf_columns(tout);); + m_mpq_lar_core_solver.solve(); + set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); + lp_assert(inf_int_set_is_correct()); + lp_assert(m_status != lp_status::OPTIMAL || all_constraints_hold()); +} + + +numeric_pair lar_solver::get_basic_var_value_from_row_directly(unsigned i) { + numeric_pair r = zero_of_type>(); + + unsigned bj = m_mpq_lar_core_solver.m_r_solver.m_basis[i]; + for (const auto & c: A_r().m_rows[i]) { + if (c.m_j == bj) continue; + const auto & x = m_mpq_lar_core_solver.m_r_x[c.m_j]; + if (!is_zero(x)) + r -= c.m_value * x; + } + return r; +} + +numeric_pair lar_solver::get_basic_var_value_from_row(unsigned i) { + if (settings().use_tableau()) { + return get_basic_var_value_from_row_directly(i); + } + + numeric_pair r = zero_of_type>(); + m_mpq_lar_core_solver.calculate_pivot_row(i); + for (unsigned j : m_mpq_lar_core_solver.m_r_solver.m_pivot_row.m_index) { + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis_heading[j] < 0); + r -= m_mpq_lar_core_solver.m_r_solver.m_pivot_row.m_data[j] * m_mpq_lar_core_solver.m_r_x[j]; + } + return r; +} + +template +void lar_solver::add_last_rows_to_lu(lp_primal_core_solver & s) { + auto & f = s.m_factorization; + if (f != nullptr) { + auto columns_to_replace = f->get_set_of_columns_to_replace_for_add_last_rows(s.m_basis_heading); + if (f->m_refactor_counter + columns_to_replace.size() >= 200 || f->has_dense_submatrix()) { + delete f; + f = nullptr; + } else { + f->add_last_rows_to_B(s.m_basis_heading, columns_to_replace); + } + } + if (f == nullptr) { + init_factorization(f, s.m_A, s.m_basis, m_settings); + if (f->get_status() != LU_status::OK) { + delete f; + f = nullptr; + } + } + +} + +bool lar_solver::x_is_correct() const { + if (m_mpq_lar_core_solver.m_r_x.size() != A_r().column_count()) { + // std::cout << "the size is off " << m_r_solver.m_x.size() << ", " << A().column_count() << std::endl; + return false; + } + for (unsigned i = 0; i < A_r().row_count(); i++) { + numeric_pair delta = A_r().dot_product_with_row(i, m_mpq_lar_core_solver.m_r_x); + if (!delta.is_zero()) { + // std::cout << "x is off ("; + // std::cout << "m_b[" << i << "] = " << m_b[i] << " "; + // std::cout << "left side = " << A().dot_product_with_row(i, m_r_solver.m_x) << ' '; + // std::cout << "delta = " << delta << ' '; + // std::cout << "iters = " << total_iterations() << ")" << std::endl; + // std::cout << "row " << i << " is off" << std::endl; + return false; + } + } + return true;; + +} + +bool lar_solver::var_is_registered(var_index vj) const { + if (vj >= m_terms_start_index) { + if (vj - m_terms_start_index >= m_terms.size()) + return false; + } else if ( vj >= A_r().column_count()) { + return false; + } + return true; +} + +unsigned lar_solver::constraint_stack_size() const { + return m_constraint_count.stack_size(); +} + +void lar_solver::fill_last_row_of_A_r(static_matrix> & A, const lar_term * ls) { + lp_assert(A.row_count() > 0); + lp_assert(A.column_count() > 0); + unsigned last_row = A.row_count() - 1; + lp_assert(A.m_rows[last_row].size() == 0); + for (auto & t : ls->m_coeffs) { + lp_assert(!is_zero(t.second)); + var_index j = t.first; + A.set(last_row, j, - t.second); + } + unsigned basis_j = A.column_count() - 1; + A.set(last_row, basis_j, mpq(1)); +} + +template +void lar_solver::create_matrix_A(static_matrix & matr) { + lp_assert(false); // not implemented + /* + unsigned m = number_or_nontrivial_left_sides(); + unsigned n = m_vec_of_canonic_left_sides.size(); + if (matr.row_count() == m && matr.column_count() == n) + return; + matr.init_empty_matrix(m, n); + copy_from_mpq_matrix(matr); + */ +} + +template +void lar_solver::copy_from_mpq_matrix(static_matrix & matr) { + matr.m_rows.resize(A_r().row_count()); + matr.m_columns.resize(A_r().column_count()); + for (unsigned i = 0; i < matr.row_count(); i++) { + for (auto & it : A_r().m_rows[i]) { + matr.set(i, it.m_j, convert_struct::convert(it.get_val())); + } + } +} + + +bool lar_solver::try_to_set_fixed(column_info & ci) { + if (ci.upper_bound_is_set() && ci.low_bound_is_set() && ci.get_upper_bound() == ci.get_low_bound() && !ci.is_fixed()) { + ci.set_fixed_value(ci.get_upper_bound()); + return true; + } + 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_low_bound() == ci.get_upper_bound()) + ret = column_type::fixed; + } + return ret; +} + +std::string lar_solver::get_column_name(unsigned j) const { + if (j >= m_terms_start_index) + return std::string("_t") + T_to_string(j); + if (j >= m_columns_to_ext_vars_or_term_indices.size()) + return std::string("_s") + T_to_string(j); + + return std::string("v") + T_to_string(m_columns_to_ext_vars_or_term_indices[j]); +} + +bool lar_solver::all_constrained_variables_are_registered(const vector>& left_side) { + for (auto it : left_side) { + if (! var_is_registered(it.second)) + return false; + } + return true; +} + +bool lar_solver::all_constraints_hold() const { + if (m_settings.get_cancel_flag()) + return true; + std::unordered_map var_map; + get_model_do_not_care_about_diff_vars(var_map); + + for (unsigned i = 0; i < m_constraints.size(); i++) { + if (!constraint_holds(*m_constraints[i], var_map)) { + print_constraint(i, std::cout); + return false; + } + } + return true; +} + +bool lar_solver::constraint_holds(const lar_base_constraint & constr, std::unordered_map & var_map) const { + return true; + mpq left_side_val = get_left_side_val(constr, var_map); + switch (constr.m_kind) { + case LE: return left_side_val <= constr.m_right_side; + case LT: return left_side_val < constr.m_right_side; + case GE: 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(); + } + return false; // it is unreachable +} + +bool lar_solver::the_relations_are_of_same_type(const vector> & evidence, lconstraint_kind & the_kind_of_sum) const { + unsigned n_of_G = 0, n_of_L = 0; + bool strict = false; + for (auto & it : evidence) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + lconstraint_kind kind = coeff.is_pos() ? + m_constraints[con_ind]->m_kind : + flip_kind(m_constraints[con_ind]->m_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++; + } + the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ); + if (strict) + the_kind_of_sum = static_cast((static_cast(the_kind_of_sum) / 2)); + + return n_of_G == 0 || n_of_L == 0; +} + +void lar_solver::register_in_map(std::unordered_map & coeffs, const lar_base_constraint & cn, const mpq & a) { + for (auto & it : cn.get_left_side_coefficients()) { + unsigned j = it.second; + auto p = coeffs.find(j); + if (p == coeffs.end()) + coeffs[j] = it.first * a; + else { + p->second += it.first * a; + if (p->second.is_zero()) + coeffs.erase(p); + } + } +} + +bool lar_solver::the_left_sides_sum_to_zero(const vector> & evidence) const { + std::unordered_map coeff_map; + for (auto & it : evidence) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + lp_assert(con_ind < m_constraints.size()); + register_in_map(coeff_map, *m_constraints[con_ind], coeff); + } + + if (!coeff_map.empty()) { + std::cout << "left side = "; + vector> t; + for (auto & it : coeff_map) { + t.push_back(std::make_pair(it.second, it.first)); + } + print_linear_combination_of_column_indices(t, std::cout); + std::cout << std::endl; + return false; + } + + return true; +} + +bool lar_solver::the_right_sides_do_not_sum_to_zero(const vector> & evidence) { + mpq ret = numeric_traits::zero(); + for (auto & it : evidence) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + lp_assert(con_ind < m_constraints.size()); + const lar_constraint & constr = *m_constraints[con_ind]; + ret += constr.m_right_side * coeff; + } + return !numeric_traits::is_zero(ret); +} + +bool lar_solver::explanation_is_correct(const vector>& explanation) const { +#ifdef LEAN_DEBUG + lconstraint_kind kind; + lp_assert(the_relations_are_of_same_type(explanation, kind)); + lp_assert(the_left_sides_sum_to_zero(explanation)); + mpq rs = sum_of_right_sides_of_explanation(explanation); + switch (kind) { + case LE: lp_assert(rs < zero_of_type()); + break; + case LT: lp_assert(rs <= zero_of_type()); + break; + case GE: lp_assert(rs > zero_of_type()); + break; + case GT: lp_assert(rs >= zero_of_type()); + break; + case EQ: lp_assert(rs != zero_of_type()); + break; + default: + lp_assert(false); + return false; + } +#endif + return true; +} + +bool lar_solver::inf_explanation_is_correct() const { +#ifdef LEAN_DEBUG + vector> explanation; + get_infeasibility_explanation(explanation); + return explanation_is_correct(explanation); +#endif + return true; +} + +mpq lar_solver::sum_of_right_sides_of_explanation(const vector> & explanation) const { + mpq ret = numeric_traits::zero(); + for (auto & it : explanation) { + mpq coeff = it.first; + constraint_index con_ind = it.second; + lp_assert(con_ind < m_constraints.size()); + ret += (m_constraints[con_ind]->m_right_side - m_constraints[con_ind]->get_free_coeff_of_left_side()) * coeff; + } + return ret; +} + +bool lar_solver::has_lower_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) { + + if (var >= m_columns_to_ul_pairs.size()) { + // TBD: bounds on terms could also be used, caller may have to track these. + return false; + } + const ul_pair & ul = m_columns_to_ul_pairs[var]; + ci = ul.low_bound_witness(); + if (ci != static_cast(-1)) { + auto& p = m_mpq_lar_core_solver.m_r_low_bounds()[var]; + value = p.x; + is_strict = p.y.is_pos(); + return true; + } + else { + return false; + } +} + +bool lar_solver::has_upper_bound(var_index var, constraint_index& ci, mpq& value, bool& is_strict) { + + if (var >= m_columns_to_ul_pairs.size()) { + // TBD: bounds on terms could also be used, caller may have to track these. + return false; + } + const ul_pair & ul = m_columns_to_ul_pairs[var]; + ci = ul.upper_bound_witness(); + if (ci != static_cast(-1)) { + auto& p = m_mpq_lar_core_solver.m_r_upper_bounds()[var]; + value = p.x; + is_strict = p.y.is_neg(); + return true; + } + else { + return false; + } +} + +void lar_solver::get_infeasibility_explanation(vector> & explanation) const { + explanation.clear(); + if (m_infeasible_column_index != -1) { + fill_explanation_from_infeasible_column(explanation); + return; + } + if (m_mpq_lar_core_solver.get_infeasible_sum_sign() == 0) { + return; + } + // the infeasibility sign + int inf_sign; + auto inf_row = m_mpq_lar_core_solver.get_infeasibility_info(inf_sign); + get_infeasibility_explanation_for_inf_sign(explanation, inf_row, inf_sign); + lp_assert(explanation_is_correct(explanation)); +} + + + +void lar_solver::get_infeasibility_explanation_for_inf_sign( + vector> & explanation, + const vector> & inf_row, + int inf_sign) const { + + for (auto & it : inf_row) { + mpq coeff = it.first; + unsigned j = it.second; + + int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign; + const ul_pair & ul = m_columns_to_ul_pairs[j]; + + constraint_index bound_constr_i = adj_sign < 0 ? ul.upper_bound_witness() : ul.low_bound_witness(); + lp_assert(bound_constr_i < m_constraints.size()); + explanation.push_back(std::make_pair(coeff, bound_constr_i)); + } +} + +void lar_solver::get_model(std::unordered_map & variable_values) const { + mpq delta = mpq(1, 2); // start from 0.5 to have less clashes + lp_assert(m_status == lp_status::OPTIMAL); + unsigned i; + do { + // different pairs have to produce different singleton 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); + 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); + mpq x = rp.x + delta * rp.y; + set_of_different_singles.insert(x); + if (set_of_different_pairs.size() + != set_of_different_singles.size()) { + delta /= mpq(2); + break; + } + + variable_values[i] = x; + } + } while (i != m_mpq_lar_core_solver.m_r_x.size()); +} + +void lar_solver::get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const { + mpq delta = mpq(1); + delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta); + for (unsigned i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) { + const impq & rp = m_mpq_lar_core_solver.m_r_x[i]; + variable_values[i] = rp.x + delta * rp.y; + } +} + + +std::string lar_solver::get_variable_name(var_index vi) const { + return get_column_name(vi); +} + +// ********** print region start +void 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; + } + + print_constraint(m_constraints[ci], out); +} + +void lar_solver::print_constraints(std::ostream& out) const { + for (auto c : m_constraints) { + print_constraint(c, out); + } +} + +void lar_solver::print_terms(std::ostream& out) const { + for (auto it : m_terms) { + print_term(*it, out); + out << "\n"; + } +} + +void 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; + +} + +void lar_solver::print_term(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(term.coeffs_as_vector(), out); +} + +void 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); +} + +mpq lar_solver::get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const { + mpq ret = cns.get_free_coeff_of_left_side(); + for (auto & it : cns.get_left_side_coefficients()) { + var_index j = it.second; + auto vi = var_map.find(j); + lp_assert(vi != var_map.end()); + ret += it.first * vi->second; + } + return ret; +} + +void 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; +} + +void lar_solver::fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list) { + for (unsigned i = 0; i < sz; i++) { + var_index var = vars[i]; + if (var >= m_terms_start_index) { // handle the term + for (auto & it : m_terms[var - m_terms_start_index]->m_coeffs) { + column_list.push_back(it.first); + } + } else { + column_list.push_back(var); + } + } +} + +void lar_solver::random_update(unsigned sz, var_index const * vars) { + vector column_list; + fill_var_set_for_random_update(sz, vars, column_list); + random_updater ru(*this, column_list); + ru.update(); + lp_assert(inf_int_set_is_correct()); +} + + +void lar_solver::pivot_fixed_vars_from_basis() { + m_mpq_lar_core_solver.m_r_solver.pivot_fixed_vars_from_basis(); +} + +void lar_solver::pop() { + pop(1); +} + +bool lar_solver::column_represents_row_in_tableau(unsigned j) { + return m_columns_to_ul_pairs()[j].m_i != static_cast(-1); +} + +void lar_solver::make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j) { + // i, j - is the indices of the bottom-right element of the tableau + lp_assert(A_r().row_count() == i + 1 && A_r().column_count() == j + 1); + auto & last_column = A_r().m_columns[j]; + int non_zero_column_cell_index = -1; + for (unsigned k = last_column.size(); k-- > 0;){ + auto & cc = last_column[k]; + if (cc.m_i == i) + return; + non_zero_column_cell_index = k; + } + + lp_assert(non_zero_column_cell_index != -1); + lp_assert(static_cast(non_zero_column_cell_index) != i); + m_mpq_lar_core_solver.m_r_solver.transpose_rows_tableau(last_column[non_zero_column_cell_index].m_i, i); +} + +void lar_solver::remove_last_row_and_column_from_tableau(unsigned j) { + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); + auto & slv = m_mpq_lar_core_solver.m_r_solver; + unsigned i = A_r().row_count() - 1; //last row index + make_sure_that_the_bottom_right_elem_not_zero_in_tableau(i, j); + if (slv.m_basis_heading[j] < 0) { + slv.pivot_column_tableau(j, i); + } + + auto & last_row = A_r().m_rows[i]; + mpq &cost_j = m_mpq_lar_core_solver.m_r_solver.m_costs[j]; + bool cost_is_nz = !is_zero(cost_j); + for (unsigned k = last_row.size(); k-- > 0;) { + auto &rc = last_row[k]; + if (cost_is_nz) { + m_mpq_lar_core_solver.m_r_solver.m_d[rc.m_j] += cost_j*rc.get_val(); + } + + A_r().remove_element(last_row, rc); + } + lp_assert(last_row.size() == 0); + lp_assert(A_r().m_columns[j].size() == 0); + A_r().m_rows.pop_back(); + A_r().m_columns.pop_back(); + slv.m_b.pop_back(); +} + +void lar_solver::remove_last_column_from_A() { + // the last column has to be empty + lp_assert(A_r().m_columns.back().size() == 0); + A_r().m_columns.pop_back(); +} + +void lar_solver::remove_last_column_from_basis_tableau(unsigned j) { + auto& rslv = m_mpq_lar_core_solver.m_r_solver; + int i = rslv.m_basis_heading[j]; + if (i >= 0) { // j is a basic var + int last_pos = static_cast(rslv.m_basis.size()) - 1; + lp_assert(last_pos >= 0); + if (i != last_pos) { + unsigned j_at_last_pos = rslv.m_basis[last_pos]; + rslv.m_basis[i] = j_at_last_pos; + rslv.m_basis_heading[j_at_last_pos] = i; + } + rslv.m_basis.pop_back(); // remove j from the basis + } else { + int last_pos = static_cast(rslv.m_nbasis.size()) - 1; + lp_assert(last_pos >= 0); + i = - 1 - i; + if (i != last_pos) { + unsigned j_at_last_pos = rslv.m_nbasis[last_pos]; + rslv.m_nbasis[i] = j_at_last_pos; + rslv.m_basis_heading[j_at_last_pos] = - i - 1; + } + rslv.m_nbasis.pop_back(); // remove j from the basis + } + rslv.m_basis_heading.pop_back(); + lp_assert(rslv.m_basis.size() == A_r().row_count()); + lp_assert(rslv.basis_heading_is_correct()); +} + +void lar_solver::remove_last_column_from_tableau() { + auto& rslv = m_mpq_lar_core_solver.m_r_solver; + unsigned j = A_r().column_count() - 1; + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); + if (column_represents_row_in_tableau(j)) { + remove_last_row_and_column_from_tableau(j); + if (rslv.m_basis_heading[j] < 0) + rslv.change_basis_unconditionally(j, rslv.m_basis[A_r().row_count()]); // A_r().row_count() is the index of the last row in the basis still + } + else { + remove_last_column_from_A(); + } + rslv.m_x.pop_back(); + rslv.m_d.pop_back(); + rslv.m_costs.pop_back(); + + remove_last_column_from_basis_tableau(j); + lp_assert(m_mpq_lar_core_solver.r_basis_is_OK()); + lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); +} + +void lar_solver::pop_tableau() { + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); + + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); + // We remove last variables starting from m_column_names.size() to m_vec_of_canonic_left_sides.size(). + // At this moment m_column_names is already popped + while (A_r().column_count() > m_columns_to_ext_vars_or_term_indices.size()) + remove_last_column_from_tableau(); + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_costs.size() == A_r().column_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis.size() == A_r().row_count()); + lp_assert(m_mpq_lar_core_solver.m_r_solver.basis_heading_is_correct()); +} + +void lar_solver::clean_inf_set_of_r_solver_after_pop() { + lp_assert(inf_int_set_is_correct()); + vector became_feas; + clean_popped_elements(A_r().column_count(), m_mpq_lar_core_solver.m_r_solver.m_inf_set); + std::unordered_set basic_columns_with_changed_cost; + auto inf_index_copy = m_mpq_lar_core_solver.m_r_solver.m_inf_set.m_index; + for (auto j: inf_index_copy) { + if (m_mpq_lar_core_solver.m_r_heading[j] >= 0) { + continue; + } + // some basic columns might become non-basic - these columns need to be made feasible + numeric_pair delta; + if (m_mpq_lar_core_solver.m_r_solver.make_column_feasible(j, delta)) { + + change_basic_columns_dependend_on_a_given_nb_column(j, delta); + } + became_feas.push_back(j); + } + + for (unsigned j : became_feas) { + lp_assert(m_mpq_lar_core_solver.m_r_solver.m_basis_heading[j] < 0); + m_mpq_lar_core_solver.m_r_solver.m_d[j] -= m_mpq_lar_core_solver.m_r_solver.m_costs[j]; + m_mpq_lar_core_solver.m_r_solver.m_costs[j] = zero_of_type(); + m_mpq_lar_core_solver.m_r_solver.m_inf_set.erase(j); + } + became_feas.clear(); + for (unsigned j : m_mpq_lar_core_solver.m_r_solver.m_inf_set.m_index) { + lp_assert(m_mpq_lar_core_solver.m_r_heading[j] >= 0); + if (m_mpq_lar_core_solver.m_r_solver.column_is_feasible(j)) + became_feas.push_back(j); + } + for (unsigned j : became_feas) + m_mpq_lar_core_solver.m_r_solver.m_inf_set.erase(j); + + + if (use_tableau_costs()) { + for (unsigned j : became_feas) + m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); + for (unsigned j : basic_columns_with_changed_cost) + m_mpq_lar_core_solver.m_r_solver.update_inf_cost_for_column_tableau(j); + lp_assert(m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); + } +} + +void lar_solver::shrink_explanation_to_minimum(vector> & explanation) const { + // implementing quickXplain + quick_xplain::run(explanation, *this); + lp_assert(this->explanation_is_correct(explanation)); +} + +bool lar_solver::model_is_int_feasible() const { + unsigned n = A_r().column_count(); + for (unsigned j = 0; j < n; j++) { + if (column_is_int(j) && !column_value_is_integer(j)) + return false; + } + return true; +} + +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(); +} + +bool lar_solver::var_is_int(var_index v) const { + if (is_term(v)) { + lar_term const& t = get_term(v); + return term_is_int(&t); + } + else { + return column_is_int(v); + } +} + +bool lar_solver::column_is_int(unsigned j) const { + unsigned ext_var = m_columns_to_ext_vars_or_term_indices[j]; + lp_assert(contains(m_ext_vars_to_columns, ext_var)); + return m_ext_vars_to_columns.find(ext_var)->second.is_integer(); +} + +bool lar_solver::column_is_fixed(unsigned j) const { + return m_mpq_lar_core_solver.column_is_fixed(j); +} + + +bool lar_solver::ext_var_is_int(var_index ext_var) const { + auto it = m_ext_vars_to_columns.find(ext_var); + lp_assert(it != m_ext_vars_to_columns.end()); + return it == m_ext_vars_to_columns.end() || it->second.is_integer(); +} + +// below is the initialization functionality of lar_solver + +bool lar_solver::strategy_is_undecided() const { + return m_settings.simplex_strategy() == simplex_strategy_enum::undecided; +} + +var_index lar_solver::add_var(unsigned ext_j, bool is_int) { + TRACE("add_var", tout << "adding var " << ext_j << (is_int? " int" : " nonint") << std::endl;); + var_index i; + lp_assert(ext_j < m_terms_start_index); + + if (ext_j >= m_terms_start_index) + throw 0; // todo : what is the right way to exit? + auto it = m_ext_vars_to_columns.find(ext_j); + if (it != m_ext_vars_to_columns.end()) { + return it->second.ext_j(); + } + lp_assert(m_columns_to_ul_pairs.size() == A_r().column_count()); + i = A_r().column_count(); + m_columns_to_ul_pairs.push_back(ul_pair(static_cast(-1))); + add_non_basic_var_to_core_fields(ext_j, is_int); + lp_assert(sizes_are_correct()); + if (is_int) { + m_mpq_lar_core_solver.m_r_solver.set_tracker_of_x(& m_tracker_of_x_change); + } + lp_assert(inf_int_set_is_correct()); + return i; +} + +void lar_solver::register_new_ext_var_index(unsigned ext_v, bool is_int) { + lp_assert(!contains(m_ext_vars_to_columns, ext_v)); + unsigned j = static_cast(m_ext_vars_to_columns.size()); + m_ext_vars_to_columns.insert(std::make_pair(ext_v, ext_var_info(j, is_int))); + lp_assert(m_columns_to_ext_vars_or_term_indices.size() == j); + m_columns_to_ext_vars_or_term_indices.push_back(ext_v); +} + +void lar_solver::add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int) { + register_new_ext_var_index(ext_j, is_int); + m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); + m_columns_with_changed_bound.increase_size_by_one(); + m_inf_int_set.increase_size_by_one(); + add_new_var_to_core_fields_for_mpq(false); + if (use_lu()) + add_new_var_to_core_fields_for_doubles(false); +} + +void lar_solver::add_new_var_to_core_fields_for_doubles(bool register_in_basis) { + unsigned j = A_d().column_count(); + A_d().add_column(); + lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); + // lp_assert(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later + m_mpq_lar_core_solver.m_d_x.resize(j + 1); + m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1); + m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); + lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method + if (register_in_basis) { + A_d().add_row(); + m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); + m_mpq_lar_core_solver.m_d_basis.push_back(j); + } + else { + m_mpq_lar_core_solver.m_d_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); + m_mpq_lar_core_solver.m_d_nbasis.push_back(j); + } +} + +void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { + unsigned j = A_r().column_count(); + A_r().add_column(); + lp_assert(m_mpq_lar_core_solver.m_r_x.size() == j); + // lp_assert(m_mpq_lar_core_solver.m_r_low_bounds.size() == j && m_mpq_lar_core_solver.m_r_upper_bounds.size() == j); // restore later + m_mpq_lar_core_solver.m_r_x.resize(j + 1); + m_mpq_lar_core_solver.m_r_low_bounds.increase_size_by_one(); + m_mpq_lar_core_solver.m_r_upper_bounds.increase_size_by_one(); + m_mpq_lar_core_solver.m_r_solver.m_inf_set.increase_size_by_one(); + m_mpq_lar_core_solver.m_r_solver.m_costs.resize(j + 1); + m_mpq_lar_core_solver.m_r_solver.m_d.resize(j + 1); + lp_assert(m_mpq_lar_core_solver.m_r_heading.size() == j); // as A().column_count() on the entry to the method + if (register_in_basis) { + A_r().add_row(); + m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size()); + m_mpq_lar_core_solver.m_r_basis.push_back(j); + if (m_settings.bound_propagation()) + m_rows_with_changed_bounds.insert(A_r().row_count() - 1); + } + else { + m_mpq_lar_core_solver.m_r_heading.push_back(-static_cast(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1); + m_mpq_lar_core_solver.m_r_nbasis.push_back(j); + } +} + + +var_index lar_solver::add_term_undecided(const vector> & coeffs, + const mpq &m_v) { + m_terms.push_back(new lar_term(coeffs, m_v)); + return m_terms_start_index + m_terms.size() - 1; +} + +// terms +var_index lar_solver::add_term(const vector> & coeffs, + const mpq &m_v) { + if (strategy_is_undecided()) + return add_term_undecided(coeffs, m_v); + + m_terms.push_back(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()) { + add_row_from_term_no_constraint(m_terms.back(), ret); + if (m_settings.bound_propagation()) + m_rows_with_changed_bounds.insert(A_r().row_count() - 1); + } + lp_assert(m_ext_vars_to_columns.size() == A_r().column_count()); + return ret; +} + +void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) { + register_new_ext_var_index(term_ext_index, term_is_int(term)); + // j will be a new variable + unsigned j = A_r().column_count(); + ul_pair ul(j); + m_columns_to_ul_pairs.push_back(ul); + add_basic_var_to_core_fields(); + if (use_tableau()) { + auto it = iterator_on_term_with_basis_var(*term, j); + A_r().fill_last_row_with_pivoting(it, + m_mpq_lar_core_solver.m_r_solver.m_basis_heading); + m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type()); + } + else { + fill_last_row_of_A_r(A_r(), term); + } + m_mpq_lar_core_solver.m_r_solver.update_x_and_call_tracker(j, get_basic_var_value_from_row_directly(A_r().row_count() - 1)); + if (use_lu()) + fill_last_row_of_A_d(A_d(), term); + lp_assert(inf_int_set_is_correct()); +} + +void lar_solver::add_basic_var_to_core_fields() { + bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver(); + lp_assert(!use_lu || A_r().column_count() == A_d().column_count()); + m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column); + m_columns_with_changed_bound.increase_size_by_one(); + m_rows_with_changed_bounds.increase_size_by_one(); + m_inf_int_set.increase_size_by_one(); + add_new_var_to_core_fields_for_mpq(true); + if (use_lu) + add_new_var_to_core_fields_for_doubles(true); +} + +bool lar_solver::bound_is_integer_if_needed(unsigned j, const mpq & right_side) const { + if (!column_is_int(j)) + return true; + return right_side.is_int(); +} + +constraint_index lar_solver::add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) { + TRACE("lar_solver", tout << "j = " << j << std::endl;); + constraint_index ci = m_constraints.size(); + if (!is_term(j)) { // j is a var + lp_assert(bound_is_integer_if_needed(j, right_side)); + auto vc = new lar_var_constraint(j, kind, right_side); + m_constraints.push_back(vc); + update_column_type_and_bound(j, kind, right_side, ci); + } + else { + add_var_bound_on_constraint_for_term(j, kind, right_side, ci); + } + lp_assert(sizes_are_correct()); + lp_assert(inf_int_set_is_correct()); + return ci; +} + +void lar_solver::update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index) { + switch (m_mpq_lar_core_solver.m_column_types[j]) { + case column_type::free_column: + update_free_column_type_and_bound(j, kind, right_side, constr_index); + break; + case column_type::boxed: + update_boxed_column_type_and_bound(j, kind, right_side, constr_index); + break; + case column_type::low_bound: + update_low_bound_column_type_and_bound(j, kind, right_side, constr_index); + break; + case column_type::upper_bound: + update_upper_bound_column_type_and_bound(j, kind, right_side, constr_index); + break; + case column_type::fixed: + update_fixed_column_type_and_bound(j, kind, right_side, constr_index); + break; + default: + lp_assert(false); // cannot be here + } +} + +void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(is_term(j)); + unsigned adjusted_term_index = adjust_term_index(j); + lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); + auto it = m_ext_vars_to_columns.find(j); + if (it != m_ext_vars_to_columns.end()) { + unsigned term_j = it->second.ext_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); + } + else { + add_constraint_from_term_and_create_new_column_row(j, m_terms[adjusted_term_index], kind, right_side); + } +} + +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()); + constraint_index ci = m_constraints.size(); + add_var_bound_on_constraint_for_term(term_index, kind_par, -rs, ci); + return ci; +} + +void lar_solver::add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term, + lconstraint_kind kind, const mpq & right_side) { + + 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()); + 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()); +} + +void lar_solver::decide_on_strategy_and_adjust_initial_state() { + lp_assert(strategy_is_undecided()); + if (m_columns_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) { + m_settings.simplex_strategy() = simplex_strategy_enum::lu; + } + else { + m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; // todo: when to switch to tableau_costs? + } + adjust_initial_state(); +} + +void lar_solver::adjust_initial_state() { + switch (m_settings.simplex_strategy()) { + case simplex_strategy_enum::lu: + adjust_initial_state_for_lu(); + break; + case simplex_strategy_enum::tableau_rows: + adjust_initial_state_for_tableau_rows(); + break; + case simplex_strategy_enum::tableau_costs: + lp_assert(false); // not implemented + case simplex_strategy_enum::undecided: + adjust_initial_state_for_tableau_rows(); + break; + } +} + +void lar_solver::adjust_initial_state_for_lu() { + copy_from_mpq_matrix(A_d()); + unsigned n = A_d().column_count(); + m_mpq_lar_core_solver.m_d_x.resize(n); + m_mpq_lar_core_solver.m_d_low_bounds.resize(n); + m_mpq_lar_core_solver.m_d_upper_bounds.resize(n); + m_mpq_lar_core_solver.m_d_heading = m_mpq_lar_core_solver.m_r_heading; + m_mpq_lar_core_solver.m_d_basis = m_mpq_lar_core_solver.m_r_basis; + + /* + unsigned j = A_d().column_count(); + A_d().add_column(); + lp_assert(m_mpq_lar_core_solver.m_d_x.size() == j); + // lp_assert(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later + m_mpq_lar_core_solver.m_d_x.resize(j + 1 ); + m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1); + m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1); + lp_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method + if (register_in_basis) { + A_d().add_row(); + m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size()); + m_mpq_lar_core_solver.m_d_basis.push_back(j); + }else { + m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1); + m_mpq_lar_core_solver.m_d_nbasis.push_back(j); + }*/ +} + +void lar_solver::adjust_initial_state_for_tableau_rows() { + for (unsigned j = 0; j < m_terms.size(); j++) { + if (contains(m_ext_vars_to_columns, j + m_terms_start_index)) + continue; + add_row_from_term_no_constraint(m_terms[j], j + m_terms_start_index); + } +} + +// this fills the last row of A_d and sets the basis column: -1 in the last column of the row +void lar_solver::fill_last_row_of_A_d(static_matrix & A, const lar_term* ls) { + lp_assert(A.row_count() > 0); + lp_assert(A.column_count() > 0); + unsigned last_row = A.row_count() - 1; + lp_assert(A.m_rows[last_row].empty()); + + for (auto & t : ls->m_coeffs) { + lp_assert(!is_zero(t.second)); + var_index j = t.first; + A.set(last_row, j, -t.second.get_double()); + } + + unsigned basis_j = A.column_count() - 1; + A.set(last_row, basis_j, -1); +} + +void lar_solver::update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind) { + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound; + lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); + lp_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j); + { + auto up = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + } + set_upper_bound_witness(j, constr_ind); + break; + case GT: + y_of_bound = 1; + case GE: + m_mpq_lar_core_solver.m_column_types[j] = column_type::low_bound; + lp_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j); + { + auto low = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + } + set_low_bound_witness(j, constr_ind); + break; + case EQ: + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = numeric_pair(right_side, zero_of_type()); + set_upper_bound_witness(j, constr_ind); + set_low_bound_witness(j, constr_ind); + break; + + default: + lp_unreachable(); + + } + m_columns_with_changed_bound.insert(j); +} + +void lar_solver::update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound); + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + { + auto up = numeric_pair(right_side, y_of_bound); + if (up < m_mpq_lar_core_solver.m_r_upper_bounds()[j]) { + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_columns_with_changed_bound.insert(j); + } + } + break; + case GT: + y_of_bound = 1; + case GE: + m_mpq_lar_core_solver.m_column_types[j] = column_type::boxed; + { + auto low = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + set_low_bound_witness(j, ci); + m_columns_with_changed_bound.insert(j); + if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + } + else { + m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; + } + } + break; + case EQ: + { + auto v = numeric_pair(right_side, zero_of_type()); + if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + set_low_bound_witness(j, ci); + m_infeasible_column_index = j; + } + else { + m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + m_columns_with_changed_bound.insert(j); + set_low_bound_witness(j, ci); + set_upper_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + break; + } + break; + + default: + lp_unreachable(); + + } +} + +void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::boxed && m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j])); + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + { + auto up = numeric_pair(right_side, y_of_bound); + if (up < m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_columns_with_changed_bound.insert(j); + } + + if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + lp_assert(false); + m_infeasible_column_index = j; + } + else { + if (m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j]) + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + } + break; + case GT: + y_of_bound = 1; + case GE: + { + auto low = numeric_pair(right_side, y_of_bound); + if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + m_columns_with_changed_bound.insert(j); + set_low_bound_witness(j, ci); + } + if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + } + else if (low == m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + } + break; + case EQ: + { + auto v = numeric_pair(right_side, zero_of_type()); + if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_low_bound_witness(j, ci); + } + else { + m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + set_low_bound_witness(j, ci); + set_upper_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + m_columns_with_changed_bound.insert(j); + } + + break; + } + + default: + lp_unreachable(); + + } +} +void lar_solver::update_low_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::low_bound); + mpq y_of_bound(0); + switch (kind) { + case LT: + y_of_bound = -1; + case LE: + { + auto up = numeric_pair(right_side, y_of_bound); + m_mpq_lar_core_solver.m_r_upper_bounds[j] = up; + set_upper_bound_witness(j, ci); + m_columns_with_changed_bound.insert(j); + + if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + } + else { + m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j] ? column_type::boxed : column_type::fixed; + } + } + break; + case GT: + y_of_bound = 1; + case GE: + { + auto low = numeric_pair(right_side, y_of_bound); + if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_mpq_lar_core_solver.m_r_low_bounds[j] = low; + m_columns_with_changed_bound.insert(j); + set_low_bound_witness(j, ci); + } + } + break; + case EQ: + { + auto v = numeric_pair(right_side, zero_of_type()); + if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + else { + m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v; + set_low_bound_witness(j, ci); + set_upper_bound_witness(j, ci); + m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed; + } + m_columns_with_changed_bound.insert(j); + break; + } + + default: + lp_unreachable(); + + } +} + +void lar_solver::update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) { + lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::fixed && m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j])); + lp_assert(m_status == lp_status::INFEASIBLE || (m_mpq_lar_core_solver.m_r_low_bounds()[j].y.is_zero() && m_mpq_lar_core_solver.m_r_upper_bounds()[j].y.is_zero())); + auto v = numeric_pair(right_side, mpq(0)); + + mpq y_of_bound(0); + switch (kind) { + case LT: + if (v <= m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + break; + case LE: + { + if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + } + break; + case GT: + { + if (v >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_low_bound_witness(j, ci); + } + } + break; + case GE: + { + if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_low_bound_witness(j, ci); + } + } + break; + case EQ: + { + if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_upper_bound_witness(j, ci); + } + else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) { + m_status = lp_status::INFEASIBLE; + m_infeasible_column_index = j; + set_low_bound_witness(j, ci); + } + break; + } + + default: + lp_unreachable(); + + } +} + + +} // namespace lp + + diff --git a/src/util/lp/lp_dual_core_solver.h b/src/util/lp/lp_dual_core_solver.h index ba4be494f..7aa572171 100644 --- a/src/util/lp/lp_dual_core_solver.h +++ b/src/util/lp/lp_dual_core_solver.h @@ -207,6 +207,6 @@ public: void solve(); - bool low_bounds_are_set() const { return true; } + bool low_bounds_are_set() const override { return true; } }; } diff --git a/src/util/lp/lp_dual_simplex.h b/src/util/lp/lp_dual_simplex.h index c17a9e99e..15463fc34 100644 --- a/src/util/lp/lp_dual_simplex.h +++ b/src/util/lp/lp_dual_simplex.h @@ -33,7 +33,7 @@ class lp_dual_simplex: public lp_solver { vector m_column_types_of_logicals; vector m_can_enter_basis; public: - ~lp_dual_simplex() { + ~lp_dual_simplex() override { if (m_core_solver != nullptr) { delete m_core_solver; } @@ -84,12 +84,12 @@ public: void copy_m_b_aside_and_set_it_to_zeros(); - void find_maximal_solution(); + void find_maximal_solution() override; - virtual T get_column_value(unsigned column) const { + T get_column_value(unsigned column) const override { return this->get_column_value_with_core_solver(column, m_core_solver); } - T get_current_cost() const; + T get_current_cost() const override; }; } diff --git a/src/util/lp/lp_primal_core_solver.h b/src/util/lp/lp_primal_core_solver.h index a7614862b..139cf3ad5 100644 --- a/src/util/lp/lp_primal_core_solver.h +++ b/src/util/lp/lp_primal_core_solver.h @@ -770,7 +770,7 @@ public: void init_reduced_costs(); - bool low_bounds_are_set() const { return true; } + bool low_bounds_are_set() const override { return true; } int advance_on_sorted_breakpoints(unsigned entering, X & t); diff --git a/src/util/lp/lp_primal_simplex.h b/src/util/lp/lp_primal_simplex.h index d8fd114e4..06ea26f8f 100644 --- a/src/util/lp/lp_primal_simplex.h +++ b/src/util/lp/lp_primal_simplex.h @@ -70,7 +70,7 @@ public: void set_core_solver_bounds(); - void find_maximal_solution(); + void find_maximal_solution() override; void fill_A_x_and_basis_for_stage_one_total_inf(); @@ -79,7 +79,7 @@ public: void solve_with_total_inf(); - ~lp_primal_simplex(); + ~lp_primal_simplex() override; bool bounds_hold(std::unordered_map const & solution); @@ -96,11 +96,11 @@ public: return bounds_hold(solution) && row_constraints_hold(solution); } - virtual T get_column_value(unsigned column) const { + T get_column_value(unsigned column) const override { return this->get_column_value_with_core_solver(column, m_core_solver); } - T get_current_cost() const; + T get_current_cost() const override; }; diff --git a/src/util/lp/lp_settings.h b/src/util/lp/lp_settings.h index a7e6e2665..cd9e78321 100644 --- a/src/util/lp/lp_settings.h +++ b/src/util/lp/lp_settings.h @@ -109,7 +109,7 @@ private: default_lp_resource_limit(lp_settings& s): m_settings(s) { m_sw.start(); } - virtual bool get_cancel_flag() { + bool get_cancel_flag() override { return (m_sw.get_current_seconds() > m_settings.time_limit); } }; diff --git a/src/util/lp/lp_solver_instances.cpp b/src/util/lp/lp_solver_instances.cpp index 4fe04c05f..d7caf9a18 100644 --- a/src/util/lp/lp_solver_instances.cpp +++ b/src/util/lp/lp_solver_instances.cpp @@ -34,6 +34,7 @@ template void lp::lp_solver::print_statistics_on_A(std::ostream template bool lp::lp_solver::problem_is_empty(); template void lp::lp_solver::scale(); template void lp::lp_solver::set_scaled_cost(unsigned int); +template std::string lp::lp_solver::get_column_name(unsigned int) const; template lp::lp_solver::~lp_solver(); template void lp::lp_solver::add_constraint(lp::lp_relation, lp::mpq, unsigned int); template void lp::lp_solver::cleanup(); @@ -53,3 +54,4 @@ template void lp::lp_solver::scale(); template void lp::lp_solver::set_scaled_cost(unsigned int); template lp::lp_solver::~lp_solver(); template double lp::lp_solver::get_column_value_by_name(std::string) const; +template std::string lp::lp_solver::get_column_name(unsigned int) const; diff --git a/src/util/lp/lu.h b/src/util/lp/lu.h index 5498a1849..f2cae7961 100644 --- a/src/util/lp/lu.h +++ b/src/util/lp/lu.h @@ -67,31 +67,31 @@ public: #endif } - bool is_dense() const { return false; } + bool is_dense() const override { return false; } one_elem_on_diag(const one_elem_on_diag & o); #ifdef Z3DEBUG unsigned m_m; unsigned m_n; - virtual void set_number_of_rows(unsigned m) { m_m = m; m_n = m; } - virtual void set_number_of_columns(unsigned n) { m_m = n; m_n = n; } + void set_number_of_rows(unsigned m) override { m_m = m; m_n = m; } + void set_number_of_columns(unsigned n) override { m_m = n; m_n = n; } T m_one_over_val; - T get_elem (unsigned i, unsigned j) const; + T get_elem (unsigned i, unsigned j) const override; - unsigned row_count() const { return m_m; } // not defined } - unsigned column_count() const { return m_m; } // not defined } + unsigned row_count() const override { return m_m; } // not defined } + unsigned column_count() const override { return m_m; } // not defined } #endif - void apply_from_left(vector & w, lp_settings &) { + void apply_from_left(vector & w, lp_settings &) override { w[m_i] /= m_val; } - void apply_from_right(vector & w) { + void apply_from_right(vector & w) override { w[m_i] /= m_val; } - void apply_from_right(indexed_vector & w) { + void apply_from_right(indexed_vector & w) override { if (is_zero(w.m_data[m_i])) return; auto & v = w.m_data[m_i] /= m_val; @@ -102,7 +102,7 @@ public: } - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings); + void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; void conjugate_by_permutation(permutation_matrix & p) { // this = p * this * p(-1) diff --git a/src/util/lp/mps_reader.h b/src/util/lp/mps_reader.h index 1b020407d..916e56322 100644 --- a/src/util/lp/mps_reader.h +++ b/src/util/lp/mps_reader.h @@ -95,7 +95,7 @@ inline vector string_split(const std::string &source, const char *d return results; } -inline vector split_and_trim(std::string line) { +inline vector split_and_trim(const std::string &line) { auto split = string_split(line, " \t", false); vector ret; for (auto s : split) { @@ -127,9 +127,9 @@ class mps_reader { std::string m_name; bound * m_bound; unsigned m_index; - column(std::string name, unsigned index): m_name(name), - m_bound(nullptr), - m_index(index) { + column(const std::string &name, unsigned index): m_name(name), + m_bound(nullptr), + m_index(index) { } }; @@ -140,7 +140,7 @@ class mps_reader { unsigned m_index; T m_right_side; T m_range; - row(row_type type, std::string name, unsigned index) : + row(row_type type, const std::string &name, unsigned index) : m_type(type), m_name(name), m_index(index), @@ -254,7 +254,7 @@ class mps_reader { } while (m_is_OK); } - void read_column_by_columns(std::string column_name, std::string column_data) { + void read_column_by_columns(const std::string & column_name, std::string column_data) { // uph, let us try to work with columns if (column_data.size() >= 22) { std::string ss = column_data.substr(0, 8); @@ -283,7 +283,7 @@ class mps_reader { } } - void read_column(std::string column_name, std::string column_data){ + void read_column(const std::string & column_name, const std::string & column_data){ auto tokens = split_and_trim(column_data); for (unsigned i = 0; i < tokens.size() - 1; i+= 2) { auto row_name = tokens[i]; @@ -421,7 +421,7 @@ class mps_reader { } - void read_bound_by_columns(std::string colstr) { + void read_bound_by_columns(const std::string & colstr) { if (colstr.size() < 14) { (*m_message_stream) << "line is too short" << std::endl; (*m_message_stream) << m_line << std::endl; @@ -763,7 +763,7 @@ public: } } - mps_reader(std::string file_name): + mps_reader(const std::string & file_name): m_is_OK(true), m_file_name(file_name), m_file_stream(file_name), diff --git a/src/util/lp/numeric_pair.h b/src/util/lp/numeric_pair.h index 4ebe63613..a8a743a71 100644 --- a/src/util/lp/numeric_pair.h +++ b/src/util/lp/numeric_pair.h @@ -38,10 +38,10 @@ template class numeric_traits {}; template <> class numeric_traits { public: static bool precise() { return true; } - static unsigned const zero() { return 0; } - static unsigned const one() { return 1; } + static unsigned zero() { return 0; } + static unsigned one() { return 1; } static bool is_zero(unsigned v) { return v == 0; } - static double const get_double(unsigned const & d) { return d; } + static double get_double(unsigned const & d) { return d; } }; template <> class numeric_traits { @@ -66,7 +66,7 @@ template <> class numeric_traits { static rational const & zero() { return rational::zero(); } static rational const & one() { return rational::one(); } static bool is_zero(const rational & v) { return v.is_zero(); } - static double const get_double(const rational & d) { return d.get_double();} + static double get_double(const rational & d) { return d.get_double();} static rational log(rational const& r) { UNREACHABLE(); return r; } static rational from_string(std::string const & str) { return rational(str.c_str()); } static bool is_pos(const rational & d) {return d.is_pos();} diff --git a/src/util/lp/permutation_matrix.h b/src/util/lp/permutation_matrix.h index 7cf64a5c7..8e664bba0 100644 --- a/src/util/lp/permutation_matrix.h +++ b/src/util/lp/permutation_matrix.h @@ -64,7 +64,7 @@ class permutation_matrix : public tail_matrix { // create a unit permutation of the given length void init(unsigned length); unsigned get_rev(unsigned i) { return m_rev[i]; } - bool is_dense() const { return false; } + bool is_dense() const override { return false; } #ifdef Z3DEBUG permutation_matrix get_inverse() const { return permutation_matrix(size(), m_rev); @@ -76,13 +76,13 @@ class permutation_matrix : public tail_matrix { unsigned operator[](unsigned i) const { return m_permutation[i]; } - void apply_from_left(vector & w, lp_settings &); + void apply_from_left(vector & w, lp_settings &) override; - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings); + void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override; - void apply_from_right(vector & w); + void apply_from_right(vector & w) override; - void apply_from_right(indexed_vector & w); + void apply_from_right(indexed_vector & w) override; template void copy_aside(vector & t, vector & tmp_index, indexed_vector & w); @@ -109,13 +109,13 @@ class permutation_matrix : public tail_matrix { void transpose_from_right(unsigned i, unsigned j); #ifdef Z3DEBUG - T get_elem(unsigned i, unsigned j) const{ + T get_elem(unsigned i, unsigned j) const override { return m_permutation[i] == j? numeric_traits::one() : numeric_traits::zero(); } - unsigned row_count() const{ return size(); } - unsigned column_count() const { return size(); } - virtual void set_number_of_rows(unsigned /*m*/) { } - virtual void set_number_of_columns(unsigned /*n*/) { } + unsigned row_count() const override { return size(); } + unsigned column_count() const override { return size(); } + void set_number_of_rows(unsigned /*m*/) override { } + void set_number_of_columns(unsigned /*n*/) override { } #endif void multiply_by_permutation_from_left(permutation_matrix & p); @@ -132,7 +132,7 @@ class permutation_matrix : public tail_matrix { unsigned size() const { return static_cast(m_rev.size()); } - unsigned * values() const { return m_permutation; } + unsigned * values() const { return m_permutation.c_ptr(); } void resize(unsigned size) { unsigned old_size = m_permutation.size(); diff --git a/src/util/lp/row_eta_matrix.h b/src/util/lp/row_eta_matrix.h index c287e263a..2bc0ac24e 100644 --- a/src/util/lp/row_eta_matrix.h +++ b/src/util/lp/row_eta_matrix.h @@ -50,7 +50,7 @@ public: m_row_start(row_start), m_row(row) { } - bool is_dense() const { return false; } + bool is_dense() const override { return false; } void print(std::ostream & out) { print_matrix(*this, out); @@ -60,12 +60,12 @@ public: return m_row_vector.m_data[m_row]; } - void apply_from_left(vector & w, lp_settings &); + void apply_from_left(vector & w, lp_settings &) override; void apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings); void apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings); - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) { + void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { apply_from_left_local_to_T(w, settings); } @@ -74,16 +74,16 @@ public: m_row_vector.push_back(row_index, val); } - void apply_from_right(vector & w); - void apply_from_right(indexed_vector & w); + void apply_from_right(vector & w) override; + void apply_from_right(indexed_vector & w) override; void conjugate_by_permutation(permutation_matrix & p); #ifdef Z3DEBUG - T get_elem(unsigned row, unsigned col) const; - unsigned row_count() const { return m_dimension; } - unsigned column_count() const { return m_dimension; } - void set_number_of_rows(unsigned m) { m_dimension = m; } - void set_number_of_columns(unsigned n) { m_dimension = n; } + T get_elem(unsigned row, unsigned col) const override; + unsigned row_count() const override { return m_dimension; } + unsigned column_count() const override { return m_dimension; } + void set_number_of_rows(unsigned m) override { m_dimension = m; } + void set_number_of_columns(unsigned n) override { m_dimension = n; } #endif }; // end of row_eta_matrix } diff --git a/src/util/lp/sparse_matrix.h b/src/util/lp/sparse_matrix.h index 400f2bfc0..44111a9fd 100644 --- a/src/util/lp/sparse_matrix.h +++ b/src/util/lp/sparse_matrix.h @@ -162,8 +162,8 @@ public: unsigned dimension() const {return static_cast(m_row_permutation.size());} #ifdef Z3DEBUG - unsigned row_count() const {return dimension();} - unsigned column_count() const {return dimension();} + unsigned row_count() const override {return dimension();} + unsigned column_count() const override {return dimension();} #endif void init_row_headers(); @@ -302,11 +302,11 @@ public: void solve_U_y_indexed_only(indexed_vector & y, const lp_settings&, vector & sorted_active_rows ); #ifdef Z3DEBUG - T get_elem(unsigned i, unsigned j) const { return get(i, j); } + T get_elem(unsigned i, unsigned j) const override { return get(i, j); } unsigned get_number_of_rows() const { return dimension(); } unsigned get_number_of_columns() const { return dimension(); } - virtual void set_number_of_rows(unsigned /*m*/) { } - virtual void set_number_of_columns(unsigned /*n*/) { } + void set_number_of_rows(unsigned /*m*/) override { } + void set_number_of_columns(unsigned /*n*/) override { } #endif template L dot_product_with_row (unsigned row, const vector & y) const; diff --git a/src/util/lp/square_dense_submatrix.h b/src/util/lp/square_dense_submatrix.h index 3e88e8114..d43b2eadd 100644 --- a/src/util/lp/square_dense_submatrix.h +++ b/src/util/lp/square_dense_submatrix.h @@ -70,7 +70,7 @@ public: void init(sparse_matrix *parent_matrix, unsigned index_start); - bool is_dense() const { return true; } + bool is_dense() const override { return true; } ref operator[] (unsigned i) { SASSERT(i >= m_index_start); @@ -137,13 +137,13 @@ public: bool is_L_matrix() const; - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) { + void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) override { apply_from_left_local(w, settings); } - void apply_from_right(indexed_vector & w) { + void apply_from_right(indexed_vector & w) override { #if 1==0 indexed_vector wcopy = w; apply_from_right(wcopy.m_data); @@ -207,18 +207,18 @@ public: w = m_work_vector; #endif } - void apply_from_left(vector & w, lp_settings & /*settings*/) { + void apply_from_left(vector & w, lp_settings & /*settings*/) override { apply_from_left_to_vector(w);// , settings); } - void apply_from_right(vector & w); + void apply_from_right(vector & w) override; #ifdef Z3DEBUG - T get_elem (unsigned i, unsigned j) const; - unsigned row_count() const { return m_parent->row_count();} - unsigned column_count() const { return row_count();} - void set_number_of_rows(unsigned) {} - void set_number_of_columns(unsigned) {}; + T get_elem (unsigned i, unsigned j) const override; + unsigned row_count() const override { return m_parent->row_count();} + unsigned column_count() const override { return row_count();} + void set_number_of_rows(unsigned) override {} + void set_number_of_columns(unsigned) override {} #endif void conjugate_by_permutation(permutation_matrix & q); }; diff --git a/src/util/map.h b/src/util/map.h index 3a59c8975..2ee540932 100644 --- a/src/util/map.h +++ b/src/util/map.h @@ -129,7 +129,7 @@ public: if (e) { v = e->get_data().m_value; } - return (0 != e); + return (nullptr != e); } value const& get(key const& k, value const& default_value) const { @@ -164,7 +164,7 @@ public: bool contains(key const & k) const { - return find_core(k) != 0; + return find_core(k) != nullptr; } void remove(key const & k) { diff --git a/src/util/max_cliques.h b/src/util/max_cliques.h index 340d3fee7..0bf67592c 100644 --- a/src/util/max_cliques.h +++ b/src/util/max_cliques.h @@ -128,7 +128,12 @@ public: turn = !turn; } if (clique.size() > 1) { - cliques.push_back(clique); + if (clique.size() == 2 && clique[0] == negate(clique[1])) { + // no op + } + else { + cliques.push_back(clique); + } } } } diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index 44157aa65..59a6cb009 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -11,6 +11,7 @@ Copyright (c) 2015 Microsoft Corporation #include "util/memory_manager.h" #include "util/error_codes.h" #include "util/z3_omp.h" +#include "util/debug.h" // The following two function are automatically generated by the mk_make.py script. // The script collects ADD_INITIALIZER and ADD_FINALIZER commands in the .h files. // For example, rational.h contains @@ -57,6 +58,7 @@ static void throw_out_of_memory() { { g_memory_out_of_memory = true; } + if (g_exit_when_out_of_memory) { std::cerr << g_out_of_memory_msg << "\n"; exit(ERR_MEMOUT); @@ -179,6 +181,22 @@ unsigned long long memory::get_max_used_memory() { return r; } +#if defined(_WINDOWS) +#include +#endif + +unsigned long long memory::get_max_memory_size() { +#if defined(_WINDOWS) + MEMORYSTATUSEX statex; + statex.dwLength = sizeof (statex); + GlobalMemoryStatusEx (&statex); + return statex.ullTotalPhys; +#else + // 16 GB + return 1ull << 34ull; +#endif +} + unsigned long long memory::get_allocation_count() { return g_memory_alloc_count; } @@ -282,6 +300,7 @@ void * memory::allocate(size_t s) { if (g_memory_thread_alloc_size > SYNCH_THRESHOLD) { synchronize_counters(true); } + return static_cast(r) + 1; // we return a pointer to the location after the extra field } @@ -342,7 +361,7 @@ void * memory::allocate(size_t s) { if (counts_exceeded) throw_alloc_counts_exceeded(); void * r = malloc(s); - if (r == 0) + if (r == nullptr) throw_out_of_memory(); *(static_cast(r)) = s; return static_cast(r) + 1; // we return a pointer to the location after the extra field @@ -370,7 +389,7 @@ void* memory::reallocate(void *p, size_t s) { if (counts_exceeded) throw_alloc_counts_exceeded(); void *r = realloc(real_p, s); - if (r == 0) + if (r == nullptr) throw_out_of_memory(); *(static_cast(r)) = s; return static_cast(r) + 1; // we return a pointer to the location after the extra field diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index 5aa512018..5cb7dc467 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -67,6 +67,7 @@ public: static unsigned long long get_allocation_size(); static unsigned long long get_max_used_memory(); static unsigned long long get_allocation_count(); + static unsigned long long get_max_memory_size(); // temporary hack to avoid out-of-memory crash in z3.exe static void exit_when_out_of_memory(bool flag, char const * msg); }; @@ -90,7 +91,7 @@ void deallocf(char const* file, int line, T * ptr) { template void dealloc(T * ptr) { - if (ptr == 0) return; + if (ptr == nullptr) return; ptr->~T(); memory::deallocate(ptr); } @@ -111,7 +112,7 @@ T * alloc_vect(unsigned sz) { template void dealloc_vect(T * ptr, unsigned sz) { - if (ptr == 0) return; + if (ptr == nullptr) return; T * curr = ptr; for (unsigned i = 0; i < sz; i++, curr++) curr->~T(); @@ -122,7 +123,7 @@ void dealloc_vect(T * ptr, unsigned sz) { template void dealloc_svect(T * ptr) { - if (ptr == 0) return; + if (ptr == nullptr) return; memory::deallocate(ptr); } diff --git a/src/util/mpbq.cpp b/src/util/mpbq.cpp index 9fcd1b58f..3edbdab67 100644 --- a/src/util/mpbq.cpp +++ b/src/util/mpbq.cpp @@ -250,7 +250,7 @@ void mpbq_manager::mul(mpbq const & a, mpz const & b, mpbq & r) { } void mpbq_manager::power(mpbq & a, unsigned k) { - SASSERT(static_cast(k) * static_cast(a.k()) <= static_cast(UINT_MAX)); + SASSERT(static_cast(k) * static_cast(a.k()) <= static_cast(UINT_MAX)); // We don't need to normalize because: // If a.m_k == 0, then a is an integer, and the result be an integer // If a.m_k > 0, then a.m_num must be odd, and the (a.m_num)^k will also be odd diff --git a/src/util/mpbq.h b/src/util/mpbq.h index 61ef2b0e2..ed8a8c523 100644 --- a/src/util/mpbq.h +++ b/src/util/mpbq.h @@ -84,7 +84,7 @@ public: void set(mpbq & a, mpz const & n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); } void set(mpbq & a, mpz const & n) { m_manager.set(a.m_num, n); a.m_k = 0; } void set(mpbq & a, mpbq const & b) { m_manager.set(a.m_num, b.m_num); a.m_k = b.m_k; } - void set(mpbq & a, int64 n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); } + void set(mpbq & a, int64_t n, unsigned k) { m_manager.set(a.m_num, n); a.m_k = k; normalize(a); } bool is_int(mpbq const & a) const { return a.m_k == 0; } void get_numerator(mpbq const & a, mpz & n) { m_manager.set(n, a.m_num); } diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 9e309a726..7549a7bb3 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -115,11 +115,11 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, double value) { // double === mpf(11, 53) static_assert(sizeof(double) == 8, "doubles are 8 bytes"); - uint64 raw; + uint64_t raw; memcpy(&raw, &value, sizeof(double)); bool sign = (raw >> 63) != 0; - int64 e = ((raw & 0x7FF0000000000000ull) >> 52) - 1023; - uint64 s = raw & 0x000FFFFFFFFFFFFFull; + int64_t e = ((raw & 0x7FF0000000000000ull) >> 52) - 1023; + uint64_t s = raw & 0x000FFFFFFFFFFFFFull; TRACE("mpf_dbg", tout << "set: " << value << " is: raw=" << raw << " (double)" << " sign=" << sign << " s=" << s << " e=" << e << std::endl;); @@ -300,7 +300,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } -void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, uint64 significand) { +void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, uint64_t significand) { // Assumption: this represents (sign * -1) * (significand/2^sbits) * 2^exponent. o.ebits = ebits; o.sbits = sbits; @@ -1194,7 +1194,7 @@ void mpf_manager::to_sbv_mpq(mpf_rounding_mode rm, const mpf & x, scoped_mpq & o m_mpz_manager.set(z, t.significand()); mpf_exp_t e = (mpf_exp_t)t.exponent() - t.sbits() + 1; if (e < 0) { - bool last = false, round = false, sticky = m_mpz_manager.is_odd(z); + bool last = m_mpz_manager.is_odd(z), round = false, sticky = false; for (; e != 0; e++) { m_mpz_manager.machine_div2k(z, 1); sticky |= round; @@ -1204,7 +1204,7 @@ void mpf_manager::to_sbv_mpq(mpf_rounding_mode rm, const mpf & x, scoped_mpq & o bool inc = false; switch (rm) { case MPF_ROUND_NEAREST_TEVEN: inc = round && (last || sticky); break; - case MPF_ROUND_NEAREST_TAWAY: inc = round && (!last || sticky); break; // CMW: Check! + case MPF_ROUND_NEAREST_TAWAY: inc = round; break; case MPF_ROUND_TOWARD_POSITIVE: inc = (!x.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_NEGATIVE: inc = (x.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_ZERO: inc = false; break; @@ -1711,8 +1711,8 @@ void mpf_manager::to_rational(mpf const & x, unsynch_mpq_manager & qm, mpq & o) double mpf_manager::to_double(mpf const & x) { SASSERT(x.ebits <= 11 && x.sbits <= 53); - uint64 raw = 0; - int64 sig = 0, exp = 0; + uint64_t raw = 0; + int64_t sig = 0, exp = 0; sig = m_mpz_manager.get_uint64(x.significand); sig <<= 53 - x.sbits; @@ -1741,7 +1741,7 @@ float mpf_manager::to_float(mpf const & x) { unsigned int raw = 0; unsigned int sig = 0, exp = 0; - uint64 q = m_mpz_manager.get_uint64(x.significand); + uint64_t q = m_mpz_manager.get_uint64(x.significand); SASSERT(q < 4294967296ull); sig = q & 0x00000000FFFFFFFF; sig <<= 24 - x.sbits; @@ -1751,7 +1751,7 @@ float mpf_manager::to_float(mpf const & x) { else if (has_bot_exp(x)) exp = -127; else { - int64 q = x.exponent; + int64_t q = x.exponent; SASSERT(q < 4294967296ll); exp = q & 0x00000000FFFFFFFF; } @@ -2045,7 +2045,7 @@ void mpf_manager::round(mpf_rounding_mode rm, mpf & o) { bool inc = false; switch (rm) { case MPF_ROUND_NEAREST_TEVEN: inc = round && (last || sticky); break; - case MPF_ROUND_NEAREST_TAWAY: inc = round && (!last || sticky); break; + case MPF_ROUND_NEAREST_TAWAY: inc = round; break; case MPF_ROUND_TOWARD_POSITIVE: inc = (!o.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_NEGATIVE: inc = (o.sign && (round || sticky)); break; case MPF_ROUND_TOWARD_ZERO: inc = false; break; diff --git a/src/util/mpf.h b/src/util/mpf.h index e679be558..107ada0bd 100644 --- a/src/util/mpf.h +++ b/src/util/mpf.h @@ -35,7 +35,7 @@ typedef enum { MPF_ROUND_TOWARD_ZERO } mpf_rounding_mode; -typedef int64 mpf_exp_t; +typedef int64_t mpf_exp_t; class mpf { friend class mpf_manager; @@ -80,7 +80,7 @@ public: void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpq const & value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, char const * value); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpz const & exponent, mpq const & significand); - void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, uint64 significand); + void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, uint64_t significand); void set(mpf & o, unsigned ebits, unsigned sbits, bool sign, mpf_exp_t exponent, mpz const & significand); void set(mpf & o, mpf const & x); void set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode rm, mpf const & x); @@ -190,8 +190,8 @@ public: void mk_pinf(unsigned ebits, unsigned sbits, mpf & o); void mk_ninf(unsigned ebits, unsigned sbits, mpf & o); - unsynch_mpz_manager & mpz_manager(void) { return m_mpz_manager; } - unsynch_mpq_manager & mpq_manager(void) { return m_mpq_manager; } + unsynch_mpz_manager & mpz_manager() { return m_mpz_manager; } + unsynch_mpq_manager & mpq_manager() { return m_mpq_manager; } unsigned hash(mpf const & a) { return hash_u_u(m_mpz_manager.hash(a.significand), diff --git a/src/util/mpff.cpp b/src/util/mpff.cpp index eac9cc80c..64e07f9f0 100644 --- a/src/util/mpff.cpp +++ b/src/util/mpff.cpp @@ -155,27 +155,27 @@ bool mpff_manager::is_uint64(mpff const & n) const { !has_one_at_first_k_bits(m_precision, sig(n), -n.m_exponent); } -uint64 mpff_manager::get_uint64(mpff const & a) const { +uint64_t mpff_manager::get_uint64(mpff const & a) const { SASSERT(is_uint64(a)); if (is_zero(a)) return 0; int exp = -a.m_exponent - sizeof(unsigned) * 8 * (m_precision - 2); SASSERT(exp >= 0); - uint64 * s = reinterpret_cast(sig(a) + (m_precision - 2)); + uint64_t * s = reinterpret_cast(sig(a) + (m_precision - 2)); return *s >> exp; } -int64 mpff_manager::get_int64(mpff const & a) const { +int64_t mpff_manager::get_int64(mpff const & a) const { SASSERT(is_int64(a)); if (is_zero(a)) return 0; int exp = -a.m_exponent - sizeof(unsigned) * 8 * (m_precision - 2); SASSERT(exp >= 0); - uint64 * s = reinterpret_cast(sig(a) + (m_precision - 2)); + uint64_t * s = reinterpret_cast(sig(a) + (m_precision - 2)); // INT64_MIN case if (exp == 0 && *s == 0x8000000000000000ull && is_neg(a)) { return INT64_MIN; } else { - int64 r = *s >> exp; + int64_t r = *s >> exp; if (is_neg(a)) r = -r; return r; @@ -249,26 +249,26 @@ void mpff_manager::set(mpff & n, unsigned v) { SASSERT(check(n)); } -void mpff_manager::set(mpff & n, int64 v) { +void mpff_manager::set(mpff & n, int64_t v) { if (v == 0) { reset(n); } else { if (v < 0) { - set(n, 1 + static_cast(-(1+v))); + set(n, 1 + static_cast(-(1+v))); n.m_sign = 1; } else { - set(n, static_cast(v)); + set(n, static_cast(v)); } } SASSERT(check(n)); SASSERT(get_int64(n) == v); } -void mpff_manager::set(mpff & n, uint64 v) { +void mpff_manager::set(mpff & n, uint64_t v) { #ifdef Z3DEBUG - uint64 old_v = v; + uint64_t old_v = v; #endif if (v == 0) { reset(n); @@ -278,7 +278,7 @@ void mpff_manager::set(mpff & n, uint64 v) { n.m_sign = 0; unsigned * _v = reinterpret_cast(&v); int num_leading_zeros = nlz(2, _v); - n.m_exponent = static_cast(8 * sizeof(uint64)) - num_leading_zeros - static_cast(m_precision_bits); + n.m_exponent = static_cast(8 * sizeof(uint64_t)) - num_leading_zeros - static_cast(m_precision_bits); v <<= num_leading_zeros; SASSERT(m_precision >= 2); unsigned * s = sig(n); @@ -299,7 +299,7 @@ void mpff_manager::set(mpff & n, int num, unsigned den) { SASSERT(check(n)); } -void mpff_manager::set(mpff & n, int64 num, uint64 den) { +void mpff_manager::set(mpff & n, int64_t num, uint64_t den) { scoped_mpff a(*this), b(*this); set(a, num); set(b, den); @@ -470,7 +470,7 @@ bool mpff_manager::lt(mpff const & a, mpff const & b) const { } } -void mpff_manager::inc_significand(unsigned * s, int64 & exp) { +void mpff_manager::inc_significand(unsigned * s, int64_t & exp) { if (!::inc(m_precision, s)) { SASSERT(::is_zero(m_precision, s)); s[m_precision - 1] = MIN_MSW; @@ -597,7 +597,7 @@ void mpff_manager::prev(mpff & a) { SASSERT(check(a)); } -void mpff_manager::set_big_exponent(mpff & a, int64 e) { +void mpff_manager::set_big_exponent(mpff & a, int64_t e) { SASSERT(e > INT_MAX || e < INT_MIN); if (e > INT_MAX) { if (a.m_sign == 1) { @@ -715,7 +715,7 @@ void mpff_manager::add_sub(bool is_sub, mpff const & a, mpff const & b, mpff & c else if (num_leading_zeros == sizeof(unsigned) * 8 - 1) { // shift 1 right bool _inc_significand = ((c.m_sign == 1) != m_to_plus_inf) && has_one_at_first_k_bits(m_precision*2, sig_r, 1); - int64 exp_c = exp_a; + int64_t exp_c = exp_a; exp_c++; shr(m_precision + 1, sig_r, 1, m_precision, sig_c); if (_inc_significand) @@ -728,7 +728,7 @@ void mpff_manager::add_sub(bool is_sub, mpff const & a, mpff const & b, mpff & c // Now, we can assume sig_r has size m_precision SASSERT(num_leading_zeros > 0); // shift left num_leading_zeros - int64 exp_c = exp_a; + int64_t exp_c = exp_a; exp_c -= num_leading_zeros; shl(m_precision, sig_r, num_leading_zeros, m_precision, sig_c); set_exponent(c, exp_c); @@ -752,7 +752,7 @@ void mpff_manager::add_sub(bool is_sub, mpff const & a, mpff const & b, mpff & c reset(c); } else if (num_leading_zeros > 0) { - int64 exp_c = exp_a; + int64_t exp_c = exp_a; exp_c -= num_leading_zeros; shl(m_precision, sig_c, num_leading_zeros, m_precision, sig_c); set_exponent(c, exp_c); @@ -787,10 +787,10 @@ void mpff_manager::mul(mpff const & a, mpff const & b, mpff & c) { allocate_if_needed(c); TRACE("mpff", tout << "mul("; display(tout, a); tout << ", "; display(tout, b); tout << ")\n";); c.m_sign = a.m_sign ^ b.m_sign; - // use int64 to make sure we do not have overflows - int64 exp_a = a.m_exponent; - int64 exp_b = b.m_exponent; - int64 exp_c = exp_a + exp_b; + // use int64_t to make sure we do not have overflows + int64_t exp_a = a.m_exponent; + int64_t exp_b = b.m_exponent; + int64_t exp_c = exp_a + exp_b; // store result in m_buffers[0] unsigned * r = m_buffers[0].c_ptr(); m_mpn_manager.mul(sig(a), m_precision, sig(b), m_precision, r); @@ -834,7 +834,7 @@ void mpff_manager::div(mpff const & a, mpff const & b, mpff & c) { #if 1 else if (is_two(b)) { set(c, a); - int64 exp_c = a.m_exponent; + int64_t exp_c = a.m_exponent; exp_c--; set_exponent(c, exp_c); } @@ -842,10 +842,10 @@ void mpff_manager::div(mpff const & a, mpff const & b, mpff & c) { else { allocate_if_needed(c); c.m_sign = a.m_sign ^ b.m_sign; - // use int64 to make sure we do not have overflows - int64 exp_a = a.m_exponent; - int64 exp_b = b.m_exponent; - int64 exp_c = exp_a - exp_b; + // use int64_t to make sure we do not have overflows + int64_t exp_a = a.m_exponent; + int64_t exp_b = b.m_exponent; + int64_t exp_c = exp_a - exp_b; exp_c -= m_precision_bits; // we will multiplying (shifting) a by 2^m_precision_bits. // copy a to buffer 0, and shift by m_precision_bits @@ -1023,7 +1023,7 @@ void mpff_manager::power(mpff const & a, unsigned p, mpff & b) { b.m_sign = 0; else b.m_sign = a.m_sign; - int64 exp = a.m_exponent; + int64_t exp = a.m_exponent; exp *= p; if (exp > INT_MAX || exp < INT_MIN) throw overflow_exception(); @@ -1057,7 +1057,7 @@ void mpff_manager::power(mpff const & a, unsigned p, mpff & b) { bool mpff_manager::is_power_of_two(mpff const & a, unsigned & k) const { if (!is_power_of_two(a)) return false; - int64 exp = a.m_exponent + m_precision_bits - 1; + int64_t exp = a.m_exponent + m_precision_bits - 1; SASSERT(exp >= 0); k = static_cast(exp); return true; @@ -1132,7 +1132,7 @@ void mpff_manager::to_mpq_core(mpff const & n, mpq_manager & m, mpq & t) if (exp < 0) { // Avoid -INT_MIN == INT_MIN issue. It is not really useful, since we will run out of memory anyway. if (exp == INT_MIN) - abs_exp = static_cast(-static_cast(INT_MIN)); + abs_exp = static_cast(-static_cast(INT_MIN)); else abs_exp = -exp; } @@ -1177,7 +1177,7 @@ void mpff_manager::display(std::ostream & out, mpff const & n) const { svector & u_buffer = const_cast(this)->m_buffers[0]; int num_trailing_zeros = ntz(m_precision, u_buffer.c_ptr()); int shift = 0; - int64 exp = n.m_exponent; // use int64 to avoid -INT_MIN == INT_MIN issue + int64_t exp = n.m_exponent; // use int64_t to avoid -INT_MIN == INT_MIN issue if (exp < 0) { if (num_trailing_zeros >= -exp) { shift = static_cast(-exp); @@ -1194,7 +1194,7 @@ void mpff_manager::display(std::ostream & out, mpff const & n) const { out << m_mpn_manager.to_string(u_buffer.c_ptr(), m_precision, str_buffer.begin(), str_buffer.size()); if (exp > 0) { if (exp <= 63) { - uint64 _exp = 1; + uint64_t _exp = 1; _exp <<= exp; out << "*" << _exp; } @@ -1209,7 +1209,7 @@ void mpff_manager::display(std::ostream & out, mpff const & n) const { else if (exp < 0) { exp = -exp; if (exp <= 63) { - uint64 _exp = 1; + uint64_t _exp = 1; _exp <<= exp; out << "/" << _exp; } @@ -1225,8 +1225,8 @@ void mpff_manager::display(std::ostream & out, mpff const & n) const { void mpff_manager::display_decimal(std::ostream & out, mpff const & n, unsigned prec, unsigned min_exponent) { // The result in scientific notation when n.m_exponent >= min_exponent or n.m_exponent <= - min_exponent - m_precision_bits - int64 exp = n.m_exponent; - if (exp >= min_exponent || exp <= -static_cast(min_exponent) - m_precision_bits || is_int(n)) { + int64_t exp = n.m_exponent; + if (exp >= min_exponent || exp <= -static_cast(min_exponent) - m_precision_bits || is_int(n)) { display(out, n); return; } @@ -1327,7 +1327,7 @@ void mpff_manager::display_smt2(std::ostream & out, mpff const & n, bool decimal svector & u_buffer = const_cast(this)->m_buffers[0]; int num_trailing_zeros = ntz(m_precision, u_buffer.c_ptr()); int shift = 0; - int64 exp = n.m_exponent; + int64_t exp = n.m_exponent; if (exp < 0) { if (num_trailing_zeros >= -exp) { shift = static_cast(-exp); @@ -1353,7 +1353,7 @@ void mpff_manager::display_smt2(std::ostream & out, mpff const & n, bool decimal if (exp != 0) { if (exp < 0) exp = -exp; if (exp <= 63) { - uint64 _exp = 1; + uint64_t _exp = 1; _exp <<= exp; out << _exp; if (decimal) out << ".0"; @@ -1387,8 +1387,8 @@ unsigned mpff_manager::prev_power_of_two(mpff const & a) { return 0; if (a.m_exponent <= -static_cast(m_precision_bits)) return 0; // Number is smaller than 1 - SASSERT(static_cast(a.m_exponent) + static_cast(m_precision_bits) - 1 >= 0); - SASSERT(static_cast(a.m_exponent) + static_cast(m_precision_bits) - 1 <= static_cast(static_cast(UINT_MAX))); + SASSERT(static_cast(a.m_exponent) + static_cast(m_precision_bits) - 1 >= 0); + SASSERT(static_cast(a.m_exponent) + static_cast(m_precision_bits) - 1 <= static_cast(static_cast(UINT_MAX))); return m_precision_bits + a.m_exponent - 1; } diff --git a/src/util/mpff.h b/src/util/mpff.h index 4fdd0baef..8023053d2 100644 --- a/src/util/mpff.h +++ b/src/util/mpff.h @@ -92,7 +92,7 @@ class mpff_manager { // // Remarks: // - // - All values of type int, unsigned, int64 and uint64 can be precisely represented as mpff numerals. + // - All values of type int, unsigned, int64_t and uint64_t can be precisely represented as mpff numerals. // // - Hardware float and double values (corresponding to rationals) can also be precisely represented as mpff numerals. // That is, NaN, +oo and -oo are not supported by this module. @@ -141,14 +141,14 @@ class mpff_manager { // copy (and shift by m_precision_bits) n to buffer idx void to_buffer_shifting(unsigned idx, mpff const & n) const; - void inc_significand(unsigned * s, int64 & exp); + void inc_significand(unsigned * s, int64_t & exp); void inc_significand(mpff & a); void dec_significand(mpff & a); bool min_significand(mpff const & a) const; void set_min_significand(mpff & a); void set_max_significand(mpff & a); - void set_big_exponent(mpff & a, int64 e); - void set_exponent(mpff & a, int64 e) { + void set_big_exponent(mpff & a, int64_t e); + void set_exponent(mpff & a, int64_t e) { if (e > INT_MAX || e < INT_MIN) set_big_exponent(a, e); else @@ -178,15 +178,15 @@ public: static bool field() { return true; } class exception : public z3_exception { - virtual char const * msg() const { return "multi-precision floating point (mpff) exception"; } + char const * msg() const override { return "multi-precision floating point (mpff) exception"; } }; class overflow_exception : public exception { - virtual char const * msg() const { return "multi-precision floating point (mpff) overflow"; } + char const * msg() const override { return "multi-precision floating point (mpff) overflow"; } }; class div0_exception : public exception { - virtual char const * msg() const { return "multi-precision floating point (mpff) division by zero"; } + char const * msg() const override { return "multi-precision floating point (mpff) division by zero"; } }; mpff_manager(unsigned prec = 2, unsigned initial_capacity = 1024); @@ -286,12 +286,12 @@ public: bool is_plus_epsilon(mpff const & a) const; /** - \brief Return true if \c a is an integer and fits in an int64 machine integer. + \brief Return true if \c a is an integer and fits in an int64_t machine integer. */ bool is_int64(mpff const & a) const; /** - \brief Return true if \c a is a non-negative integer and fits in an int64 machine integer. + \brief Return true if \c a is a non-negative integer and fits in an int64_t machine integer. */ bool is_uint64(mpff const & a) const; @@ -372,10 +372,10 @@ public: void set(mpff & n, int v); void set(mpff & n, unsigned v); - void set(mpff & n, int64 v); - void set(mpff & n, uint64 v); + void set(mpff & n, int64_t v); + void set(mpff & n, uint64_t v); void set(mpff & n, int num, unsigned den); - void set(mpff & n, int64 num, uint64 den); + void set(mpff & n, int64_t num, uint64_t den); void set(mpff & n, mpff const & v); void set(mpff & n, unsynch_mpz_manager & m, mpz const & v); void set(mpff & n, synch_mpz_manager & m, mpz const & v); @@ -448,14 +448,14 @@ public: \pre is_int64(n) */ - int64 get_int64(mpff const & n) const; + int64_t get_int64(mpff const & n) const; /** \brief Return n as an uint64. \pre is_uint64(n) */ - uint64 get_uint64(mpff const & n) const; + uint64_t get_uint64(mpff const & n) const; /** \brief Return the biggest k s.t. 2^k <= a. diff --git a/src/util/mpfx.cpp b/src/util/mpfx.cpp index 24ef4dc73..e17a5e766 100644 --- a/src/util/mpfx.cpp +++ b/src/util/mpfx.cpp @@ -164,10 +164,10 @@ void mpfx_manager::set(mpfx & n, unsigned v) { SASSERT(check(n)); } -void mpfx_manager::set(mpfx & n, int64 v) { +void mpfx_manager::set(mpfx & n, int64_t v) { if (m_int_part_sz == 1) { - if (v < -static_cast(static_cast(UINT_MAX)) || - v > static_cast(static_cast(UINT_MAX))) + if (v < -static_cast(static_cast(UINT_MAX)) || + v > static_cast(static_cast(UINT_MAX))) throw overflow_exception(); } if (v == 0) { @@ -175,11 +175,11 @@ void mpfx_manager::set(mpfx & n, int64 v) { } else { if (v < 0) { - set(n, static_cast(-v)); + set(n, static_cast(-v)); n.m_sign = 1; } else { - set(n, static_cast(v)); + set(n, static_cast(v)); } } SASSERT(is_int(n)); @@ -187,9 +187,9 @@ void mpfx_manager::set(mpfx & n, int64 v) { SASSERT(check(n)); } -void mpfx_manager::set(mpfx & n, uint64 v) { +void mpfx_manager::set(mpfx & n, uint64_t v) { if (m_int_part_sz == 1) { - if (v > static_cast(UINT_MAX)) + if (v > static_cast(UINT_MAX)) throw overflow_exception(); } @@ -200,8 +200,8 @@ void mpfx_manager::set(mpfx & n, uint64 v) { allocate_if_needed(n); n.m_sign = 0; unsigned * w = words(n); - uint64 * _vp = &v; - unsigned * _v = 0; + uint64_t * _vp = &v; + unsigned * _v = nullptr; memcpy(&_v, &_vp, sizeof(unsigned*)); for (unsigned i = 0; i < m_total_sz; i++) w[i] = 0; @@ -226,7 +226,7 @@ void mpfx_manager::set(mpfx & n, int num, unsigned den) { SASSERT(check(n)); } -void mpfx_manager::set(mpfx & n, int64 num, uint64 den) { +void mpfx_manager::set(mpfx & n, int64_t num, uint64_t den) { scoped_mpfx a(*this), b(*this); set(a, num); set(b, den); @@ -677,27 +677,27 @@ bool mpfx_manager::is_power_of_two(mpfx const & a) const { return is_power_of_two(a, k); } -int64 mpfx_manager::get_int64(mpfx const & n) const { +int64_t mpfx_manager::get_int64(mpfx const & n) const { SASSERT(is_int64(n)); unsigned * w = words(n); w += m_frac_part_sz; - uint64 r = 0; - memcpy(&r, w, sizeof(uint64)); + uint64_t r = 0; + memcpy(&r, w, sizeof(uint64_t)); if (r == 0x8000000000000000ull) { SASSERT(is_neg(n)); return INT64_MIN; } else { - return is_neg(n) ? -static_cast(r) : r; + return is_neg(n) ? -static_cast(r) : r; } } -uint64 mpfx_manager::get_uint64(mpfx const & n) const { +uint64_t mpfx_manager::get_uint64(mpfx const & n) const { SASSERT(is_uint64(n)); unsigned * w = words(n); w += m_frac_part_sz; - uint64 r = 0; - memcpy(&r, w, sizeof(uint64)); + uint64_t r = 0; + memcpy(&r, w, sizeof(uint64_t)); return r; } diff --git a/src/util/mpfx.h b/src/util/mpfx.h index 98f9bb322..a8e93f0b0 100644 --- a/src/util/mpfx.h +++ b/src/util/mpfx.h @@ -125,15 +125,15 @@ public: static bool field() { return true; } class exception : public z3_exception { - virtual char const * msg() const { return "multi-precision fixed point (mpfx) exception"; } + char const * msg() const override { return "multi-precision fixed point (mpfx) exception"; } }; class overflow_exception : public exception { - virtual char const * msg() const { return "multi-precision fixed point (mpfx) overflow"; } + char const * msg() const override { return "multi-precision fixed point (mpfx) overflow"; } }; class div0_exception : public exception { - virtual char const * msg() const { return "multi-precision fixed point (mpfx) division by zero"; } + char const * msg() const override { return "multi-precision fixed point (mpfx) division by zero"; } }; mpfx_manager(unsigned int_sz = 2, unsigned frac_sz = 1, unsigned initial_capacity = 1024); @@ -200,12 +200,12 @@ public: bool is_minus_one(mpfx const & n) const { return is_neg(n) && is_abs_one(n); } /** - \brief Return true if \c a is an integer and fits in an int64 machine integer. + \brief Return true if \c a is an integer and fits in an \c int64_t machine integer. */ bool is_int64(mpfx const & a) const; /** - \brief Return true if \c a is a non-negative integer and fits in an int64 machine integer. + \brief Return true if \c a is a non-negative integer and fits in an \c int64_t machine integer. */ bool is_uint64(mpfx const & a) const; @@ -306,10 +306,10 @@ public: void set(mpfx & n, int v); void set(mpfx & n, unsigned v); - void set(mpfx & n, int64 v); - void set(mpfx & n, uint64 v); + void set(mpfx & n, int64_t v); + void set(mpfx & n, uint64_t v); void set(mpfx & n, int num, unsigned den); - void set(mpfx & n, int64 num, uint64 den); + void set(mpfx & n, int64_t num, uint64_t den); void set(mpfx & n, mpfx const & v); void set(mpfx & n, unsynch_mpz_manager & m, mpz const & v); void set(mpfx & n, synch_mpz_manager & m, mpz const & v); @@ -343,14 +343,14 @@ public: \pre is_int64(n) */ - int64 get_int64(mpfx const & n) const; + int64_t get_int64(mpfx const & n) const; /** \brief Return n as an uint64. \pre is_uint64(n) */ - uint64 get_uint64(mpfx const & n) const; + uint64_t get_uint64(mpfx const & n) const; /** \brief Convert n into a mpz numeral. diff --git a/src/util/mpn.cpp b/src/util/mpn.cpp index 2059ea6fd..b8cab4a9f 100644 --- a/src/util/mpn.cpp +++ b/src/util/mpn.cpp @@ -23,7 +23,7 @@ Revision History: #define max(a,b) (((a) > (b)) ? (a) : (b)) -typedef uint64 mpn_double_digit; +typedef uint64_t mpn_double_digit; static_assert(sizeof(mpn_double_digit) == 2 * sizeof(mpn_digit), "size alignment"); const mpn_digit mpn_manager::zero = 0; diff --git a/src/util/mpq.h b/src/util/mpq.h index fd0ae13d4..010bb2c8a 100644 --- a/src/util/mpq.h +++ b/src/util/mpq.h @@ -501,6 +501,8 @@ public: void machine_div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::machine_div(a, b, c); } + void machine_div_rem(mpz const & a, mpz const & b, mpz & c, mpz& d) { mpz_manager::machine_div_rem(a, b, c, d); } + void div(mpz const & a, mpz const & b, mpz & c) { mpz_manager::div(a, b, c); } void rat_div(mpz const & a, mpz const & b, mpq & c) { @@ -515,6 +517,13 @@ public: reset_denominator(c); } + void machine_idiv_rem(mpq const & a, mpq const & b, mpq & c, mpq & d) { + SASSERT(is_int(a) && is_int(b)); + machine_div_rem(a.m_num, b.m_num, c.m_num, d.m_num); + reset_denominator(c); + reset_denominator(d); + } + void machine_idiv(mpq const & a, mpq const & b, mpz & c) { SASSERT(is_int(a) && is_int(b)); machine_div(a.m_num, b.m_num, c); @@ -686,7 +695,7 @@ public: normalize(a); } - void set(mpq & a, int64 n, uint64 d) { + void set(mpq & a, int64_t n, uint64_t d) { SASSERT(d != 0); set(a.m_num, n); set(a.m_den, d); @@ -718,16 +727,16 @@ public: void set(mpq & a, char const * val); - void set(mpz & a, int64 val) { mpz_manager::set(a, val); } + void set(mpz & a, int64_t val) { mpz_manager::set(a, val); } - void set(mpq & a, int64 val) { + void set(mpq & a, int64_t val) { set(a.m_num, val); reset_denominator(a); } - void set(mpz & a, uint64 val) { mpz_manager::set(a, val); } + void set(mpz & a, uint64_t val) { mpz_manager::set(a, val); } - void set(mpq & a, uint64 val) { + void set(mpq & a, uint64_t val) { set(a.m_num, val); reset_denominator(a); } @@ -765,17 +774,17 @@ public: bool is_int64(mpz const & a) const { return mpz_manager::is_int64(a); } - uint64 get_uint64(mpz const & a) const { return mpz_manager::get_uint64(a); } + uint64_t get_uint64(mpz const & a) const { return mpz_manager::get_uint64(a); } - int64 get_int64(mpz const & a) const { return mpz_manager::get_int64(a); } + int64_t get_int64(mpz const & a) const { return mpz_manager::get_int64(a); } bool is_uint64(mpq const & a) const { return is_int(a) && is_uint64(a.m_num); } bool is_int64(mpq const & a) const { return is_int(a) && is_int64(a.m_num); } - uint64 get_uint64(mpq const & a) const { SASSERT(is_uint64(a)); return get_uint64(a.m_num); } + uint64_t get_uint64(mpq const & a) const { SASSERT(is_uint64(a)); return get_uint64(a.m_num); } - int64 get_int64(mpq const & a) const { SASSERT(is_int64(a)); return get_int64(a.m_num); } + int64_t get_int64(mpq const & a) const { SASSERT(is_int64(a)); return get_int64(a.m_num); } double get_double(mpz const & a) const { return mpz_manager::get_double(a); } diff --git a/src/util/mpz.cpp b/src/util/mpz.cpp index 7ad472ef1..861a31cfb 100644 --- a/src/util/mpz.cpp +++ b/src/util/mpz.cpp @@ -45,43 +45,93 @@ Revision History: #define LEHMER_GCD #endif -template -static T gcd_core(T u, T v) { - if (u == 0) - return v; - if (v == 0) - return u; - int k; - - for (k = 0; ((u | v) & 1) == 0; ++k) { - u >>= 1; - v >>= 1; - } - - while ((u & 1) == 0) - u >>= 1; - +#include + +#if defined(__GNUC__) +#define _trailing_zeros32(X) __builtin_ctz(X) +#else +#define _trailing_zeros32(X) _tzcnt_u32(X) +#endif + +#if defined(_AMD64_) + #if defined(__GNUC__) + #define _trailing_zeros64(X) __builtin_ctzll(X) + #else + #define _trailing_zeros64(X) _tzcnt_u64(X) + #endif +#else +inline uint64_t _trailing_zeros64(uint64_t x) { + uint64_t r = 0; + for (; 0 == (x & 1) && r < 64; ++r, x >>= 1); + return r; +} +#endif + + +#define _bit_min(x, y) (y + ((x - y) & ((int)(x - y) >> 31))) +#define _bit_max(x, y) (x - ((x - y) & ((int)(x - y) >> 31))) + + +unsigned u_gcd(unsigned u, unsigned v) { + if (u == 0) return v; + if (v == 0) return u; + unsigned shift = _trailing_zeros32(u | v); + u >>= _trailing_zeros32(u); + if (u == 1 || v == 1) return 1 << shift; + if (u == v) return u << shift; do { - while ((v & 1) == 0) - v >>= 1; - - if (u < v) { - v -= u; - } - else { - T diff = u - v; - u = v; - v = diff; - } - v >>= 1; - } while (v != 0); - - return u << k; + v >>= _trailing_zeros32(v); +#if 1 + unsigned diff = u - v; + unsigned mdiff = diff & (unsigned)((int)diff >> 31); + u = v + mdiff; // min + v = diff - 2 * mdiff; // if v <= u: u - v, if v > u: v - u = u - v - 2 * (u - v) +#endif +#if 0 + unsigned t = _bit_max(u, v); + u = _bit_min(u, v); + v = t; + v -= u; +#endif +#if 0 + unsigned t = std::max(u, v); + u = std::min(u,v); + v = t; + v -= u; +#endif +#if 0 + if (u > v) std::swap(u, v); + v -= u; +#endif +#if 0 + unsigned d1 = u - v; + unsigned d2 = v - u; + unsigned md21 = d2 & (unsigned)((int)d1 >> 31); + unsigned md12 = d1 & (unsigned)((int)d2 >> 31); + u = _bit_min(u, v); + v = md12 | md21; +#endif + } + while (v != 0); + return u << shift; +} + +uint64_t u64_gcd(uint64_t u, uint64_t v) { + if (u == 0) return v; + if (v == 0) return u; + if (u == 1 || v == 1) return 1; + auto shift = _trailing_zeros64(u | v); + u >>= _trailing_zeros64(u); + do { + v >>= _trailing_zeros64(v); + if (u > v) std::swap(u, v); + v -= u; + } + while (v != 0); + return u << shift; } -unsigned u_gcd(unsigned u, unsigned v) { return gcd_core(u, v); } -uint64 u64_gcd(uint64 u, uint64 v) { return gcd_core(u, v); } template mpz_manager::mpz_manager(): @@ -89,7 +139,7 @@ mpz_manager::mpz_manager(): if (SYNCH) omp_init_nest_lock(&m_lock); #ifndef _MP_GMP - if (sizeof(digit_t) == sizeof(uint64)) { + if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine m_init_cell_capacity = 4; } @@ -101,7 +151,7 @@ mpz_manager::mpz_manager(): m_arg[i] = allocate(m_init_cell_capacity); m_arg[i]->m_size = 1; } - set(m_int_min, -static_cast(INT_MIN)); + set(m_int_min, -static_cast(INT_MIN)); #else // GMP mpz_init(m_tmp); @@ -122,8 +172,8 @@ mpz_manager::mpz_manager(): mpz_init(m_int64_max); mpz_init(m_int64_min); - max_l = static_cast(INT64_MAX % static_cast(UINT_MAX)); - max_h = static_cast(INT64_MAX / static_cast(UINT_MAX)); + max_l = static_cast(INT64_MAX % static_cast(UINT_MAX)); + max_h = static_cast(INT64_MAX / static_cast(UINT_MAX)); mpz_set_ui(m_int64_max, max_h); mpz_set_ui(m_tmp, UINT_MAX); mpz_mul(m_int64_max, m_tmp, m_int64_max); @@ -134,7 +184,7 @@ mpz_manager::mpz_manager(): #endif mpz one(1); - set(m_two64, (uint64)UINT64_MAX); + set(m_two64, (uint64_t)UINT64_MAX); add(m_two64, one, m_two64); } @@ -162,13 +212,13 @@ mpz_manager::~mpz_manager() { } template -void mpz_manager::set_big_i64(mpz & c, int64 v) { +void mpz_manager::set_big_i64(mpz & c, int64_t v) { #ifndef _MP_GMP if (is_small(c)) { c.m_ptr = allocate(m_init_cell_capacity); } SASSERT(capacity(c) >= m_init_cell_capacity); - uint64 _v; + uint64_t _v; if (v < 0) { _v = -v; c.m_val = -1; @@ -177,7 +227,7 @@ void mpz_manager::set_big_i64(mpz & c, int64 v) { _v = v; c.m_val = 1; } - if (sizeof(digit_t) == sizeof(uint64)) { + if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine digits(c)[0] = static_cast(_v); c.m_ptr->m_size = 1; @@ -192,7 +242,7 @@ void mpz_manager::set_big_i64(mpz & c, int64 v) { if (is_small(c)) { c.m_ptr = allocate(); } - uint64 _v; + uint64_t _v; bool sign; if (v < 0) { _v = -v; @@ -212,14 +262,14 @@ void mpz_manager::set_big_i64(mpz & c, int64 v) { } template -void mpz_manager::set_big_ui64(mpz & c, uint64 v) { +void mpz_manager::set_big_ui64(mpz & c, uint64_t v) { #ifndef _MP_GMP if (is_small(c)) { c.m_ptr = allocate(m_init_cell_capacity); } SASSERT(capacity(c) >= m_init_cell_capacity); c.m_val = 1; - if (sizeof(digit_t) == sizeof(uint64)) { + if (sizeof(digit_t) == sizeof(uint64_t)) { // 64-bit machine digits(c)[0] = static_cast(v); c.m_ptr->m_size = 1; @@ -726,7 +776,7 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { // For now, it only works if sizeof(digit_t) == sizeof(unsigned) static_assert(sizeof(digit_t) == sizeof(unsigned), ""); - int64 a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz; + int64_t a_hat, b_hat, A, B, C, D, T, q, a_sz, b_sz; mpz a1, b1, t, r, tmp; set(a1, a); set(b1, b); @@ -766,10 +816,10 @@ void mpz_manager::gcd(mpz const & a, mpz const & b, mpz & c) { D = 1; while (true) { // Loop invariants - SASSERT(a_hat + A <= static_cast(UINT_MAX) + 1); - SASSERT(a_hat + B < static_cast(UINT_MAX) + 1); - SASSERT(b_hat + C < static_cast(UINT_MAX) + 1); - SASSERT(b_hat + D <= static_cast(UINT_MAX) + 1); + SASSERT(a_hat + A <= static_cast(UINT_MAX) + 1); + SASSERT(a_hat + B < static_cast(UINT_MAX) + 1); + SASSERT(b_hat + C < static_cast(UINT_MAX) + 1); + SASSERT(b_hat + D <= static_cast(UINT_MAX) + 1); // overflows can't happen since I'm using int64 if (b_hat + C == 0 || b_hat + D == 0) break; @@ -1035,7 +1085,7 @@ void mpz_manager::bitwise_or(mpz const & a, mpz const & b, mpz & c) { mod(a1, m_two64, a2); mod(b1, m_two64, b2); TRACE("mpz", tout << "a2: " << to_string(a2) << ", b2: " << to_string(b2) << "\n";); - uint64 v = get_uint64(a2) | get_uint64(b2); + uint64_t v = get_uint64(a2) | get_uint64(b2); TRACE("mpz", tout << "uint(a2): " << get_uint64(a2) << ", uint(b2): " << get_uint64(b2) << "\n";); set(tmp, v); mul(tmp, m, tmp); @@ -1082,7 +1132,7 @@ void mpz_manager::bitwise_and(mpz const & a, mpz const & b, mpz & c) { while (!is_zero(a1) && !is_zero(b1)) { mod(a1, m_two64, a2); mod(b1, m_two64, b2); - uint64 v = get_uint64(a2) & get_uint64(b2); + uint64_t v = get_uint64(a2) & get_uint64(b2); set(tmp, v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v @@ -1119,7 +1169,7 @@ void mpz_manager::bitwise_xor(mpz const & a, mpz const & b, mpz & c) { while (!is_zero(a1) && !is_zero(b1)) { mod(a1, m_two64, a2); mod(b1, m_two64, b2); - uint64 v = get_uint64(a2) ^ get_uint64(b2); + uint64_t v = get_uint64(a2) ^ get_uint64(b2); set(tmp, v); mul(tmp, m, tmp); add(c, tmp, c); // c += m * v @@ -1151,7 +1201,7 @@ template void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { SASSERT(is_nonneg(a)); if (is_small(a) && sz <= 63) { - int64 mask = (static_cast(1) << sz) - static_cast(1); + int64_t mask = (static_cast(1) << sz) - static_cast(1); set_i64(c, (~ i64(a)) & mask); } else { @@ -1161,11 +1211,11 @@ void mpz_manager::bitwise_not(unsigned sz, mpz const & a, mpz & c) { set(c, 0); while (sz > 0) { mod(a1, m_two64, a2); - uint64 n = get_uint64(a2); - uint64 v = ~n; + uint64_t n = get_uint64(a2); + uint64_t v = ~n; SASSERT(~v == n); if (sz < 64) { - uint64 mask = (1ull << static_cast(sz)) - 1ull; + uint64_t mask = (1ull << static_cast(sz)) - 1ull; v = mask & v; } TRACE("bitwise_not", tout << "sz: " << sz << ", v: " << v << ", n: " << n << "\n";); @@ -1265,7 +1315,7 @@ bool mpz_manager::is_uint64(mpz const & a) const { return false; if (is_small(a)) return true; - if (sizeof(digit_t) == sizeof(uint64)) { + if (sizeof(digit_t) == sizeof(uint64_t)) { return size(a) <= 1; } else { @@ -1286,9 +1336,9 @@ bool mpz_manager::is_int64(mpz const & a) const { #ifndef _MP_GMP if (!is_abs_uint64(a)) return false; - uint64 num = big_abs_to_uint64(a); - uint64 msb = static_cast(1) << 63; - uint64 msb_val = msb & num; + uint64_t num = big_abs_to_uint64(a); + uint64_t msb = static_cast(1) << 63; + uint64_t msb_val = msb & num; if (a.m_val >= 0) { // non-negative number. return (0 == msb_val); @@ -1307,54 +1357,54 @@ bool mpz_manager::is_int64(mpz const & a) const { } template -uint64 mpz_manager::get_uint64(mpz const & a) const { +uint64_t mpz_manager::get_uint64(mpz const & a) const { if (is_small(a)) - return static_cast(a.m_val); + return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(a.m_ptr->m_size > 0); return big_abs_to_uint64(a); #else // GMP version - if (sizeof(uint64) == sizeof(unsigned long)) { + if (sizeof(uint64_t) == sizeof(unsigned long)) { return mpz_get_ui(*a.m_ptr); } else { mpz_manager * _this = const_cast(this); mpz_set(_this->m_tmp, *a.m_ptr); mpz_mod(_this->m_tmp, m_tmp, m_two32); - uint64 r = static_cast(mpz_get_ui(m_tmp)); + uint64_t r = static_cast(mpz_get_ui(m_tmp)); mpz_set(_this->m_tmp, *a.m_ptr); mpz_div(_this->m_tmp, m_tmp, m_two32); - r += static_cast(mpz_get_ui(m_tmp)) << static_cast(32); + r += static_cast(mpz_get_ui(m_tmp)) << static_cast(32); return r; } #endif } template -int64 mpz_manager::get_int64(mpz const & a) const { +int64_t mpz_manager::get_int64(mpz const & a) const { if (is_small(a)) - return static_cast(a.m_val); + return static_cast(a.m_val); #ifndef _MP_GMP SASSERT(is_int64(a)); - uint64 num = big_abs_to_uint64(a); + uint64_t num = big_abs_to_uint64(a); if (a.m_val < 0) { if (num != 0 && (num << 1) == 0) return INT64_MIN; - return -static_cast(num); + return -static_cast(num); } - return static_cast(num); + return static_cast(num); #else // GMP - if (sizeof(int64) == sizeof(long) || mpz_fits_slong_p(*a.m_ptr)) { + if (sizeof(int64_t) == sizeof(long) || mpz_fits_slong_p(*a.m_ptr)) { return mpz_get_si(*a.m_ptr); } else { mpz_manager * _this = const_cast(this); mpz_mod(_this->m_tmp, *a.m_ptr, m_two32); - int64 r = static_cast(mpz_get_ui(m_tmp)); + int64_t r = static_cast(mpz_get_ui(m_tmp)); mpz_div(_this->m_tmp, *a.m_ptr, m_two32); - r += static_cast(mpz_get_si(m_tmp)) << static_cast(32); + r += static_cast(mpz_get_si(m_tmp)) << static_cast(32); return r; } #endif @@ -1370,7 +1420,7 @@ double mpz_manager::get_double(mpz const & a) const { unsigned sz = size(a); for (unsigned i = 0; i < sz; i++) { r += d * static_cast(digits(a)[i]); - if (sizeof(digit_t) == sizeof(uint64)) + if (sizeof(digit_t) == sizeof(uint64_t)) d *= static_cast(UINT64_MAX); // 64-bit version else d *= static_cast(UINT_MAX); // 32-bit version @@ -1696,7 +1746,7 @@ void mpz_manager::mul2k(mpz & a, unsigned k) { if (k == 0 || is_zero(a)) return; if (is_small(a) && k < 32) { - set_i64(a, i64(a) * (static_cast(1) << k)); + set_i64(a, i64(a) * (static_cast(1) << k)); return; } #ifndef _MP_GMP @@ -1798,9 +1848,9 @@ unsigned mpz_manager::power_of_two_multiple(mpz const & a) { if (sizeof(digit_t) == 8) { // TODO: we can remove this if after we move to MPN // In MPN the digit_t is always an unsigned integer - if (static_cast(v) % (static_cast(1) << 32) == 0) { + if (static_cast(v) % (static_cast(1) << 32) == 0) { r += 32; - v = static_cast(static_cast(v) / (static_cast(1) << 32)); + v = static_cast(static_cast(v) / (static_cast(1) << 32)); } } COUNT_DIGIT_RIGHT_ZEROS(); diff --git a/src/util/mpz.h b/src/util/mpz.h index f04430e17..92c6d0d10 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -30,7 +30,7 @@ Revision History: #include "util/mpn.h" unsigned u_gcd(unsigned u, unsigned v); -uint64 u64_gcd(uint64 u, uint64 v); +uint64_t u64_gcd(uint64_t u, uint64_t v); #ifdef _MP_GMP typedef unsigned digit_t; @@ -92,9 +92,9 @@ class mpz { friend class mpbq_manager; mpz & operator=(mpz const & other) { UNREACHABLE(); return *this; } public: - mpz(int v):m_val(v), m_ptr(0) {} - mpz():m_val(0), m_ptr(0) {} - mpz(mpz && other) : m_val(other.m_val), m_ptr(0) { + mpz(int v):m_val(v), m_ptr(nullptr) {} + mpz():m_val(0), m_ptr(nullptr) {} + mpz(mpz && other) : m_val(other.m_val), m_ptr(nullptr) { std::swap(m_ptr, other.m_ptr); } void swap(mpz & other) { @@ -187,16 +187,16 @@ class mpz_manager { /** \brief Set \c a with the value stored at m_tmp[IDX], and the given sign. - \c sz is an overapproximation of the the size of the number stored at \c tmp. + \c sz is an overapproximation of the size of the number stored at \c tmp. */ template void set(mpz & a, int sign, unsigned sz); - static int64 i64(mpz const & a) { return static_cast(a.m_val); } + static int64_t i64(mpz const & a) { return static_cast(a.m_val); } - void set_big_i64(mpz & c, int64 v); + void set_big_i64(mpz & c, int64_t v); - void set_i64(mpz & c, int64 v) { + void set_i64(mpz & c, int64_t v) { if (v >= INT_MIN && v <= INT_MAX) { del(c); c.m_val = static_cast(v); @@ -208,7 +208,7 @@ class mpz_manager { } } - void set_big_ui64(mpz & c, uint64 v); + void set_big_ui64(mpz & c, uint64_t v); #ifndef _MP_GMP static unsigned capacity(mpz const & c) { return c.m_ptr->m_capacity; } @@ -221,24 +221,24 @@ class mpz_manager { static bool is_abs_uint64(mpz const & a) { if (is_small(a)) return true; - if (sizeof(digit_t) == sizeof(uint64)) + if (sizeof(digit_t) == sizeof(uint64_t)) return size(a) <= 1; else return size(a) <= 2; } // CAST the absolute value into a UINT64 - static uint64 big_abs_to_uint64(mpz const & a) { + static uint64_t big_abs_to_uint64(mpz const & a) { SASSERT(is_abs_uint64(a)); SASSERT(!is_small(a)); if (a.m_ptr->m_size == 1) return digits(a)[0]; - if (sizeof(digit_t) == sizeof(uint64)) + if (sizeof(digit_t) == sizeof(uint64_t)) // 64-bit machine return digits(a)[0]; else // 32-bit machine - return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); + return ((static_cast(digits(a)[1]) << 32) | (static_cast(digits(a)[0]))); } template @@ -333,16 +333,16 @@ public: ~mpz_manager(); - static bool is_small(mpz const & a) { return a.m_ptr == 0; } + static bool is_small(mpz const & a) { return a.m_ptr == nullptr; } static mpz mk_z(int val) { return mpz(val); } void del(mpz & a) { - if (a.m_ptr != 0) { + if (a.m_ptr != nullptr) { MPZ_BEGIN_CRITICAL(); deallocate(a.m_ptr); MPZ_END_CRITICAL(); - a.m_ptr = 0; + a.m_ptr = nullptr; } } @@ -426,8 +426,8 @@ public: void machine_div_rem(mpz const & a, mpz const & b, mpz & q, mpz & r) { STRACE("mpz", tout << "[mpz-ext] divrem(" << to_string(a) << ", " << to_string(b) << ") == ";); if (is_small(a) && is_small(b)) { - int64 _a = i64(a); - int64 _b = i64(b); + int64_t _a = i64(a); + int64_t _b = i64(b); set_i64(q, _a / _b); set_i64(r, _a % _b); } @@ -686,16 +686,16 @@ public: if (val <= INT_MAX) set(a, static_cast(val)); else - set(a, static_cast(static_cast(val))); + set(a, static_cast(static_cast(val))); } void set(mpz & a, char const * val); - void set(mpz & a, int64 val) { + void set(mpz & a, int64_t val) { set_i64(a, val); } - void set(mpz & a, uint64 val) { + void set(mpz & a, uint64_t val) { if (val < INT_MAX) { del(a); a.m_val = static_cast(val); @@ -729,9 +729,9 @@ public: bool is_int64(mpz const & a) const; - uint64 get_uint64(mpz const & a) const; + uint64_t get_uint64(mpz const & a) const; - int64 get_int64(mpz const & a) const; + int64_t get_int64(mpz const & a) const; bool is_uint(mpz const & a) const { return is_uint64(a) && get_uint64(a) < UINT_MAX; } diff --git a/src/util/mpzzp.h b/src/util/mpzzp.h index a30eff7b6..82ed55bbc 100644 --- a/src/util/mpzzp.h +++ b/src/util/mpzzp.h @@ -87,7 +87,7 @@ public: setup_p(); } - mpzzp_manager(numeral_manager & _m, uint64 p, bool prime = true): + mpzzp_manager(numeral_manager & _m, uint64_t p, bool prime = true): m_manager(_m), m_z(false) { m().set(m_p, p); @@ -120,7 +120,7 @@ public: void set_z() { m_z = true; } void set_zp(mpz const & new_p) { m_z = false; m_p_prime = true; m().set(m_p, new_p); setup_p(); } - void set_zp(uint64 new_p) { m_z = false; m_p_prime = true; m().set(m_p, new_p); setup_p(); } + void set_zp(uint64_t new_p) { m_z = false; m_p_prime = true; m().set(m_p, new_p); setup_p(); } // p = p^2 void set_p_sq() { SASSERT(!m_z); m_p_prime = false; m().mul(m_p, m_p, m_p); setup_p(); } void set_zp_swap(mpz & new_p) { SASSERT(!m_z); m().swap(m_p, new_p); setup_p(); } @@ -230,14 +230,14 @@ public: void set(mpz & a, int val) { m().set(a, val); p_normalize(a); } void set(mpz & a, unsigned val) { m().set(a, val); p_normalize(a); } void set(mpz & a, char const * val) { m().set(a, val); p_normalize(a); } - void set(mpz & a, int64 val) { m().set(a, val); p_normalize(a); } - void set(mpz & a, uint64 val) { m().set(a, val); p_normalize(a); } + void set(mpz & a, int64_t val) { m().set(a, val); p_normalize(a); } + void set(mpz & a, uint64_t val) { m().set(a, val); p_normalize(a); } void set(mpz & a, mpz const & val) { m().set(a, val); p_normalize(a); } bool is_uint64(mpz & a) const { const_cast(this)->p_normalize(a); return m().is_uint64(a); } bool is_int64(mpz & a) const { const_cast(this)->p_normalize(a); return m().is_int64(a); } - uint64 get_uint64(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_uint64(a); } - int64 get_int64(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_int64(a); } + uint64_t get_uint64(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_uint64(a); } + int64_t get_int64(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_int64(a); } double get_double(mpz & a) const { const_cast(this)->p_normalize(a); return m().get_double(a); } void power(mpz const & a, unsigned k, mpz & b) { SASSERT(is_p_normalized(a)); @@ -265,8 +265,8 @@ public: bool is_uint64(mpz const & a) const { return m().is_uint64(a); } bool is_int64(mpz const & a) const { return m().is_int64(a); } - uint64 get_uint64(mpz const & a) const { return m().get_uint64(a); } - int64 get_int64(mpz const & a) const { return m().get_int64(a); } + uint64_t get_uint64(mpz const & a) const { return m().get_uint64(a); } + int64_t get_int64(mpz const & a) const { return m().get_int64(a); } void mul2k(mpz & a, unsigned k) { m().mul2k(a, k); p_normalize(a); } void mul2k(mpz const & a, unsigned k, mpz & r) { m().mul2k(a, k, r); p_normalize(r); } diff --git a/src/util/obj_hashtable.h b/src/util/obj_hashtable.h index df279383b..c5cb0c319 100644 --- a/src/util/obj_hashtable.h +++ b/src/util/obj_hashtable.h @@ -33,9 +33,9 @@ class obj_hash_entry { T * m_ptr; public: typedef T * data; - obj_hash_entry():m_ptr(0) {} + obj_hash_entry():m_ptr(nullptr) {} unsigned get_hash() const { return m_ptr->hash(); } - bool is_free() const { return m_ptr == 0; } + bool is_free() const { return m_ptr == nullptr; } bool is_deleted() const { return m_ptr == reinterpret_cast(1); } bool is_used() const { return m_ptr != reinterpret_cast(0) && m_ptr != reinterpret_cast(1); } T * get_data() const { return m_ptr; } @@ -43,7 +43,7 @@ public: void set_data(T * d) { m_ptr = d; } void set_hash(unsigned h) { SASSERT(h == m_ptr->hash()); } void mark_as_deleted() { m_ptr = reinterpret_cast(1); } - void mark_as_free() { m_ptr = 0; } + void mark_as_free() { m_ptr = nullptr; } }; template @@ -60,7 +60,7 @@ public: struct key_data { Key * m_key; Value m_value; - key_data():m_key(0) { + key_data():m_key(nullptr) { } key_data(Key * k): m_key(k) { @@ -85,7 +85,7 @@ public: typedef key_data data; obj_map_entry() {} unsigned get_hash() const { return m_data.hash(); } - bool is_free() const { return m_data.m_key == 0; } + bool is_free() const { return m_data.m_key == nullptr; } bool is_deleted() const { return m_data.m_key == reinterpret_cast(1); } bool is_used() const { return m_data.m_key != reinterpret_cast(0) && m_data.m_key != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } @@ -93,7 +93,7 @@ public: void set_data(key_data && d) { m_data = std::move(d); } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key = reinterpret_cast(1); } - void mark_as_free() { m_data.m_key = 0; } + void mark_as_free() { m_data.m_key = nullptr; } }; typedef core_hashtable, default_eq > table; @@ -163,7 +163,7 @@ public: if (e) { v = e->get_data().m_value; } - return (0 != e); + return (nullptr != e); } value const & find(key * k) const { @@ -191,7 +191,7 @@ public: } bool contains(Key * k) const { - return find_core(k) != 0; + return find_core(k) != nullptr; } void remove(Key * k) { @@ -204,6 +204,14 @@ public: unsigned long long get_num_collision() const { return m_table.get_num_collision(); } + void get_collisions(Key * k, vector& collisions) { + vector cs; + m_table.get_collisions(key_data(k), cs); + for (key_data const& kd : cs) { + collisions.push_back(kd.m_key); + } + } + void swap(obj_map & other) { m_table.swap(other.m_table); } diff --git a/src/util/obj_pair_hashtable.h b/src/util/obj_pair_hashtable.h index 2addc3902..2b8924e9f 100644 --- a/src/util/obj_pair_hashtable.h +++ b/src/util/obj_pair_hashtable.h @@ -34,7 +34,7 @@ class obj_pair_hash_entry { public: typedef std::pair data; - obj_pair_hash_entry():m_data(static_cast(0),static_cast(0)) {} + obj_pair_hash_entry():m_data(static_cast(nullptr),static_cast(nullptr)) {} unsigned get_hash() const { return m_hash; } bool is_free() const { return m_data.first == 0; } bool is_deleted() const { return m_data.first == reinterpret_cast(1); } @@ -68,8 +68,8 @@ public: friend class entry; public: key_data(): - m_key1(0), - m_key2(0), + m_key1(nullptr), + m_key2(nullptr), m_hash(0) { } key_data(Key1 * k1, Key2 * k2): @@ -96,7 +96,7 @@ protected: typedef key_data data; entry() {} unsigned get_hash() const { return m_data.hash(); } - bool is_free() const { return m_data.m_key1 == 0; } + bool is_free() const { return m_data.m_key1 == nullptr; } bool is_deleted() const { return m_data.m_key1 == reinterpret_cast(1); } bool is_used() const { return m_data.m_key1 != reinterpret_cast(0) && m_data.m_key1 != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } @@ -104,7 +104,7 @@ protected: void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key1 = reinterpret_cast(1); } - void mark_as_free() { m_data.m_key1 = 0; } + void mark_as_free() { m_data.m_key1 = nullptr; } }; typedef core_hashtable, default_eq > table; @@ -158,11 +158,24 @@ public: if (e) { v = e->get_data().get_value(); } - return (0 != e); + return (nullptr != e); + } + + Value const & find(Key1 * k1, Key2 * k2) const { + entry * e = find_core(k1, k2); + return e->get_data().get_value(); + } + + Value const& operator[](std::pair const& key) const { + return find(key.first, key.second); } bool contains(Key1 * k1, Key2 * k2) const { - return find_core(k1, k2) != 0; + return find_core(k1, k2) != nullptr; + } + + bool contains(std::pair const& key) const { + return contains(key.first, key.second); } void erase(Key1 * k1, Key2 * k2) { diff --git a/src/util/obj_ref.h b/src/util/obj_ref.h index 72762ea5b..409a77fc0 100644 --- a/src/util/obj_ref.h +++ b/src/util/obj_ref.h @@ -43,7 +43,7 @@ public: } explicit obj_ref(TManager & m): - m_obj(0), + m_obj(nullptr), m_manager(m) { } @@ -53,7 +53,7 @@ public: inc_ref(); } - obj_ref(obj_ref && other) : m_obj(0), m_manager(other.m_manager) { + obj_ref(obj_ref && other) : m_obj(nullptr), m_manager(other.m_manager) { std::swap(m_obj, other.m_obj); } @@ -67,9 +67,9 @@ public: T * get() const { return m_obj; } - operator bool() const { return m_obj != 0; } + operator bool() const { return m_obj != nullptr; } - bool operator!() const { return m_obj == 0; } + bool operator!() const { return m_obj == nullptr; } operator T*() const { return m_obj; } @@ -94,7 +94,7 @@ public: void reset() { dec_ref(); - m_obj = 0; + m_obj = nullptr; } void swap(obj_ref & n) { diff --git a/src/util/obj_triple_hashtable.h b/src/util/obj_triple_hashtable.h index 316b63b93..d9034c2e3 100644 --- a/src/util/obj_triple_hashtable.h +++ b/src/util/obj_triple_hashtable.h @@ -70,9 +70,9 @@ public: friend class entry; public: key_data(): - m_key1(0), - m_key2(0), - m_key3(0), + m_key1(nullptr), + m_key2(nullptr), + m_key3(nullptr), m_hash(0) { } key_data(Key1 * k1, Key2 * k2, Key3 * k3): @@ -102,7 +102,7 @@ protected: typedef key_data data; entry() {} unsigned get_hash() const { return m_data.hash(); } - bool is_free() const { return m_data.m_key1 == 0; } + bool is_free() const { return m_data.m_key1 == nullptr; } bool is_deleted() const { return m_data.m_key1 == reinterpret_cast(1); } bool is_used() const { return m_data.m_key1 != reinterpret_cast(0) && m_data.m_key1 != reinterpret_cast(1); } key_data const & get_data() const { return m_data; } @@ -110,7 +110,7 @@ protected: void set_data(key_data const & d) { m_data = d; } void set_hash(unsigned h) { SASSERT(h == m_data.hash()); } void mark_as_deleted() { m_data.m_key1 = reinterpret_cast(1); } - void mark_as_free() { m_data.m_key1 = 0; } + void mark_as_free() { m_data.m_key1 = nullptr; } }; typedef core_hashtable, default_eq > table; @@ -164,7 +164,7 @@ public: if (e) { v = e->get_data().get_value(); } - return (0 != e); + return (nullptr != e); } bool contains(Key1 * k1, Key2 * k2, Key3 * k3) const { diff --git a/src/util/optional.h b/src/util/optional.h index 5b3753ac6..d496fd322 100644 --- a/src/util/optional.h +++ b/src/util/optional.h @@ -34,14 +34,14 @@ class optional { void destroy() { if (m_initialized == 1) { dealloc(m_obj); - m_obj = 0; + m_obj = nullptr; } m_initialized = 0; } public: optional(): - m_obj(0), m_initialized(0) {} + m_obj(nullptr), m_initialized(0) {} explicit optional(const T & val) { construct(val); @@ -128,7 +128,7 @@ class optional { public: - optional():m_ptr(0) {} + optional():m_ptr(nullptr) {} explicit optional(T * val):m_ptr(val) {} @@ -140,7 +140,7 @@ public: operator bool() const { return m_ptr != 0; } - bool operator!() const { return m_ptr == 0; } + bool operator!() const { return m_ptr == nullptr; } void reset() { m_ptr = 0; } diff --git a/src/util/page.cpp b/src/util/page.cpp index 4f6cea83c..ebf246fc1 100644 --- a/src/util/page.cpp +++ b/src/util/page.cpp @@ -31,7 +31,7 @@ inline char * alloc_page(size_t s) { char * r = alloc_svect(char, s+PAGE_HEADER_ inline void del_page(char * page) { dealloc_svect(page - PAGE_HEADER_SZ); } void del_pages(char * page) { - while (page != 0) { + while (page != nullptr) { char * prev = prev_page(page); del_page(page); page = prev; diff --git a/src/util/params.cpp b/src/util/params.cpp index ee6ce6627..d7e05cb00 100644 --- a/src/util/params.cpp +++ b/src/util/params.cpp @@ -24,7 +24,7 @@ Notes: params_ref params_ref::g_empty_params_ref; std::string norm_param_name(char const * n) { - if (n == 0) + if (n == nullptr) return "_"; if (*n == ':') n++; @@ -62,9 +62,9 @@ struct param_descrs::imp { info(): m_kind(CPK_INVALID), - m_descr(0), - m_default(0), - m_module(0) { + m_descr(nullptr), + m_default(nullptr), + m_module(nullptr) { } }; @@ -130,21 +130,21 @@ struct param_descrs::imp { info i; if (m_info.find(name, i)) return i.m_module; - return 0; + return nullptr; } char const * get_descr(symbol const & name) const { info i; if (m_info.find(name, i)) return i.m_descr; - return 0; + return nullptr; } char const * get_default(symbol const & name) const { info i; if (m_info.find(name, i)) return i.m_default; - return 0; + return nullptr; } unsigned size() const { @@ -191,7 +191,7 @@ struct param_descrs::imp { out << " (" << d.m_kind << ")"; if (include_descr) out << " " << d.m_descr; - if (d.m_default != 0) + if (d.m_default != nullptr) out << " (default: " << d.m_default << ")"; out << "\n"; } @@ -323,7 +323,6 @@ class params { typedef std::pair entry; svector m_entries; unsigned m_ref_count; - void del_value(entry & e); void del_values(); @@ -334,7 +333,10 @@ public: } void inc_ref() { m_ref_count++; } - void dec_ref() { SASSERT(m_ref_count > 0); m_ref_count--; if (m_ref_count == 0) dealloc(this); } + void dec_ref() { + SASSERT(m_ref_count > 0); + if (--m_ref_count == 0) dealloc(this); + } bool empty() const { return m_entries.empty(); } bool contains(symbol const & k) const; @@ -344,7 +346,6 @@ public: void reset(symbol const & k); void reset(char const * k); - void validate(param_descrs const & p) { svector::iterator it = m_entries.begin(); svector::iterator end = m_entries.end(); @@ -510,7 +511,7 @@ params_ref::~params_ref() { } params_ref::params_ref(params_ref const & p): - m_params(0) { + m_params(nullptr) { operator=(p); } @@ -554,7 +555,7 @@ params_ref & params_ref::operator=(params_ref const & p) { } void params_ref::copy(params_ref const & src) { - if (m_params == 0) + if (m_params == nullptr) operator=(src); else { init(); @@ -563,29 +564,27 @@ void params_ref::copy(params_ref const & src) { } void params_ref::copy_core(params const * src) { - if (src == 0) + if (src == nullptr) return; - svector::const_iterator it = src->m_entries.begin(); - svector::const_iterator end = src->m_entries.end(); - for (; it != end; ++it) { - switch (it->second.m_kind) { + for (auto const& p : src->m_entries) { + switch (p.second.m_kind) { case CPK_BOOL: - m_params->set_bool(it->first, it->second.m_bool_value); + m_params->set_bool(p.first, p.second.m_bool_value); break; case CPK_UINT: - m_params->set_uint(it->first, it->second.m_uint_value); + m_params->set_uint(p.first, p.second.m_uint_value); break; case CPK_DOUBLE: - m_params->set_double(it->first, it->second.m_double_value); + m_params->set_double(p.first, p.second.m_double_value); break; case CPK_NUMERAL: - m_params->set_rat(it->first, *(it->second.m_rat_value)); + m_params->set_rat(p.first, *(p.second.m_rat_value)); break; case CPK_SYMBOL: - m_params->set_sym(it->first, symbol::mk_symbol_from_c_ptr(it->second.m_sym_value)); + m_params->set_sym(p.first, symbol::mk_symbol_from_c_ptr(p.second.m_sym_value)); break; case CPK_STRING: - m_params->set_str(it->first, it->second.m_str_value); + m_params->set_str(p.first, p.second.m_str_value); break; default: UNREACHABLE(); diff --git a/src/util/params.h b/src/util/params.h index bad70a318..0a68a9606 100644 --- a/src/util/params.h +++ b/src/util/params.h @@ -37,7 +37,7 @@ class params_ref { void init(); void copy_core(params const * p); public: - params_ref():m_params(0) {} + params_ref():m_params(nullptr) {} params_ref(params_ref const & p); ~params_ref(); @@ -115,8 +115,8 @@ public: param_descrs(); ~param_descrs(); void copy(param_descrs & other); - void insert(char const * name, param_kind k, char const * descr, char const * def = 0, char const* module = 0); - void insert(symbol const & name, param_kind k, char const * descr, char const * def = 0, char const* module = 0); + void insert(char const * name, param_kind k, char const * descr, char const * def = nullptr, char const* module = nullptr); + void insert(symbol const & name, param_kind k, char const * descr, char const * def = nullptr, char const* module = nullptr); bool contains(char const * name) const; bool contains(symbol const & name) const; void erase(char const * name); diff --git a/src/util/parray.h b/src/util/parray.h index 44b075db9..38d6987d0 100644 --- a/src/util/parray.h +++ b/src/util/parray.h @@ -30,7 +30,7 @@ public: typedef typename C::allocator allocator; private: static size_t capacity(value * vs) { - return vs == 0 ? 0 : (reinterpret_cast(vs))[-1]; + return vs == nullptr ? 0 : (reinterpret_cast(vs))[-1]; } value * allocate_values(size_t c) { @@ -44,7 +44,7 @@ private: } void deallocate_values(value * vs) { - if (vs == 0) + if (vs == nullptr) return; size_t c = capacity(vs); TRACE("parray_mem", tout << "deallocated values[" << c << "]: " << vs << "\n";); @@ -73,7 +73,7 @@ private: unsigned size() const { SASSERT(kind() == ROOT); return m_size; } cell * next() const { SASSERT(kind() != ROOT); return m_next; } value const & elem() const { SASSERT(kind() == SET || kind() == PUSH_BACK); return m_elem; } - cell(ckind k):m_ref_count(1), m_kind(k), m_size(0), m_values(0) {} + cell(ckind k):m_ref_count(1), m_kind(k), m_size(0), m_values(nullptr) {} }; value_manager & m_vmanager; @@ -105,7 +105,7 @@ private: void del(cell * c) { while (true) { - cell * next = 0; + cell * next = nullptr; switch (c->kind()) { case SET: case PUSH_BACK: @@ -123,7 +123,7 @@ private: TRACE("parray_mem", tout << "deallocated cell: " << c << "\n";); c->~cell(); m_allocator.deallocate(sizeof(cell), c); - if (next == 0) + if (next == nullptr) return; SASSERT(next->m_ref_count > 0); next->m_ref_count--; @@ -214,7 +214,7 @@ private: } SASSERT(r->kind() == ROOT); unsigned sz = r->m_size; - vs = 0; + vs = nullptr; copy_values(r->m_values, sz, vs); unsigned i = cs.size(); while (i > 0) { @@ -246,7 +246,7 @@ private: dec_ref(c->m_next); if (c->kind() == SET || c->kind() == PUSH_BACK) dec_ref(c->m_elem); - c->m_next = 0; + c->m_next = nullptr; c->m_kind = ROOT; c->m_size = sz; c->m_values = vs; @@ -262,7 +262,7 @@ public: bool unshared() const { return m_ref->m_ref_count == 1; } friend class parray_manager; public: - ref():m_ref(0), m_updt_counter(0) {} + ref():m_ref(nullptr), m_updt_counter(0) {} }; public: @@ -283,7 +283,7 @@ public: void del(ref & r) { dec_ref(r.m_ref); - r.m_ref = 0; + r.m_ref = nullptr; r.m_updt_counter = 0; } @@ -296,7 +296,7 @@ public: unsigned size(ref const & r) const { cell * c = r.m_ref; - if (c == 0) return 0; + if (c == nullptr) return 0; while (true) { switch (c->kind()) { case SET: @@ -397,7 +397,7 @@ public: } void push_back(ref & r, value const & v) { - if (r.m_ref == 0) + if (r.m_ref == nullptr) mk(r); if (r.root()) { if (r.unshared()) { diff --git a/src/util/plugin_manager.h b/src/util/plugin_manager.h index da7017aa1..2c4223eeb 100644 --- a/src/util/plugin_manager.h +++ b/src/util/plugin_manager.h @@ -27,7 +27,12 @@ class plugin_manager { ptr_vector m_plugins; public: ~plugin_manager() { + reset(); + } + + void reset() { std::for_each(m_plugins.begin(), m_plugins.end(), delete_proc()); + release(); } /** @@ -48,7 +53,7 @@ public: Plugin * get_plugin(family_id fid) const { if (fid == null_family_id) { - return 0; + return nullptr; } return m_fid2plugins.get(fid, 0); } diff --git a/src/util/prime_generator.cpp b/src/util/prime_generator.cpp index eac47f61b..6c0376e52 100644 --- a/src/util/prime_generator.cpp +++ b/src/util/prime_generator.cpp @@ -26,11 +26,11 @@ prime_generator::prime_generator() { process_next_k_numbers(128); } -void prime_generator::process_next_k_numbers(uint64 k) { - svector todo; - uint64 begin = m_primes.back() + 2; - uint64 end = begin + k; - for (uint64 i = begin; i < end; i+=2) { +void prime_generator::process_next_k_numbers(uint64_t k) { + svector todo; + uint64_t begin = m_primes.back() + 2; + uint64_t end = begin + k; + for (uint64_t i = begin; i < end; i+=2) { todo.push_back(i); } unsigned j = 1; @@ -38,7 +38,7 @@ void prime_generator::process_next_k_numbers(uint64 k) { while (!todo.empty()) { unsigned sz = m_primes.size(); for (; j < sz; j++) { - uint64 p = m_primes[j]; + uint64_t p = m_primes[j]; unsigned todo_sz = todo.size(); unsigned k1 = 0; unsigned k2 = 0; @@ -59,7 +59,7 @@ void prime_generator::process_next_k_numbers(uint64 k) { return; } } - uint64 p = m_primes.back(); + uint64_t p = m_primes.back(); p = p*p; unsigned todo_sz = todo.size(); unsigned k1 = 0; @@ -83,7 +83,7 @@ void prime_generator::finalize() { m_primes.finalize(); } -uint64 prime_generator::operator()(unsigned idx) { +uint64_t prime_generator::operator()(unsigned idx) { if (idx < m_primes.size()) return m_primes[idx]; if (idx > PRIME_LIST_MAX_SIZE) @@ -99,7 +99,7 @@ uint64 prime_generator::operator()(unsigned idx) { prime_generator g_prime_generator; prime_iterator::prime_iterator(prime_generator * g):m_idx(0) { - if (g == 0) { + if (g == nullptr) { m_generator = &g_prime_generator; m_global = true; } @@ -109,14 +109,14 @@ prime_iterator::prime_iterator(prime_generator * g):m_idx(0) { } } -uint64 prime_iterator::next() { +uint64_t prime_iterator::next() { unsigned idx = m_idx; m_idx++; if (!m_global) { return (*m_generator)(idx); } else { - uint64 r; + uint64_t r; #pragma omp critical (prime_iterator) { r = (*m_generator)(idx); diff --git a/src/util/prime_generator.h b/src/util/prime_generator.h index 6a284c57c..bd388954c 100644 --- a/src/util/prime_generator.h +++ b/src/util/prime_generator.h @@ -32,11 +32,11 @@ public: \brief Prime generator */ class prime_generator { - svector m_primes; - void process_next_k_numbers(uint64 k); + svector m_primes; + void process_next_k_numbers(uint64_t k); public: prime_generator(); - uint64 operator()(unsigned idx); + uint64_t operator()(unsigned idx); void finalize(); }; @@ -45,8 +45,8 @@ class prime_iterator { prime_generator * m_generator; bool m_global; public: - prime_iterator(prime_generator * g = 0); - uint64 next(); + prime_iterator(prime_generator * g = nullptr); + uint64_t next(); static void finalize(); /* ADD_FINALIZER('prime_iterator::finalize();') diff --git a/src/util/queue.h b/src/util/queue.h new file mode 100644 index 000000000..a517efc24 --- /dev/null +++ b/src/util/queue.h @@ -0,0 +1,73 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + queue.h + +Abstract: + + Generic queue. + +Author: + + Nikolaj Bjorner (nbjorner) 2017-4-17 + +Notes: + +--*/ +#ifndef _QUEUE_H_ +#define _QUEUE_H_ + +#include "vector.h" + +template +class queue { + vector m_elems; + unsigned m_head; + unsigned m_capacity; + +public: + + queue(): m_head(0), m_capacity(0) {} + + void push(T const& t) { m_elems.push_back(t); } + + bool empty() const { + return m_head == m_elems.size(); + } + + T top() const { + return m_elems[m_head]; + } + + T pop_front() { + SASSERT(!empty()); + m_capacity = std::max(m_capacity, m_elems.size()); + SASSERT(m_head < m_elems.size()); + if (2 * m_head > m_capacity && m_capacity > 10) { + for (unsigned i = 0; i < m_elems.size() - m_head; ++i) { + m_elems[i] = m_elems[i + m_head]; + } + m_elems.shrink(m_elems.size() - m_head); + m_head = 0; + } + return m_elems[m_head++]; + } + + + T back() const { + return m_elems[m_elems.size() - 1]; + } + + T pop_back() { + SASSERT(!empty()); + SASSERT(m_head < m_elems.size()); + T result = back(); + m_elems.shrink(m_elems.size() - 1); + return result; + } +}; + +#endif + diff --git a/src/util/rational.cpp b/src/util/rational.cpp index ce38bcaa7..e8c2d52d1 100644 --- a/src/util/rational.cpp +++ b/src/util/rational.cpp @@ -23,7 +23,7 @@ Revision History: #include #endif -synch_mpq_manager * rational::g_mpq_manager = 0; +synch_mpq_manager * rational::g_mpq_manager = nullptr; rational rational::m_zero; rational rational::m_one; rational rational::m_minus_one; @@ -80,6 +80,6 @@ void rational::finalize() { m_one.~rational(); m_minus_one.~rational(); dealloc(g_mpq_manager); - g_mpq_manager = 0; + g_mpq_manager = nullptr; } diff --git a/src/util/rational.h b/src/util/rational.h index 392a1982b..dc7b79419 100644 --- a/src/util/rational.h +++ b/src/util/rational.h @@ -59,10 +59,10 @@ public: explicit rational(char const * v) { m().set(m_val, v); } struct i64 {}; - rational(int64 i, i64) { m().set(m_val, i); } + rational(int64_t i, i64) { m().set(m_val, i); } struct ui64 {}; - rational(uint64 i, ui64) { m().set(m_val, i); } + rational(uint64_t i, ui64) { m().set(m_val, i); } ~rational() { m().del(m_val); } @@ -98,9 +98,9 @@ public: bool is_int64() const { return m().is_int64(m_val); } - uint64 get_uint64() const { return m().get_uint64(m_val); } + uint64_t get_uint64() const { return m().get_uint64(m_val); } - int64 get_int64() const { return m().get_int64(m_val); } + int64_t get_int64() const { return m().get_int64(m_val); } bool is_unsigned() const { return is_uint64() && (get_uint64() < (1ull << 32)); } @@ -113,7 +113,7 @@ public: if (is_small() && is_int()) return true; // we don't assume that if it is small, then it is int32. if (!is_int64()) return false; - int64 v = get_int64(); + int64_t v = get_int64(); return INT_MIN <= v && v <= INT_MAX; } diff --git a/src/util/ref.h b/src/util/ref.h index 811aba9af..ea6b7a624 100644 --- a/src/util/ref.h +++ b/src/util/ref.h @@ -37,7 +37,7 @@ class ref { public: ref(): - m_ptr(0) { + m_ptr(nullptr) { } ref(T * ptr): @@ -64,7 +64,7 @@ public: } operator bool() const { - return m_ptr != 0; + return m_ptr != nullptr; } const T & operator*() const { @@ -100,12 +100,12 @@ public: void reset() { dec_ref(); - m_ptr = 0; + m_ptr = nullptr; } T* detach() { T* tmp = m_ptr; - m_ptr = 0; + m_ptr = nullptr; return tmp; } diff --git a/src/util/ref_buffer.h b/src/util/ref_buffer.h index 612dde2da..4768c3f28 100644 --- a/src/util/ref_buffer.h +++ b/src/util/ref_buffer.h @@ -82,6 +82,9 @@ public: return m_buffer[idx]; } + T* const* begin() const { return c_ptr(); } + T* const* end() const { return c_ptr() + size(); } + void set(unsigned idx, T * n) { inc_ref(n); dec_ref(m_buffer[idx]); diff --git a/src/util/region.cpp b/src/util/region.cpp index 4e5b5f2f2..3cf3f502f 100644 --- a/src/util/region.cpp +++ b/src/util/region.cpp @@ -38,11 +38,11 @@ inline void region::allocate_page() { } region::region() { - m_curr_page = 0; - m_curr_ptr = 0; - m_curr_end_ptr = 0; - m_free_pages = 0; - m_mark = 0; + m_curr_page = nullptr; + m_curr_ptr = nullptr; + m_curr_end_ptr = nullptr; + m_free_pages = nullptr; + m_mark = nullptr; allocate_page(); } @@ -81,13 +81,13 @@ inline void region::recycle_curr_page() { } void region::reset() { - while (m_curr_page != 0) { + while (m_curr_page != nullptr) { recycle_curr_page(); } SASSERT(m_curr_page == 0); - m_curr_ptr = 0; - m_curr_end_ptr = 0; - m_mark = 0; + m_curr_ptr = nullptr; + m_curr_end_ptr = nullptr; + m_mark = nullptr; allocate_page(); } @@ -113,7 +113,7 @@ void region::pop_scope() { void region::display_mem_stats(std::ostream & out) const { unsigned n = 0; char * page = m_curr_page; - while (page != 0) { + while (page != nullptr) { n++; page = prev_page(page); } diff --git a/src/util/rlimit.cpp b/src/util/rlimit.cpp index e625cab95..e3afd0d92 100644 --- a/src/util/rlimit.cpp +++ b/src/util/rlimit.cpp @@ -26,7 +26,7 @@ reslimit::reslimit(): m_limit(0) { } -uint64 reslimit::count() const { +uint64_t reslimit::count() const { return m_count; } @@ -41,7 +41,7 @@ bool reslimit::inc(unsigned offset) { } void reslimit::push(unsigned delta_limit) { - uint64 new_limit = delta_limit + m_count; + uint64_t new_limit = delta_limit + m_count; if (new_limit <= m_count) { new_limit = 0; } diff --git a/src/util/rlimit.h b/src/util/rlimit.h index 0c81f9449..60d26be7f 100644 --- a/src/util/rlimit.h +++ b/src/util/rlimit.h @@ -24,9 +24,9 @@ Revision History: class reslimit { volatile unsigned m_cancel; bool m_suspend; - uint64 m_count; - uint64 m_limit; - svector m_limits; + uint64_t m_count; + uint64_t m_limit; + svector m_limits; ptr_vector m_children; void set_cancel(unsigned f); @@ -41,7 +41,7 @@ public: bool inc(); bool inc(unsigned offset); - uint64 count() const; + uint64_t count() const; bool get_cancel_flag() const { return m_cancel > 0 && !m_suspend; } diff --git a/src/util/s_integer.cpp b/src/util/s_integer.cpp index cfaf34fff..e76781388 100644 --- a/src/util/s_integer.cpp +++ b/src/util/s_integer.cpp @@ -24,7 +24,7 @@ s_integer s_integer::m_one(1); s_integer s_integer::m_minus_one(-1); s_integer::s_integer(const char * str) { - m_val = static_cast(strtol(str, 0, 10)); + m_val = static_cast(strtol(str, nullptr, 10)); } s_integer power(const s_integer & r, unsigned p) { diff --git a/src/util/s_integer.h b/src/util/s_integer.h index 658ae8eea..80310368d 100644 --- a/src/util/s_integer.h +++ b/src/util/s_integer.h @@ -46,9 +46,9 @@ public: s_integer(const s_integer & r):m_val(r.m_val) {} explicit s_integer(int n):m_val(n) {} struct i64 {}; - explicit s_integer(int64 i, i64):m_val(static_cast(i)) {} + explicit s_integer(int64_t i, i64):m_val(static_cast(i)) {} struct ui64 {}; - explicit s_integer(uint64 i, ui64):m_val(static_cast(i)) {} + explicit s_integer(uint64_t i, ui64):m_val(static_cast(i)) {} explicit s_integer(const char * str); explicit s_integer(const rational & r):m_val(static_cast(r.get_int64())) {} @@ -60,8 +60,8 @@ public: static bool is_int64() { return true; } static bool is_uint64() { return true; } int get_int() const { return m_val; } - int64 get_int64() const { return m_val; } - uint64 get_uint64() const { return m_val; } + int64_t get_int64() const { return m_val; } + uint64_t get_uint64() const { return m_val; } static bool is_unsigned() { return true; } unsigned get_unsigned() const { return static_cast(m_val); } s_integer const& get_s_integer() const { return *this; } diff --git a/src/util/scoped_ctrl_c.cpp b/src/util/scoped_ctrl_c.cpp index 1cb3a03c4..2dbbaec9b 100644 --- a/src/util/scoped_ctrl_c.cpp +++ b/src/util/scoped_ctrl_c.cpp @@ -20,7 +20,7 @@ Revision History: #include #include "util/scoped_ctrl_c.h" -scoped_ctrl_c * scoped_ctrl_c::g_obj = 0; +scoped_ctrl_c * scoped_ctrl_c::g_obj = nullptr; void scoped_ctrl_c::on_ctrl_c(int) { if (g_obj->m_first) { diff --git a/src/util/scoped_ptr_vector.h b/src/util/scoped_ptr_vector.h index 0bd0fd47e..920ecb2c9 100644 --- a/src/util/scoped_ptr_vector.h +++ b/src/util/scoped_ptr_vector.h @@ -30,7 +30,7 @@ public: ~scoped_ptr_vector() { reset(); } void reset() { std::for_each(m_vector.begin(), m_vector.end(), delete_proc()); m_vector.reset(); } void push_back(T * ptr) { m_vector.push_back(ptr); } - void pop_back() { SASSERT(!empty()); set(size()-1, 0); m_vector.pop_back(); } + void pop_back() { SASSERT(!empty()); set(size()-1, nullptr); m_vector.pop_back(); } T * operator[](unsigned idx) const { return m_vector[idx]; } void set(unsigned idx, T * ptr) { if (m_vector[idx] == ptr) @@ -48,7 +48,7 @@ public: } else { for (unsigned i = m_vector.size(); i < sz; i++) - push_back(0); + push_back(nullptr); } } }; diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index 3991abd36..56d40c47a 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -33,8 +33,8 @@ Revision History: #include #include #include -#elif defined(_LINUX_) || defined(_FREEBSD_) -// Linux +#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NetBSD_) +// Linux & FreeBSD & NetBSD #include #include #include @@ -66,8 +66,8 @@ struct scoped_timer::imp { pthread_mutex_t m_mutex; pthread_cond_t m_condition_var; struct timespec m_end_time; -#elif defined(_LINUX_) || defined(_FREEBSD_) - // Linux & FreeBSD +#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NETBSD_) + // Linux & FreeBSD & NetBSD pthread_t m_thread_id; pthread_mutex_t m_mutex; pthread_cond_t m_cond; @@ -104,7 +104,7 @@ struct scoped_timer::imp { return st; } -#elif defined(_LINUX_) || defined(_FREEBSD_) +#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NETBSD_) static void* thread_func(void *arg) { scoped_timer::imp *st = static_cast(arg); @@ -157,9 +157,9 @@ struct scoped_timer::imp { m_interval = ms?ms:0xFFFFFFFF; if (pthread_attr_init(&m_attributes) != 0) throw default_exception("failed to initialize timer thread attributes"); - if (pthread_cond_init(&m_condition_var, NULL) != 0) + if (pthread_cond_init(&m_condition_var, nullptr) != 0) throw default_exception("failed to initialize timer condition variable"); - if (pthread_mutex_init(&m_mutex, NULL) != 0) + if (pthread_mutex_init(&m_mutex, nullptr) != 0) throw default_exception("failed to initialize timer mutex"); clock_serv_t host_clock; @@ -175,8 +175,8 @@ struct scoped_timer::imp { if (pthread_create(&m_thread_id, &m_attributes, &thread_func, this) != 0) throw default_exception("failed to start timer thread"); -#elif defined(_LINUX_) || defined(_FREEBSD_) - // Linux & FreeBSD +#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NETBSD_) + // Linux & FreeBSD & NetBSD m_ms = ms; m_initialized = false; m_signal_sent = false; @@ -208,16 +208,24 @@ struct scoped_timer::imp { pthread_cond_signal(&m_condition_var); pthread_mutex_unlock(&m_mutex); - if (pthread_join(m_thread_id, NULL) != 0) - throw default_exception("failed to join thread"); - if (pthread_mutex_destroy(&m_mutex) != 0) - throw default_exception("failed to destroy pthread mutex"); - if (pthread_cond_destroy(&m_condition_var) != 0) - throw default_exception("failed to destroy pthread condition variable"); - if (pthread_attr_destroy(&m_attributes) != 0) - throw default_exception("failed to destroy pthread attributes object"); -#elif defined(_LINUX_) || defined(_FREEBSD_) - // Linux & FreeBSD + if (pthread_join(m_thread_id, nullptr) != 0) { + warning_msg("failed to join thread"); + return; + } + if (pthread_mutex_destroy(&m_mutex) != 0) { + warning_msg("failed to destroy pthread mutex"); + return; + } + if (pthread_cond_destroy(&m_condition_var) != 0) { + warning_msg("failed to destroy pthread condition variable"); + return; + } + if (pthread_attr_destroy(&m_attributes) != 0) { + warning_msg("failed to destroy pthread attributes object"); + return; + } +#elif defined(_LINUX_) || defined(_FREEBSD_) || defined(_NETBSD_) + // Linux & FreeBSD & NetBSD bool init = false; // spin until timer thread has been created @@ -248,7 +256,7 @@ scoped_timer::scoped_timer(unsigned ms, event_handler * eh) { if (ms != UINT_MAX && ms != 0) m_imp = alloc(imp, ms, eh); else - m_imp = 0; + m_imp = nullptr; } scoped_timer::~scoped_timer() { diff --git a/src/util/small_object_allocator.cpp b/src/util/small_object_allocator.cpp index 0eb7b9ec3..9f15aadaf 100644 --- a/src/util/small_object_allocator.cpp +++ b/src/util/small_object_allocator.cpp @@ -27,8 +27,8 @@ Revision History: small_object_allocator::small_object_allocator(char const * id) { for (unsigned i = 0; i < NUM_SLOTS; i++) { - m_chunks[i] = 0; - m_free_list[i] = 0; + m_chunks[i] = nullptr; + m_free_list[i] = nullptr; } DEBUG_CODE({ m_id = id; @@ -60,8 +60,8 @@ void small_object_allocator::reset() { dealloc(c); c = next; } - m_chunks[i] = 0; - m_free_list[i] = 0; + m_chunks[i] = nullptr; + m_free_list[i] = nullptr; } m_alloc_size = 0; } @@ -95,7 +95,7 @@ void small_object_allocator::deallocate(size_t size, void * p) { void * small_object_allocator::allocate(size_t size) { - if (size == 0) return 0; + if (size == 0) return nullptr; #if defined(Z3DEBUG) && !defined(_WINDOWS) // Valgrind friendly @@ -113,7 +113,7 @@ void * small_object_allocator::allocate(size_t size) { slot_id++; SASSERT(slot_id < NUM_SLOTS); SASSERT(slot_id > 0); - if (m_free_list[slot_id] != 0) { + if (m_free_list[slot_id] != nullptr) { void * r = m_free_list[slot_id]; m_free_list[slot_id] = *(reinterpret_cast(r)); return r; @@ -121,7 +121,7 @@ void * small_object_allocator::allocate(size_t size) { chunk * c = m_chunks[slot_id]; size = slot_id << PTR_ALIGNMENT; SASSERT(size >= osize); - if (c != 0) { + if (c != nullptr) { char * new_curr = c->m_curr + size; if (new_curr < c->m_data + CHUNK_SIZE) { void * r = c->m_curr; @@ -142,7 +142,7 @@ size_t small_object_allocator::get_wasted_size() const { for (unsigned slot_id = 0; slot_id < NUM_SLOTS; slot_id++) { size_t slot_obj_size = slot_id << PTR_ALIGNMENT; void ** ptr = reinterpret_cast(const_cast(this)->m_free_list[slot_id]); - while (ptr != 0) { + while (ptr != nullptr) { r += slot_obj_size; ptr = reinterpret_cast(*ptr); } @@ -154,7 +154,7 @@ size_t small_object_allocator::get_num_free_objs() const { size_t r = 0; for (unsigned slot_id = 0; slot_id < NUM_SLOTS; slot_id++) { void ** ptr = reinterpret_cast(const_cast(this)->m_free_list[slot_id]); - while (ptr != 0) { + while (ptr != nullptr) { r ++; ptr = reinterpret_cast(*ptr); } @@ -177,17 +177,17 @@ void small_object_allocator::consolidate() { ptr_vector chunks; ptr_vector free_objs; for (unsigned slot_id = 1; slot_id < NUM_SLOTS; slot_id++) { - if (m_free_list[slot_id] == 0) + if (m_free_list[slot_id] == nullptr) continue; chunks.reset(); free_objs.reset(); chunk * c = m_chunks[slot_id]; - while (c != 0) { + while (c != nullptr) { chunks.push_back(c); c = c->m_next; } char * ptr = static_cast(m_free_list[slot_id]); - while (ptr != 0) { + while (ptr != nullptr) { free_objs.push_back(ptr); ptr = *(reinterpret_cast(ptr)); } @@ -198,8 +198,8 @@ void small_object_allocator::consolidate() { SASSERT(!chunks.empty()); std::sort(chunks.begin(), chunks.end(), ptr_lt()); std::sort(free_objs.begin(), free_objs.end(), ptr_lt()); - chunk * last_chunk = 0; - void * last_free_obj = 0; + chunk * last_chunk = nullptr; + void * last_free_obj = nullptr; unsigned chunk_idx = 0; unsigned obj_idx = 0; unsigned num_chunks = chunks.size(); diff --git a/src/util/smt2_util.cpp b/src/util/smt2_util.cpp index 731c91848..365d8fe70 100644 --- a/src/util/smt2_util.cpp +++ b/src/util/smt2_util.cpp @@ -29,11 +29,21 @@ bool is_smt2_simple_symbol_char(char s) { } bool is_smt2_quoted_symbol(char const * s) { - if (s == 0) + if (s == nullptr) return false; if ('0' <= s[0] && s[0] <= '9') return true; unsigned len = static_cast(strlen(s)); + if (len >= 2 && s[0] == '|' && s[len-1] == '|') { + for (unsigned i = 1; i + 1 < len; i++) { + if (s[i] == '\\' && i + 2 < len && (s[i+1] == '\\' || s[i+1] == '|')) { + i++; + } + else if (s[i] == '\\' || s[i] == '|') + return true; + } + return false; + } for (unsigned i = 0; i < len; i++) if (!is_smt2_simple_symbol_char(s[i])) return true; diff --git a/src/util/sorting_network.h b/src/util/sorting_network.h index 17808ea48..f2906b00c 100644 --- a/src/util/sorting_network.h +++ b/src/util/sorting_network.h @@ -24,6 +24,28 @@ Notes: #ifndef SORTING_NETWORK_H_ #define SORTING_NETWORK_H_ + enum sorting_network_encoding { + grouped_at_most_1, + bimander_at_most_1, + ordered_at_most_1 + }; + + inline std::ostream& operator<<(std::ostream& out, sorting_network_encoding enc) { + switch (enc) { + case grouped_at_most_1: return out << "grouped"; + case bimander_at_most_1: return out << "bimander"; + case ordered_at_most_1: return out << "ordered"; + } + return out << "???"; + } + + struct sorting_network_config { + sorting_network_encoding m_encoding; + sorting_network_config() { + m_encoding = grouped_at_most_1; + } + }; + template class sorting_network { typedef typename Ext::vector vect; @@ -88,7 +110,7 @@ Notes: } public: - sorting_network(Ext& ext): + sorting_network(Ext& ext): m_ext(ext), m_current(&m_currentv), m_next(&m_nextv) @@ -119,8 +141,9 @@ Notes: // Described in Abio et.al. CP 2013. template class psort_nw { - typedef typename psort_expr::literal literal; - typedef typename psort_expr::literal_vector literal_vector; + typedef typename psort_expr::pliteral literal; + typedef typename psort_expr::pliteral_vector literal_vector; + sorting_network_config m_cfg; class vc { unsigned v; // number of vertices @@ -187,6 +210,8 @@ Notes: psort_nw(psort_expr& c): ctx(c) {} + sorting_network_config& cfg() { return m_cfg; } + literal ge(bool full, unsigned k, unsigned n, literal const* xs) { if (k > n) { return ctx.mk_false(); @@ -220,7 +245,17 @@ Notes: else if (k == 1) { literal_vector ors; // scoped_stats _ss(m_stats, k, n); - return mk_at_most_1(full, n, xs, ors, false); + switch (m_cfg.m_encoding) { + case grouped_at_most_1: + return mk_at_most_1(full, n, xs, ors, false); + case bimander_at_most_1: + return mk_at_most_1_bimander(full, n, xs, ors); + case ordered_at_most_1: + return mk_ordered_atmost_1(full, n, xs); + default: + UNREACHABLE(); + return xs[0]; + } } else { SASSERT(2*k <= n); @@ -278,7 +313,7 @@ Notes: if (n == 1) { return ors[0]; } - literal result = fresh(); + literal result = fresh("or"); add_implies_or(result, n, ors); add_or_implies(result, n, ors); return result; @@ -293,15 +328,15 @@ Notes: } void add_implies_and(literal l, literal_vector const& xs) { - for (unsigned j = 0; j < xs.size(); ++j) { - add_clause(ctx.mk_not(l), xs[j]); + for (literal const& x : xs) { + add_clause(ctx.mk_not(l), x); } } void add_and_implies(literal l, literal_vector const& xs) { literal_vector lits; - for (unsigned j = 0; j < xs.size(); ++j) { - lits.push_back(ctx.mk_not(xs[j])); + for (literal const& x : xs) { + lits.push_back(ctx.mk_not(x)); } lits.push_back(l); add_clause(lits); @@ -317,15 +352,29 @@ Notes: if (ands.size() == 1) { return ands[0]; } - literal result = fresh(); + literal result = fresh("and"); add_implies_and(result, ands); add_and_implies(result, ands); return result; } literal mk_exactly_1(bool full, unsigned n, literal const* xs) { + TRACE("pb", tout << "exactly 1 with " << n << " arguments " << (full?"full":"not full") << "\n";); literal_vector ors; - literal r1 = mk_at_most_1(full, n, xs, ors, true); + literal r1; + switch (m_cfg.m_encoding) { + case grouped_at_most_1: + r1 = mk_at_most_1(full, n, xs, ors, true); + break; + case bimander_at_most_1: + r1 = mk_at_most_1_bimander(full, n, xs, ors); + break; + case ordered_at_most_1: + return mk_ordered_exactly_1(full, n, xs); + default: + UNREACHABLE(); + return mk_ordered_exactly_1(full, n, xs); + } if (full) { r1 = mk_and(r1, mk_or(ors)); @@ -337,15 +386,11 @@ Notes: } literal mk_at_most_1(bool full, unsigned n, literal const* xs, literal_vector& ors, bool use_ors) { - TRACE("pb", tout << (full?"full":"partial") << " "; + TRACE("pb_verbose", tout << (full?"full":"partial") << " "; for (unsigned i = 0; i < n; ++i) tout << xs[i] << " "; tout << "\n";); - - if (n >= 4 && false) { - return mk_at_most_1_bimander(full, n, xs, ors); - } literal_vector in(n, xs); - literal result = fresh(); + literal result = fresh("at-most-1"); unsigned inc_size = 4; literal_vector ands; ands.push_back(result); @@ -387,7 +432,7 @@ Notes: // xs[0] + ... + xs[n-1] <= 1 => and_x if (full) { - literal and_i = fresh(); + literal and_i = fresh("and"); for (unsigned i = 0; i < n; ++i) { literal_vector lits; lits.push_back(and_i); @@ -407,29 +452,6 @@ Notes: } -#if 0 - literal result = fresh(); - - // result => xs[0] + ... + xs[n-1] <= 1 - for (unsigned i = 0; i < n; ++i) { - for (unsigned j = i + 1; j < n; ++j) { - add_clause(ctx.mk_not(result), ctx.mk_not(xs[i]), ctx.mk_not(xs[j])); - } - } - - // xs[0] + ... + xs[n-1] <= 1 => result - for (unsigned i = 0; i < n; ++i) { - literal_vector lits; - lits.push_back(result); - for (unsigned j = 0; j < n; ++j) { - if (j != i) lits.push_back(xs[j]); - } - add_clause(lits); - } - - return result; -#endif -#if 1 // r <=> and( or(!xi,!xj)) // literal_vector ands; @@ -439,30 +461,105 @@ Notes: } } return mk_and(ands); -#else - // r <=> or (and !x_{j != i}) - - literal_vector ors; - for (unsigned i = 0; i < n; ++i) { - literal_vector ands; - for (unsigned j = 0; j < n; ++j) { - if (j != i) { - ands.push_back(ctx.mk_not(xs[j])); - } - } - ors.push_back(mk_and(ands)); - } - return mk_or(ors); - -#endif } + literal mk_ordered_exactly_1(bool full, unsigned n, literal const* xs) { + return mk_ordered_1(full, true, n, xs); + } + + literal mk_ordered_atmost_1(bool full, unsigned n, literal const* xs) { + return mk_ordered_1(full, false, n, xs); + } + + literal mk_ordered_1(bool full, bool is_eq, unsigned n, literal const* xs) { + if (n <= 1 && !is_eq) { + return ctx.mk_true(); + } + if (n == 0) { + return ctx.mk_false(); + } + if (n == 1) { + return xs[0]; + } + + SASSERT(n > 1); + + // y0 -> y1 + // x0 -> y0 + // x1 -> y1 + // r, y0 -> ~x1 + // r, y1 -> ~x2 + // r -> x3 | y1 + // r -> ~x3 | ~y1 + + // x0,x1,x2, .., x_{n-1}, x_n + // y0,y1,y2, .., y_{n-1} + // y_i -> y_{i+1} i = 0, ..., n - 2 + // x_i -> y_i i = 0, ..., n - 1 + // r, y_i -> ~x_{i+1} i = 0, ..., n - 1 + // exactly 1: + // r -> x_n | y_{n-1} + // full (exactly 1): + // two_i -> y_i & x_{i+1} + // zero -> ~x_n + // zero -> ~y_{n-1} + // r | zero | two_0 | ... | two_{n-1} + // full atmost 1: + // r | two | two_0 | ... | two_{n-1} + + literal r = fresh("ordered"); + literal_vector ys; + for (unsigned i = 0; i + 1 < n; ++i) { + ys.push_back(fresh("y")); + } + for (unsigned i = 0; i + 2 < n; ++i) { + add_clause(ctx.mk_not(ys[i]), ys[i + 1]); + } + for (unsigned i = 0; i + 1 < n; ++i) { + add_clause(ctx.mk_not(xs[i]), ys[i]); + add_clause(ctx.mk_not(r), ctx.mk_not(ys[i]), ctx.mk_not(xs[i + 1])); + } + + if (is_eq) { + add_clause(ctx.mk_not(r), ys[n-2], xs[n-1]); + } + for (unsigned i = 1; i < n - 1; ++i) { + add_clause(ctx.mk_not(ys[i]), xs[i], ys[i-1]); + } + + add_clause(ctx.mk_not(ys[0]), xs[0]); + if (full) { + literal_vector twos; + for (unsigned i = 0; i < n - 1; ++i) { + twos.push_back(fresh("two")); + } + add_clause(ctx.mk_not(twos[0]), ys[0]); + add_clause(ctx.mk_not(twos[0]), xs[1]); + for (unsigned i = 1; i < n - 1; ++i) { + add_clause(ctx.mk_not(twos[i]), ys[i], twos[i-1]); + add_clause(ctx.mk_not(twos[i]), xs[i + 1], twos[i-1]); + } + if (is_eq) { + literal zero = fresh("zero"); + add_clause(ctx.mk_not(zero), ctx.mk_not(xs[n-1])); + add_clause(ctx.mk_not(zero), ctx.mk_not(ys[n-2])); + add_clause(r, zero, twos.back()); + } + else { + add_clause(r, twos.back()); + } + } + return r; + } // literal mk_at_most_1_bimander(bool full, unsigned n, literal const* xs, literal_vector& ors) { + if (full) { + return mk_at_most_1(full, n, xs, ors, true); + } literal_vector in(n, xs); - literal result = fresh(); + literal result = fresh("bimander"); unsigned inc_size = 2; literal_vector ands; for (unsigned i = 0; i < n; i += inc_size) { @@ -477,7 +574,7 @@ Notes: } literal_vector bits; for (unsigned k = 0; k < nbits; ++k) { - bits.push_back(fresh()); + bits.push_back(fresh("bit")); } for (unsigned i = 0; i < ors.size(); ++i) { for (unsigned k = 0; k < nbits; ++k) { @@ -494,7 +591,7 @@ Notes: } std::ostream& pp(std::ostream& out, literal_vector const& lits) { - for (unsigned i = 0; i < lits.size(); ++i) ctx.pp(out, lits[i]) << " "; + for (literal const& l : lits) ctx.pp(out, l) << " "; return out; } @@ -513,7 +610,7 @@ Notes: for (unsigned i = 0; i < N; ++i) { in.push_back(ctx.mk_not(xs[i])); } - TRACE("pb", + TRACE("pb_verbose", pp(tout << N << ": ", in); tout << " ~ " << k << "\n";); return true; @@ -539,9 +636,9 @@ Notes: return ctx.mk_min(a, b); } - literal fresh() { + literal fresh(char const* n) { m_stats.m_num_compiled_vars++; - return ctx.fresh(); + return ctx.fresh(n); } void add_clause(literal l1, literal l2, literal l3) { literal lits[3] = { l1, l2, l3 }; @@ -558,7 +655,6 @@ Notes: m_stats.m_num_compiled_clauses++; m_stats.m_num_clause_vars += n; literal_vector tmp(n, ls); - TRACE("pb", for (unsigned i = 0; i < n; ++i) tout << ls[i] << " "; tout << "\n";); ctx.mk_clause(n, tmp.c_ptr()); } @@ -595,7 +691,7 @@ Notes: } void card(unsigned k, unsigned n, literal const* xs, literal_vector& out) { - TRACE("pb", tout << "card k: " << k << " n: " << n << "\n";); + TRACE("pb_verbose", tout << "card k: " << k << " n: " << n << "\n";); if (n <= k) { psort_nw::sorting(n, xs, out); } @@ -609,7 +705,7 @@ Notes: card(k, n-l, xs + l, out2); smerge(k, out1.size(), out1.c_ptr(), out2.size(), out2.c_ptr(), out); } - TRACE("pb", tout << "card k: " << k << " n: " << n << "\n"; + TRACE("pb_verbose", tout << "card k: " << k << " n: " << n << "\n"; pp(tout << "in:", n, xs) << "\n"; pp(tout << "out:", out) << "\n";); @@ -637,7 +733,7 @@ Notes: void merge(unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { - TRACE("pb", tout << "merge a: " << a << " b: " << b << "\n";); + TRACE("pb_verbose", tout << "merge a: " << a << " b: " << b << "\n";); if (a == 1 && b == 1) { literal y1 = mk_max(as[0], bs[0]); literal y2 = mk_min(as[0], bs[0]); @@ -672,7 +768,7 @@ Notes: odd_b.size(), odd_b.c_ptr(), out2); interleave(out1, out2, out); } - TRACE("pb", tout << "merge a: " << a << " b: " << b << "\n"; + TRACE("pb_verbose", tout << "merge a: " << a << " b: " << b << "\n"; pp(tout << "a:", a, as) << "\n"; pp(tout << "b:", b, bs) << "\n"; pp(tout << "out:", out) << "\n";); @@ -709,7 +805,7 @@ Notes: void interleave(literal_vector const& as, literal_vector const& bs, literal_vector& out) { - TRACE("pb", tout << "interleave: " << as.size() << " " << bs.size() << "\n";); + TRACE("pb_verbose", tout << "interleave: " << as.size() << " " << bs.size() << "\n";); SASSERT(as.size() >= bs.size()); SASSERT(as.size() <= bs.size() + 2); SASSERT(!as.empty()); @@ -729,7 +825,7 @@ Notes: out.push_back(as[sz+1]); } SASSERT(out.size() == as.size() + bs.size()); - TRACE("pb", tout << "interleave: " << as.size() << " " << bs.size() << "\n"; + TRACE("pb_verbose", tout << "interleave: " << as.size() << " " << bs.size() << "\n"; pp(tout << "a: ", as) << "\n"; pp(tout << "b: ", bs) << "\n"; pp(tout << "out: ", out) << "\n";); @@ -741,7 +837,7 @@ Notes: public: void sorting(unsigned n, literal const* xs, literal_vector& out) { - TRACE("pb", tout << "sorting: " << n << "\n";); + TRACE("pb_verbose", tout << "sorting: " << n << "\n";); switch(n) { case 0: break; @@ -766,7 +862,7 @@ Notes: } break; } - TRACE("pb", tout << "sorting: " << n << "\n"; + TRACE("pb_verbose", tout << "sorting: " << n << "\n"; pp(tout << "in:", n, xs) << "\n"; pp(tout << "out:", out) << "\n";); } @@ -802,7 +898,7 @@ Notes: unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { - TRACE("pb", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n";); + TRACE("pb_verbose", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n";); if (a == 1 && b == 1 && c == 1) { literal y = mk_max(as[0], bs[0]); if (m_t != GE) { @@ -876,7 +972,7 @@ Notes: out.push_back(y); } } - TRACE("pb", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n"; + TRACE("pb_verbose", tout << "smerge: c:" << c << " a:" << a << " b:" << b << "\n"; pp(tout << "a:", a, as) << "\n"; pp(tout << "b:", b, bs) << "\n"; pp(tout << "out:", out) << "\n"; @@ -920,12 +1016,12 @@ Notes: unsigned a, literal const* as, unsigned b, literal const* bs, literal_vector& out) { - TRACE("pb", tout << "dsmerge: c:" << c << " a:" << a << " b:" << b << "\n";); + TRACE("pb_verbose", tout << "dsmerge: c:" << c << " a:" << a << " b:" << b << "\n";); SASSERT(a <= c); SASSERT(b <= c); SASSERT(a + b >= c); for (unsigned i = 0; i < c; ++i) { - out.push_back(fresh()); + out.push_back(fresh("dsmerge")); } if (m_t != GE) { for (unsigned i = 0; i < a; ++i) { @@ -979,11 +1075,11 @@ Notes: void dsorting(unsigned m, unsigned n, literal const* xs, literal_vector& out) { - TRACE("pb", tout << "dsorting m: " << m << " n: " << n << "\n";); + TRACE("pb_verbose", tout << "dsorting m: " << m << " n: " << n << "\n";); SASSERT(m <= n); literal_vector lits; for (unsigned i = 0; i < m; ++i) { - out.push_back(fresh()); + out.push_back(fresh("dsort")); } if (m_t != GE) { for (unsigned k = 1; k <= m; ++k) { @@ -1014,7 +1110,7 @@ Notes: void add_subset(bool polarity, unsigned k, unsigned offset, literal_vector& lits, unsigned n, literal const* xs) { - TRACE("pb", tout << "k:" << k << " offset: " << offset << " n: " << n << " "; + TRACE("pb_verbose", tout << "k:" << k << " offset: " << offset << " n: " << n << " "; pp(tout, lits) << "\n";); SASSERT(k + offset <= n); if (k == 0) { diff --git a/src/util/sstream.h b/src/util/sstream.h index 23d5bdfbb..fba13c5d5 100644 --- a/src/util/sstream.h +++ b/src/util/sstream.h @@ -1,10 +1,20 @@ - - /* -Copyright (c) 2013 Microsoft Corporation. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + nat_set.h + +Abstract: + + Wrapper for sstream. + +Author: + + Leonardo de Moura (leonardo) 2013 + +Revision History: -Author: Leonardo de Moura */ #pragma once #include diff --git a/src/util/stack.cpp b/src/util/stack.cpp index 81383e2fe..156436496 100644 --- a/src/util/stack.cpp +++ b/src/util/stack.cpp @@ -62,10 +62,10 @@ inline void stack::store_mark(void * ptr, bool external) { } stack::stack() { - m_curr_page = 0; - m_curr_ptr = 0; - m_curr_end_ptr = 0; - m_free_pages = 0; + m_curr_page = nullptr; + m_curr_ptr = nullptr; + m_curr_end_ptr = nullptr; + m_free_pages = nullptr; allocate_page(0); SASSERT(empty()); } diff --git a/src/util/stats.h b/src/util/stats.h index 0acc18f43..680f9c9df 100644 --- a/src/util/stats.h +++ b/src/util/stats.h @@ -29,7 +29,7 @@ inline void print_stat(std::ostream& out, char const* msg, unsigned num) { } inline void print_stat_f(std::ostream& out, char const* msg, float num) { - if (num > 0.0) { + if (num > 0.0f) { out << msg << num << "\n"; } } diff --git a/src/util/stopwatch.h b/src/util/stopwatch.h index 9ba707af0..83e03a2f7 100644 --- a/src/util/stopwatch.h +++ b/src/util/stopwatch.h @@ -134,6 +134,11 @@ public: #include +#ifndef CLOCK_PROCESS_CPUTIME_ID +/* BSD */ +# define CLOCK_PROCESS_CPUTIME_ID CLOCK_MONOTONIC +#endif + class stopwatch { unsigned long long m_time; // elapsed time in ns bool m_running; diff --git a/src/util/symbol.cpp b/src/util/symbol.cpp index 8f46272f0..220711003 100644 --- a/src/util/symbol.cpp +++ b/src/util/symbol.cpp @@ -22,7 +22,7 @@ Revision History: #include "util/string_buffer.h" #include "util/z3_omp.h" -symbol symbol::m_dummy(TAG(void*, static_cast(0), 2)); +symbol symbol::m_dummy(TAG(void*, nullptr, 2)); const symbol symbol::null; /** @@ -60,7 +60,7 @@ public: } }; -internal_symbol_table* g_symbol_table = 0; +internal_symbol_table* g_symbol_table = nullptr; void initialize_symbols() { if (!g_symbol_table) { @@ -70,12 +70,12 @@ void initialize_symbols() { void finalize_symbols() { dealloc(g_symbol_table); - g_symbol_table = 0; + g_symbol_table = nullptr; } symbol::symbol(char const * d) { - if (d == 0) - m_data = 0; + if (d == nullptr) + m_data = nullptr; else m_data = g_symbol_table->get_str(d); } @@ -103,7 +103,7 @@ std::string symbol::str() const { bool symbol::contains(char ch) const { SASSERT(!is_marked()); if (GET_TAG(m_data) == 0) { - return strchr(m_data, ch) != 0; + return strchr(m_data, ch) != nullptr; } else { return false; diff --git a/src/util/symbol.h b/src/util/symbol.h index 874b258e9..6a6e93747 100644 --- a/src/util/symbol.h +++ b/src/util/symbol.h @@ -51,7 +51,7 @@ class symbol { static symbol m_dummy; public: symbol(): - m_data(0) { + m_data(nullptr) { } explicit symbol(char const * d); explicit symbol(unsigned idx): @@ -69,9 +69,9 @@ public: unsigned int get_num() const { SASSERT(is_numerical()); return UNBOXINT(m_data); } std::string str() const; friend bool operator==(symbol const & s1, char const * s2) { - if (s1.m_data == 0 && s2 == 0) + if (s1.m_data == nullptr && s2 == nullptr) return true; - if (s1.m_data == 0 || s2 == 0) + if (s1.m_data == nullptr || s2 == nullptr) return false; if (!s1.is_numerical()) return strcmp(s1.bare_str(), s2) == 0; @@ -87,7 +87,7 @@ public: return s; } unsigned hash() const { - if (m_data == 0) return 0x9e3779d9; + if (m_data == nullptr) return 0x9e3779d9; else if (is_numerical()) return get_num(); else return static_cast(reinterpret_cast(m_data)[-1]); } diff --git a/src/util/symbol_table.h b/src/util/symbol_table.h index 818cb7584..e3abd4051 100644 --- a/src/util/symbol_table.h +++ b/src/util/symbol_table.h @@ -121,7 +121,7 @@ public: bool find(symbol key, T & result) const { key_data dummy(key); hash_entry * e = m_sym_table.find_core(dummy); - if (e == 0) { + if (e == nullptr) { return false; } result = e->get_data().m_data; @@ -140,7 +140,7 @@ public: if (get_scope_level() > 0) { key_data dummy(key); hash_entry * e = m_sym_table.find_core(dummy); - if (e != 0) { + if (e != nullptr) { m_trail_stack.push_back(e->m_data); e->m_data.m_data = data; return; diff --git a/src/util/timeit.cpp b/src/util/timeit.cpp index 97df87ecd..2a78f08a3 100644 --- a/src/util/timeit.cpp +++ b/src/util/timeit.cpp @@ -49,7 +49,7 @@ timeit::timeit(bool enable, char const * msg, std::ostream & out) { if (enable) m_imp = alloc(imp, msg, out); else - m_imp = 0; + m_imp = nullptr; } timeit::~timeit() { diff --git a/src/util/timeout.cpp b/src/util/timeout.cpp index 67995c2aa..283b4c206 100644 --- a/src/util/timeout.cpp +++ b/src/util/timeout.cpp @@ -27,12 +27,12 @@ Revision History: #include "util/event_handler.h" #include "util/scoped_timer.h" -scoped_timer * g_timeout = 0; -void (* g_on_timeout)() = 0; +scoped_timer * g_timeout = nullptr; +void (* g_on_timeout)() = nullptr; class g_timeout_eh : public event_handler { public: - void operator()(event_handler_caller_t caller_id) { + void operator()(event_handler_caller_t caller_id) override { #pragma omp critical (g_timeout_cs) { std::cout << "timeout\n"; @@ -41,7 +41,7 @@ public: g_on_timeout(); if (g_timeout) delete g_timeout; - g_timeout = 0; + g_timeout = nullptr; throw z3_error(ERR_TIMEOUT); } } diff --git a/src/util/top_sort.h b/src/util/top_sort.h new file mode 100644 index 000000000..5f7db9c3e --- /dev/null +++ b/src/util/top_sort.h @@ -0,0 +1,101 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + top_sort.h + +Abstract: + Topological sort over objects + +Author: + + Nikolaj Bjorner (nbjorner) 2018-02-14 + +Revision History: + +--*/ + +#ifndef TOP_SORT_H_ +#define TOP_SORT_H_ + +#include "util/obj_hashtable.h" +#include "util/vector.h" +#include +#include +#include +#include "util/memory_manager.h" + + +template +class top_sort { + typedef obj_hashtable T_set; + obj_map m_partition_id; + obj_map m_dfs_num; + ptr_vector m_top_sorted; + ptr_vector m_stack_S; + ptr_vector m_stack_P; + unsigned m_next_preorder; + obj_map m_deps; + + void traverse(T* f) { + unsigned p_id = 0; + if (m_dfs_num.find(f, p_id)) { + if (!m_partition_id.contains(f)) { + while (!m_stack_P.empty() && m_partition_id.contains(m_stack_P.back()) && m_partition_id[m_stack_P.back()] > p_id) { + m_stack_P.pop_back(); + } + } + } + else if (!m_deps.contains(f)) { + return; + } + else { + m_dfs_num.insert(f, m_next_preorder++); + m_stack_S.push_back(f); + m_stack_P.push_back(f); + for (T* g : *m_deps[f]) { + traverse(g); + } + if (f == m_stack_P.back()) { + + p_id = m_top_sorted.size(); + T* s_f; + do { + s_f = m_stack_S.back(); + m_stack_S.pop_back(); + m_top_sorted.push_back(s_f); + m_partition_id.insert(s_f, p_id); + } + while (s_f != f); + m_stack_P.pop_back(); + } + } + } + +public: + + ~top_sort() { + for (auto & kv : m_deps) dealloc(kv.m_value); + } + + void topological_sort() { + m_next_preorder = 0; + m_partition_id.reset(); + m_top_sorted.reset(); + for (auto & kv : m_deps) { + traverse(kv.m_key); + } + SASSERT(m_stack_S.empty()); + SASSERT(m_stack_P.empty()); + m_dfs_num.reset(); + } + + void insert(T* t, T_set* s) { + m_deps.insert(t, s); + } + + ptr_vector const& top_sorted() { return m_top_sorted; } +}; + +#endif /* TOP_SORT_H_ */ diff --git a/src/util/total_order.h b/src/util/total_order.h index a309b63a1..44ab6f749 100644 --- a/src/util/total_order.h +++ b/src/util/total_order.h @@ -35,7 +35,7 @@ class total_order { struct cell { cell * m_next; cell * m_prev; - uint64 m_val; + uint64_t m_val; T m_data; }; @@ -53,11 +53,11 @@ class total_order { m_base.m_val = 0; } - uint64 v(cell * a) const { return a->m_val; } + uint64_t v(cell * a) const { return a->m_val; } - uint64 vb(cell * a) const { return v(a) - v(base()); } + uint64_t vb(cell * a) const { return v(a) - v(base()); } - uint64 vbn(cell * a) const { return a->m_next == base() ? UINT64_MAX : vb(a->m_next); } + uint64_t vbn(cell * a) const { return a->m_next == base() ? UINT64_MAX : vb(a->m_next); } cell * mk_cell(T const & a) { SASSERT(!m_map.contains(a)); @@ -84,18 +84,18 @@ class total_order { } void _insert_after(cell * a, cell * b) { - uint64 vb_a = vb(a); - uint64 vbn_a = vbn(a); + uint64_t vb_a = vb(a); + uint64_t vbn_a = vbn(a); SASSERT(vb_a < vbn_a); if (vbn_a < 2 || (vb_a > vbn_a - 2)) { TRACE("total_order", tout << "relabeling...\n"; tout << "\n";); - uint64 v0 = v(a); - unsigned sz = size(); - uint64 ideal_gap = UINT64_MAX / sz; - uint64 goal_gap = ideal_gap / 32; - cell * c = a->m_next->m_next; - unsigned j = 2; - uint64 curr_gap = (v(c) - v0) / j; + uint64_t v0 = v(a); + unsigned sz = size(); + uint64_t ideal_gap = UINT64_MAX / sz; + uint64_t goal_gap = ideal_gap / 32; + cell * c = a->m_next->m_next; + unsigned j = 2; + uint64_t curr_gap = (v(c) - v0) / j; while (j < sz && curr_gap < goal_gap) { j++; c = c->m_next; @@ -105,7 +105,7 @@ class total_order { if (j == sz) curr_gap = ideal_gap; c = a->m_next; - uint64 inc = curr_gap; + uint64_t inc = curr_gap; for (unsigned i = 0; i < j; i++) { c->m_val = v0 + inc; c = c->m_next; @@ -116,7 +116,7 @@ class total_order { vbn_a = vbn(a); } SASSERT(vb_a <= vbn_a - 2); - uint64 vb_b = vb_a + ((vbn_a - vb_a)/2); + uint64_t vb_b = vb_a + ((vbn_a - vb_a)/2); SASSERT(vb_b > vb_a); SASSERT(vb_b < vbn_a); b->m_val = vb_b + v(base()); diff --git a/src/util/trace.cpp b/src/util/trace.cpp index d993ebd11..9571e99e6 100644 --- a/src/util/trace.cpp +++ b/src/util/trace.cpp @@ -24,7 +24,7 @@ std::ofstream tout(".z3-trace"); #endif static bool g_enable_all_trace_tags = false; -static str_hashtable* g_enabled_trace_tags = 0; +static str_hashtable* g_enabled_trace_tags = nullptr; static str_hashtable& get_enabled_trace_tags() { if (!g_enabled_trace_tags) { @@ -35,7 +35,7 @@ static str_hashtable& get_enabled_trace_tags() { void finalize_trace() { dealloc(g_enabled_trace_tags); - g_enabled_trace_tags = 0; + g_enabled_trace_tags = nullptr; } void enable_trace(const char * tag) { diff --git a/src/util/trail.h b/src/util/trail.h index bba71fb00..b9f4602c7 100644 --- a/src/util/trail.h +++ b/src/util/trail.h @@ -43,10 +43,10 @@ public: m_old_value(value) { } - virtual ~value_trail() { + ~value_trail() override { } - virtual void undo(Ctx & ctx) { + void undo(Ctx & ctx) override { m_value = m_old_value; } }; @@ -59,10 +59,10 @@ public: m_value(value) { } - virtual ~reset_flag_trail() { + ~reset_flag_trail() override { } - virtual void undo(Ctx & ctx) { + void undo(Ctx & ctx) override { m_value = false; } }; @@ -76,8 +76,8 @@ public: SASSERT(m_ptr == 0); } - virtual void undo(Ctx & ctx) { - m_ptr = 0; + void undo(Ctx & ctx) override { + m_ptr = nullptr; } }; @@ -94,9 +94,9 @@ public: m_vector(v), m_old_size(v.size()) { } - virtual ~restore_size_trail() { + ~restore_size_trail() override { } - virtual void undo(Ctx & ctx) { + void undo(Ctx & ctx) override { m_vector.shrink(m_old_size); } }; @@ -113,10 +113,10 @@ public: m_old_value(v[idx]) { } - virtual ~vector_value_trail() { + ~vector_value_trail() override { } - virtual void undo(Ctx & ctx) { + void undo(Ctx & ctx) override { m_vector[m_idx] = m_old_value; } }; @@ -128,8 +128,8 @@ class insert_obj_map : public trail { D* m_obj; public: insert_obj_map(obj_map& t, D* o) : m_map(t), m_obj(o) {} - virtual ~insert_obj_map() {} - virtual void undo(Ctx & ctx) { m_map.remove(m_obj); } + ~insert_obj_map() override {} + void undo(Ctx & ctx) override { m_map.remove(m_obj); } }; template @@ -138,8 +138,8 @@ class insert_map : public trail { D m_obj; public: insert_map(M& t, D o) : m_map(t), m_obj(o) {} - virtual ~insert_map() {} - virtual void undo(Ctx & ctx) { m_map.remove(m_obj); } + ~insert_map() override {} + void undo(Ctx & ctx) override { m_map.remove(m_obj); } }; @@ -152,7 +152,7 @@ public: m_vector(v) { } - virtual void undo(Ctx & ctx) { + void undo(Ctx & ctx) override { m_vector.pop_back(); } }; @@ -167,10 +167,10 @@ public: m_idx(idx) { } - virtual ~set_vector_idx_trail() { + ~set_vector_idx_trail() override { } - virtual void undo(Ctx & ctx) { + void undo(Ctx & ctx) override { m_vector[m_idx] = 0; } }; @@ -218,7 +218,7 @@ public: m_vector(v) { } - virtual void undo(Ctx & ctx) { + void undo(Ctx & ctx) override { m_vector.pop_back(); } }; @@ -251,7 +251,7 @@ public: m_vector[m_idx] = true; } - virtual void undo(Ctx & ctx) { + void undo(Ctx & ctx) override { m_vector[m_idx] = false; } }; @@ -264,7 +264,7 @@ public: m_obj(obj) { } - virtual void undo(Ctx & ctx) { + void undo(Ctx & ctx) override { dealloc(m_obj); } }; @@ -288,8 +288,8 @@ class insert_obj_trail : public trail { T* m_obj; public: insert_obj_trail(obj_hashtable& t, T* o) : m_table(t), m_obj(o) {} - virtual ~insert_obj_trail() {} - virtual void undo(Ctx & ctx) { m_table.remove(m_obj); } + ~insert_obj_trail() override {} + void undo(Ctx & ctx) override { m_table.remove(m_obj); } }; diff --git a/src/util/uint_set.h b/src/util/uint_set.h index 352189ef1..0f3715cb1 100644 --- a/src/util/uint_set.h +++ b/src/util/uint_set.h @@ -24,9 +24,9 @@ Revision History: class uint_set : unsigned_vector { - + public: - + typedef unsigned data; uint_set() {} @@ -252,5 +252,128 @@ inline std::ostream & operator<<(std::ostream & target, const uint_set & s) { return target; } + +class tracked_uint_set { + svector m_in_set; + svector m_set; +public: + typedef svector::const_iterator iterator; + void insert(unsigned v) { + m_in_set.reserve(v+1, false); + if (m_in_set[v]) + return; + m_in_set[v] = true; + m_set.push_back(v); + } + + void remove(unsigned v) { + if (contains(v)) { + m_in_set[v] = false; + unsigned i = 0; + for (i = 0; i < m_set.size() && m_set[i] != v; ++i) + ; + SASSERT(i < m_set.size()); + m_set[i] = m_set.back(); + m_set.pop_back(); + } + } + + tracked_uint_set& operator=(tracked_uint_set const& other) { + m_in_set = other.m_in_set; + m_set = other.m_set; + return *this; + } + + bool contains(unsigned v) const { + return v < m_in_set.size() && m_in_set[v] != 0; + } + + bool empty() const { + return m_set.empty(); + } + + // erase some variable from the set + unsigned erase() { + SASSERT(!empty()); + unsigned v = m_set.back(); + m_set.pop_back(); + m_in_set[v] = false; + return v; + } + unsigned size() const { return m_set.size(); } + iterator begin() const { return m_set.begin(); } + iterator end() const { return m_set.end(); } + // void reset() { m_set.reset(); m_in_set.reset(); } + void reset() { + unsigned sz = m_set.size(); + for (unsigned i = 0; i < sz; ++i) m_in_set[m_set[i]] = false; + m_set.reset(); + } + void finalize() { m_set.finalize(); m_in_set.finalize(); } + tracked_uint_set& operator&=(tracked_uint_set const& other) { + unsigned j = 0; + for (unsigned i = 0; i < m_set.size(); ++i) { + if (other.contains(m_set[i])) { + m_set[j] = m_set[i]; + ++j; + } + else { + m_in_set[m_set[i]] = false; + } + } + m_set.resize(j); + return *this; + } + tracked_uint_set& operator|=(tracked_uint_set const& other) { + for (unsigned i = 0; i < other.m_set.size(); ++i) { + insert(other.m_set[i]); + } + return *this; + } +}; + +class indexed_uint_set { + unsigned m_size; + unsigned_vector m_elems; + unsigned_vector m_index; +public: + indexed_uint_set(): + m_size(0) + {} + + void insert(unsigned x) { + SASSERT(!contains(x)); + m_index.reserve(x + 1, UINT_MAX); + m_elems.reserve(m_size + 1); + m_index[x] = m_size; + m_elems[m_size] = x; + m_size++; + SASSERT(contains(x)); + } + + void remove(unsigned x) { + SASSERT(contains(x)); + unsigned y = m_elems[--m_size]; + if (x != y) { + unsigned idx = m_index[x]; + m_index[y] = idx; + m_elems[idx] = y; + m_index[x] = m_size; + m_elems[m_size] = x; + } + SASSERT(!contains(x)); + } + + bool contains(unsigned x) const { return x < m_index.size() && m_index[x] < m_size && m_elems[m_index[x]] == x; } + void reset() { m_size = 0; } + bool empty() const { return m_size == 0; } + unsigned size() const { return m_size; } + unsigned max_var() const { return m_index.size(); } + typedef unsigned_vector::const_iterator iterator; + iterator begin() const { return m_elems.begin(); } + iterator end() const { return m_elems.begin() + m_size; } + +}; + #endif /* UINT_SET_H_ */ diff --git a/src/util/union_find.h b/src/util/union_find.h index 416b653af..9dd0b6fbd 100644 --- a/src/util/union_find.h +++ b/src/util/union_find.h @@ -52,8 +52,8 @@ class union_find { union_find & m_owner; public: mk_var_trail(union_find & o):m_owner(o) {} - virtual ~mk_var_trail() {} - virtual void undo(Ctx & ctx) { + ~mk_var_trail() override {} + void undo(Ctx & ctx) override { m_owner.m_find.pop_back(); m_owner.m_size.pop_back(); m_owner.m_next.pop_back(); @@ -70,8 +70,8 @@ class union_find { unsigned m_r1; public: merge_trail(union_find & o, unsigned r1):m_owner(o), m_r1(r1) {} - virtual ~merge_trail() {} - virtual void undo(Ctx & ctx) { m_owner.unmerge(m_r1); } + ~merge_trail() override {} + void undo(Ctx & ctx) override { m_owner.unmerge(m_r1); } }; void unmerge(unsigned r1) { diff --git a/src/util/util.cpp b/src/util/util.cpp index b16dbe292..59d6d752c 100644 --- a/src/util/util.cpp +++ b/src/util/util.cpp @@ -17,6 +17,10 @@ Revision History: --*/ +#ifdef _WINDOWS +#include +#endif +#include #include "util/util.h" static unsigned g_verbosity_level = 0; @@ -35,12 +39,31 @@ void set_verbose_stream(std::ostream& str) { g_verbose_stream = &str; } +#ifdef _WINDOWS +static int g_thread_id = 0; +#else +static std::thread::id g_thread_id = std::this_thread::get_id(); +#endif +static bool g_is_threaded = false; + +bool is_threaded() { + if (g_is_threaded) return true; +#ifdef _WINDOWS + int thid = GetCurrentThreadId(); + g_is_threaded = g_thread_id != thid && g_thread_id != 0; + g_thread_id = thid; +#else + g_is_threaded = std::this_thread::get_id() != g_thread_id; +#endif + return g_is_threaded; +} + std::ostream& verbose_stream() { return *g_verbose_stream; } -static void (*g_fatal_error_handler)(int) = 0; +static void (*g_fatal_error_handler)(int) = nullptr; void fatal_error(int error_code) { if (g_fatal_error_handler) { @@ -80,7 +103,7 @@ unsigned log2(unsigned v) { return r; } -unsigned uint64_log2(uint64 v) { +unsigned uint64_log2(uint64_t v) { unsigned r = 0; if (v & 0xFFFFFFFF00000000ull) { v >>= 32; @@ -120,7 +143,7 @@ bool product_iterator_next(unsigned n, unsigned const * sz, unsigned * it) { } char const * escaped::end() const { - if (m_str == 0) return 0; + if (m_str == nullptr) return nullptr; char const * it = m_str; char const * e = m_str; while (*it) { diff --git a/src/util/util.h b/src/util/util.h index 6d38231ba..12fd2fe3b 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -21,31 +21,26 @@ Revision History: #include "util/debug.h" #include "util/memory_manager.h" +#include "util/z3_omp.h" #include #include #include +#include #ifndef SIZE_MAX #define SIZE_MAX std::numeric_limits::max() #endif -#ifndef uint64 -typedef unsigned long long uint64; -#endif -static_assert(sizeof(uint64) == 8, "64 bits please"); +static_assert(sizeof(uint64_t) == 8, "64 bits please"); -#ifndef int64 -typedef long long int64; -#endif - -static_assert(sizeof(int64) == 8, "64 bits"); +static_assert(sizeof(int64_t) == 8, "64 bits"); #ifndef INT64_MIN -#define INT64_MIN static_cast(0x8000000000000000ull) +#define INT64_MIN static_cast(0x8000000000000000ull) #endif #ifndef INT64_MAX -#define INT64_MAX static_cast(0x7fffffffffffffffull) +#define INT64_MAX static_cast(0x7fffffffffffffffull) #endif #ifndef UINT64_MAX #define UINT64_MAX 0xffffffffffffffffull @@ -110,7 +105,7 @@ inline unsigned next_power_of_two(unsigned v) { \brief Return the position of the most significant bit. */ unsigned log2(unsigned v); -unsigned uint64_log2(uint64 v); +unsigned uint64_log2(uint64_t v); static_assert(sizeof(unsigned) == 4, "unsigned are 32 bits"); @@ -136,11 +131,11 @@ static inline unsigned get_num_1bits(unsigned v) { // Remark: on gcc, the operators << and >> do not produce zero when the second argument >= 64. // So, I'm using the following two definitions to fix the problem -static inline uint64 shift_right(uint64 x, uint64 y) { +static inline uint64_t shift_right(uint64_t x, uint64_t y) { return y < 64ull ? (x >> y) : 0ull; } -static inline uint64 shift_left(uint64 x, uint64 y) { +static inline uint64_t shift_left(uint64_t x, uint64_t y) { return y < 64ull ? (x << y) : 0ull; } @@ -183,16 +178,38 @@ void set_verbosity_level(unsigned lvl); unsigned get_verbosity_level(); std::ostream& verbose_stream(); void set_verbose_stream(std::ostream& str); +bool is_threaded(); -#define IF_VERBOSE(LVL, CODE) { if (get_verbosity_level() >= LVL) { CODE } } ((void) 0) + +#define IF_VERBOSE(LVL, CODE) { \ + if (get_verbosity_level() >= LVL) { \ + if (is_threaded()) { \ + LOCK_CODE(CODE); \ + } \ + else { \ + CODE; \ + } \ + } } ((void) 0) -#ifdef _EXTERNAL_RELEASE -#define IF_IVERBOSE(LVL, CODE) ((void) 0) +#ifdef _MSC_VER +#define DO_PRAGMA(x) __pragma(x) +#define PRAGMA_LOCK __pragma(omp critical (verbose_lock)) #else -#define IF_IVERBOSE(LVL, CODE) { if (get_verbosity_level() >= LVL) { CODE } } ((void) 0) +#define DO_PRAGMA(x) _Pragma(#x) +#define PRAGMA_LOCK _Pragma("omp critical (verbose_lock)") #endif - +#ifdef _NO_OMP_ +#define LOCK_CODE(CODE) CODE; +#else +#define LOCK_CODE(CODE) \ + { \ + PRAGMA_LOCK \ + { \ + CODE; \ + } \ + } +#endif template struct default_eq { @@ -222,7 +239,7 @@ template class scoped_ptr { T * m_ptr; public: - scoped_ptr(T * ptr=0): + scoped_ptr(T * ptr=nullptr): m_ptr(ptr) { } @@ -239,7 +256,7 @@ public: } operator bool() const { - return m_ptr != 0; + return m_ptr != nullptr; } const T & operator*() const { @@ -260,7 +277,7 @@ public: T * detach() { T* tmp = m_ptr; - m_ptr = 0; + m_ptr = nullptr; return tmp; } diff --git a/src/util/vector.h b/src/util/vector.h index f5792a5d3..c4443255a 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -57,7 +57,7 @@ class vector { } void expand_vector() { - if (m_data == 0) { + if (m_data == nullptr) { SZ capacity = 2; SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * capacity + sizeof(SZ) * 2)); *mem = capacity; @@ -132,12 +132,12 @@ public: typedef const T * const_iterator; vector(): - m_data(0) { + m_data(nullptr) { } vector(SZ s) { if (s == 0) { - m_data = 0; + m_data = nullptr; return; } SZ * mem = reinterpret_cast(memory::allocate(sizeof(T) * s + sizeof(SZ) * 2)); @@ -155,24 +155,24 @@ public: } vector(SZ s, T const & elem): - m_data(0) { + m_data(nullptr) { resize(s, elem); } vector(vector const & source): - m_data(0) { + m_data(nullptr) { if (source.m_data) { copy_core(source); } SASSERT(size() == source.size()); } - vector(vector&& other) : m_data(0) { + vector(vector&& other) : m_data(nullptr) { std::swap(m_data, other.m_data); } vector(SZ s, T const * data): - m_data(0) { + m_data(nullptr) { for (SZ i = 0; i < s; i++) { push_back(data[i]); } @@ -185,7 +185,7 @@ public: void finalize() { destroy(); - m_data = 0; + m_data = nullptr; } vector & operator=(vector const & source) { @@ -197,7 +197,7 @@ public: copy_core(source); } else { - m_data = 0; + m_data = nullptr; } return *this; } @@ -207,7 +207,7 @@ public: return *this; } destroy(); - m_data = 0; + m_data = nullptr; std::swap(m_data, source.m_data); return *this; } @@ -224,18 +224,18 @@ public: void clear() { reset(); } bool empty() const { - return m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == 0; + return m_data == nullptr || reinterpret_cast(m_data)[SIZE_IDX] == 0; } SZ size() const { - if (m_data == 0) { + if (m_data == nullptr) { return 0; } return reinterpret_cast(m_data)[SIZE_IDX]; } SZ capacity() const { - if (m_data == 0) { + if (m_data == nullptr) { return 0; } return reinterpret_cast(m_data)[CAPACITY_IDX]; @@ -349,7 +349,7 @@ public: } void push_back(T const & elem) { - if (m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { + if (m_data == nullptr || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { expand_vector(); } new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(elem); @@ -357,7 +357,7 @@ public: } void push_back(T && elem) { - if (m_data == 0 || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { + if (m_data == nullptr || reinterpret_cast(m_data)[SIZE_IDX] == reinterpret_cast(m_data)[CAPACITY_IDX]) { expand_vector(); } new (m_data + reinterpret_cast(m_data)[SIZE_IDX]) T(std::move(elem)); @@ -471,7 +471,7 @@ public: void fill(unsigned sz, T const & elem) { resize(sz); - fill(sz, elem); + fill(elem); } bool contains(T const & elem) const { @@ -548,6 +548,11 @@ typedef svector char_vector; typedef svector signed_char_vector; typedef svector double_vector; +inline std::ostream& operator<<(std::ostream& out, unsigned_vector const& v) { + for (unsigned u : v) out << u << " "; + return out; +} + template struct vector_hash_tpl { Hash m_hash; diff --git a/src/util/warning.cpp b/src/util/warning.cpp index a794b064a..6184db880 100644 --- a/src/util/warning.cpp +++ b/src/util/warning.cpp @@ -62,8 +62,8 @@ void STD_CALL myInvalidParameterHandler( static bool g_warning_msgs = true; static bool g_use_std_stdout = false; -static std::ostream* g_error_stream = 0; -static std::ostream* g_warning_stream = 0; +static std::ostream* g_error_stream = nullptr; +static std::ostream* g_warning_stream = nullptr; static bool g_show_error_msg_prefix = true; void send_warnings_to_stdout(bool flag) { diff --git a/src/util/z3_exception.h b/src/util/z3_exception.h index 8d0acda28..e3260e89d 100644 --- a/src/util/z3_exception.h +++ b/src/util/z3_exception.h @@ -33,8 +33,8 @@ class z3_error : public z3_exception { unsigned m_error_code; public: z3_error(unsigned error_code); - virtual char const * msg() const; - virtual unsigned error_code() const; + char const * msg() const override; + unsigned error_code() const override; }; class default_exception : public z3_exception { @@ -43,8 +43,8 @@ public: struct fmt {}; default_exception(std::string const& msg); default_exception(fmt, char const* msg, ...); - virtual ~default_exception() {} - virtual char const * msg() const; + ~default_exception() override {} + char const * msg() const override; }; #endif diff --git a/todo.txt b/todo.txt new file mode 100644 index 000000000..82038c3b4 --- /dev/null +++ b/todo.txt @@ -0,0 +1,9 @@ +- consolidate virtual-solver and pool solver + +- fixup mbp/mbi + +- API additions to expose functionality + +- Equality solver + +- Generalizer index \ No newline at end of file From 1892d31794c86044a15d0251bace3948cc20ded6 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Wed, 27 Jun 2018 18:10:40 +0800 Subject: [PATCH 16/17] add parameter to enable splitting guided by length constraints --- src/smt/params/smt_params.h | 2 ++ src/smt/params/smt_params_helper.pyg | 1 + src/smt/params/theory_seq_params.cpp | 23 +++++++++++++++++ src/smt/params/theory_seq_params.h | 38 ++++++++++++++++++++++++++++ src/smt/smt_setup.cpp | 4 +-- src/smt/theory_seq.cpp | 5 ++-- src/smt/theory_seq.h | 6 +++-- 7 files changed, 73 insertions(+), 6 deletions(-) create mode 100644 src/smt/params/theory_seq_params.cpp create mode 100644 src/smt/params/theory_seq_params.h diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index 32b634626..8901697b7 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -26,6 +26,7 @@ Revision History: #include "smt/params/theory_array_params.h" #include "smt/params/theory_bv_params.h" #include "smt/params/theory_str_params.h" +#include "smt/params/theory_seq_params.h" #include "smt/params/theory_pb_params.h" #include "smt/params/theory_datatype_params.h" #include "smt/params/preprocessor_params.h" @@ -79,6 +80,7 @@ struct smt_params : public preprocessor_params, public theory_array_params, public theory_bv_params, public theory_str_params, + public theory_seq_params, public theory_pb_params, public theory_datatype_params { bool m_display_proof; diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 816764896..3f4105c34 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -71,6 +71,7 @@ def_module_params(module_name='smt', ('theory_case_split', BOOL, False, 'Allow the context to use heuristics involving theory case splits, which are a set of literals of which exactly one can be assigned True. If this option is false, the context will generate extra axioms to enforce this instead.'), ('string_solver', SYMBOL, 'seq', 'solver for string/sequence theories. options are: \'z3str3\' (specialized string solver), \'seq\' (sequence solver), \'auto\' (use static features to choose best solver)'), ('core.validate', BOOL, False, 'validate unsat core produced by SMT context'), + ('seq.split_w_len', BOOL, True, 'enable splitting guided by length constraints'), ('str.strong_arrangements', BOOL, True, 'assert equivalences instead of implications when generating string arrangement axioms'), ('str.aggressive_length_testing', BOOL, False, 'prioritize testing concrete length values over generating more options'), ('str.aggressive_value_testing', BOOL, False, 'prioritize testing concrete string constant values over generating more options'), diff --git a/src/smt/params/theory_seq_params.cpp b/src/smt/params/theory_seq_params.cpp new file mode 100644 index 000000000..e521298d3 --- /dev/null +++ b/src/smt/params/theory_seq_params.cpp @@ -0,0 +1,23 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + theory_seq_params.cpp + +Abstract: + + Parameters for sequence theory plugin + +Revision History: + + +--*/ + +#include "smt/params/theory_seq_params.h" +#include "smt/params/smt_params_helper.hpp" + +void theory_seq_params::updt_params(params_ref const & _p) { + smt_params_helper p(_p); + m_split_w_len = p.seq_split_w_len(); +} diff --git a/src/smt/params/theory_seq_params.h b/src/smt/params/theory_seq_params.h new file mode 100644 index 000000000..24e67b8ff --- /dev/null +++ b/src/smt/params/theory_seq_params.h @@ -0,0 +1,38 @@ +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + theory_seq_params.h + +Abstract: + + Parameters for sequence theory plugin + +Revision History: + + +--*/ + +#ifndef THEORY_SEQ_PARAMS_H +#define THEORY_SEQ_PARAMS_H + +#include "util/params.h" + +struct theory_seq_params { + /* + * Enable splitting guided by length constraints + */ + bool m_split_w_len; + + + theory_seq_params(params_ref const & p = params_ref()): + m_split_w_len(true) + { + updt_params(p); + } + + void updt_params(params_ref const & p); +}; + +#endif /* THEORY_SEQ_PARAMS_H */ diff --git a/src/smt/smt_setup.cpp b/src/smt/smt_setup.cpp index 294aa7b08..6e996e409 100644 --- a/src/smt/smt_setup.cpp +++ b/src/smt/smt_setup.cpp @@ -222,7 +222,7 @@ namespace smt { void setup::setup_QF_BVRE() { setup_QF_BV(); setup_QF_LIA(); - m_context.register_plugin(alloc(theory_seq, m_manager)); + m_context.register_plugin(alloc(theory_seq, m_manager, m_params)); } void setup::setup_QF_UF(static_features const & st) { @@ -895,7 +895,7 @@ namespace smt { } void setup::setup_seq() { - m_context.register_plugin(alloc(smt::theory_seq, m_manager)); + m_context.register_plugin(alloc(smt::theory_seq, m_manager, m_params)); } void setup::setup_unknown() { diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 26ab92665..ffa0b857a 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -191,9 +191,10 @@ void theory_seq::exclusion_table::display(std::ostream& out) const { } -theory_seq::theory_seq(ast_manager& m): +theory_seq::theory_seq(ast_manager& m, theory_seq_params const & params): theory(m.mk_family_id("seq")), m(m), + m_params(params), m_rep(m, m_dm), m_reset_cache(false), m_eq_id(0), @@ -273,7 +274,7 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>zero_length\n";); return FC_CONTINUE; } - if (len_based_split()) { + if (m_params.m_split_w_len && len_based_split()) { ++m_stats.m_branch_variable; TRACE("seq", tout << ">>split_based_on_length\n";); return FC_CONTINUE; diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index b5ce30236..f32b6254b 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -49,6 +49,8 @@ namespace smt { typedef union_find th_union_find; class seq_value_proc; + + theory_seq_params const & m_params; // cache to track evaluations under equalities class eval_cache { @@ -361,7 +363,7 @@ namespace smt { void pop_scope_eh(unsigned num_scopes) override; void restart_eh() override; void relevant_eh(app* n) override; - theory* mk_fresh(context* new_ctx) override { return alloc(theory_seq, new_ctx->get_manager()); } + theory* mk_fresh(context* new_ctx) override { return alloc(theory_seq, new_ctx->get_manager(), m_params); } char const * get_name() const override { return "seq"; } theory_var mk_var(enode* n) override; void apply_sort_cnstr(enode* n, sort* s) override; @@ -621,7 +623,7 @@ namespace smt { void display_deps(std::ostream& out, literal_vector const& lits, enode_pair_vector const& eqs) const; void display_nc(std::ostream& out, nc const& nc) const; public: - theory_seq(ast_manager& m); + theory_seq(ast_manager& m, theory_seq_params const & params); ~theory_seq() override; // model building From cd62017afd604b84a31605b610e1ecbb49539e14 Mon Sep 17 00:00:00 2001 From: Thai Trinh Date: Sat, 30 Jun 2018 15:52:20 +0800 Subject: [PATCH 17/17] fixed failures with regression tests --- src/smt/params/CMakeLists.txt | 1 + src/smt/theory_seq.cpp | 48 ++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/smt/params/CMakeLists.txt b/src/smt/params/CMakeLists.txt index c965f0a62..4beec80f0 100644 --- a/src/smt/params/CMakeLists.txt +++ b/src/smt/params/CMakeLists.txt @@ -8,6 +8,7 @@ z3_add_component(smt_params theory_array_params.cpp theory_bv_params.cpp theory_pb_params.cpp + theory_seq_params.cpp theory_str_params.cpp COMPONENT_DEPENDENCIES ast diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index ffa0b857a..40aae8043 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -954,32 +954,29 @@ void theory_seq::prop_arith_to_len_offset() { } } -int theory_seq::find_fst_non_empty_idx(expr_ref_vector const& x) const { - context & ctx = get_context(); - int i = 0; - for (; i < x.size(); ++i) { - if (!is_var(x[i])) return -1; - expr_ref e(m_util.str.mk_length(x[i]), m); +int theory_seq::find_fst_non_empty_idx(expr_ref_vector const& xs) const { + context & ctx = get_context(); + for (unsigned i = 0; i < xs.size(); ++i) { + expr* x = xs[i]; + if (!is_var(x)) return -1; + expr_ref e(m_util.str.mk_length(x), m); if (ctx.e_internalized(e)) { enode* root = ctx.get_enode(e)->get_root(); rational val; - if (m_autil.is_numeral(root->get_owner(), val) && val.is_zero()) + if (m_autil.is_numeral(root->get_owner(), val) && val.is_zero()) { continue; + } } - break; - } - if (i == x.size()) - return -1; - else { return i; } + return -1; } expr* theory_seq::find_fst_non_empty_var(expr_ref_vector const& x) const { int i = find_fst_non_empty_idx(x); if (i >= 0) return x[i]; - return NULL; + return nullptr; } void theory_seq::find_max_eq_len(expr_ref_vector const& ls, expr_ref_vector const& rs) { @@ -1094,7 +1091,7 @@ bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector cons dependency*& deps, expr_ref_vector & res) { context& ctx = get_context(); - if (ls.size() == 0 || rs.size() == 0) + if (ls.empty() || rs.empty()) return false; expr* l_fst = find_fst_non_empty_var(ls); expr* r_fst = find_fst_non_empty_var(rs); @@ -2338,10 +2335,10 @@ bool theory_seq::solve_unit_eq(expr* l, expr* r, dependency* deps) { if (is_var(r) && !occurs(r, l) && add_solution(r, l, deps)) { return true; } - if (is_nth(l) && !occurs(l, r) && add_solution(l, r, deps)) - return true; - if (is_nth(r) && !occurs(r, l) && add_solution(r, l, deps)) - return true; +// if (is_nth(l) && !occurs(l, r) && add_solution(l, r, deps)) +// return true; +// if (is_nth(r) && !occurs(r, l) && add_solution(r, l, deps)) +// return true; return false; } @@ -2731,10 +2728,12 @@ bool theory_seq::reduce_length(unsigned i, unsigned j, bool front, expr_ref_vect expr_ref r = mk_concat(r1, rs1); expr_ref lenl(m_util.str.mk_length(l), m); expr_ref lenr(m_util.str.mk_length(r), m); - expr_ref len_eq(m.mk_eq(lenl, lenr), m); - if (ctx.find_assignment(len_eq) == l_true) { - literal lit = mk_eq(lenl, lenr, false); - literal_vector lits; + literal lit = mk_eq(lenl, lenr, false); + if (ctx.get_assignment(lit) == l_true) { +// expr_ref len_eq(m.mk_eq(lenl, lenr), m); +// if (ctx.find_assignment(len_eq) == l_true) { +// literal lit = mk_eq(lenl, lenr, false); +// literal_vector lits; expr_ref_vector lhs(m), rhs(m); lhs.append(l2, ls2); rhs.append(r2, rs2); @@ -4978,6 +4977,7 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4, liter } expr* theory_seq::coalesce_chars(expr* const& e) { + context& ctx = get_context(); expr* s; if (m_util.str.is_concat(e)) { expr_ref_vector concats(m); @@ -5020,7 +5020,9 @@ expr* theory_seq::coalesce_chars(expr* const& e) { if (bvu.is_bv(s)) { expr_ref result(m); expr * args[1] = {s}; - if (m_seq_rewrite.mk_app_core(to_app(s)->get_decl(), 1, args, result)) { + if (m_seq_rewrite.mk_app_core(to_app(e)->get_decl(), 1, args, result)) { + if (!ctx.e_internalized(result)) + ctx.internalize(result, false); return result; } }