From 3b92128ed8e404d07d9ae15ebff48aaae667dfeb Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 16 Feb 2016 12:12:59 +0000 Subject: [PATCH 01/57] Fixed old-style C variable declaration problem in interpolation C example. --- examples/c/test_capi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/c/test_capi.c b/examples/c/test_capi.c index 5e70bac81..88fdaa1cf 100644 --- a/examples/c/test_capi.c +++ b/examples/c/test_capi.c @@ -2351,11 +2351,12 @@ void interpolation_example() { 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"); - Z3_lbool result = Z3_compute_interpolant(ctx,f,0,&interpolant,&m); + result = Z3_compute_interpolant(ctx,f,0,&interpolant,&m); switch (result) { case Z3_L_FALSE: From c05a0dfa61fda18f57e2c9e670ba40d0eccf08e4 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 16 Feb 2016 13:10:17 +0000 Subject: [PATCH 02/57] revert my previous attempt to simplify the destructor of ctx-simplify there can be assertions at level 0 --- src/tactic/core/ctx_simplify_tactic.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 894aea117..95be200a8 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -177,7 +177,8 @@ struct ctx_simplify_tactic::imp { ~imp() { pop(scope_level()); - SASSERT(scope_level() == 0 && m_cache_undo.empty()); + SASSERT(scope_level() == 0); + restore_cache(0); DEBUG_CODE({ for (unsigned i = 0; i < m_cache.size(); i++) { CTRACE("ctx_simplify_tactic_bug", m_cache[i].m_from, From 6c966bba59677589fbaf76d6c29a8aceab70eaa6 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 16 Feb 2016 13:58:49 +0000 Subject: [PATCH 03/57] Fix incorrect (off by one) bound check. Also assert that we don't increment ``m_num_segments`` beyond the maximum value (``c_max_segments``). This is related to #436. When doing an AddressSanitized build and running the ``c_example`` it looks like Z3 tries to create too many segments and index out of bounds. Fixing the checks here causes them to fail which should help us narrow down the problem. --- src/sat/sat_clause.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sat/sat_clause.cpp b/src/sat/sat_clause.cpp index 279d84375..2881c03ae 100644 --- a/src/sat/sat_clause.cpp +++ b/src/sat/sat_clause.cpp @@ -145,7 +145,8 @@ namespace sat { return i; i = m_num_segments; m_num_segments++; - if (i > c_max_segments) + SASSERT(m_num_segments <= c_max_segments); + if (i >= c_max_segments) throw default_exception("segment out of range"); m_segments[i] = ptr; return i; From 98a92b92554e1e1a496b184a0e7a869fdfffa317 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 17 Feb 2016 10:02:40 +0000 Subject: [PATCH 04/57] bv_bounds tactic: change representation to intervals Code by myself and Nikolaj Bjorner --- src/tactic/bv/bv_bounds_tactic.cpp | 338 ++++++++++++++++------------- 1 file changed, 184 insertions(+), 154 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 10f050166..d1b96fa23 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -21,191 +21,221 @@ Author: #include "bv_decl_plugin.h" #include "ast_pp.h" +static rational uMaxInt(unsigned sz) { + return rational::power_of_two(sz) - rational::one(); +} + +namespace { + +struct interval { + // l < h: [l, h] + // l > h: [0, h] U [l, UMAX_INT] + rational l, h; + unsigned sz; + + explicit interval() : l(0), h(0), sz(0) {} + interval(const rational& l, const rational& h, unsigned sz) : l(l), h(h), sz(sz) { + SASSERT(invariant()); + } + + bool invariant() const { + return !l.is_neg() && !h.is_neg() && l <= uMaxInt(sz) && h <= uMaxInt(sz); + } + + bool is_full() const { return l.is_zero() && h == uMaxInt(sz); } + bool is_wrapped() const { return l > h; } + + bool implies(const interval& b) const { + if (b.is_full()) + return true; + if (is_full()) + return false; + + if (is_wrapped()) { + // l >= b.l >= b.h >= h + return b.is_wrapped() && h <= b.h && l >= b.l; + } else if (b.is_wrapped()) { + // b.l > b.h >= h >= l + // h >= l >= b.l > b.h + return h <= b.h || l >= b.l; + } else { + // + return l >= b.l && h <= b.h; + } + } + + /// return false if intersection is unsat + bool intersect(const interval& b, interval& result) const { + if (is_full() || (l == b.l && h == b.h)) { + result = b; + return true; + } + if (b.is_full()) { + result = *this; + return true; + } + + if (is_wrapped()) { + if (b.is_wrapped()) { + if (h > b.l) { + result = b; + } else if (b.h > l) { + result = *this; + } else { + result = interval(std::max(l, b.l), std::min(h, b.h), sz); + } + } else { + return b.intersect(*this, result); + } + } else if (b.is_wrapped()) { + // ... b.h ... l ... h ... b.l .. + if (h < b.l && l > b.h) { + return false; + } + // ... l ... b.l ... h ... + if (h >= b.l && l <= b.h) { + result = b; + } else if (h >= b.l) { + result = interval(b.l, h, sz); + } else { + // ... l .. b.h .. h .. b.l ... + SASSERT(l <= b.h); + result = interval(l, std::min(h, b.h), sz); + } + } else { + // 0 .. l.. l' ... h ... h' + result = interval(std::max(l, b.l), std::min(h, b.h), sz); + } + return true; + } + + /// return false if negation is empty + bool negate(interval& result) const { + if (is_full()) + return false; + if (l.is_zero()) { + result = interval(h + rational::one(), uMaxInt(sz), sz); + } else if (uMaxInt(sz) == h) { + result = interval(rational::zero(), l - rational::one(), sz); + } else { + result = interval(h + rational::one(), l - rational::one(), sz); + } + return true; + } +}; + +std::ostream& operator<<(std::ostream& o, const interval& I) { + o << "[" << I.l << ", " << I.h << "]"; + return o; +} + + class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { - ast_manager& m; - bv_util m_bv; - unsigned_vector m_scopes; - expr_ref_vector m_trail; - unsigned_vector m_trail_kind; - obj_map m_bound[4]; + ast_manager& m; + bv_util m_bv; + vector > m_scopes; + obj_map *m_bound; - obj_map & sle() { return m_bound[0]; } - obj_map & ule() { return m_bound[1]; } - obj_map & sge() { return m_bound[2]; } - obj_map & uge() { return m_bound[3]; } + bool is_bound(expr *e, expr*& v, interval& b) { + rational n; + expr *lhs, *rhs; + unsigned sz; - obj_map & bound(bool lo, bool s) { - if (lo) { - if (s) return sle(); return ule(); - } - else { - if (s) return sge(); return uge(); + if (m_bv.is_bv_ule(e, lhs, rhs)) { + if (m_bv.is_numeral(lhs, n, sz)) { // C ule x <=> x uge C + b = interval(n, uMaxInt(sz), sz); + v = rhs; + return true; + } + if (m_bv.is_numeral(rhs, n, sz)) { // x ule C + b = interval(rational::zero(), n, sz); + v = lhs; + return true; + } + } else if (m_bv.is_bv_sle(e, lhs, rhs)) { + if (m_bv.is_numeral(lhs, n, sz)) { // C sle x <=> x sge C + b = interval(n, rational::power_of_two(sz-1) - rational::one(), sz); + v = rhs; + return true; + } + if (m_bv.is_numeral(rhs, n, sz)) { // x sle C + b = interval(rational::power_of_two(sz-1), n, sz); + v = lhs; + return true; + } + } else if (m.is_eq(e, lhs, rhs)) { + if (m_bv.is_numeral(lhs, n, sz)) { + b = interval(n, n, sz); + v = rhs; + return true; + } + if (m_bv.is_numeral(rhs, n, sz)) { + b = interval(n, n, sz); + v = lhs; + return true; + } } + return false; } - void add_bound(bool lo, bool s, expr* t, rational const& n) { + bool add_bound(expr* t, const interval& b) { push(); - bound(lo, s).insert(t, n); - m_trail.push_back(t); - m_trail_kind.push_back(lo?(s?0:1):(s?2:3)); + interval& r = m_bound->insert_if_not_there2(t, b)->get_data().m_value; + return r.intersect(b, r); } - bool is_bound(expr* t, expr*& b, bool& lo, bool& sign, rational& n) { - expr* t1, *t2; - unsigned sz; - if (m_bv.is_bv_ule(t, t1, t2)) { - sign = false; - if (m_bv.is_numeral(t1, n, sz)) { - lo = true; - b = t2; - return true; - } - else if (m_bv.is_numeral(t2, n, sz)) { - lo = false; - b = t1; - return true; - } - } - else if (m_bv.is_bv_sle(t, t1, t2)) { - sign = true; - if (m_bv.is_numeral(t2, n, sz)) { - n = m_bv.norm(n, sz, true); - lo = false; - b = t1; - return true; - } - else if (m_bv.is_numeral(t1, n, sz)) { - n = m_bv.norm(n, sz, true); - lo = true; - b = t2; - return true; - } - } - return false; - } - - bool is_eq_const(expr* t, expr*& b, rational& n) { - expr* t1, *t2; - unsigned sz; - if (m.is_eq(t, t1, t2)) { - if (m_bv.is_numeral(t1, n, sz)) { - b = t2; - return true; - } - if (m_bv.is_numeral(t2, n, sz)) { - b = t1; - return true; - } - } - return false; - } - public: - bv_bounds_simplifier(ast_manager& m): m(m), m_bv(m), m_trail(m) {} + bv_bounds_simplifier(ast_manager& m) : m(m), m_bv(m) { + m_scopes.push_back(obj_map()); + m_bound = &m_scopes.back(); + } virtual ~bv_bounds_simplifier() {} virtual void assert_expr(expr * t, bool sign) { - bool lo, s; + interval b; expr* t1; - rational n; - if (!shared(t)) { - return; - } - if (is_bound(t, t1, lo, s, n)) { - if (sign) { - // !(n <= t1) <=> t1 <= n - 1 - // !(t1 <= n) <=> t1 >= n + 1 - if (lo) { - n -= rational::one(); - } - else { - n += rational::one(); - } - // check overflow conditions: - rational n1 = m_bv.norm(n, m_bv.get_bv_size(t1), s); - if (n1 == n) { - TRACE("bv", tout << "(not " << mk_pp(t, m) << "): " << mk_pp(t1, m) << (lo?" <= ":" >= ") << n << "\n";); - add_bound(!lo, s, t1, n); - } - } - else { - TRACE("bv", tout << mk_pp(t, m) << ": " << mk_pp(t1, m) << (lo?" >= ":" <= ") << n << "\n";); - add_bound(lo, s, t1, n); - } + if (is_bound(t, t1, b)) { + if (sign) + VERIFY(b.negate(b)); + + TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); + VERIFY(add_bound(t1, b)); } } virtual bool simplify(expr* t, expr_ref& result) { - bool lo, s; expr* t1; - rational b1, b2; + interval b, ctx, intr; result = 0; - if (is_bound(t, t1, lo, s, b1)) { - if (bound(!lo, s).find(t1, b2)) { - // t1 >= b1 > b2 >= t1 - if (lo && b1 > b2) { - result = m.mk_false(); - } - // t1 <= b1 < b2 <= t1 - else if (!lo && b1 < b2) { - result = m.mk_false(); - } - else if (b1 == b2) { - result = m.mk_eq(t1, m_bv.mk_numeral(b1, m.get_sort(t1))); - } - } - if (result == 0 && bound(lo, s).find(t1, b2)) { - // b1 <= b2 <= t1 - if (lo && b1 <= b2) { - result = m.mk_true(); - } - // b1 >= b2 >= t1 - else if (!lo && b1 >= b2) { - result = m.mk_true(); - } + if (!is_bound(t, t1, b)) + return false; + + if (m_bound->find(t1, ctx)) { + if (!b.intersect(ctx, intr)) { + result = m.mk_false(); + } else if (intr.l == intr.h) { + result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); + } else if (ctx.implies(b)) { + result = m.mk_true(); } } - if (is_eq_const(t, t1, b1)) { - if (bound(true, false).find(t1, b2) && b2 > b1) { - result = m.mk_false(); - } - else if (bound(false, false).find(t1, b2) && b2 < b1) { - result = m.mk_false(); - } - else { - if (bound(true, true).find(t1, b2)) { - b1 = m_bv.norm(b1, m_bv.get_bv_size(t1), true); - if (b2 > b1) result = m.mk_false(); - } - if (result == 0 && bound(false, true).find(t1, b2)) { - b1 = m_bv.norm(b1, m_bv.get_bv_size(t1), true); - if (b2 < b1) result = m.mk_false(); - } - } - } - CTRACE("bv", result != 0, tout << mk_pp(t, m) << " " << (lo?"lo":"hi") << " " << b1 << " " << b2 << ": " << result << "\n";); + + CTRACE("bv", result != 0, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); return result != 0; } virtual void push() { TRACE("bv", tout << "push\n";); - m_scopes.push_back(m_trail.size()); + m_scopes.push_back(*m_bound); + m_bound = &m_scopes.back(); } virtual void pop(unsigned num_scopes) { TRACE("bv", tout << "pop: " << num_scopes << "\n";); - if (num_scopes == 0) return; - unsigned old_sz = m_scopes[m_scopes.size() - num_scopes]; - for (unsigned i = old_sz; i < m_trail.size(); ++i) { - TRACE("bv", tout << "remove: " << mk_pp(m_trail[i].get(), m) << "\n";); - SASSERT(m_bound[m_trail_kind[i]].contains(m_trail[i].get())); - m_bound[m_trail_kind[i]].erase(m_trail[i].get()); - } - m_trail_kind.resize(old_sz); - m_trail.resize(old_sz); m_scopes.shrink(m_scopes.size() - num_scopes); + m_bound = &m_scopes.back(); } virtual simplifier * translate(ast_manager & m) { @@ -213,12 +243,12 @@ public: } virtual unsigned scope_level() const { - return m_scopes.size(); + return m_scopes.size() - 1; } - }; +} + tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p) { return clean(alloc(ctx_simplify_tactic, m, alloc(bv_bounds_simplifier, m), p)); } - From ac20d8bc11fa4bb53752394fa1691f140d14c4c1 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 17 Feb 2016 15:41:12 +0000 Subject: [PATCH 05/57] bv_bounds: fix intersection of wrapped intervals e.g., [117, 115] /\ [115, 113] -> [115, 113] --- src/tactic/bv/bv_bounds_tactic.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index d1b96fa23..12d578a77 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -45,6 +45,11 @@ struct interval { bool is_full() const { return l.is_zero() && h == uMaxInt(sz); } bool is_wrapped() const { return l > h; } + bool operator==(const interval& b) const { + SASSERT(sz == b.sz); + return l == b.l && h == b.h; + } + bool implies(const interval& b) const { if (b.is_full()) return true; @@ -77,9 +82,9 @@ struct interval { if (is_wrapped()) { if (b.is_wrapped()) { - if (h > b.l) { + if (h >= b.l) { result = b; - } else if (b.h > l) { + } else if (b.h >= l) { result = *this; } else { result = interval(std::max(l, b.l), std::min(h, b.h), sz); @@ -194,6 +199,10 @@ public: virtual ~bv_bounds_simplifier() {} virtual void assert_expr(expr * t, bool sign) { + while (m.is_not(t, t)) { + sign = !sign; + } + interval b; expr* t1; if (is_bound(t, t1, b)) { @@ -215,6 +224,8 @@ public: if (m_bound->find(t1, ctx)) { if (!b.intersect(ctx, intr)) { result = m.mk_false(); + } else if (intr == b) { + // no improvement in range; do nothing } else if (intr.l == intr.h) { result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); } else if (ctx.implies(b)) { From a4cfcd455052429867a64c7f01a939f33e9de270 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 17 Feb 2016 16:32:43 +0000 Subject: [PATCH 06/57] bv_bounds: fix bug in interval intersection for non-wrapping disjoint values --- src/tactic/bv/bv_bounds_tactic.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 12d578a77..96115bad6 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -108,6 +108,9 @@ struct interval { result = interval(l, std::min(h, b.h), sz); } } else { + if (l > b.h || h < b.l) + return false; + // 0 .. l.. l' ... h ... h' result = interval(std::max(l, b.l), std::min(h, b.h), sz); } @@ -183,12 +186,6 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { return false; } - bool add_bound(expr* t, const interval& b) { - push(); - interval& r = m_bound->insert_if_not_there2(t, b)->get_data().m_value; - return r.intersect(b, r); - } - public: bv_bounds_simplifier(ast_manager& m) : m(m), m_bv(m) { @@ -209,8 +206,10 @@ public: if (sign) VERIFY(b.negate(b)); + push(); TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); - VERIFY(add_bound(t1, b)); + interval& r = m_bound->insert_if_not_there2(t1, b)->get_data().m_value; + VERIFY(r.intersect(b, r)); } } From 8718c1c99f8946e43ce84e246f0000f909e52c6d Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Wed, 17 Feb 2016 19:14:02 +0000 Subject: [PATCH 07/57] bv_bounds: simplify negated expressions as well --- src/tactic/bv/bv_bounds_tactic.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 96115bad6..5a37d166a 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -49,6 +49,7 @@ struct interval { SASSERT(sz == b.sz); return l == b.l && h == b.h; } + bool operator!=(const interval& b) const { return !(*this == b); } bool implies(const interval& b) const { if (b.is_full()) @@ -182,6 +183,9 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { v = lhs; return true; } + } else if (m.is_not(e, lhs)) { + if (is_bound(lhs, v, b)) + return b.negate(b); } return false; } @@ -223,9 +227,7 @@ public: if (m_bound->find(t1, ctx)) { if (!b.intersect(ctx, intr)) { result = m.mk_false(); - } else if (intr == b) { - // no improvement in range; do nothing - } else if (intr.l == intr.h) { + } else if (intr.l == intr.h && intr != b) { result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); } else if (ctx.implies(b)) { result = m.mk_true(); From 67958efed25de9e39e3e2919331cbb289dd023d6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 17 Feb 2016 21:20:39 -0800 Subject: [PATCH 08/57] add fixed length heuristic Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 55 ++++++++++++++++++++++++++++++++++++++++++ src/smt/theory_seq.h | 16 ++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index e11ca806a..11bf2b678 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -233,6 +233,11 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>solve_nqs\n";); return FC_CONTINUE; } + if (fixed_length()) { + ++m_stats.m_fixed_length; + TRACE("seq", tout << ">>fixed_length\n";); + return FC_CONTINUE; + } if (branch_variable()) { ++m_stats.m_branch_variable; TRACE("seq", tout << ">>branch_variable\n";); @@ -430,6 +435,7 @@ lbool theory_seq::assume_equality(expr* l, expr* r) { return l_undef; } + bool theory_seq::propagate_length_coherence(expr* e) { expr_ref head(m), tail(m); rational lo, hi; @@ -509,6 +515,54 @@ bool theory_seq::check_length_coherence() { return false; } +bool theory_seq::fixed_length() { + obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); + bool found = false; + for (; it != end; ++it) { + if (fixed_length(*it)) { + found = true; + } + } + return found; +} + +bool theory_seq::fixed_length(expr* e) { + rational lo, hi; + if (!(is_var(e) && lower_bound(e, lo) && upper_bound(e, hi) && lo == hi && lo.is_unsigned() && lo.is_pos())) { + return false; + } + if (is_skolem(m_tail, e) || is_skolem(m_seq_first, e) || + is_skolem(m_indexof_left, e) || is_skolem(m_indexof_right, e) || + is_skolem(m_contains_left, e) || is_skolem(m_contains_right, e) || + m_fixed.contains(e)) { + return false; + } + + context& ctx = get_context(); + + m_trail_stack.push(insert_obj_trail(m_fixed, e)); + m_fixed.insert(e); + + + unsigned _lo = lo.get_unsigned(); + expr_ref seq(e, m), head(m), tail(m); + expr_ref_vector elems(m); + + for (unsigned j = 0; j < _lo; ++j) { + mk_decompose(seq, head, tail); + elems.push_back(head); + seq = tail; + } + seq = mk_concat(elems.size(), elems.c_ptr()); + TRACE("seq", tout << "Fixed: " << mk_pp(e, m) << " " << lo << "\n";); + add_axiom(~mk_eq(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true), false), mk_seq_eq(seq, e)); + if (!ctx.at_base_level()) { + m_trail_stack.push(push_replay(alloc(replay_fixed_length, m, e))); + } + return true; +} + + /* lit => s != "" */ @@ -1778,6 +1832,7 @@ void theory_seq::collect_statistics(::statistics & st) const { st.update("seq solve =", m_stats.m_solve_eqs); st.update("seq add axiom", m_stats.m_add_axiom); st.update("seq extensionality", m_stats.m_extensionality); + st.update("seq fixed length", m_stats.m_fixed_length); } void theory_seq::init_model(expr_ref_vector const& es) { diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index a28458e02..3aedac835 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -208,6 +208,17 @@ namespace smt { } }; + class replay_fixed_length : public apply { + 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) { + th.fixed_length(m_e); + m_e.reset(); + } + }; + class replay_axiom : public apply { expr_ref m_e; public: @@ -251,6 +262,7 @@ namespace smt { unsigned m_solve_eqs; unsigned m_add_axiom; unsigned m_extensionality; + unsigned m_fixed_length; }; ast_manager& m; dependency_manager m_dm; @@ -292,6 +304,8 @@ namespace smt { bool m_new_propagation; // new propagation to core re2automaton m_mk_aut; + obj_hashtable m_fixed; // string variables that are fixed length. + virtual final_check_status final_check_eh(); virtual bool internalize_atom(app* atom, bool) { return internalize_term(atom); } virtual bool internalize_term(app*); @@ -322,6 +336,8 @@ namespace smt { bool is_solved(); bool check_length_coherence(); bool check_length_coherence(expr* e); + bool fixed_length(); + bool fixed_length(expr* e); bool propagate_length_coherence(expr* e); bool check_extensionality(); From d32b4c71d12dbdbf26887de7e4018e5dcaa1ebf2 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 18 Feb 2016 15:53:11 +0000 Subject: [PATCH 09/57] [bv_bounds] introduce a tight bit in intervals to denote they are tight (over and under approx) use this to ensure certain transformations remain sound --- src/tactic/bv/bv_bounds_tactic.cpp | 72 +++++++++++++++++-------- src/tactic/core/ctx_simplify_tactic.cpp | 27 ++++++---- src/tactic/core/ctx_simplify_tactic.h | 2 +- 3 files changed, 69 insertions(+), 32 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 5a37d166a..d9703990c 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -32,14 +32,16 @@ struct interval { // l > h: [0, h] U [l, UMAX_INT] rational l, h; unsigned sz; + bool tight; - explicit interval() : l(0), h(0), sz(0) {} - interval(const rational& l, const rational& h, unsigned sz) : l(l), h(h), sz(sz) { + explicit interval() : l(0), h(0), sz(0), tight(false) {} + interval(const rational& l, const rational& h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { SASSERT(invariant()); } bool invariant() const { - return !l.is_neg() && !h.is_neg() && l <= uMaxInt(sz) && h <= uMaxInt(sz); + return !l.is_neg() && !h.is_neg() && l <= uMaxInt(sz) && h <= uMaxInt(sz) && + (!is_wrapped() || l != h+rational::one()); } bool is_full() const { return l.is_zero() && h == uMaxInt(sz); } @@ -113,13 +115,18 @@ struct interval { return false; // 0 .. l.. l' ... h ... h' - result = interval(std::max(l, b.l), std::min(h, b.h), sz); + result = interval(std::max(l, b.l), std::min(h, b.h), sz, tight && b.tight); } return true; } /// return false if negation is empty bool negate(interval& result) const { + if (!tight) { + result = interval(rational::zero(), uMaxInt(sz), true); + return true; + } + if (is_full()) return false; if (l.is_zero()) { @@ -152,40 +159,43 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { if (m_bv.is_bv_ule(e, lhs, rhs)) { if (m_bv.is_numeral(lhs, n, sz)) { // C ule x <=> x uge C - b = interval(n, uMaxInt(sz), sz); + if (m_bv.is_numeral(rhs)) + return false; + b = interval(n, uMaxInt(sz), sz, true); v = rhs; return true; } if (m_bv.is_numeral(rhs, n, sz)) { // x ule C - b = interval(rational::zero(), n, sz); + b = interval(rational::zero(), n, sz, true); v = lhs; return true; } } else if (m_bv.is_bv_sle(e, lhs, rhs)) { if (m_bv.is_numeral(lhs, n, sz)) { // C sle x <=> x sge C - b = interval(n, rational::power_of_two(sz-1) - rational::one(), sz); + if (m_bv.is_numeral(rhs)) + return false; + b = interval(n, rational::power_of_two(sz-1) - rational::one(), sz, true); v = rhs; return true; } if (m_bv.is_numeral(rhs, n, sz)) { // x sle C - b = interval(rational::power_of_two(sz-1), n, sz); + b = interval(rational::power_of_two(sz-1), n, sz, true); v = lhs; return true; } } else if (m.is_eq(e, lhs, rhs)) { if (m_bv.is_numeral(lhs, n, sz)) { - b = interval(n, n, sz); + if (m_bv.is_numeral(rhs)) + return false; + b = interval(n, n, sz, true); v = rhs; return true; } if (m_bv.is_numeral(rhs, n, sz)) { - b = interval(n, n, sz); + b = interval(n, n, sz, true); v = lhs; return true; } - } else if (m.is_not(e, lhs)) { - if (is_bound(lhs, v, b)) - return b.negate(b); } return false; } @@ -199,7 +209,7 @@ public: virtual ~bv_bounds_simplifier() {} - virtual void assert_expr(expr * t, bool sign) { + virtual bool assert_expr(expr * t, bool sign) { while (m.is_not(t, t)) { sign = !sign; } @@ -207,34 +217,54 @@ public: interval b; expr* t1; if (is_bound(t, t1, b)) { + SASSERT(!m_bv.is_numeral(t1)); if (sign) VERIFY(b.negate(b)); push(); TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); interval& r = m_bound->insert_if_not_there2(t1, b)->get_data().m_value; - VERIFY(r.intersect(b, r)); + return r.intersect(b, r); } + return true; } virtual bool simplify(expr* t, expr_ref& result) { expr* t1; interval b, ctx, intr; result = 0; + bool sign = false; + + while (m.is_not(t, t)) { + sign = !sign; + } + if (!is_bound(t, t1, b)) return false; - if (m_bound->find(t1, ctx)) { - if (!b.intersect(ctx, intr)) { + if (sign && b.tight) { + sign = false; + if (!b.negate(b)) { result = m.mk_false(); - } else if (intr.l == intr.h && intr != b) { - result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); - } else if (ctx.implies(b)) { - result = m.mk_true(); + return true; } } + if (m_bound->find(t1, ctx)) { + if (ctx.implies(b)) { + result = m.mk_true(); + } else if (!b.intersect(ctx, intr)) { + result = m.mk_false(); + } else if (intr.l == intr.h) { + result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); + } + } else if (b.is_full() && b.tight) { + result = m.mk_true(); + } + CTRACE("bv", result != 0, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); + if (sign && result != 0) + result = m.mk_not(result); return result != 0; } diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 95be200a8..01937eb18 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -34,7 +34,7 @@ class ctx_propagate_assertions : public ctx_simplify_tactic::simplifier { public: ctx_propagate_assertions(ast_manager& m); virtual ~ctx_propagate_assertions() {} - virtual void assert_expr(expr * t, bool sign); + virtual bool assert_expr(expr * t, bool sign); virtual bool simplify(expr* t, expr_ref& result); virtual void push(); virtual void pop(unsigned num_scopes); @@ -45,7 +45,7 @@ public: ctx_propagate_assertions::ctx_propagate_assertions(ast_manager& m): m(m), m_trail(m) {} -void ctx_propagate_assertions::assert_expr(expr * t, bool sign) { +bool ctx_propagate_assertions::assert_expr(expr * t, bool sign) { expr * p = t; while (m.is_not(t, t)) { @@ -64,6 +64,7 @@ void ctx_propagate_assertions::assert_expr(expr * t, bool sign) { else if (m.is_value(lhs)) assert_eq_val(rhs, to_app(lhs), mk_scope); } + return true; } void ctx_propagate_assertions::assert_eq_val(expr * t, app * val, bool mk_scope) { @@ -307,8 +308,8 @@ struct ctx_simplify_tactic::imp { CASSERT("ctx_simplify_tactic", check_cache()); } - void assert_expr(expr * t, bool sign) { - m_simp->assert_expr(t, sign); + bool assert_expr(expr * t, bool sign) { + return m_simp->assert_expr(t, sign); } bool is_cached(expr * t, expr_ref & r) { @@ -370,6 +371,9 @@ struct ctx_simplify_tactic::imp { simplify(arg, new_arg); if (new_arg != arg) modified = true; + if (i < num_args - 1 && !m.is_true(new_arg) && !m.is_false(new_arg) && !assert_expr(new_arg, OR)) + new_arg = m.mk_false(); + if ((OR && m.is_false(new_arg)) || (!OR && m.is_true(new_arg))) { modified = true; @@ -383,8 +387,6 @@ struct ctx_simplify_tactic::imp { return; } new_args.push_back(new_arg); - if (i < num_args - 1) - assert_expr(new_arg, OR); } pop(scope_level() - old_lvl); @@ -398,6 +400,9 @@ struct ctx_simplify_tactic::imp { simplify(arg, new_arg); if (new_arg != arg) modified = true; + if (i > 0 && !m.is_true(new_arg) && !m.is_false(new_arg) && !assert_expr(new_arg, OR)) + new_arg = m.mk_false(); + if ((OR && m.is_false(new_arg)) || (!OR && m.is_true(new_arg))) { modified = true; @@ -411,8 +416,6 @@ struct ctx_simplify_tactic::imp { return; } new_new_args.push_back(new_arg); - if (i > 0) - assert_expr(new_arg, OR); } pop(scope_level() - old_lvl); @@ -448,10 +451,14 @@ struct ctx_simplify_tactic::imp { else { expr_ref new_t(m); expr_ref new_e(m); - assert_expr(new_c, false); + if (!assert_expr(new_c, false)) { + simplify(e, r); + cache(ite, r); + return; + } simplify(t, new_t); pop(scope_level() - old_lvl); - assert_expr(new_c, true); + VERIFY(assert_expr(new_c, true)); simplify(e, new_e); pop(scope_level() - old_lvl); if (c == new_c && t == new_t && e == new_e) { diff --git a/src/tactic/core/ctx_simplify_tactic.h b/src/tactic/core/ctx_simplify_tactic.h index fccb25d3b..3c95ca554 100644 --- a/src/tactic/core/ctx_simplify_tactic.h +++ b/src/tactic/core/ctx_simplify_tactic.h @@ -28,7 +28,7 @@ public: goal_num_occurs* m_occs; public: virtual ~simplifier() {} - virtual void assert_expr(expr * t, bool sign) = 0; + virtual bool assert_expr(expr * t, bool sign) = 0; virtual bool simplify(expr* t, expr_ref& result) = 0; virtual void push() = 0; virtual void pop(unsigned num_scopes) = 0; From 73da4dda076f026871371290270100cebdd12182 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 18 Feb 2016 17:45:55 +0000 Subject: [PATCH 10/57] add a bv rewrite pattern: (bvsle (- x (srem x c1)) c2) -> (bvsle x (+ c1 c2 - 1)) --- src/ast/rewriter/bv_rewriter.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index fc0e5a807..be9b5f375 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -320,6 +320,21 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref } } + expr* a1, *a2, *a3, *a4, *a5, *a6; + // (bvsle (- x (srem x c1)) c2) -> (bvsle x (+ c1 c2 - 1)) + // (bvsle (+ x (* -1 (srem_i x c1))) c2) + // pre: (and (> c1 0) (> c2 0) (= c2 % c1 0) (<= (+ c1 c2 -1) max_int)) + if (is_signed && is_num2 && m_util.is_bv_add(a, a1, a2) && + m_util.is_bv_mul(a2, a3, a4) && is_numeral(a3, r1, sz) && + m_util.norm(r1, sz, is_signed).is_minus_one() && + m_util.is_bv_sremi(a4, a5, a6) && is_numeral(a6, r1, sz) && + (r1 = m_util.norm(r1, sz, is_signed), r1.is_pos()) && + r2.is_pos() && + (r2 % r1).is_zero() && r1 + r2 - rational::one() < rational::power_of_two(sz-1)) { + result = m_util.mk_sle(a1, m_util.mk_numeral(r1 + r2 - rational::one(), sz)); + return BR_REWRITE2; + } + #if 0 if (!is_signed && m_util.is_concat(b) && to_app(b)->get_num_args() == 2 && m_util.is_zero(to_app(b)->get_arg(0))) { // From 5962ca2a627bc81c0518cbde7bea7851182f2f4c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 18 Feb 2016 12:08:35 -0800 Subject: [PATCH 11/57] seq Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/bv_rewriter.cpp | 11 +++++++ src/smt/theory_seq.cpp | 55 ++++++++++++++++++++++++++++++++ src/smt/theory_seq.h | 16 ++++++++++ 3 files changed, 82 insertions(+) diff --git a/src/ast/rewriter/bv_rewriter.cpp b/src/ast/rewriter/bv_rewriter.cpp index fc0e5a807..29b892029 100644 --- a/src/ast/rewriter/bv_rewriter.cpp +++ b/src/ast/rewriter/bv_rewriter.cpp @@ -320,6 +320,17 @@ br_status bv_rewriter::mk_leq_core(bool is_signed, expr * a, expr * b, expr_ref } } + expr* a1, *a2, *a3, *a4, *a5, *a6; + // (bvsle (- x (rem x c1)) c2) -> (bvsle x (+ c1 c2 - 1)) + if (is_signed && is_num2 && m_util.is_bv_add(a, a1, a2) && + m_util.is_bv_mul(a2, a3, a4) && is_numeral(a3, r3, sz) && + (r3 = m_util.norm(r3, sz, is_signed), r3.is_minus_one()) && + m_util.is_bv_sremi(a4, a5, a6) && is_numeral(a6, r3, sz) && + a1 == a5 /* && r1 + r3 - rational::one() < power(rational(2), sz) */) { + result = m_util.mk_sle(a1, m_util.mk_numeral(r3 + r1 - rational::one(), sz)); + return BR_REWRITE2; + } + #if 0 if (!is_signed && m_util.is_concat(b) && to_app(b)->get_num_args() == 2 && m_util.is_zero(to_app(b)->get_arg(0))) { // diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index e11ca806a..2fab37969 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -233,6 +233,11 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>solve_nqs\n";); return FC_CONTINUE; } + if (fixed_length()) { + ++m_stats.m_fixed_length; + TRACE("seq", tout << ">>fixed_length\n";); + return FC_CONTINUE; + } if (branch_variable()) { ++m_stats.m_branch_variable; TRACE("seq", tout << ">>branch_variable\n";); @@ -430,6 +435,7 @@ lbool theory_seq::assume_equality(expr* l, expr* r) { return l_undef; } + bool theory_seq::propagate_length_coherence(expr* e) { expr_ref head(m), tail(m); rational lo, hi; @@ -509,6 +515,54 @@ bool theory_seq::check_length_coherence() { return false; } +bool theory_seq::fixed_length() { + obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); + for (; it != end; ++it) { + if (fixed_length(*it)) { + return true; + } + } + return false; +} + +bool theory_seq::fixed_length(expr* e) { + rational lo, hi; + if (!(is_var(e) && lower_bound(e, lo) && upper_bound(e, hi) && lo == hi && lo.is_unsigned() && lo.is_pos())) { + return false; + } + if (m_fixed.contains(e)) { + return false; + } + + context& ctx = get_context(); + + m_trail_stack.push(insert_obj_trail(m_fixed, e)); + m_fixed.insert(e); + + if (is_skolem(m_tail, e) || is_skolem(m_pre, e) || is_skolem(m_post, e) || is_skolem(m_seq_first, e)) { + return false; + } + + unsigned _lo = lo.get_unsigned(); + expr_ref seq(e, m), head(m), tail(m); + expr_ref_vector elems(m); + + for (unsigned j = 0; j < _lo; ++j) { + mk_decompose(seq, head, tail); + elems.push_back(head); + seq = tail; + } + seq = mk_concat(elems.size(), elems.c_ptr()); + std::cout << "Fixed " << mk_pp(e, m) << " " << lo << " " << get_context().get_scope_level() << "\n"; + TRACE("seq", tout << "Fixed: " << mk_pp(e, m) << " " << lo << "\n";); + add_axiom(~mk_eq(m_util.str.mk_length(e), m_autil.mk_numeral(lo, true), false), mk_seq_eq(seq, e)); + if (!ctx.at_base_level()) { + m_trail_stack.push(push_replay(alloc(replay_fixed_length, m, e))); + } + return true; +} + + /* lit => s != "" */ @@ -1778,6 +1832,7 @@ void theory_seq::collect_statistics(::statistics & st) const { st.update("seq solve =", m_stats.m_solve_eqs); st.update("seq add axiom", m_stats.m_add_axiom); st.update("seq extensionality", m_stats.m_extensionality); + st.update("seq fixed length", m_stats.m_fixed_length); } void theory_seq::init_model(expr_ref_vector const& es) { diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index a28458e02..3aedac835 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -208,6 +208,17 @@ namespace smt { } }; + class replay_fixed_length : public apply { + 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) { + th.fixed_length(m_e); + m_e.reset(); + } + }; + class replay_axiom : public apply { expr_ref m_e; public: @@ -251,6 +262,7 @@ namespace smt { unsigned m_solve_eqs; unsigned m_add_axiom; unsigned m_extensionality; + unsigned m_fixed_length; }; ast_manager& m; dependency_manager m_dm; @@ -292,6 +304,8 @@ namespace smt { bool m_new_propagation; // new propagation to core re2automaton m_mk_aut; + obj_hashtable m_fixed; // string variables that are fixed length. + virtual final_check_status final_check_eh(); virtual bool internalize_atom(app* atom, bool) { return internalize_term(atom); } virtual bool internalize_term(app*); @@ -322,6 +336,8 @@ namespace smt { bool is_solved(); bool check_length_coherence(); bool check_length_coherence(expr* e); + bool fixed_length(); + bool fixed_length(expr* e); bool propagate_length_coherence(expr* e); bool check_extensionality(); From a073b37ce30b0e2f81161de37d87b7de6f2cc6a8 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 18 Feb 2016 18:10:16 -0800 Subject: [PATCH 12/57] fix bugs in seq solver: add relevancy and axiom Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 11bf2b678..a576685cc 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2678,6 +2678,7 @@ literal theory_seq::mk_seq_eq(expr* a, expr* b) { } literal theory_seq::mk_eq_empty(expr* _e) { + context& ctx = get_context(); expr_ref e(_e, m); SASSERT(m_util.is_seq(e)); expr_ref emp(m); @@ -2697,9 +2698,9 @@ literal theory_seq::mk_eq_empty(expr* _e) { } emp = m_util.str.mk_empty(m.get_sort(e)); - literal lit = mk_eq(e, emp, false); - get_context().force_phase(lit); + ctx.force_phase(lit); + ctx.mark_as_relevant(lit); return lit; } @@ -3385,16 +3386,15 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { } expr_ref head1(m), tail1(m), head2(m), tail2(m), conc(m); - TRACE("seq", tout << mk_pp(e, m) << "\n";); - literal e2_is_emp = mk_eq_empty(e2); switch (ctx.get_assignment(e2_is_emp)) { case l_true: - TRACE("seq", tout << mk_pp(e2, m) << " = empty\n";); + TRACE("seq", tout << mk_pp(e, m) << ": " << mk_pp(e2, m) << " = empty\n"; + ctx.display_literals_verbose(tout, 1, &e2_is_emp); tout << "\n"; ); return false; // done case l_undef: // ctx.force_phase(e2_is_emp); - TRACE("seq", tout << mk_pp(e2, m) << " ~ empty\n";); + TRACE("seq", tout << mk_pp(e, m) << ": " << mk_pp(e2, m) << " ~ empty\n";); return true; // retry default: break; @@ -3407,10 +3407,11 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { literal e1_is_emp = mk_eq_empty(e1); switch (ctx.get_assignment(e1_is_emp)) { case l_true: - TRACE("seq", tout << mk_pp(e1, m) << " = empty\n";); + TRACE("seq", tout << mk_pp(e, m) << ": " << mk_pp(e1, m) << " != empty\n";); + add_axiom(ctx.get_literal(e), ~e1_is_emp); return false; // done case l_undef: - TRACE("seq", tout << mk_pp(e1, m) << " ~ empty\n";); + TRACE("seq", tout << mk_pp(e, m) << ": " << mk_pp(e1, m) << " ~ empty\n";); return true; // retry default: break; @@ -3426,11 +3427,11 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { case l_true: break; case l_false: - TRACE("seq", tout << head1 << " = " << head2 << "\n";); + TRACE("seq", tout << mk_pp(e, m) << ": " << head1 << " != " << head2 << "\n";); return false; case l_undef: ctx.force_phase(~lit); - TRACE("seq", tout << head1 << " ~ " << head2 << "\n";); + TRACE("seq", tout << mk_pp(e, m) << ": " << head1 << " ~ " << head2 << "\n";); return true; } change = true; @@ -3439,7 +3440,7 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { 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))); - TRACE("seq", tout << "saturate: " << tail1 << " = " << tail2 << "\n";); + TRACE("seq", tout << mk_pp(e, m) << " saturate: " << tail1 << " = " << tail2 << "\n";); return false; } From 121b3b60f3dba70d6a5189233df764f422d9e1d7 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 19 Feb 2016 09:42:42 +0000 Subject: [PATCH 13/57] bv_bounds/ctx_simplify: improve handling of (ite x a b) where (not x) is proved to be false --- src/tactic/bv/bv_bounds_tactic.cpp | 5 +++++ src/tactic/core/ctx_simplify_tactic.cpp | 6 +++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index d9703990c..052345df1 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -36,6 +36,11 @@ struct interval { explicit interval() : l(0), h(0), sz(0), tight(false) {} interval(const rational& l, const rational& h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { + // canonicalize full set + if (is_wrapped() && l == h + rational::one()) { + this->l = rational::zero(); + this->h = uMaxInt(sz); + } SASSERT(invariant()); } diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 01937eb18..f0e0b0059 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -458,7 +458,11 @@ struct ctx_simplify_tactic::imp { } simplify(t, new_t); pop(scope_level() - old_lvl); - VERIFY(assert_expr(new_c, true)); + if (!assert_expr(new_c, true)) { + r = new_t; + cache(ite, r); + return; + } simplify(e, new_e); pop(scope_level() - old_lvl); if (c == new_c && t == new_t && e == new_e) { From c618838ed9e4940c1de642a1b09e2eeb97effc1e Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 19 Feb 2016 11:06:22 +0000 Subject: [PATCH 14/57] bv_bounds: fix crash in push() when realloc happened --- src/tactic/bv/bv_bounds_tactic.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 052345df1..317c17f12 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -152,10 +152,12 @@ std::ostream& operator<<(std::ostream& o, const interval& I) { class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { - ast_manager& m; - bv_util m_bv; - vector > m_scopes; - obj_map *m_bound; + typedef obj_map map; + + ast_manager& m; + bv_util m_bv; + vector m_scopes; + map *m_bound; bool is_bound(expr *e, expr*& v, interval& b) { rational n; @@ -208,7 +210,7 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { public: bv_bounds_simplifier(ast_manager& m) : m(m), m_bv(m) { - m_scopes.push_back(obj_map()); + m_scopes.push_back(map()); m_bound = &m_scopes.back(); } @@ -275,8 +277,11 @@ public: virtual void push() { TRACE("bv", tout << "push\n";); - m_scopes.push_back(*m_bound); + unsigned sz = m_scopes.size(); + m_scopes.resize(sz + 1); m_bound = &m_scopes.back(); + m_bound->~map(); + new (m_bound) map(m_scopes[sz - 1]); } virtual void pop(unsigned num_scopes) { From 7d3af70a6320c30d43e8785b71b79a3cd664bdb4 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 19 Feb 2016 11:08:01 +0000 Subject: [PATCH 15/57] ctx-simplify: fix mem leak of simplifier --- src/tactic/core/ctx_simplify_tactic.cpp | 11 +++++++---- src/tactic/core/ctx_simplify_tactic.h | 5 +---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index f0e0b0059..3bcadb03d 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -180,6 +180,7 @@ struct ctx_simplify_tactic::imp { pop(scope_level()); SASSERT(scope_level() == 0); restore_cache(0); + dealloc(m_simp); DEBUG_CODE({ for (unsigned i = 0; i < m_cache.size(); i++) { CTRACE("ctx_simplify_tactic_bug", m_cache[i].m_from, @@ -572,13 +573,15 @@ struct ctx_simplify_tactic::imp { ctx_simplify_tactic::ctx_simplify_tactic(ast_manager & m, simplifier* simp, params_ref const & p): m_imp(alloc(imp, m, simp, p)), - m_params(p), - m_simp(simp) { + m_params(p) { +} + +tactic * ctx_simplify_tactic::translate(ast_manager & m) { + return alloc(ctx_simplify_tactic, m, m_imp->m_simp->translate(m), m_params); } ctx_simplify_tactic::~ctx_simplify_tactic() { dealloc(m_imp); - dealloc(m_simp); } void ctx_simplify_tactic::updt_params(params_ref const & p) { @@ -606,7 +609,7 @@ void ctx_simplify_tactic::operator()(goal_ref const & in, void ctx_simplify_tactic::cleanup() { ast_manager & m = m_imp->m; - imp * d = alloc(imp, m, m_simp->translate(m), m_params); + imp * d = alloc(imp, m, m_imp->m_simp->translate(m), m_params); std::swap(d, m_imp); dealloc(d); } diff --git a/src/tactic/core/ctx_simplify_tactic.h b/src/tactic/core/ctx_simplify_tactic.h index 3c95ca554..5689a64b7 100644 --- a/src/tactic/core/ctx_simplify_tactic.h +++ b/src/tactic/core/ctx_simplify_tactic.h @@ -42,13 +42,10 @@ protected: struct imp; imp * m_imp; params_ref m_params; - simplifier* m_simp; public: ctx_simplify_tactic(ast_manager & m, simplifier* simp, params_ref const & p = params_ref()); - virtual tactic * translate(ast_manager & m) { - return alloc(ctx_simplify_tactic, m, m_simp->translate(m), m_params); - } + virtual tactic * translate(ast_manager & m); virtual ~ctx_simplify_tactic(); From d4f41c04202e21fcf40e86b614ab0155fb096550 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 20 Feb 2016 10:13:24 -0800 Subject: [PATCH 16/57] add goal context for simplifier, disable equality creation Signed-off-by: Nikolaj Bjorner --- src/tactic/bv/bv_bounds_tactic.cpp | 2 +- src/tactic/core/ctx_simplify_tactic.cpp | 55 +++++++++++++++++++------ 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 317c17f12..381802ac5 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -262,7 +262,7 @@ public: result = m.mk_true(); } else if (!b.intersect(ctx, intr)) { result = m.mk_false(); - } else if (intr.l == intr.h) { + } else if (false && intr.l == intr.h) { result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); } } else if (b.is_full() && b.tight) { diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 3bcadb03d..2847e403f 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -529,6 +529,37 @@ struct ctx_simplify_tactic::imp { return sz; } + void process_goal(goal & g) { + SASSERT(scope_level() == 0); + // go forwards + unsigned old_lvl = scope_level(); + unsigned sz = g.size(); + expr_ref r(m); + for (unsigned i = 0; !g.inconsistent() && i < sz; ++i) { + m_depth = 0; + simplify(g.form(i), r); + 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)); + } + pop(scope_level() - old_lvl); + + // go backwards + sz = g.size(); + for (unsigned i = sz; !g.inconsistent() && i > 0; ) { + m_depth = 0; + --i; + simplify(g.form(i), r); + 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)); + } + pop(scope_level() - old_lvl); + SASSERT(scope_level() == 0); + } + void process(expr * s, expr_ref & r) { TRACE("ctx_simplify_tactic", tout << "simplifying:\n" << mk_ismt2_pp(s, m) << "\n";); SASSERT(scope_level() == 0); @@ -546,24 +577,22 @@ struct ctx_simplify_tactic::imp { void operator()(goal & g) { SASSERT(g.is_well_sorted()); - bool proofs_enabled = g.proofs_enabled(); m_occs.reset(); m_occs(g); m_num_steps = 0; - expr_ref r(m); - proof * new_pr = 0; tactic_report report("ctx-simplify", g); - unsigned sz = g.size(); - for (unsigned i = 0; i < sz; i++) { - if (g.inconsistent()) - return; - expr * t = g.form(i); - process(t, r); - if (proofs_enabled) { - proof * pr = g.pr(i); - new_pr = m.mk_modus_ponens(pr, m.mk_rewrite_star(t, r, 0, 0)); // TODO :-) + if (g.proofs_enabled()) { + expr_ref r(m); + unsigned sz = g.size(); + 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 :-) + g.update(i, r, new_pr, g.dep(i)); } - g.update(i, r, new_pr, g.dep(i)); + } + else { + process_goal(g); } IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "(ctx-simplify :num-steps " << m_num_steps << ")\n";); SASSERT(g.is_well_sorted()); From 8c538fd3f0d163e62c6224edbe6e8898ba17745e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 21 Feb 2016 10:31:13 -0800 Subject: [PATCH 17/57] setting partial equivalence priority lower so that it doesn't intefere with inlining (partial fix to the fact that inlining will remove such implicit relations). Using short-circuit negation in qe to avoid redundant double negations in intermediary results Signed-off-by: Nikolaj Bjorner --- src/muz/rel/dl_mk_partial_equiv.h | 2 +- src/muz/rel/rel_context.cpp | 2 +- src/qe/qe.cpp | 23 +++++++++-------------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/muz/rel/dl_mk_partial_equiv.h b/src/muz/rel/dl_mk_partial_equiv.h index f982952f1..be871f53e 100644 --- a/src/muz/rel/dl_mk_partial_equiv.h +++ b/src/muz/rel/dl_mk_partial_equiv.h @@ -30,7 +30,7 @@ namespace datalog { ast_manager & m; context & m_context; public: - mk_partial_equivalence_transformer(context & ctx, unsigned priority=45000) + mk_partial_equivalence_transformer(context & ctx, unsigned priority=30000) : plugin(priority), m(ctx.get_manager()), m_context(ctx) {} diff --git a/src/muz/rel/rel_context.cpp b/src/muz/rel/rel_context.cpp index 68bc25c27..6d167dfec 100644 --- a/src/muz/rel/rel_context.cpp +++ b/src/muz/rel/rel_context.cpp @@ -307,8 +307,8 @@ namespace datalog { if (m_context.similarity_compressor()) { transf.register_plugin(alloc(mk_similarity_compressor, m_context)); } - transf.register_plugin(alloc(mk_partial_equivalence_transformer, m_context)); transf.register_plugin(alloc(mk_rule_inliner, m_context)); + transf.register_plugin(alloc(mk_partial_equivalence_transformer, m_context)); transf.register_plugin(alloc(mk_interp_tail_simplifier, m_context)); transf.register_plugin(alloc(mk_separate_negated_tails, m_context)); diff --git a/src/qe/qe.cpp b/src/qe/qe.cpp index 150198bab..585944263 100644 --- a/src/qe/qe.cpp +++ b/src/qe/qe.cpp @@ -596,7 +596,7 @@ namespace qe { p = m_pols.back(); if (!m_is_relevant(e)) { pop(); - insert(e, p, p?e:m.mk_not(e)); + insert(e, p, p?e:mk_not(m, e)); continue; } if (!is_app(e)) { @@ -634,7 +634,7 @@ namespace qe { } else { pop(); - insert(e, p, p?e:m.mk_not(e)); + insert(e, p, p?e:mk_not(m, e)); } } @@ -1208,7 +1208,7 @@ namespace qe { } bool_rewriter(m).mk_or(fmls.size(), fmls.c_ptr(), fml); - fml = m.mk_not(m.mk_iff(q, fml)); + fml = mk_not(m, m.mk_iff(q, fml)); ast_smt_pp pp(m); out << "; eliminate " << mk_pp(m_var, m) << "\n"; out << "(push)\n"; @@ -1303,12 +1303,7 @@ namespace qe { } } TRACE("qe_verbose", tout << "No plugin for " << mk_ismt2_pp(e, m) << "\n";); - if (p || m.is_not(e, e)) { - result = e; - } - else { - result = m.mk_not(e); - } + result = p?e:mk_not(m, e); } void i_solver_context::mk_atom_fn::operator()(expr* e, bool p, expr_ref& result) { @@ -1436,7 +1431,7 @@ namespace qe { m_fml = f; f = m_subfml; m_solver.assert_expr(f); - } + } m_root.init(f); TRACE("qe", for (unsigned i = 0; i < num_vars; ++i) tout << mk_ismt2_pp(vars[i], m) << "\n"; @@ -1591,7 +1586,7 @@ namespace qe { } m_literals.reset(); while (node) { - m_literals.push_back(m.mk_not(node->assignment())); + m_literals.push_back(mk_not(m, node->assignment())); node = node->parent(); } add_literal(l1); @@ -1865,7 +1860,7 @@ namespace qe { // app* mk_eq_value(app* b, rational const& vl) { if (m.is_bool(b)) { - if (vl.is_zero()) return m.mk_not(b); + if (vl.is_zero()) return to_app(mk_not(m, b)); if (vl.is_one()) return b; UNREACHABLE(); } @@ -2604,12 +2599,12 @@ namespace qe { TRACE("qe", tout << "variables extracted" << mk_pp(result, m) << "\n";); if (old_q->is_forall()) { - result = m.mk_not(result); + result = mk_not(m, result); } m_ctx.solve(result, vars); if (old_q->is_forall()) { expr* e = 0; - result = m.is_not(result, e)?e:m.mk_not(result); + result = m.is_not(result, e)?e:mk_not(m, result); } var_shifter shift(m); shift(result, vars.size(), result); From 63c138c08e8cec31d078f680096825d5c852d051 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 21 Feb 2016 11:16:13 -0800 Subject: [PATCH 18/57] add option to enable equality propagation Signed-off-by: Nikolaj Bjorner --- src/tactic/bv/bv_bounds_tactic.cpp | 20 ++++++++++++++++---- src/tactic/core/ctx_simplify_tactic.cpp | 2 ++ src/tactic/core/ctx_simplify_tactic.h | 1 + 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 381802ac5..0a4411847 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -155,6 +155,8 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { typedef obj_map map; ast_manager& m; + params_ref m_params; + bool m_propagate_eq; bv_util m_bv; vector m_scopes; map *m_bound; @@ -209,9 +211,19 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { public: - bv_bounds_simplifier(ast_manager& m) : m(m), m_bv(m) { + bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { m_scopes.push_back(map()); m_bound = &m_scopes.back(); + updt_params(p); + } + + + virtual void updt_params(params_ref const & p) { + m_propagate_eq = p.get_bool("propagate_eq", false); + } + + static void get_param_descrs(param_descrs& r) { + r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); } virtual ~bv_bounds_simplifier() {} @@ -262,7 +274,7 @@ public: result = m.mk_true(); } else if (!b.intersect(ctx, intr)) { result = m.mk_false(); - } else if (false && intr.l == intr.h) { + } else if (m_propagate_eq && intr.l == intr.h) { result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); } } else if (b.is_full() && b.tight) { @@ -291,7 +303,7 @@ public: } virtual simplifier * translate(ast_manager & m) { - return alloc(bv_bounds_simplifier, m); + return alloc(bv_bounds_simplifier, m, m_params); } virtual unsigned scope_level() const { @@ -302,5 +314,5 @@ public: } tactic * mk_bv_bounds_tactic(ast_manager & m, params_ref const & p) { - return clean(alloc(ctx_simplify_tactic, m, alloc(bv_bounds_simplifier, m), p)); + return clean(alloc(ctx_simplify_tactic, m, alloc(bv_bounds_simplifier, m, p), p)); } diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 2847e403f..6d557a3ac 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -198,6 +198,7 @@ struct ctx_simplify_tactic::imp { m_max_steps = p.get_uint("max_steps", UINT_MAX); m_max_depth = p.get_uint("max_depth", 1024); m_bail_on_blowup = p.get_bool("bail_on_blowup", false); + m_simp->updt_params(p); } void checkpoint() { @@ -622,6 +623,7 @@ void ctx_simplify_tactic::get_param_descrs(param_descrs & r) { insert_max_memory(r); insert_max_steps(r); r.insert("max_depth", CPK_UINT, "(default: 1024) maximum term depth."); + r.insert("propagate_eq", CPK_BOOL, "(default: false) enable equality propagation from bounds."); } void ctx_simplify_tactic::operator()(goal_ref const & in, diff --git a/src/tactic/core/ctx_simplify_tactic.h b/src/tactic/core/ctx_simplify_tactic.h index 5689a64b7..34258362b 100644 --- a/src/tactic/core/ctx_simplify_tactic.h +++ b/src/tactic/core/ctx_simplify_tactic.h @@ -34,6 +34,7 @@ public: virtual void pop(unsigned num_scopes) = 0; virtual simplifier * translate(ast_manager & m) = 0; virtual unsigned scope_level() const = 0; + virtual void updt_params(params_ref const & p) {} void set_occs(goal_num_occurs& occs) { m_occs = &occs; }; bool shared(expr* t) const; }; From 12458b1a8404398e6d3aeb08085cb3065006a939 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 22 Feb 2016 10:22:56 +0000 Subject: [PATCH 19/57] remove dead code in qfufbv --- src/tactic/smtlogics/qfufbv_tactic.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/tactic/smtlogics/qfufbv_tactic.cpp b/src/tactic/smtlogics/qfufbv_tactic.cpp index f943b68c9..421a76cc7 100644 --- a/src/tactic/smtlogics/qfufbv_tactic.cpp +++ b/src/tactic/smtlogics/qfufbv_tactic.cpp @@ -124,7 +124,7 @@ private: }; -tactic * mk_qfufbv_preamble1(ast_manager & m, params_ref const & p) { +static tactic * mk_qfufbv_preamble1(ast_manager & m, params_ref const & p) { params_ref simp2_p = p; simp2_p.set_bool("pull_cheap_ite", true); simp2_p.set_bool("push_ite_bv", false); @@ -150,11 +150,7 @@ tactic * mk_qfufbv_preamble1(ast_manager & m, params_ref const & p) { ); } -tactic * mk_qfufbv_preamble(ast_manager & m, params_ref const & p) { - params_ref main_p; - main_p.set_bool("elim_and", true); - main_p.set_bool("blast_distinct", true); - +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), From 64888b6b1956500f3261cbf8e154433cec156407 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 23 Feb 2016 10:37:01 +0000 Subject: [PATCH 20/57] ctx_simplify: fix bug in simplification of or exprs this triggered when the or covers the whole space -> true --- src/tactic/core/ctx_simplify_tactic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index 6d557a3ac..e07f7417d 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -374,7 +374,7 @@ struct ctx_simplify_tactic::imp { if (new_arg != arg) modified = true; if (i < num_args - 1 && !m.is_true(new_arg) && !m.is_false(new_arg) && !assert_expr(new_arg, OR)) - new_arg = m.mk_false(); + new_arg = OR ? m.mk_true() : m.mk_false(); if ((OR && m.is_false(new_arg)) || (!OR && m.is_true(new_arg))) { @@ -403,7 +403,7 @@ struct ctx_simplify_tactic::imp { if (new_arg != arg) modified = true; if (i > 0 && !m.is_true(new_arg) && !m.is_false(new_arg) && !assert_expr(new_arg, OR)) - new_arg = m.mk_false(); + new_arg = OR ? m.mk_true() : m.mk_false(); if ((OR && m.is_false(new_arg)) || (!OR && m.is_true(new_arg))) { From d5383e2387df2ef1011e0bd003bdfa0f2deb6b96 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 23 Feb 2016 18:41:56 -0800 Subject: [PATCH 21/57] fix bug in definition of rewrite rule for replace, tighten constraints for tightest-prefix Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 9 +++++---- src/smt/theory_seq.cpp | 15 +++++++++------ src/smt/theory_seq.h | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index af0fc6b74..13ba7d67d 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -444,11 +444,12 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { bool found = false; for (unsigned i = 0; !found && i < as.size(); ++i) { - if (bs.size() > as.size() - i) break; all_values &= m().is_value(as[i].get()); - unsigned j = 0; - for (; j < bs.size() && as[j+i].get() == bs[j].get(); ++j) {}; - found = j == bs.size(); + if (bs.size() <= as.size() - i) { + unsigned j = 0; + for (; j < bs.size() && as[j+i].get() == bs[j].get(); ++j) {}; + found = j == bs.size(); + } } if (found) { result = m().mk_true(); diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index a576685cc..d7882d893 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2135,13 +2135,13 @@ void theory_seq::deque_axiom(expr* n) { lit or s = "" or s = s1*(unit c) lit or s = "" or !contains(x*s1, s) */ -void theory_seq::tightest_prefix(expr* s, expr* x, literal lit1, literal lit2) { +void theory_seq::tightest_prefix(expr* s, expr* x) { expr_ref s1 = mk_first(s); expr_ref c = mk_last(s); expr_ref s1c = mk_concat(s1, m_util.str.mk_unit(c)); literal s_eq_emp = mk_eq_empty(s); add_axiom(s_eq_emp, mk_seq_eq(s, s1c)); - add_axiom(lit1, lit2, s_eq_emp, ~mk_literal(m_util.str.mk_contains(mk_concat(x, s1), s))); + add_axiom(s_eq_emp, ~mk_literal(m_util.str.mk_contains(mk_concat(x, s1), s))); } /* @@ -2155,7 +2155,7 @@ void theory_seq::tightest_prefix(expr* s, expr* x, literal lit1, literal lit2) { len(t) != 0 & !contains(t, s) => i = -1 len(t) != 0 & contains(t, s) => t = xsy & i = len(x) - len(t) != 0 & contains(t, s) & s != emp => tightest_prefix(x, s) + tightest_prefix(x, s) offset not fixed: @@ -2193,7 +2193,7 @@ void theory_seq::add_indexof_axiom(expr* i) { add_axiom(s_eq_empty, ~mk_eq_empty(t), mk_eq(i, minus_one, false)); add_axiom(~cnt, s_eq_empty, mk_seq_eq(t, xsy)); add_axiom(~cnt, s_eq_empty, mk_eq(i, lenx, false)); - tightest_prefix(s, x, ~cnt); + tightest_prefix(s, x); } else { // offset >= len(t) => indexof(s, t, offset) = -1 @@ -2227,7 +2227,7 @@ void theory_seq::add_indexof_axiom(expr* i) { /* let r = replace(a, s, t) - (contains(a, s) -> tightest_prefix(s,xs)) + tightest_prefix(s, x) (contains(a, s) -> r = xty & a = xsy) & (!contains(a, s) -> r = a) @@ -2243,7 +2243,7 @@ void theory_seq::add_replace_axiom(expr* r) { add_axiom(cnt, mk_seq_eq(r, a)); add_axiom(~cnt, mk_seq_eq(a, xsy)); add_axiom(~cnt, mk_seq_eq(r, xty)); - tightest_prefix(s, x, ~cnt); + tightest_prefix(s, x); } void theory_seq::add_elim_string_axiom(expr* n) { @@ -3382,6 +3382,7 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { VERIFY(m_util.str.is_prefix(e, e1, e2)); SASSERT(ctx.get_assignment(e) == l_false); if (canonizes(false, e)) { + TRACE("seq", tout << mk_pp(e, m) << " is false\n";); return false; } expr_ref head1(m), tail1(m), head2(m), tail2(m), conc(m); @@ -3515,11 +3516,13 @@ bool theory_seq::canonizes(bool sign, expr* e) { TRACE("seq", tout << mk_pp(e, m) << " -> " << cont << "\n";); if ((m.is_true(cont) && !sign) || (m.is_false(cont) && sign)) { + TRACE("seq", display(tout);); propagate_lit(deps, 0, 0, ctx.get_literal(e)); return true; } if ((m.is_false(cont) && !sign) || (m.is_true(cont) && sign)) { + TRACE("seq", display(tout);); return true; } return false; diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 3aedac835..d275cd788 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -443,7 +443,7 @@ namespace smt { literal mk_literal(expr* n); literal mk_eq_empty(expr* n); literal mk_seq_eq(expr* a, expr* b); - void tightest_prefix(expr* s, expr* x, literal lit, literal lit2 = null_literal); + void tightest_prefix(expr* s, expr* x); expr_ref mk_sub(expr* a, expr* b); enode* ensure_enode(expr* a); From 5679fb5d6b89727e4eeed97c5f0be5f6cab07033 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 24 Feb 2016 08:55:22 -0800 Subject: [PATCH 22/57] experimenting with alternative prefix encodings Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 45 ++++++++++++++++++++++++++++++++++++------ src/smt/theory_seq.h | 3 ++- 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index fb2ed362d..e03cce6c1 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2032,7 +2032,7 @@ expr_ref theory_seq::expand(expr* e0, dependency*& eqs) { return result; } expr* e = m_rep.find(e0, deps); - expr* e1, *e2; + expr* e1, *e2, *e3; if (m_util.str.is_concat(e, e1, e2)) { result = mk_concat(expand(e1, deps), expand(e2, deps)); } @@ -2051,6 +2051,12 @@ expr_ref theory_seq::expand(expr* e0, dependency*& eqs) { else if (m_util.str.is_unit(e, e1)) { result = m_util.str.mk_unit(expand(e1, deps)); } + else if (m_util.str.is_index(e, e1, e2)) { + result = m_util.str.mk_index(expand(e1, deps), expand(e2, deps), m_autil.mk_int(0)); + } + else if (m_util.str.is_index(e, e1, e2, e3)) { + result = m_util.str.mk_index(expand(e1, deps), expand(e2, deps), e3); + } else { result = e; } @@ -2179,7 +2185,6 @@ void theory_seq::add_indexof_axiom(expr* i) { expr_ref zero(m_autil.mk_int(0), m); expr_ref xsy(m); - if (!offset || (m_autil.is_numeral(offset, r) && r.is_zero())) { expr_ref x = mk_skolem(m_indexof_left, t, s); expr_ref y = mk_skolem(m_indexof_right, t, s); @@ -2676,7 +2681,7 @@ literal theory_seq::mk_seq_eq(expr* a, expr* b) { return mk_literal(mk_skolem(m_eq, a, b, 0, m.mk_bool_sort())); } -literal theory_seq::mk_eq_empty(expr* _e) { +literal theory_seq::mk_eq_empty(expr* _e, bool phase) { context& ctx = get_context(); expr_ref e(_e, m); SASSERT(m_util.is_seq(e)); @@ -2698,8 +2703,9 @@ literal theory_seq::mk_eq_empty(expr* _e) { emp = m_util.str.mk_empty(m.get_sort(e)); literal lit = mk_eq(e, emp, false); - ctx.force_phase(lit); + ctx.force_phase(phase?lit:~lit); ctx.mark_as_relevant(lit); + TRACE("seq", tout << mk_pp(e, m) << " = empty\n";); return lit; } @@ -2797,7 +2803,7 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { } else { #if 0 - propagate_not_prefix(e); + propagate_not_prefix2(e); #else propagate_non_empty(lit, e1); if (add_prefix2prefix(e, change)) { @@ -3340,6 +3346,33 @@ void theory_seq::propagate_not_prefix(expr* e) { add_axiom(lit, e2_is_emp, ~mk_eq(c, d, false), mk_seq_eq(e2, x)); } +/* + !prefix(e1,e2) => len(e1) > 0 + !prefix(e1,e2) => len(e1) > len(e2) or e2 = pre(e2,len(e1))post(e2,len(e2)-len(e1)) & pre(e2, len(e1)) != e1 +*/ + +void theory_seq::propagate_not_prefix2(expr* e) { + context& ctx = get_context(); + //std::cout << mk_pp(e, m) << " " << ctx.get_scope_level() << "\n"; + expr* e1, *e2; + VERIFY(m_util.str.is_prefix(e, e1, e2)); + literal lit = ctx.get_literal(e); + SASSERT(ctx.get_assignment(lit) == l_false); + if (canonizes(false, e)) { + return; + } + propagate_non_empty(~lit, e1); + expr_ref len_e1(m_util.str.mk_length(e1), m); + expr_ref len_e2(m_util.str.mk_length(e2), m); + expr_ref len_e2_e1(mk_sub(len_e2, len_e1), m); + expr_ref x = mk_skolem(m_pre, e2, len_e1); + expr_ref y = mk_skolem(m_post, e2, len_e2_e1); + literal e2_ge_e1 = mk_literal(m_autil.mk_ge(len_e2_e1, m_autil.mk_int(0))); + add_axiom(lit, ~e2_ge_e1, mk_seq_eq(e2, mk_concat(x, y))); + add_axiom(lit, ~e2_ge_e1, mk_eq(m_util.str.mk_length(x), len_e1, false)); + add_axiom(lit, ~e2_ge_e1, ~mk_eq(e1, x, false)); +} + /* !suffix(e1,e2) => e1 != "" !suffix(e1,e2) => e2 = "" or e1 = ycx & (e2 = zdx & c != d or x = e2) @@ -3403,7 +3436,7 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { conc = mk_concat(head2, tail2); propagate_eq(~e2_is_emp, e2, conc, true); - literal e1_is_emp = mk_eq_empty(e1); + literal e1_is_emp = mk_eq_empty(e1, false); switch (ctx.get_assignment(e1_is_emp)) { case l_true: TRACE("seq", tout << mk_pp(e, m) << ": " << mk_pp(e1, m) << " != empty\n";); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 3aedac835..f681fbcba 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -441,7 +441,7 @@ namespace smt { void add_at_axiom(expr* n); void add_in_re_axiom(expr* n); literal mk_literal(expr* n); - literal mk_eq_empty(expr* n); + literal mk_eq_empty(expr* n, bool phase = true); literal mk_seq_eq(expr* a, expr* b); void tightest_prefix(expr* s, expr* x, literal lit, literal lit2 = null_literal); expr_ref mk_sub(expr* a, expr* b); @@ -489,6 +489,7 @@ namespace smt { bool add_suffix2suffix(expr* e, bool& change); bool add_contains2contains(expr* e, bool& change); void propagate_not_prefix(expr* e); + void propagate_not_prefix2(expr* e); void propagate_not_suffix(expr* e); void ensure_nth(literal lit, expr* s, expr* idx); bool canonizes(bool sign, expr* e); From c1aa33339d2e95c6b8099debe75bd457ebd47b20 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 25 Feb 2016 09:32:10 +0000 Subject: [PATCH 23/57] bv_bounds: early exit in is_bound in case the expr is not boolean ~2% speedup --- src/tactic/bv/bv_bounds_tactic.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 0a4411847..587b9df9a 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -162,6 +162,9 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { map *m_bound; bool is_bound(expr *e, expr*& v, interval& b) { + if (!m.is_bool(e)) + return false; + rational n; expr *lhs, *rhs; unsigned sz; From d642d5fe4c06d1edd6dc7883161bdcf5277f4489 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 25 Feb 2016 09:47:51 +0000 Subject: [PATCH 24/57] API: add smt.logic parameter to enable setting the logic through the API currently only Z3_solver_set_params() is supported logic has to be set before solver first usage or before solver reset --- src/api/api_solver.cpp | 5 +++++ src/smt/params/smt_params_helper.pyg | 1 + 2 files changed, 6 insertions(+) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 94e91ae05..be03e5daf 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -153,6 +153,11 @@ extern "C" { LOG_Z3_solver_set_params(c, s, p); RESET_ERROR_CODE(); + symbol logic = to_param_ref(p).get_sym("smt.logic", symbol::null); + if (logic != symbol::null) { + to_solver(s)->m_logic = logic; + } + if (to_solver(s)->m_solver) { bool old_model = to_solver(s)->m_params.get_bool("model", true); bool new_model = to_param_ref(p).get_bool("model", true); diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index a1a38babf..a9f6ccc18 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -3,6 +3,7 @@ def_module_params(module_name='smt', description='smt solver based on lazy smt', export=True, params=(('auto_config', BOOL, True, 'automatically configure solver'), + ('logic', SYMBOL, '', 'logic used to setup the SMT solver'), ('random_seed', UINT, 0, 'random seed for the smt solver'), ('relevancy', UINT, 2, 'relevancy propagation heuristic: 0 - disabled, 1 - relevancy is tracked by only affects quantifier instantiation, 2 - relevancy is tracked, and an atom is only asserted if it is relevant'), ('macro_finder', BOOL, False, 'try to find universally quantified formulas that can be viewed as macros'), From c693c990df9e2babb34b5e4b8e0e09b1513dd65d Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 25 Feb 2016 16:53:35 +0000 Subject: [PATCH 25/57] bv_bounds: speedup up to 10x in larger formulas introduce a may_simplify() function to short-circuit evaluation of expression trees that are guaranteed to not be simplifiable --- src/tactic/bv/bv_bounds_tactic.cpp | 88 +++++++++++++++++++++---- src/tactic/core/ctx_simplify_tactic.cpp | 8 +-- src/tactic/core/ctx_simplify_tactic.h | 1 + 3 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 587b9df9a..122096677 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -34,7 +34,7 @@ struct interval { unsigned sz; bool tight; - explicit interval() : l(0), h(0), sz(0), tight(false) {} + interval() {} interval(const rational& l, const rational& h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { // canonicalize full set if (is_wrapped() && l == h + rational::one()) { @@ -51,10 +51,11 @@ struct interval { bool is_full() const { return l.is_zero() && h == uMaxInt(sz); } bool is_wrapped() const { return l > h; } + bool is_singleton() const { return l == h; } bool operator==(const interval& b) const { SASSERT(sz == b.sz); - return l == b.l && h == b.h; + return l == b.l && h == b.h && tight == b.tight; } bool operator!=(const interval& b) const { return !(*this == b); } @@ -79,7 +80,7 @@ struct interval { /// return false if intersection is unsat bool intersect(const interval& b, interval& result) const { - if (is_full() || (l == b.l && h == b.h)) { + if (is_full() || *this == b) { result = b; return true; } @@ -153,6 +154,8 @@ std::ostream& operator<<(std::ostream& o, const interval& I) { class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { typedef obj_map map; + typedef obj_map expr_set; + typedef obj_map expr_list_map; ast_manager& m; params_ref m_params; @@ -160,11 +163,9 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { bv_util m_bv; vector m_scopes; map *m_bound; + expr_list_map m_expr_vars; bool is_bound(expr *e, expr*& v, interval& b) { - if (!m.is_bool(e)) - return false; - rational n; expr *lhs, *rhs; unsigned sz; @@ -212,6 +213,45 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { return false; } + expr_set* get_expr_vars(expr* t) { + expr_set*& entry = m_expr_vars.insert_if_not_there2(t, 0)->get_data().m_value; + if (entry) + return entry; + + expr_set* set = alloc(expr_set); + entry = set; + + if (!m_bv.is_numeral(t)) + set->insert(t, true); + + if (!is_app(t)) + return set; + + app* a = to_app(t); + for (unsigned i = 0; i < a->get_num_args(); ++i) { + expr_set* set_arg = get_expr_vars(a->get_arg(i)); + for (expr_set::iterator I = set_arg->begin(), E = set_arg->end(); I != E; ++I) { + set->insert(I->m_key, true); + } + } + return set; + } + + bool expr_has_bounds(expr* t) { + if (!m.is_bool(t)) + return false; + + app* a = to_app(t); + if (m_bv.is_bv_ule(t) || m_bv.is_bv_sle(t) || m.is_eq(t)) + return m_bv.is_numeral(a->get_arg(0)) ^ m_bv.is_numeral(a->get_arg(1)); + + for (unsigned i = 0; i < a->get_num_args(); ++i) { + if (expr_has_bounds(a->get_arg(i))) + return true; + } + return false; + } + public: bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { @@ -229,7 +269,11 @@ public: r.insert("propagate-eq", CPK_BOOL, "(default: false) propagate equalities from inequalities"); } - virtual ~bv_bounds_simplifier() {} + virtual ~bv_bounds_simplifier() { + for (expr_list_map::iterator I = m_expr_vars.begin(), E = m_expr_vars.end(); I != E; ++I) { + dealloc(I->m_value); + } + } virtual bool assert_expr(expr * t, bool sign) { while (m.is_not(t, t)) { @@ -253,10 +297,17 @@ public: virtual bool simplify(expr* t, expr_ref& result) { expr* t1; - interval b, ctx, intr; - result = 0; - bool sign = false; + interval b; + if (m_bound->find(t, b) && b.is_singleton()) { + result = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t)); + return true; + } + + if (!m.is_bool(t)) + return false; + + bool sign = false; while (m.is_not(t, t)) { sign = !sign; } @@ -272,12 +323,15 @@ public: } } + interval ctx, intr; + result = 0; + if (m_bound->find(t1, ctx)) { if (ctx.implies(b)) { result = m.mk_true(); } else if (!b.intersect(ctx, intr)) { result = m.mk_false(); - } else if (m_propagate_eq && intr.l == intr.h) { + } else if (m_propagate_eq && intr.is_singleton()) { result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); } } else if (b.is_full() && b.tight) { @@ -290,6 +344,18 @@ public: return result != 0; } + virtual bool may_simplify(expr* t) { + if (m_bv.is_numeral(t)) + return false; + + expr_set* used_exprs = get_expr_vars(t); + for (map::iterator I = m_bound->begin(), E = m_bound->end(); I != E; ++I) { + if (I->m_value.is_singleton() && used_exprs->contains(I->m_key)) + return true; + } + return expr_has_bounds(t); + } + virtual void push() { TRACE("bv", tout << "push\n";); unsigned sz = m_scopes.size(); diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index e07f7417d..f127614aa 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -331,17 +331,13 @@ struct ctx_simplify_tactic::imp { void simplify(expr * t, expr_ref & r) { r = 0; - if (m_depth >= m_max_depth || m_num_steps >= m_max_steps || !is_app(t)) { + if (m_depth >= m_max_depth || m_num_steps >= m_max_steps || !is_app(t) || !m_simp->may_simplify(t)) { r = t; return; } checkpoint(); TRACE("ctx_simplify_tactic_detail", tout << "processing: " << mk_bounded_pp(t, m) << "\n";); - if (m_simp->simplify(t, r)) { - SASSERT(r.get() != 0); - return; - } - if (is_cached(t, r)) { + if (is_cached(t, r) || m_simp->simplify(t, r)) { SASSERT(r.get() != 0); return; } diff --git a/src/tactic/core/ctx_simplify_tactic.h b/src/tactic/core/ctx_simplify_tactic.h index 34258362b..7b1507f67 100644 --- a/src/tactic/core/ctx_simplify_tactic.h +++ b/src/tactic/core/ctx_simplify_tactic.h @@ -30,6 +30,7 @@ public: virtual ~simplifier() {} virtual bool assert_expr(expr * t, bool sign) = 0; virtual bool simplify(expr* t, expr_ref& result) = 0; + virtual bool may_simplify(expr* t) { return true; } virtual void push() = 0; virtual void pop(unsigned num_scopes) = 0; virtual simplifier * translate(ast_manager & m) = 0; From 97d6098d0063b7e3a2e3c9325f0df3e371cda208 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Thu, 25 Feb 2016 19:41:01 +0000 Subject: [PATCH 26/57] bv_bounds: make may_simplify() more aggressive for the common case of a single comparison fix expr_has_bounds to handle cases like (bvadd (ite c t e) ...) --- src/tactic/bv/bv_bounds_tactic.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 122096677..16154c7ef 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -238,12 +238,10 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { } bool expr_has_bounds(expr* t) { - if (!m.is_bool(t)) - return false; - app* a = to_app(t); - if (m_bv.is_bv_ule(t) || m_bv.is_bv_sle(t) || m.is_eq(t)) - return m_bv.is_numeral(a->get_arg(0)) ^ m_bv.is_numeral(a->get_arg(1)); + if ((m_bv.is_bv_ule(t) || m_bv.is_bv_sle(t) || m.is_eq(t)) && + (m_bv.is_numeral(a->get_arg(0)) || m_bv.is_numeral(a->get_arg(1)))) + return true; for (unsigned i = 0; i < a->get_num_args(); ++i) { if (expr_has_bounds(a->get_arg(i))) @@ -353,6 +351,15 @@ public: if (I->m_value.is_singleton() && used_exprs->contains(I->m_key)) return true; } + + while (m.is_not(t, t)); + + expr* t1; + interval b; + // skip common case: single bound constraint without any context for simplification + if (is_bound(t, t1, b)) { + return m_bound->contains(t1); + } return expr_has_bounds(t); } From ce8862d415d6ecd0dbdd27315e9a598ce4c2ab2b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 26 Feb 2016 18:15:45 -0800 Subject: [PATCH 27/57] fix bug in conflict clause generation in seq-branch-variable Signed-off-by: Nikolaj Bjorner --- scripts/mk_util.py | 45 ++++-- src/ast/ast_smt_pp.cpp | 21 +++ src/ast/rewriter/seq_rewriter.cpp | 40 +++++- src/ast/seq_decl_plugin.h | 12 +- src/smt/smt_conflict_resolution.cpp | 40 +++--- src/smt/smt_conflict_resolution.h | 9 +- src/smt/smt_context.cpp | 2 +- src/smt/smt_context.h | 10 ++ src/smt/smt_context_pp.cpp | 14 +- src/smt/theory_seq.cpp | 204 ++++++++++++++++++++++------ src/smt/theory_seq.h | 34 ++++- 11 files changed, 344 insertions(+), 87 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 33f70c98d..fc0d3b5e0 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -71,6 +71,7 @@ VERBOSE=True DEBUG_MODE=False SHOW_CPPS = True VS_X64 = False +VS_ARM = False LINUX_X64 = True ONLY_MAKEFILES = False Z3PY_SRC_DIR=None @@ -99,6 +100,7 @@ USE_OMP=True FPMATH="Default" FPMATH_FLAGS="-mfpmath=sse -msse -msse2" + def check_output(cmd): out = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] if out != None: @@ -1557,6 +1559,8 @@ class DotNetDLLComponent(Component): if IS_WINDOWS: if VS_X64: cscCmdLine.extend(['/platform:x64']) + elif VS_ARM: + cscCmdLine.extend(['/platform:arm']) else: cscCmdLine.extend(['/platform:x86']) else: @@ -1997,6 +2001,8 @@ class DotNetExampleComponent(ExampleComponent): out.write('\t%s /out:%s /reference:%s /debug:full /reference:System.Numerics.dll' % (CSC, exefile, dll)) if VS_X64: out.write(' /platform:x64') + elif VS_ARM: + out.write(' /platform:arm') else: out.write(' /platform:x86') for csfile in get_cs_files(self.ex_dir): @@ -2186,18 +2192,21 @@ def mk_config(): 'AR_FLAGS=/nologo\n' 'LINK_FLAGS=/nologo /MDd\n' 'SLINK_FLAGS=/nologo /LDd\n') - if not VS_X64: - config.write( - 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG %s /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % extra_opt) - config.write( - 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' - 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') - else: + if VS_X64: config.write( 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _AMD64_ /D _DEBUG /D Z3DEBUG %s /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze-\n' % extra_opt) config.write( 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') + elif VS_ARM: + print "ARM on VS is unsupported" + exit(1) + else: + config.write( + 'CXXFLAGS=/c /Zi /nologo /W3 /WX- /Od /Oy- /D WIN32 /D _DEBUG /D Z3DEBUG %s /D _CONSOLE /D _TRACE /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % extra_opt) + config.write( + 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' + 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') else: # Windows Release mode LTCG=' /LTCG' if SLOW_OPTIMIZE else '' @@ -2209,18 +2218,23 @@ def mk_config(): % LTCG) if TRACE: extra_opt = '%s /D _TRACE ' % extra_opt - if not VS_X64: - config.write( - 'CXXFLAGS=/nologo /c%s /Zi /W3 /WX- /O2 /Oy- /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG %s /D _CONSOLE /D _WINDOWS /D ASYNC_COMMANDS /Gm- /EHsc /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % (GL, extra_opt)) - config.write( - 'LINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' - 'SLINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n' % (LTCG, LTCG)) - else: + if VS_X64: config.write( 'CXXFLAGS=/c%s /Zi /nologo /W3 /WX- /O2 /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG %s /D _LIB /D _WINDOWS /D _AMD64_ /D _UNICODE /D UNICODE /Gm- /EHsc /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /TP\n' % (GL, extra_opt)) config.write( 'LINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608\n' 'SLINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608\n' % (LTCG, LTCG)) + elif VS_ARM: + print "ARM on VS is unsupported" + exit(1) + else: + config.write( + 'CXXFLAGS=/nologo /c%s /Zi /W3 /WX- /O2 /Oy- /D _EXTERNAL_RELEASE /D WIN32 /D NDEBUG %s /D _CONSOLE /D _WINDOWS /D ASYNC_COMMANDS /Gm- /EHsc /MD /GS /fp:precise /Zc:wchar_t /Zc:forScope /Gd /analyze- /arch:SSE2\n' % (GL, extra_opt)) + config.write( + 'LINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' + 'SLINK_EXTRA_FLAGS=/link%s /DEBUG /MACHINE:X86 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n' % (LTCG, LTCG)) + + # End of Windows VS config.mk if is_verbose(): @@ -2446,6 +2460,9 @@ def mk_makefile(): if VS_X64: print(" platform: x64\n") print("To build Z3, open a [Visual Studio x64 Command Prompt], then") + elif VS_ARM: + print(" platform: ARM\n") + print("To build Z3, open a [Visual Studio ARM Command Prompt], then") else: print(" platform: x86") print("To build Z3, open a [Visual Studio Command Prompt], then") diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 9279f50fe..82137483a 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -30,6 +30,7 @@ Revision History: #include"for_each_ast.h" #include"decl_collector.h" #include"smt2_util.h" +#include"seq_decl_plugin.h" // --------------------------------------- // smt_renaming @@ -160,6 +161,7 @@ class smt_printer { unsigned m_num_lets; arith_util m_autil; bv_util m_bvutil; + seq_util m_sutil; family_id m_basic_fid; family_id m_bv_fid; family_id m_arith_fid; @@ -247,6 +249,10 @@ class smt_printer { } if (m_is_smt2) { + if (is_sort_symbol && sym == symbol("String")) { + m_out << "String"; + return; + } if (is_sort_symbol && sym != symbol("BitVec")) { m_out << "(" << sym << " "; } @@ -397,6 +403,7 @@ class smt_printer { bool is_int, pos; buffer names; unsigned bv_size; + zstring s; unsigned num_args = n->get_num_args(); func_decl* decl = n->get_decl(); if (m_autil.is_numeral(n, val, is_int)) { @@ -415,6 +422,19 @@ class smt_printer { display_rational(val, is_int); } } + else if (m_sutil.str.is_string(n, s)) { + std::string encs = s.encode(); + m_out << "\""; + for (unsigned i = 0; i < encs.length(); ++i) { + if (encs[i] == '\"') { + m_out << "\"\""; + } + else { + m_out << encs[i]; + } + } + m_out << "\""; + } else if (m_bvutil.is_numeral(n, val, bv_size)) { if (m_is_smt2) { m_out << "(_ bv" << val << " " << bv_size << ")"; @@ -797,6 +817,7 @@ public: m_num_lets(0), m_autil(m), m_bvutil(m), + m_sutil(m), m_logic(logic), m_AUFLIRA("AUFLIRA"), // It's much easier to read those testcases with that. diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index 13ba7d67d..a26fcfe2c 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -437,7 +437,12 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { m_util.str.get_concat(a, as); m_util.str.get_concat(b, bs); bool all_values = true; - + + if (bs.empty()) { + result = m().mk_true(); + return BR_DONE; + } + for (unsigned i = 0; all_values && i < bs.size(); ++i) { all_values = m().is_value(bs[i].get()); } @@ -459,6 +464,39 @@ br_status seq_rewriter::mk_seq_contains(expr* a, expr* b, expr_ref& result) { result = m().mk_false(); return BR_DONE; } + + unsigned lenA = 0, lenB = 0; + bool lA = min_length(as.size(), as.c_ptr(), lenA); + if (lA) { + bool lB = min_length(bs.size(), bs.c_ptr(), lenB); + if (lenB > lenA) { + result = m().mk_false(); + return BR_DONE; + } + } + + + if (as.empty()) { + result = m().mk_eq(b, m_util.str.mk_empty(m().get_sort(b))); + return BR_REWRITE2; + } + + unsigned offs = 0; + unsigned sz = as.size(); + expr* b0 = bs[0].get(); + expr* bL = bs[bs.size()-1].get(); + for (; offs < as.size() && m().are_distinct(b0, as[offs].get()); ++offs) {}; + for (; sz > offs && m().are_distinct(bL, as[sz-1].get()); --sz) {} + if (offs == sz) { + result = m().mk_eq(b, m_util.str.mk_empty(m().get_sort(b))); + return BR_REWRITE2; + } + if (offs > 0 || sz < as.size()) { + SASSERT(sz > offs); + result = m_util.str.mk_contains(m_util.str.mk_concat(sz-offs, as.c_ptr()+offs), b); + return BR_REWRITE2; + } + return BR_FAILED; } diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 4100fe6cc..6200d910f 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -222,16 +222,16 @@ 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() { return m.mk_sort(m_fid, _STRING_SORT, 0, 0); } - app* mk_empty(sort* s) { 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, 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)); } app* mk_string(zstring const& s); app* mk_string(symbol const& s) { return u.seq.mk_string(s); } app* mk_char(char ch); - app* mk_concat(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONCAT, 2, es); } + app* mk_concat(expr* a, expr* b) const { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONCAT, 2, es); } app* mk_concat(expr* a, expr* b, expr* c) { return mk_concat(a, mk_concat(b, c)); } - expr* mk_concat(unsigned n, expr* const* es) { if (n == 1) return es[0]; SASSERT(n > 1); return m.mk_app(m_fid, OP_SEQ_CONCAT, n, es); } - expr* mk_concat(expr_ref_vector const& es) { return mk_concat(es.size(), es.c_ptr()); } - app* mk_length(expr* a) { return m.mk_app(m_fid, OP_SEQ_LENGTH, 1, &a); } + expr* mk_concat(unsigned n, expr* const* es) const { if (n == 1) return es[0]; SASSERT(n > 1); return m.mk_app(m_fid, OP_SEQ_CONCAT, n, es); } + expr* mk_concat(expr_ref_vector const& es) const { return mk_concat(es.size(), es.c_ptr()); } + app* mk_length(expr* a) const { return m.mk_app(m_fid, OP_SEQ_LENGTH, 1, &a); } app* mk_substr(expr* a, expr* b, expr* c) { expr* es[3] = { a, b, c }; return m.mk_app(m_fid, OP_SEQ_EXTRACT, 3, es); } app* mk_contains(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_CONTAINS, 2, es); } app* mk_prefix(expr* a, expr* b) { expr* es[2] = { a, b }; return m.mk_app(m_fid, OP_SEQ_PREFIX, 2, es); } diff --git a/src/smt/smt_conflict_resolution.cpp b/src/smt/smt_conflict_resolution.cpp index 3aa02c0b6..86593b0f2 100644 --- a/src/smt/smt_conflict_resolution.cpp +++ b/src/smt/smt_conflict_resolution.cpp @@ -99,22 +99,29 @@ namespace smt { This method may update m_antecedents, m_todo_js and m_todo_eqs. */ void conflict_resolution::eq_justification2literals(enode * lhs, enode * rhs, eq_justification js) { + ast_manager& m = get_manager(); SASSERT(m_antecedents); + TRACE("conflict_detail", tout << mk_pp(lhs->get_owner(), m) << " = " << mk_pp(rhs->get_owner(), m); + switch (js.get_kind()) { + case eq_justification::AXIOM: tout << " axiom\n"; break; + case eq_justification::EQUATION: + tout << " was asserted\nliteral: "; m_ctx.display_literal(tout, js.get_literal()); tout << "\n"; + break; + case eq_justification::JUSTIFICATION: tout << " justification\n"; break; + case eq_justification::CONGRUENCE: tout << " congruence\n"; break; + default: break; + }); + switch(js.get_kind()) { case eq_justification::AXIOM: - TRACE("conflict_detail", tout << "#" << lhs->get_owner_id() << " = " << rhs->get_owner_id() << " axiom\n";); break; case eq_justification::EQUATION: - TRACE("conflict_detail", tout << "#" << lhs->get_owner_id() << " = " << rhs->get_owner_id() << " was asserted\n" - << "literal: "; m_ctx.display_literal(tout, js.get_literal()); tout << "\n";); m_antecedents->push_back(js.get_literal()); break; case eq_justification::JUSTIFICATION: - TRACE("conflict_detail", tout << "#" << lhs->get_owner_id() << " = " << rhs->get_owner_id() << " justification\n";); mark_justification(js.get_justification()); break; case eq_justification::CONGRUENCE: { - TRACE("conflict_detail", tout << "#" << lhs->get_owner_id() << " = " << rhs->get_owner_id() << " congruence\n";); CTRACE("dyn_ack_target", !lhs->is_eq(), tout << "dyn_ack_target2: " << lhs->get_owner_id() << " " << rhs->get_owner_id() << "\n";); m_dyn_ack_manager.used_cg_eh(lhs->get_owner(), rhs->get_owner()); unsigned num_args = lhs->get_num_args(); @@ -206,7 +213,6 @@ namespace smt { justification_vector::iterator it = m_todo_js.begin() + old_js_qhead; justification_vector::iterator end = m_todo_js.end(); for (; it != end; ++it) { - TRACE("conflict_detail", tout << "unmarking: " << *it << "\n";); (*it)->unset_mark(); } m_todo_js.shrink(old_js_qhead); @@ -371,11 +377,9 @@ namespace smt { tout << "conflict_lvl: " << m_conflict_lvl << " scope_lvl: " << m_ctx.get_scope_level() << " base_lvl: " << m_ctx.get_base_level() << " search_lvl: " << m_ctx.get_search_level() << "\n"; tout << "js.kind: " << js.get_kind() << "\n"; - tout << "consequent: " << consequent << "\n"; - for (unsigned i = 0; i < m_assigned_literals.size(); ++i) { - tout << m_assigned_literals[i] << " "; - } - tout << "\n"; + tout << "consequent: " << consequent << ": "; + m_ctx.display_literal_verbose(tout, consequent); tout << "\n"; + m_ctx.display(tout, js); tout << "\n"; ); // m_conflict_lvl can be smaller than m_ctx.get_search_level() when: @@ -416,12 +420,12 @@ namespace smt { TRACE("conflict", tout << "before minimization:\n"; - m_ctx.display_literals(tout, m_lemma.size(), m_lemma.c_ptr()); + m_ctx.display_literals(tout, m_lemma); tout << "\n";); TRACE("conflict_verbose", tout << "before minimization:\n"; - m_ctx.display_literals_verbose(tout, m_lemma.size(), m_lemma.c_ptr()); + m_ctx.display_literals_verbose(tout, m_lemma); tout << "\n";); if (m_params.m_minimize_lemmas) @@ -429,12 +433,16 @@ namespace smt { TRACE("conflict", tout << "after minimization:\n"; - m_ctx.display_literals(tout, m_lemma.size(), m_lemma.c_ptr()); + m_ctx.display_literals(tout, m_lemma); tout << "\n";); TRACE("conflict_verbose", tout << "after minimization:\n"; - m_ctx.display_literals_verbose(tout, m_lemma.size(), m_lemma.c_ptr()); + m_ctx.display_literals_verbose(tout, m_lemma); + tout << "\n";); + + TRACE("conflict_bug", + m_ctx.display_literals_verbose(tout, m_lemma); tout << "\n";); literal_vector::iterator it = m_lemma.begin(); @@ -1423,7 +1431,7 @@ namespace smt { } end_unsat_core: - TRACE("unsat_core", tout << "assumptions:\n"; m_ctx.display_literals(tout, m_assumptions.size(), m_assumptions.c_ptr()); tout << "\n";); + TRACE("unsat_core", tout << "assumptions:\n"; m_ctx.display_literals(tout, m_assumptions); tout << "\n";); reset_unmark_and_justifications(0, 0); } diff --git a/src/smt/smt_conflict_resolution.h b/src/smt/smt_conflict_resolution.h index 6d8abbf23..daccadbb7 100644 --- a/src/smt/smt_conflict_resolution.h +++ b/src/smt/smt_conflict_resolution.h @@ -114,7 +114,6 @@ namespace smt { void mark_justification(justification * js) { if (!js->is_marked()) { - TRACE("conflict_detail", tout << "marking: " << js << "\n";); js->set_mark(); m_todo_js.push_back(js); } @@ -126,7 +125,7 @@ namespace smt { std::swap(n1, n2); enode_pair p(n1, n2); if (m_already_processed_eqs.insert_if_not_there(p)) { - TRACE("conflict_detail", tout << "marking eq #" << p.first->get_owner_id() << " = #" << + TRACE("conflict_detail_verbose", tout << "marking eq #" << p.first->get_owner_id() << " = #" << p.second->get_owner_id() << "\n";); m_todo_eqs.push_back(p); SASSERT(m_already_processed_eqs.contains(p)); @@ -168,9 +167,8 @@ namespace smt { void eq_justification2literals(enode * lhs, enode * rhs, eq_justification js); void eq_branch2literals(enode * n1, enode * n2); void eq2literals(enode * n1, enode * n2); - void justification2literals_core(justification * js, literal_vector & result); + void justification2literals_core(justification * js, literal_vector & result) ; void unmark_justifications(unsigned old_js_qhead); - void justification2literals(justification * js, literal_vector & result); literal_vector m_tmp_literal_vector; @@ -256,6 +254,9 @@ namespace smt { literal_vector::const_iterator end_unsat_core() const { return m_assumptions.end(); } + + void justification2literals(justification * js, literal_vector & result); + }; inline void mark_literals(conflict_resolution & cr, unsigned sz, literal const * ls) { diff --git a/src/smt/smt_context.cpp b/src/smt/smt_context.cpp index 1a8658c99..7d07f3997 100644 --- a/src/smt/smt_context.cpp +++ b/src/smt/smt_context.cpp @@ -295,7 +295,7 @@ namespace smt { void context::assign_core(literal l, b_justification j, bool decision) { TRACE("assign_core", tout << (decision?"decision: ":"propagating: ") << l << " "; - display_literal(tout, l); tout << " level: " << m_scope_lvl << "\n"; + display_literal_verbose(tout, l); tout << " level: " << m_scope_lvl << "\n"; display(tout, j);); SASSERT(l.var() < static_cast(m_b_internalized_stack.size())); m_assigned_literals.push_back(l); diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index 85fd63033..b918be2cf 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1176,8 +1176,18 @@ namespace smt { void display_literals(std::ostream & out, unsigned num_lits, literal const * lits) const; + void display_literals(std::ostream & out, literal_vector const& lits) const { + display_literals(out, lits.size(), lits.c_ptr()); + } + + void display_literal_verbose(std::ostream & out, literal lit) const; + void display_literals_verbose(std::ostream & out, unsigned num_lits, literal const * lits) const; + void display_literals_verbose(std::ostream & out, literal_vector const& lits) const { + display_literals_verbose(out, lits.size(), lits.c_ptr()); + } + void display_watch_list(std::ostream & out, literal l) const; void display_watch_lists(std::ostream & out) const; diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index d04165c5b..1f5566a03 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -96,6 +96,10 @@ namespace smt { display_compact(out, num_lits, lits, m_bool_var2expr.c_ptr()); } + void context::display_literal_verbose(std::ostream & out, literal lit) const { + display_literals_verbose(out, 1, &lit); + } + void context::display_literals_verbose(std::ostream & out, unsigned num_lits, literal const * lits) const { display_verbose(out, m_manager, num_lits, lits, m_bool_var2expr.c_ptr(), "\n"); } @@ -599,12 +603,16 @@ namespace smt { case b_justification::CLAUSE: { clause * cls = j.get_clause(); out << "clause "; - display_literals(out, cls->get_num_literals(), cls->begin_literals()); + if (cls) display_literals_verbose(out, cls->get_num_literals(), cls->begin_literals()); break; } - case b_justification::JUSTIFICATION: - out << "justification"; + case b_justification::JUSTIFICATION: { + out << "justification "; + literal_vector lits; + const_cast(*m_conflict_resolution).justification2literals(j.get_justification(), lits); + display_literals_verbose(out, lits.size(), lits.c_ptr()); break; + } default: UNREACHABLE(); break; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 40b9fa04b..cfd043895 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -258,6 +258,11 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>propagate_automata\n";); return FC_CONTINUE; } + if (check_contains()) { + ++m_stats.m_propagate_contains; + TRACE("seq", tout << ">>propagate_contains\n";); + return FC_CONTINUE; + } if (is_solved()) { TRACE("seq", tout << ">>is_solved\n";); return FC_DONE; @@ -288,7 +293,7 @@ bool theory_seq::branch_variable() { unsigned id = e.id(); s = find_branch_start(2*id); - TRACE("seq", tout << s << " " << 2*id << ": " << e.ls() << " = " << e.rs() << "\n";); + TRACE("seq", tout << s << " " << id << ": " << e.ls() << " = " << e.rs() << "\n";); bool found = find_branch_candidate(s, e.dep(), e.ls(), e.rs()); insert_branch_start(2*id, s); if (found) { @@ -337,15 +342,15 @@ bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_re return false; } + TRACE("seq", tout << mk_pp(l, m) << ": " << get_context().get_scope_level() << " - start:" << start << "\n";); + expr_ref v0(m); v0 = m_util.str.mk_empty(m.get_sort(l)); - literal_vector lits; if (can_be_equal(ls.size() - 1, ls.c_ptr() + 1, rs.size(), rs.c_ptr())) { if (l_false != assume_equality(l, v0)) { TRACE("seq", tout << mk_pp(l, m) << " " << v0 << "\n";); return true; } - lits.push_back(~mk_eq_empty(l)); } for (; start < rs.size(); ++start) { unsigned j = start; @@ -370,14 +375,31 @@ bool theory_seq::find_branch_candidate(unsigned& start, dependency* dep, expr_re all_units &= m_util.str.is_unit(rs[j]); } if (all_units) { + context& ctx = get_context(); + literal_vector lits; + lits.push_back(~mk_eq_empty(l)); for (unsigned i = 0; i < rs.size(); ++i) { if (can_be_equal(ls.size() - 1, ls.c_ptr() + 1, rs.size() - i - 1, rs.c_ptr() + i + 1)) { v0 = mk_concat(i + 1, rs.c_ptr()); lits.push_back(~mk_eq(l, v0, false)); } } + for (unsigned i = 0; i < lits.size(); ++i) { + switch (ctx.get_assignment(lits[i])) { + case l_true: break; + case l_false: start = 0; return true; + case l_undef: ctx.force_phase(~lits[i]); start = 0; return true; + } + } set_conflict(dep, lits); - TRACE("seq", tout << mk_pp(l, m) << " " << v0 << "\n";); + TRACE("seq", + tout << "start: " << start << "\n"; + for (unsigned i = 0; i < lits.size(); ++i) { + ctx.display_literal_verbose(tout << lits[i] << ": ", lits[i]); + tout << "\n"; + ctx.display(tout, ctx.get_justification(lits[i].var())); + tout << "\n"; + }); return true; } return false; @@ -429,10 +451,19 @@ lbool theory_seq::assume_equality(expr* l, expr* r) { if (n1->get_root() == n2->get_root()) { return l_true; } + if (ctx.is_diseq(n1, n2)) { + return l_false; + } + if (false && ctx.is_diseq_slow(n1, n2)) { + return l_false; + } ctx.mark_as_relevant(n1); ctx.mark_as_relevant(n2); - ctx.assume_eq(n1, n2); - return l_undef; + if (!ctx.assume_eq(n1, n2)) { + return l_false; + } + return ctx.get_assignment(mk_eq(l, r, false)); + //return l_undef; } @@ -483,29 +514,50 @@ bool theory_seq::propagate_length_coherence(expr* e) { return true; } + bool theory_seq::check_length_coherence(expr* e) { if (is_var(e) && m_rep.is_root(e)) { - expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); - expr_ref head(m), tail(m); - if (!propagate_length_coherence(e) && - l_false == assume_equality(e, emp)) { + if (!check_length_coherence0(e)) { + expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); + expr_ref head(m), tail(m); // e = emp \/ e = unit(head.elem(e))*tail(e) mk_decompose(e, head, tail); expr_ref conc = mk_concat(head, tail); - propagate_is_conc(e, conc); - assume_equality(tail, emp); - } - else if (!get_context().at_base_level()) { - m_trail_stack.push(push_replay(alloc(replay_length_coherence, m, e))); + if (propagate_is_conc(e, conc)) { + assume_equality(tail, emp); + } } return true; } return false; } +bool theory_seq::check_length_coherence0(expr* e) { + if (is_var(e) && m_rep.is_root(e)) { + expr_ref emp(m_util.str.mk_empty(m.get_sort(e)), m); + if (propagate_length_coherence(e) || + l_false != assume_equality(e, emp)) { + if (!get_context().at_base_level()) { + m_trail_stack.push(push_replay(alloc(replay_length_coherence, m, e))); + } + return true; + } + } + return false; +} + bool theory_seq::check_length_coherence() { obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); +#if 1 + for (; it != end; ++it) { + expr* e = *it; + if (check_length_coherence0(e)) { + return true; + } + } + it = m_length.begin(); +#endif for (; it != end; ++it) { expr* e = *it; if (check_length_coherence(e)) { @@ -570,14 +622,19 @@ void theory_seq::propagate_non_empty(literal lit, expr* s) { propagate_lit(0, 1, &lit, ~mk_eq_empty(s)); } -void theory_seq::propagate_is_conc(expr* e, expr* conc) { +bool theory_seq::propagate_is_conc(expr* e, expr* conc) { TRACE("seq", tout << mk_pp(conc, m) << " is non-empty\n";); context& ctx = get_context(); literal lit = ~mk_eq_empty(e); - SASSERT(ctx.get_assignment(lit) == l_true); - propagate_lit(0, 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)); + if (ctx.get_assignment(lit) == l_true) { + propagate_lit(0, 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; + } + else { + return false; + } } bool theory_seq::is_nth(expr* e) const { @@ -717,6 +774,23 @@ bool theory_seq::check_extensionality() { return true; } +/* + \brief check negated contains constriants. + */ +bool theory_seq::check_contains() { + context & ctx = get_context(); + for (unsigned i = 0; !ctx.inconsistent() && i < m_ncs.size(); ++i) { + if (solve_nc(i)) { + if (i + 1 != m_ncs.size()) { + nc n = m_ncs[m_ncs.size()-1]; + m_ncs.set(i, n); + --i; + } + m_ncs.pop_back(); + } + } + return m_new_propagation || ctx.inconsistent(); +} /* - Eqs = 0 - Diseqs evaluate to false @@ -762,20 +836,31 @@ void theory_seq::linearize(dependency* dep, enode_pair_vector& eqs, literal_vect void theory_seq::propagate_lit(dependency* dep, unsigned n, literal const* _lits, literal lit) { + if (lit == true_literal) return; + context& ctx = get_context(); - ctx.mark_as_relevant(lit); literal_vector lits(n, _lits); + + if (lit == false_literal) { + set_conflict(dep, lits); + return; + } + + ctx.mark_as_relevant(lit); enode_pair_vector eqs; linearize(dep, eqs, lits); - TRACE("seq", ctx.display_detailed_literal(tout, lit); - tout << " <- "; ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); if (!lits.empty()) tout << "\n"; display_deps(tout, dep);); + TRACE("seq", + tout << "assert:"; + ctx.display_detailed_literal(tout, lit); + tout << " <- "; ctx.display_literals_verbose(tout, lits); + if (!lits.empty()) tout << "\n"; display_deps(tout, dep);); justification* js = ctx.mk_justification( ext_theory_propagation_justification( get_id(), ctx.get_region(), lits.size(), lits.c_ptr(), eqs.size(), eqs.c_ptr(), lit)); m_new_propagation = true; - ctx.assign(lit, js); + ctx.assign(lit, js); } void theory_seq::set_conflict(dependency* dep, literal_vector const& _lits) { @@ -783,7 +868,7 @@ void theory_seq::set_conflict(dependency* dep, literal_vector const& _lits) { enode_pair_vector eqs; literal_vector lits(_lits); linearize(dep, eqs, lits); - TRACE("seq", display_deps(tout, lits, eqs);); + TRACE("seq", display_deps(tout << "assert conflict:", lits, eqs);); m_new_propagation = true; ctx.set_conflict( ctx.mk_justification( @@ -800,7 +885,7 @@ void theory_seq::propagate_eq(dependency* dep, enode* n1, enode* n2) { enode_pair_vector eqs; linearize(dep, eqs, lits); TRACE("seq", - tout << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << " <- \n"; + tout << "assert:" << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << " <- \n"; display_deps(tout, dep); ); @@ -1484,6 +1569,30 @@ bool theory_seq::solve_ne(unsigned idx) { return updated; } +bool theory_seq::solve_nc(unsigned idx) { + context& ctx = get_context(); + nc const& n = m_ncs[idx]; + + dependency* deps = n.deps(); + expr_ref c = canonize(n.contains(), deps); + + CTRACE("seq", c != n.contains(), tout << n.contains() << " => " << c << "\n";); + + if (m.is_true(c)) { + literal_vector lits; + set_conflict(deps, lits); + return true; + } + if (m.is_false(c)) { + return true; + } + if (c != n.contains()) { + m_ncs.push_back(nc(c, deps)); + return true; + } + return false; +} + theory_seq::cell* theory_seq::mk_cell(cell* p, expr* e, dependency* d) { cell* c = alloc(cell, p, e, d); m_all_cells.push_back(c); @@ -1747,6 +1856,20 @@ void theory_seq::display(std::ostream & out) const { out << "Exclusions:\n"; m_exclude.display(out); } + + if (!m_length.empty()) { + obj_hashtable::iterator it = m_length.begin(), end = m_length.end(); + for (; it != end; ++it) { + expr* e = *it; + rational lo(-1), hi(-1); + lower_bound(e, lo); + upper_bound(e, hi); + if (lo.is_pos() || !hi.is_minus_one()) { + out << mk_pp(e, m) << " [" << lo << ":" << hi << "]\n"; + } + } + } + } void theory_seq::display_equations(std::ostream& out) const { @@ -2360,7 +2483,7 @@ void theory_seq::propagate_in_re(expr* n, bool is_true) { propagate_lit(0, 1, &lit, lits[1]); } else { - TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); + TRACE("seq", ctx.display_literals_verbose(tout, lits); tout << "\n";); ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); } } @@ -2393,7 +2516,7 @@ static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { } } -bool theory_seq::lower_bound(expr* _e, rational& lo) { +bool theory_seq::lower_bound(expr* _e, rational& lo) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); @@ -2402,7 +2525,7 @@ bool theory_seq::lower_bound(expr* _e, rational& lo) { return m_autil.is_numeral(_lo, lo) && lo.is_int(); } -bool theory_seq::upper_bound(expr* _e, rational& hi) { +bool theory_seq::upper_bound(expr* _e, rational& hi) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); @@ -2411,7 +2534,7 @@ bool theory_seq::upper_bound(expr* _e, rational& hi) { return m_autil.is_numeral(_hi, hi) && hi.is_int(); } -bool theory_seq::get_length(expr* e, rational& val) { +bool theory_seq::get_length(expr* e, rational& val) const { context& ctx = get_context(); theory* th = ctx.get_theory(m_autil.get_family_id()); if (!th) return false; @@ -2705,7 +2828,6 @@ literal theory_seq::mk_eq_empty(expr* _e, bool phase) { literal lit = mk_eq(e, emp, false); ctx.force_phase(phase?lit:~lit); ctx.mark_as_relevant(lit); - TRACE("seq", tout << mk_pp(e, m) << " = empty\n";); return lit; } @@ -2718,7 +2840,7 @@ void theory_seq::add_axiom(literal l1, literal l2, literal l3, literal l4, liter if (l3 != null_literal && l3 != false_literal) { ctx.mark_as_relevant(l3); lits.push_back(l3); } if (l4 != null_literal && l4 != false_literal) { ctx.mark_as_relevant(l4); lits.push_back(l4); } if (l5 != null_literal && l5 != false_literal) { ctx.mark_as_relevant(l5); lits.push_back(l5); } - TRACE("seq", ctx.display_literals_verbose(tout << "axiom: ", lits.size(), lits.c_ptr()); tout << "\n";); + TRACE("seq", ctx.display_literals_verbose(tout << "assert: ", lits); tout << "\n";); m_new_propagation = true; ++m_stats.m_add_axiom; ctx.mk_th_axiom(get_id(), lits.size(), lits.c_ptr()); @@ -2775,8 +2897,8 @@ void theory_seq::propagate_eq(dependency* deps, literal_vector const& _lits, exp new_eq_eh(deps, n1, n2); } TRACE("seq", - ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); - tout << " => " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "\n";); + tout << "assert: " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "<- \n"; + ctx.display_literals_verbose(tout, lits);); justification* js = ctx.mk_justification( ext_theory_eq_propagation_justification( @@ -2848,10 +2970,15 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { } else if (!canonizes(false, e)) { propagate_non_empty(lit, e2); +#if 0 + dependency* dep = m_dm.mk_leaf(assumption(lit)); + m_ncs.push_back(nc(expr_ref(e, m), dep)); +#else propagate_lit(0, 1, &lit, ~mk_literal(m_util.str.mk_prefix(e2, e1))); if (add_contains2contains(e, change)) { add_atom(e); } +#endif } } else if (is_accept(e)) { @@ -2921,7 +3048,7 @@ void theory_seq::new_diseq_eh(theory_var v1, theory_var v2) { expr_ref eq(m.mk_eq(e1, e2), m); m_rewrite(eq); if (!m.is_false(eq)) { - TRACE("seq", tout << "new disequality: " << eq << "\n";); + TRACE("seq", tout << "new disequality " << get_context().get_scope_level() << ": " << eq << "\n";); literal lit = mk_eq(e1, e2, false); @@ -2980,6 +3107,7 @@ void theory_seq::push_scope_eh() { m_trail_stack.push(value_trail(m_axioms_head)); m_eqs.push_scope(); m_nqs.push_scope(); + m_ncs.push_scope(); m_atoms_lim.push_back(m_atoms.size()); } @@ -2992,6 +3120,7 @@ void theory_seq::pop_scope_eh(unsigned num_scopes) { m_exclude.pop_scope(num_scopes); m_eqs.pop_scope(num_scopes); m_nqs.pop_scope(num_scopes); + m_ncs.pop_scope(num_scopes); m_atoms.resize(m_atoms_lim[m_atoms_lim.size()-num_scopes]); m_atoms_lim.shrink(m_atoms_lim.size()-num_scopes); m_rewrite.reset(); @@ -3196,7 +3325,7 @@ bool theory_seq::add_accept2step(expr* acc, bool& change) { if (has_undef) { return true; } - TRACE("seq", ctx.display_literals_verbose(tout, lits.size(), lits.c_ptr()); tout << "\n";); + TRACE("seq", ctx.display_literals_verbose(tout, lits); tout << "\n";); for (unsigned i = 0; i < lits.size(); ++i) { SASSERT(ctx.get_assignment(lits[i]) == l_false); lits[i].neg(); @@ -3353,7 +3482,6 @@ void theory_seq::propagate_not_prefix(expr* e) { void theory_seq::propagate_not_prefix2(expr* e) { context& ctx = get_context(); - //std::cout << mk_pp(e, m) << " " << ctx.get_scope_level() << "\n"; expr* e1, *e2; VERIFY(m_util.str.is_prefix(e, e1, e2)); literal lit = ctx.get_literal(e); @@ -3423,7 +3551,7 @@ bool theory_seq::add_prefix2prefix(expr* e, bool& change) { switch (ctx.get_assignment(e2_is_emp)) { case l_true: TRACE("seq", tout << mk_pp(e, m) << ": " << mk_pp(e2, m) << " = empty\n"; - ctx.display_literals_verbose(tout, 1, &e2_is_emp); tout << "\n"; ); + ctx.display_literal_verbose(tout, e2_is_emp); tout << "\n"; ); return false; // done case l_undef: // ctx.force_phase(e2_is_emp); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 23ab25905..c600e443f 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -191,6 +191,27 @@ namespace smt { expr_ref const& r() const { return m_r; } }; + class nc { + expr_ref m_contains; + dependency* m_dep; + public: + nc(expr_ref const& c, dependency* dep): + m_contains(c), + m_dep(dep) {} + nc(nc const& other): + m_contains(other.m_contains), + m_dep(other.m_dep) {} + nc& operator=(nc const& other) { + if (this != &other) { + m_contains = other.m_contains; + m_dep = other.m_dep; + } + return *this; + } + dependency* deps() const { return m_dep; } + expr_ref const& contains() const { return m_contains; } + }; + class apply { public: virtual ~apply() {} @@ -263,12 +284,14 @@ namespace smt { unsigned m_add_axiom; unsigned m_extensionality; unsigned m_fixed_length; + unsigned m_propagate_contains; }; ast_manager& m; dependency_manager m_dm; solution_map m_rep; // unification representative. scoped_vector m_eqs; // set of current equations. scoped_vector m_nqs; // set of current disequalities. + scoped_vector m_ncs; // set of non-contains constraints. unsigned m_eq_id; seq_factory* m_factory; // value factory @@ -335,12 +358,14 @@ namespace smt { 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 propagate_length_coherence(expr* e); 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 simplify_eq(expr_ref_vector& l, expr_ref_vector& r, dependency* dep); @@ -362,6 +387,7 @@ namespace smt { expr_ref mk_concat(expr* e1, expr* e2, expr* e3) { return expr_ref(m_util.str.mk_concat(e1, e2, e3), m); } bool solve_nqs(unsigned i); bool solve_ne(unsigned i); + bool solve_nc(unsigned i); struct cell { cell* m_parent; @@ -452,9 +478,9 @@ namespace smt { // arithmetic integration - bool lower_bound(expr* s, rational& lo); - bool upper_bound(expr* s, rational& hi); - bool get_length(expr* s, rational& val); + bool lower_bound(expr* s, rational& lo) const; + bool upper_bound(expr* s, rational& hi) const; + 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); @@ -494,7 +520,7 @@ namespace smt { void ensure_nth(literal lit, expr* s, expr* idx); bool canonizes(bool sign, expr* e); void propagate_non_empty(literal lit, expr* s); - void propagate_is_conc(expr* e, expr* conc); + bool propagate_is_conc(expr* e, expr* conc); void propagate_acc_rej_length(literal lit, expr* acc_rej); bool propagate_automata(); void add_atom(expr* e); From e659845bc0f5a2b8e46cb5ff1100131b164a1821 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 27 Feb 2016 09:56:11 -0800 Subject: [PATCH 28/57] tune handling of contains, avoid redundant equalities, merge use of indexof.left/right with contains.left/right adding only least-ness constraints in the context of index Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 27 +++++++++++++++++---------- src/smt/theory_seq.h | 12 ++++++++++-- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index cfd043895..c3c8a004c 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -182,6 +182,7 @@ theory_seq::theory_seq(ast_manager& m): m(m), m_rep(m, m_dm), m_eq_id(0), + m_find(*this), m_factory(0), m_exclude(m), m_axioms(m), @@ -200,8 +201,6 @@ theory_seq::theory_seq(ast_manager& m): m_mk_aut(m) { m_prefix = "seq.prefix.suffix"; m_suffix = "seq.suffix.prefix"; - m_contains_left = "seq.contains.left"; - m_contains_right = "seq.contains.right"; m_accept = "aut.accept"; m_reject = "aut.reject"; m_tail = "seq.tail"; @@ -585,7 +584,6 @@ bool theory_seq::fixed_length(expr* e) { } if (is_skolem(m_tail, e) || is_skolem(m_seq_first, e) || is_skolem(m_indexof_left, e) || is_skolem(m_indexof_right, e) || - is_skolem(m_contains_left, e) || is_skolem(m_contains_right, e) || m_fixed.contains(e)) { return false; } @@ -885,8 +883,8 @@ void theory_seq::propagate_eq(dependency* dep, enode* n1, enode* n2) { enode_pair_vector eqs; linearize(dep, eqs, lits); TRACE("seq", - tout << "assert:" << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << " <- \n"; - display_deps(tout, dep); + tout << "assert: " << mk_pp(n1->get_owner(), m) << " = " << mk_pp(n2->get_owner(), m) << " <-\n"; + display_deps(tout, dep); ); justification* js = ctx.mk_justification( @@ -944,6 +942,7 @@ bool theory_seq::simplify_eq(expr_ref_vector& ls, expr_ref_vector& rs, dependenc // no-op } else if (m_util.is_seq(li) || m_util.is_re(li)) { + TRACE("seq", tout << "inserting " << li << " = " << ri << "\n";); m_eqs.push_back(mk_eqdep(li, ri, deps)); } else { @@ -1101,6 +1100,7 @@ bool theory_seq::solve_eq(expr_ref_vector const& l, expr_ref_vector const& r, de return true; } if (!ctx.inconsistent() && change) { + TRACE("seq", tout << "inserting equality\n";); m_eqs.push_back(eq(m_eq_id++, ls, rs, deps)); return true; } @@ -2096,6 +2096,7 @@ theory_var theory_seq::mk_var(enode* n) { } else { theory_var v = theory::mk_var(n); + m_find.mk_var(); get_context().attach_th_var(n, this, v); get_context().mark_as_relevant(n); return v; @@ -2897,8 +2898,8 @@ void theory_seq::propagate_eq(dependency* deps, literal_vector const& _lits, exp new_eq_eh(deps, n1, n2); } TRACE("seq", - tout << "assert: " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << "<- \n"; - ctx.display_literals_verbose(tout, lits);); + tout << "assert: " << mk_pp(e1, m) << " = " << mk_pp(e2, m) << " <- \n"; + if (!lits.empty()) { ctx.display_literals_verbose(tout, lits); tout << "\n"; }); justification* js = ctx.mk_justification( ext_theory_eq_propagation_justification( @@ -2963,14 +2964,14 @@ void theory_seq::assign_eh(bool_var v, bool is_true) { } else if (m_util.str.is_contains(e, e1, e2)) { if (is_true) { - expr_ref f1 = mk_skolem(m_contains_left, e1, e2); - expr_ref f2 = mk_skolem(m_contains_right, e1, e2); + expr_ref f1 = mk_skolem(m_indexof_left, e1, e2); + expr_ref f2 = mk_skolem(m_indexof_right, e1, e2); f = mk_concat(f1, e2, f2); propagate_eq(lit, f, e1, true); } else if (!canonizes(false, e)) { propagate_non_empty(lit, e2); -#if 0 +#if 1 dependency* dep = m_dm.mk_leaf(assumption(lit)); m_ncs.push_back(nc(expr_ref(e, m), dep)); #else @@ -3030,6 +3031,12 @@ void theory_seq::new_eq_eh(theory_var v1, theory_var v2) { void theory_seq::new_eq_eh(dependency* deps, enode* n1, enode* n2) { if (n1 != n2 && m_util.is_seq(n1->get_owner())) { + theory_var v1 = n1->get_th_var(get_id()); + theory_var v2 = n2->get_th_var(get_id()); + if (m_find.find(v1) == m_find.find(v2)) { + return; + } + m_find.merge(v1, v2); expr_ref o1(n1->get_owner(), m); expr_ref o2(n2->get_owner(), m); TRACE("seq", tout << o1 << " = " << o2 << "\n";); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index c600e443f..2d6631387 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -28,6 +28,7 @@ Revision History: #include "scoped_ptr_vector.h" #include "automaton.h" #include "seq_rewriter.h" +#include "union_find.h" namespace smt { @@ -44,6 +45,7 @@ namespace smt { typedef trail_stack th_trail_stack; typedef std::pair expr_dep; typedef obj_map eqdep_map_t; + typedef union_find th_union_find; class seq_value_proc; @@ -292,7 +294,8 @@ namespace smt { scoped_vector m_eqs; // set of current equations. scoped_vector m_nqs; // set of current disequalities. scoped_vector m_ncs; // set of non-contains constraints. - unsigned m_eq_id; + unsigned m_eq_id; + th_union_find m_find; seq_factory* m_factory; // value factory exclusion_table m_exclude; // set of asserted disequalities. @@ -309,7 +312,7 @@ namespace smt { arith_util m_autil; th_trail_stack m_trail_stack; stats m_stats; - symbol m_prefix, m_suffix, m_contains_left, m_contains_right, m_accept, m_reject; + 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; ptr_vector m_todo; @@ -539,6 +542,11 @@ namespace smt { // model building app* mk_value(app* a); + th_trail_stack& get_trail_stack() { return m_trail_stack; } + void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2) {} + 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) {} + }; }; From 4cf72e23e6d51df47ed67c35ea9a90016d9b69d5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 27 Feb 2016 09:58:45 -0800 Subject: [PATCH 29/57] fix python 3 compat Signed-off-by: Nikolaj Bjorner --- scripts/mk_util.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index fc0d3b5e0..e32c9c3dd 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2199,7 +2199,7 @@ def mk_config(): 'LINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE /NXCOMPAT\n' 'SLINK_EXTRA_FLAGS=/link /DEBUG /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608 /OPT:REF /OPT:ICF /TLBID:1 /DYNAMICBASE:NO\n') elif VS_ARM: - print "ARM on VS is unsupported" + print("ARM on VS is unsupported") exit(1) else: config.write( @@ -2225,7 +2225,7 @@ def mk_config(): 'LINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:CONSOLE /INCREMENTAL:NO /STACK:8388608\n' 'SLINK_EXTRA_FLAGS=/link%s /MACHINE:X64 /SUBSYSTEM:WINDOWS /INCREMENTAL:NO /STACK:8388608\n' % (LTCG, LTCG)) elif VS_ARM: - print "ARM on VS is unsupported" + print("ARM on VS is unsupported") exit(1) else: config.write( From 51687b2be7812b41f6616055120b00813cfe3509 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 28 Feb 2016 10:56:48 +0000 Subject: [PATCH 30/57] bv_bounds: ensure (bvule x maxuint) is simplified to true --- src/tactic/bv/bv_bounds_tactic.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 16154c7ef..4d90ccfb0 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -324,7 +324,9 @@ public: interval ctx, intr; result = 0; - if (m_bound->find(t1, ctx)) { + if (b.is_full() && b.tight) { + result = m.mk_true(); + } else if (m_bound->find(t1, ctx)) { if (ctx.implies(b)) { result = m.mk_true(); } else if (!b.intersect(ctx, intr)) { @@ -332,8 +334,6 @@ public: } else if (m_propagate_eq && intr.is_singleton()) { result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); } - } else if (b.is_full() && b.tight) { - result = m.mk_true(); } CTRACE("bv", result != 0, tout << mk_pp(t, m) << " " << b << " (ctx: " << ctx << ") (intr: " << intr << "): " << result << "\n";); @@ -358,7 +358,7 @@ public: interval b; // skip common case: single bound constraint without any context for simplification if (is_bound(t, t1, b)) { - return m_bound->contains(t1); + return b.is_full() || m_bound->contains(t1); } return expr_has_bounds(t); } From e7a360ca08b6d58410aa9e2ea73e3ec767ea4611 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 28 Feb 2016 17:57:40 +0000 Subject: [PATCH 31/57] ctx_simplify: remove virtual push() method --- src/tactic/bv/bv_bounds_tactic.cpp | 2 +- src/tactic/core/ctx_simplify_tactic.cpp | 6 +----- src/tactic/core/ctx_simplify_tactic.h | 1 - 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 4d90ccfb0..cc50c1a8c 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -363,7 +363,7 @@ public: return expr_has_bounds(t); } - virtual void push() { + void push() { TRACE("bv", tout << "push\n";); unsigned sz = m_scopes.size(); m_scopes.resize(sz + 1); diff --git a/src/tactic/core/ctx_simplify_tactic.cpp b/src/tactic/core/ctx_simplify_tactic.cpp index f127614aa..1cda9cc6f 100644 --- a/src/tactic/core/ctx_simplify_tactic.cpp +++ b/src/tactic/core/ctx_simplify_tactic.cpp @@ -36,7 +36,7 @@ public: virtual ~ctx_propagate_assertions() {} virtual bool assert_expr(expr * t, bool sign); virtual bool simplify(expr* t, expr_ref& result); - virtual void push(); + void push(); virtual void pop(unsigned num_scopes); virtual unsigned scope_level() const { return m_scopes.size(); } virtual simplifier * translate(ast_manager & m); @@ -260,10 +260,6 @@ struct ctx_simplify_tactic::imp { return m_simp->scope_level(); } - void push() { - m_simp->push(); - } - void restore_cache(unsigned lvl) { if (lvl >= m_cache_undo.size()) return; diff --git a/src/tactic/core/ctx_simplify_tactic.h b/src/tactic/core/ctx_simplify_tactic.h index 7b1507f67..d6ebf5cbd 100644 --- a/src/tactic/core/ctx_simplify_tactic.h +++ b/src/tactic/core/ctx_simplify_tactic.h @@ -31,7 +31,6 @@ public: virtual bool assert_expr(expr * t, bool sign) = 0; virtual bool simplify(expr* t, expr_ref& result) = 0; virtual bool may_simplify(expr* t) { return true; } - virtual void push() = 0; virtual void pop(unsigned num_scopes) = 0; virtual simplifier * translate(ast_manager & m) = 0; virtual unsigned scope_level() const = 0; From c1eb1cc3f2be0f92403432d4e3479748f65d32a5 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Sun, 28 Feb 2016 20:07:39 +0000 Subject: [PATCH 32/57] bv_bounds: improve perf of push/pop --- src/tactic/bv/bv_bounds_tactic.cpp | 85 +++++++++++++++++++----------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index cc50c1a8c..432d3b219 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -152,20 +152,27 @@ std::ostream& operator<<(std::ostream& o, const interval& I) { } +struct undo_bound { + expr* e; + interval b; + bool fresh; + undo_bound(expr* e, const interval& b, bool fresh) : e(e), b(b), fresh(fresh) {} +}; + class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { typedef obj_map map; typedef obj_map expr_set; typedef obj_map expr_list_map; - ast_manager& m; - params_ref m_params; - bool m_propagate_eq; - bv_util m_bv; - vector m_scopes; - map *m_bound; - expr_list_map m_expr_vars; + ast_manager& m; + params_ref m_params; + bool m_propagate_eq; + bv_util m_bv; + vector m_scopes; + map m_bound; + expr_list_map m_expr_vars; - bool is_bound(expr *e, expr*& v, interval& b) { + bool is_bound(expr *e, expr*& v, interval& b) const { rational n; expr *lhs, *rhs; unsigned sz; @@ -253,8 +260,6 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { public: bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { - m_scopes.push_back(map()); - m_bound = &m_scopes.back(); updt_params(p); } @@ -285,10 +290,21 @@ public: if (sign) VERIFY(b.negate(b)); - push(); TRACE("bv", tout << (sign?"(not ":"") << mk_pp(t, m) << (sign ? ")" : "") << ": " << mk_pp(t1, m) << " in " << b << "\n";); - interval& r = m_bound->insert_if_not_there2(t1, b)->get_data().m_value; - return r.intersect(b, r); + map::obj_map_entry* e = m_bound.find_core(t1); + if (e) { + interval& old = e->get_data().m_value; + interval intr; + if (!old.intersect(b, intr)) + return false; + if (old == intr) + return true; + m_scopes.insert(undo_bound(t1, old, false)); + old = intr; + } else { + m_bound.insert(t1, b); + m_scopes.insert(undo_bound(t1, interval(), true)); + } } return true; } @@ -297,7 +313,7 @@ public: expr* t1; interval b; - if (m_bound->find(t, b) && b.is_singleton()) { + if (m_bound.find(t, b) && b.is_singleton()) { result = m_bv.mk_numeral(b.l, m_bv.get_bv_size(t)); return true; } @@ -326,7 +342,7 @@ public: if (b.is_full() && b.tight) { result = m.mk_true(); - } else if (m_bound->find(t1, ctx)) { + } else if (m_bound.find(t1, ctx)) { if (ctx.implies(b)) { result = m.mk_true(); } else if (!b.intersect(ctx, intr)) { @@ -346,36 +362,43 @@ public: if (m_bv.is_numeral(t)) return false; + while (m.is_not(t, t)); + expr_set* used_exprs = get_expr_vars(t); - for (map::iterator I = m_bound->begin(), E = m_bound->end(); I != E; ++I) { + for (map::iterator I = m_bound.begin(), E = m_bound.end(); I != E; ++I) { if (I->m_value.is_singleton() && used_exprs->contains(I->m_key)) return true; } - while (m.is_not(t, t)); - expr* t1; interval b; // skip common case: single bound constraint without any context for simplification if (is_bound(t, t1, b)) { - return b.is_full() || m_bound->contains(t1); + return b.is_full() || m_bound.contains(t1); } return expr_has_bounds(t); } - void push() { - TRACE("bv", tout << "push\n";); - unsigned sz = m_scopes.size(); - m_scopes.resize(sz + 1); - m_bound = &m_scopes.back(); - m_bound->~map(); - new (m_bound) map(m_scopes[sz - 1]); - } - virtual void pop(unsigned num_scopes) { TRACE("bv", tout << "pop: " << num_scopes << "\n";); - m_scopes.shrink(m_scopes.size() - num_scopes); - m_bound = &m_scopes.back(); + if (m_scopes.empty()) + return; + unsigned target = m_scopes.size() - num_scopes; + if (target == 0) { + m_bound.reset(); + m_scopes.reset(); + return; + } + for (unsigned i = m_scopes.size()-1; i >= target; --i) { + undo_bound& undo = m_scopes[i]; + SASSERT(m_bound.contains(undo.e)); + if (undo.fresh) { + m_bound.erase(undo.e); + } else { + m_bound.insert(undo.e, undo.b); + } + } + m_scopes.shrink(target); } virtual simplifier * translate(ast_manager & m) { @@ -383,7 +406,7 @@ public: } virtual unsigned scope_level() const { - return m_scopes.size() - 1; + return m_scopes.size(); } }; From df2d7e7628528f99b756c964d5c482f39fa00146 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 28 Feb 2016 17:05:12 -0800 Subject: [PATCH 33/57] add intersection using symbolic automata facility Signed-off-by: Nikolaj Bjorner --- src/ast/rewriter/seq_rewriter.cpp | 131 ++++++++++++- src/ast/rewriter/seq_rewriter.h | 28 ++- src/math/automata/automaton.h | 19 +- src/math/automata/boolean_algebra.h | 46 +++++ src/math/automata/symbolic_automata.h | 104 ++++++++++ src/math/automata/symbolic_automata_def.h | 227 ++++++++++++++++++++++ src/smt/theory_seq.cpp | 33 +++- src/smt/theory_seq.h | 1 + 8 files changed, 561 insertions(+), 28 deletions(-) create mode 100644 src/math/automata/boolean_algebra.h create mode 100644 src/math/automata/symbolic_automata.h create mode 100644 src/math/automata/symbolic_automata_def.h diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index a26fcfe2c..b5d07ae1e 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -25,6 +25,8 @@ Notes: #include"automaton.h" #include"well_sorted.h" #include"var_subst.h" +#include"symbolic_automata_def.h" + expr_ref sym_expr::accept(expr* e) { ast_manager& m = m_t.get_manager(); @@ -37,6 +39,7 @@ expr_ref sym_expr::accept(expr* e) { } case t_char: SASSERT(m.get_sort(e) == m.get_sort(m_t)); + SASSERT(m.get_sort(e) == m_sort); result = m.mk_eq(e, m_t); break; case t_range: { @@ -67,8 +70,114 @@ struct display_expr1 { } }; +class sym_expr_boolean_algebra : public boolean_algebra { + ast_manager& m; + expr_solver& m_solver; + typedef sym_expr* T; +public: + sym_expr_boolean_algebra(ast_manager& m, expr_solver& s): + m(m), m_solver(s) {} + + virtual T mk_false() { + 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() { + 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) { + if (x->is_char() && y->is_char()) { + if (x->get_char() == y->get_char()) { + return x; + } + if (m.are_distinct(x->get_char(), y->get_char())) { + expr_ref fml(m.mk_false(), m); + return sym_expr::mk_pred(fml, x->sort()); + } + } + var_ref v(m.mk_var(0, x->sort()), m); + expr_ref fml1 = x->accept(v); + expr_ref fml2 = y->accept(v); + if (m.is_true(fml1)) return y; + if (m.is_true(fml2)) return x; + expr_ref fml(m.mk_and(fml1, fml2), m); + return sym_expr::mk_pred(fml, x->sort()); + } + virtual T mk_or(T x, T y) { + if (x->is_char() && y->is_char() && + x->get_char() == y->get_char()) { + return x; + } + var_ref v(m.mk_var(0, x->sort()), m); + expr_ref fml1 = x->accept(v); + expr_ref fml2 = y->accept(v); + if (m.is_false(fml1)) return y; + if (m.is_false(fml2)) return x; + expr_ref fml(m.mk_or(fml1, fml2), m); + return sym_expr::mk_pred(fml, x->sort()); + } + + virtual T mk_and(unsigned sz, T const* ts) { + switch (sz) { + case 0: return mk_true(); + case 1: return ts[0]; + default: { + T t = ts[0]; + for (unsigned i = 1; i < sz; ++i) { + t = mk_and(t, ts[i]); + } + return t; + } + } + } + virtual T mk_or(unsigned sz, T const* ts) { + switch (sz) { + case 0: return mk_false(); + case 1: return ts[0]; + default: { + T t = ts[0]; + for (unsigned i = 1; i < sz; ++i) { + t = mk_or(t, ts[i]); + } + return t; + } + } + } + virtual lbool is_sat(T x) { + if (x->is_char()) { + return l_true; + } + if (x->is_range()) { + // TBD check lower is below upper. + } + expr_ref v(m.mk_fresh_const("x", x->sort()), m); + expr_ref fml = x->accept(v); + if (m.is_true(fml)) { + return l_true; + } + if (m.is_false(fml)) { + return l_false; + } + return m_solver.check_sat(fml); + } + virtual T mk_not(T x) { + var_ref v(m.mk_var(0, x->sort()), m); + expr_ref fml(m.mk_not(x->accept(v)), m); + return sym_expr::mk_pred(fml, x->sort()); + } +}; + +re2automaton::re2automaton(ast_manager& m): m(m), u(m), bv(m), m_ba(0), m_sa(0) {} + +re2automaton::~re2automaton() {} + +void re2automaton::set_solver(expr_solver* solver) { + m_solver = solver; + m_ba = alloc(sym_expr_boolean_algebra, m, *solver); + m_sa = alloc(symbolic_automata_t, sm, *m_ba.get()); +} -re2automaton::re2automaton(ast_manager& m): m(m), u(m), bv(m) {} eautomaton* re2automaton::operator()(expr* e) { eautomaton* r = re2aut(e); @@ -136,7 +245,7 @@ eautomaton* re2automaton::re2aut(expr* e) { expr_ref _start(bv.mk_numeral(start, nb), m); expr_ref _stop(bv.mk_numeral(stop, nb), m); expr_ref _pred(m.mk_not(m.mk_and(bv.mk_ule(_start, v), bv.mk_ule(v, _stop))), m); - a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred)); + a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred, s)); return a.detach(); } else if (u.re.is_to_re(e0, e1) && u.str.is_string(e1, s1) && s1.length() == 1) { @@ -145,13 +254,14 @@ eautomaton* re2automaton::re2aut(expr* e) { expr_ref v(m.mk_var(0, s), m); expr_ref _ch(bv.mk_numeral(s1[0], nb), m); expr_ref _pred(m.mk_not(m.mk_eq(v, _ch)), m); - a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred)); + a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred, s)); return a.detach(); } else if (u.re.is_to_re(e0, e1) && u.str.is_unit(e1, e2)) { - expr_ref v(m.mk_var(0, m.get_sort(e2)), m); + sort* s = m.get_sort(e2); + expr_ref v(m.mk_var(0, s), m); expr_ref _pred(m.mk_not(m.mk_eq(v, e2)), m); - a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred)); + a = alloc(eautomaton, sm, sym_expr::mk_pred(_pred, s)); return a.detach(); } else { @@ -187,14 +297,15 @@ eautomaton* re2automaton::re2aut(expr* e) { } else if (u.re.is_full(e)) { expr_ref tt(m.mk_true(), m); - sym_expr* _true = sym_expr::mk_pred(tt); + sort* seq_s, *char_s; + 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); } -#if 0 - else if (u.re.is_intersect(e, e1, e2)) { - // maybe later + else if (u.re.is_intersection(e, e1, e2) && m_sa && (a = re2aut(e1)) && (b = re2aut(e2))) { + return m_sa->mk_product(*a, *b); } -#endif return 0; } diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 83dd8f653..718658652 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -25,6 +25,7 @@ Notes: #include"params.h" #include"lbool.h" #include"automaton.h" +#include"symbolic_automata.h" class sym_expr { enum ty { @@ -33,21 +34,24 @@ class sym_expr { t_range }; ty m_ty; + sort* m_sort; expr_ref m_t; expr_ref m_s; unsigned m_ref; - sym_expr(ty ty, expr_ref& t, expr_ref& s) : m_ty(ty), m_t(t), m_s(s), m_ref(0) {} + sym_expr(ty ty, expr_ref& t, expr_ref& s, sort* srt) : m_ty(ty), m_sort(srt), m_t(t), m_s(s), m_ref(0) {} public: expr_ref accept(expr* e); - static sym_expr* mk_char(expr_ref& t) { return alloc(sym_expr, t_char, t, t); } + static sym_expr* mk_char(expr_ref& t) { return alloc(sym_expr, t_char, t, t, t.get_manager().get_sort(t)); } static sym_expr* mk_char(ast_manager& m, expr* t) { expr_ref tr(t, m); return mk_char(tr); } - static sym_expr* mk_pred(expr_ref& t) { return alloc(sym_expr, t_pred, t, t); } - static sym_expr* mk_range(expr_ref& lo, expr_ref& hi) { return alloc(sym_expr, t_range, lo, hi); } + static sym_expr* mk_pred(expr_ref& t, sort* s) { return alloc(sym_expr, t_pred, t, t, s); } + static sym_expr* mk_range(expr_ref& lo, expr_ref& hi) { return alloc(sym_expr, t_range, lo, hi, lo.get_manager().get_sort(hi)); } void inc_ref() { ++m_ref; } void dec_ref() { --m_ref; if (m_ref == 0) dealloc(this); } std::ostream& display(std::ostream& out) const; bool is_char() const { return m_ty == t_char; } bool is_pred() const { return !is_char(); } + bool is_range() const { return m_ty == t_range; } + sort* sort() const { return m_sort; } expr* get_char() const { SASSERT(is_char()); return m_t; } }; @@ -58,17 +62,31 @@ public: void dec_ref(sym_expr* s) { if (s) s->dec_ref(); } }; +class expr_solver { +public: + virtual ~expr_solver() {} + virtual lbool check_sat(expr* e) = 0; +}; + typedef automaton eautomaton; class re2automaton { + typedef boolean_algebra boolean_algebra_t; + typedef symbolic_automata symbolic_automata_t; ast_manager& m; sym_expr_manager sm; seq_util u; bv_util bv; + scoped_ptr m_solver; + scoped_ptr m_ba; + scoped_ptr m_sa; + eautomaton* re2aut(expr* e); eautomaton* seq2aut(expr* e); - public: +public: re2automaton(ast_manager& m); + ~re2automaton(); eautomaton* operator()(expr* e); + void set_solver(expr_solver* solver); }; /** diff --git a/src/math/automata/automaton.h b/src/math/automata/automaton.h index 54c969bbb..dd1ff87f5 100644 --- a/src/math/automata/automaton.h +++ b/src/math/automata/automaton.h @@ -178,15 +178,19 @@ public: return alloc(automaton, a.m, a.init(), final, mvs); } + automaton* clone() const { + return clone(*this); + } + // create the sum of disjoint automata static automaton* mk_union(automaton const& a, automaton const& b) { SASSERT(&a.m == &b.m); M& m = a.m; if (a.is_empty()) { - return clone(b); + return b.clone(); } if (b.is_empty()) { - return clone(a); + return a.clone(); } moves mvs; unsigned_vector final; @@ -213,7 +217,7 @@ public: mvs.push_back(move(m, 0, a.init() + offset)); } if (a.is_empty()) { - return clone(a); + return a.clone(); } mvs.push_back(move(m, init, a.final_state() + offset)); @@ -227,16 +231,16 @@ public: SASSERT(&a.m == &b.m); M& m = a.m; if (a.is_empty()) { - return clone(a); + return a.clone(); } if (b.is_empty()) { - return clone(b); + return b.clone(); } if (a.is_epsilon()) { - return clone(b); + return b.clone(); } if (b.is_epsilon()) { - return clone(a); + return a.clone(); } moves mvs; @@ -458,6 +462,7 @@ public: } unsigned init() const { return m_init; } + unsigned_vector const& final_states() const { return m_final_states; } unsigned in_degree(unsigned state) const { return m_delta_inv[state].size(); } unsigned out_degree(unsigned state) const { return m_delta[state].size(); } move const& get_move_from(unsigned state) const { SASSERT(m_delta[state].size() == 1); return m_delta[state][0]; } diff --git a/src/math/automata/boolean_algebra.h b/src/math/automata/boolean_algebra.h new file mode 100644 index 000000000..503878ef3 --- /dev/null +++ b/src/math/automata/boolean_algebra.h @@ -0,0 +1,46 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + boolean_algebra.h + +Abstract: + + Boolean Algebra, a la Margus Veanes Automata library. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-2-27 + +Revision History: + + +--*/ + +#ifndef BOOLEAN_ALGEBRA_H_ +#define BOOLEAN_ALGEBRA_H_ + +#include "util.h" + +template +class positive_boolean_algebra { +public: + virtual T mk_false() = 0; + virtual T mk_true() = 0; + virtual T mk_and(T x, T y) = 0; + virtual T mk_or(T x, T y) = 0; + virtual T mk_and(unsigned sz, T const* ts) = 0; + virtual T mk_or(unsigned sz, T const* ts) = 0; + virtual lbool is_sat(T x) = 0; +}; + +template +class boolean_algebra : public positive_boolean_algebra { +public: + virtual T mk_not(T x) = 0; + //virtual lbool are_equivalent(T x, T y) = 0; + //virtual T simplify(T x) = 0; +}; + +#endif diff --git a/src/math/automata/symbolic_automata.h b/src/math/automata/symbolic_automata.h new file mode 100644 index 000000000..d77e0548d --- /dev/null +++ b/src/math/automata/symbolic_automata.h @@ -0,0 +1,104 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + symbolic_automata.h + +Abstract: + + Symbolic Automata over Boolean Algebras, a la Margus Veanes Automata library. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-02-27. + +Revision History: + + +--*/ + +#ifndef SYMBOLIC_AUTOMATA_H_ +#define SYMBOLIC_AUTOMATA_H_ + + +#include "automaton.h" +#include "boolean_algebra.h" + + +template > +class symbolic_automata { + typedef automaton automaton_t; + typedef boolean_algebra ba_t; + typedef typename automaton_t::move move_t; + typedef vector moves_t; + typedef obj_ref ref_t; + typedef ref_vector refs_t; + + M& m; + ba_t& m_ba; + + + class block { + uint_set m_set; + unsigned m_rep; + bool m_rep_chosen; + public: + + block(): m_rep(0), m_rep_chosen(false) {} + + block(uint_set const& s): + m_set(s), + m_rep(0), + m_rep_chosen(false) { + } + + block(unsigned_vector const& vs) { + for (unsigned i = 0; i < vs.size(); ++i) { + m_set.insert(vs[i]); + } + m_rep_chosen = false; + m_rep = 0; + } + + block& operator=(block const& b) { + m_set = b.m_set; + m_rep = 0; + m_rep_chosen = false; + return *this; + } + + unsigned get_representative() { + if (!m_rep_chosen) { + uint_set::iterator it = m_set.begin(); + if (m_set.end() != it) { + m_rep = *it; + } + m_rep_chosen = true; + } + return m_rep; + } + + void add(unsigned i) { m_set.insert(i); } + bool contains(unsigned i) const { return m_set.contains(i); } + bool is_empty() const { return m_set.empty(); } + unsigned size() const { return m_set.num_elems(); } + void remove(unsigned i) { m_set.remove(i); m_rep_chosen = false; } + void clear() { m_set.reset(); m_rep_chosen = false; } + uint_set::iterator begin() { return m_set.begin(); } + uint_set::iterator end() { return m_set.end(); } + }; + +public: + symbolic_automata(M& m, ba_t& ba): m(m), m_ba(ba) {} + automaton_t* mk_determinstic(automaton_t& a); + automaton_t* mk_complement(automaton_t& a); + automaton_t* remove_epsilons(automaton_t& a); + automaton_t* mk_total(automaton_t& a); + automaton_t* mk_minimize(automaton_t& a); + automaton_t* mk_product(automaton_t& a, automaton_t& b); +}; + + + +#endif diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h new file mode 100644 index 000000000..90dd98d10 --- /dev/null +++ b/src/math/automata/symbolic_automata_def.h @@ -0,0 +1,227 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + symbolic_automata_def.h + +Abstract: + + Symbolic Automata over Boolean Algebras, a la Margus Veanes Automata library. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-02-27. + +Revision History: + + +--*/ + +#ifndef SYMBOLIC_AUTOMATA_DEF_H_ +#define SYMBOLIC_AUTOMATA_DEF_H_ + + +#include "symbolic_automata.h" +#include "hashtable.h" + +typedef std::pair unsigned_pair; + + + +template +typename symbolic_automata::automaton_t* symbolic_automata::mk_total(automaton_t& a) { + unsigned dead_state = a.num_states(); + moves_t mvs; + for (unsigned i = 0; i < dead_state; ++i) { + mvs.reset(); + a.get_moves(i, mvs, true); + refs_t vs(m); + + for (unsigned j = 0; j < mvs.size(); ++j) { + mv.push_back(mvs[j]()); + } + ref_t cond(m_ba.mk_not(m_ba.mk_or(vs.size(), vs.c_ptr())), m); + lbool is_sat = m_ba.is_sat(cond); + if (is_sat == l_undef) { + return 0; + } + if (is_sat == l_true) { + new_mvs.push_back(move_t(m, i, dead_state, cond)); + } + } + if (new_mvs.empty()) { + return a.clone(); + } + new_mvs.push_back(move_t(m, dead_state, dead_state, m_ba.mk_true())); + automaton_t::append_moves(0, a, new_mvs); + + return alloc(automaton_t, m, a.init(), a.final_states(), new_mvs); +} + +template +typename symbolic_automata::automaton_t* symbolic_automata::mk_minimize(automaton_t& a) { + if (a.is_empty()) { + return a.clone(); + } + if (a.is_epsilon()) { + return a.clone(); + } + // SASSERT(a.is_deterministic()); + + scoped_ptr fa = mk_total(a); + if (!fa) { + return 0; + } + + block final_block(fa->final_states()); + block non_final_block(fa->non_final_states()); + vector blocks; + for (unsigned i = 0; i < fa->num_states(); ++i) { + if (fa->is_final_state(i)) { + blocks.push_back(final_block); + } + else { + blocks.push_back(non_final_block); + } + } + vector W; + if (final_block.size() > non_final_block.size()) { + W.push_back(non_final_block); + } + else { + W.push_back(final_block); + } + +#if 0 + + refs_t trail(m); + u_map gamma; + moves_t mvs; + while (!W.empty()) { + block R(W.back()); + W.pop_back(); + block Rcopy(R); + gamma.reset(); + uint_set::iterator it = Rcopy.begin(), end = Rcopy.end(); + for (; it != end; ++it) { + unsigned q = *it; + mvs.reset(); + fa->get_moves_to(q, mvs); + for (unsigned i = 0; i < mvs.size(); ++i) { + unsigned src = mvs[i].src(); + if (blocks[src].size() > 1) { + T* t = mvs[i](); + if (gamma.find(src, t1)) { + t = m_ba.mk_or(t, t1); + trail.push_back(t); + } + gamma.insert(src, t); + } + } + } + hashtable relevant; + u_map::iterator end = gamma.end(); + for (u_map::iterator it = gamma.begin(); it != end; ++it) { + relevant.insert(blocks[it->m_key]); + } + + } +#endif + + return 0; + +} + +template +typename symbolic_automata::automaton_t* symbolic_automata::mk_product(automaton_t& a, automaton_t& b) { + map, default_eq > state_ids; + unsigned_pair init_pair(a.init(), b.init()); + svector todo; + todo.push_back(init_pair); + state_ids.insert(init_pair, 0); + moves_t mvs; + unsigned_vector final; + if (a.is_final_state(a.init()) && b.is_final_state(b.init())) { + final.push_back(0); + } + unsigned n = 1; + moves_t mvsA, mvsB; + while (!todo.empty()) { + unsigned_pair curr_pair = todo.back(); + todo.pop_back(); + unsigned src = state_ids[curr_pair]; + mvsA.reset(); mvsB.reset(); + a.get_moves_from(curr_pair.first, mvsA, true); + b.get_moves_from(curr_pair.second, mvsB, true); + for (unsigned i = 0; i < mvsA.size(); ++i) { + for (unsigned j = 0; j < mvsB.size(); ++j) { + ref_t ab(m_ba.mk_and(mvsA[i].t(), mvsB[j].t()), m); + lbool is_sat = m_ba.is_sat(ab); + if (is_sat == l_false) { + continue; + } + else if (is_sat == l_undef) { + return 0; + } + unsigned_pair tgt_pair(mvsA[i].dst(), mvsB[j].dst()); + unsigned tgt; + if (!state_ids.find(tgt_pair, tgt)) { + tgt = n++; + state_ids.insert(tgt_pair, tgt); + todo.push_back(tgt_pair); + if (a.is_final_state(tgt_pair.first) && b.is_final_state(tgt_pair.second)) { + final.push_back(tgt); + } + } + mvs.push_back(move_t(m, src, tgt, ab)); + } + } + } + + if (final.empty()) { + return alloc(automaton_t, m); + } + vector inv(n, moves_t()); + for (unsigned i = 0; i < mvs.size(); ++i) { + move_t const& mv = mvs[i]; + inv[mv.dst()].push_back(move_t(m, mv.dst(), mv.src(), mv.t())); + } + + svector back_reachable(n, false); + for (unsigned i = 0; i < final.size(); ++i) { + back_reachable[final[i]] = true; + } + + unsigned_vector stack(final); + while (!stack.empty()) { + unsigned state = stack.back(); + stack.pop_back(); + moves_t const& mv = inv[state]; + for (unsigned i = 0; i < mv.size(); ++i) { + state = mv[i].dst(); + if (!back_reachable[state]) { + back_reachable[state] = true; + stack.push_back(state); + } + } + } + + moves_t mvs1; + for (unsigned i = 0; i < mvs.size(); ++i) { + move_t const& mv = mvs[i]; + if (back_reachable[mv.dst()]) { + mvs1.push_back(mv); + } + } + if (mvs1.empty()) { + return alloc(automaton_t, m); + } + else { + return alloc(automaton_t, m, 0, final, mvs1); + } +} + + + +#endif diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index c3c8a004c..551e80c85 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -25,6 +25,7 @@ Revision History: #include "theory_seq.h" #include "ast_trail.h" #include "theory_arith.h" +#include "smt_kernel.h" using namespace smt; @@ -36,6 +37,21 @@ struct display_expr { } }; +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_seq::solution_map::update(expr* e, expr* r, dependency* d) { @@ -199,26 +215,31 @@ theory_seq::theory_seq(ast_manager& m): m_new_solution(false), m_new_propagation(false), m_mk_aut(m) { - m_prefix = "seq.prefix.suffix"; - m_suffix = "seq.suffix.prefix"; - m_accept = "aut.accept"; - m_reject = "aut.reject"; + m_prefix = "seq.p.suffix"; + m_suffix = "seq.s.prefix"; + m_accept = "aut.accept"; + m_reject = "aut.reject"; m_tail = "seq.tail"; m_nth = "seq.nth"; m_seq_first = "seq.first"; m_seq_last = "seq.last"; - m_indexof_left = "seq.indexof.left"; - m_indexof_right = "seq.indexof.right"; + m_indexof_left = "seq.idx.left"; + m_indexof_right = "seq.idx.right"; m_aut_step = "aut.step"; 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"; + } theory_seq::~theory_seq() { m_trail_stack.reset(); } +void theory_seq::init(context* ctx) { + theory::init(ctx); + m_mk_aut.set_solver(alloc(seq_expr_solver, m, get_context().get_fparams())); +} final_check_status theory_seq::final_check_eh() { TRACE("seq", display(tout << "level: " << get_context().get_scope_level() << "\n");); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index 2d6631387..8206eeca4 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -332,6 +332,7 @@ 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) { return internalize_term(atom); } virtual bool internalize_term(app*); From 006dc147a85b8569163babe4d55b5b640d05d5fd Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 29 Feb 2016 14:34:48 +0000 Subject: [PATCH 34/57] fix build with gcc 5 --- src/ast/rewriter/seq_rewriter.cpp | 16 ++++++++-------- src/ast/rewriter/seq_rewriter.h | 2 +- src/math/automata/symbolic_automata_def.h | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ast/rewriter/seq_rewriter.cpp b/src/ast/rewriter/seq_rewriter.cpp index b5d07ae1e..f04d445e1 100644 --- a/src/ast/rewriter/seq_rewriter.cpp +++ b/src/ast/rewriter/seq_rewriter.cpp @@ -93,29 +93,29 @@ public: } if (m.are_distinct(x->get_char(), y->get_char())) { expr_ref fml(m.mk_false(), m); - return sym_expr::mk_pred(fml, x->sort()); + return sym_expr::mk_pred(fml, x->get_sort()); } } - var_ref v(m.mk_var(0, x->sort()), m); + var_ref v(m.mk_var(0, x->get_sort()), m); expr_ref fml1 = x->accept(v); expr_ref fml2 = y->accept(v); if (m.is_true(fml1)) return y; if (m.is_true(fml2)) return x; expr_ref fml(m.mk_and(fml1, fml2), m); - return sym_expr::mk_pred(fml, x->sort()); + return sym_expr::mk_pred(fml, x->get_sort()); } virtual T mk_or(T x, T y) { if (x->is_char() && y->is_char() && x->get_char() == y->get_char()) { return x; } - var_ref v(m.mk_var(0, x->sort()), m); + var_ref v(m.mk_var(0, x->get_sort()), m); expr_ref fml1 = x->accept(v); expr_ref fml2 = y->accept(v); if (m.is_false(fml1)) return y; if (m.is_false(fml2)) return x; expr_ref fml(m.mk_or(fml1, fml2), m); - return sym_expr::mk_pred(fml, x->sort()); + return sym_expr::mk_pred(fml, x->get_sort()); } virtual T mk_and(unsigned sz, T const* ts) { @@ -151,7 +151,7 @@ public: if (x->is_range()) { // TBD check lower is below upper. } - expr_ref v(m.mk_fresh_const("x", x->sort()), m); + expr_ref v(m.mk_fresh_const("x", x->get_sort()), m); expr_ref fml = x->accept(v); if (m.is_true(fml)) { return l_true; @@ -162,9 +162,9 @@ public: return m_solver.check_sat(fml); } virtual T mk_not(T x) { - var_ref v(m.mk_var(0, x->sort()), m); + 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->sort()); + return sym_expr::mk_pred(fml, x->get_sort()); } }; diff --git a/src/ast/rewriter/seq_rewriter.h b/src/ast/rewriter/seq_rewriter.h index 718658652..040bae1b4 100644 --- a/src/ast/rewriter/seq_rewriter.h +++ b/src/ast/rewriter/seq_rewriter.h @@ -51,7 +51,7 @@ public: bool is_char() const { return m_ty == t_char; } bool is_pred() const { return !is_char(); } bool is_range() const { return m_ty == t_range; } - sort* sort() const { return m_sort; } + sort* get_sort() const { return m_sort; } expr* get_char() const { SASSERT(is_char()); return m_t; } }; diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index 90dd98d10..6cb3d6339 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -32,14 +32,14 @@ typedef std::pair unsigned_pair; template typename symbolic_automata::automaton_t* symbolic_automata::mk_total(automaton_t& a) { unsigned dead_state = a.num_states(); - moves_t mvs; + moves_t mvs, new_mvs; for (unsigned i = 0; i < dead_state; ++i) { mvs.reset(); a.get_moves(i, mvs, true); refs_t vs(m); for (unsigned j = 0; j < mvs.size(); ++j) { - mv.push_back(mvs[j]()); + vs.push_back(mvs[j]()); } ref_t cond(m_ba.mk_not(m_ba.mk_or(vs.size(), vs.c_ptr())), m); lbool is_sat = m_ba.is_sat(cond); From d89c39cbe2bb862daff2c183a79361360f7e2dc1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 29 Feb 2016 08:36:25 -0800 Subject: [PATCH 35/57] apply t() Signed-off-by: Nikolaj Bjorner --- src/math/automata/symbolic_automata_def.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index 6cb3d6339..db6172d49 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -39,7 +39,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_total refs_t vs(m); for (unsigned j = 0; j < mvs.size(); ++j) { - vs.push_back(mvs[j]()); + vs.push_back(mvs[j].t()); } ref_t cond(m_ba.mk_not(m_ba.mk_or(vs.size(), vs.c_ptr())), m); lbool is_sat = m_ba.is_sat(cond); @@ -64,6 +64,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim if (a.is_empty()) { return a.clone(); } + if (a.is_epsilon()) { return a.clone(); } From 9efc7f4aeab1924c5b93c5cdca3820105f91a241 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 29 Feb 2016 09:06:20 -0800 Subject: [PATCH 36/57] turn on model completion in validation code Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver/inc_sat_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 33889e4ef..bc57a4099 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -425,7 +425,7 @@ private: DEBUG_CODE( for (unsigned i = 0; i < m_fmls.size(); ++i) { expr_ref tmp(m); - VERIFY(m_model->eval(m_fmls[i].get(), tmp)); + VERIFY(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"; From 43202572ee73b204dfa472a67c437cc1abd627f5 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Mon, 29 Feb 2016 17:23:54 +0000 Subject: [PATCH 37/57] bv_bounds: switch from rational to uint64 this limits the analysis to 64-bit BVs, but gives a speedup of up to one order of magnitude --- src/tactic/bv/bv_bounds_tactic.cpp | 85 ++++++++++++++++++------------ 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 432d3b219..15bfa69d2 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -20,9 +20,11 @@ Author: #include "ctx_simplify_tactic.h" #include "bv_decl_plugin.h" #include "ast_pp.h" +#include -static rational uMaxInt(unsigned sz) { - return rational::power_of_two(sz) - rational::one(); +static uint64_t uMaxInt(unsigned sz) { + SASSERT(sz <= 64); + return ULLONG_MAX >> (64u - sz); } namespace { @@ -30,26 +32,26 @@ namespace { struct interval { // l < h: [l, h] // l > h: [0, h] U [l, UMAX_INT] - rational l, h; + uint64_t l, h; unsigned sz; bool tight; interval() {} - interval(const rational& l, const rational& 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 + rational::one()) { - this->l = rational::zero(); + if (is_wrapped() && l == h + 1) { + this->l = 0; this->h = uMaxInt(sz); } SASSERT(invariant()); } bool invariant() const { - return !l.is_neg() && !h.is_neg() && l <= uMaxInt(sz) && h <= uMaxInt(sz) && - (!is_wrapped() || l != h+rational::one()); + return l <= uMaxInt(sz) && h <= uMaxInt(sz) && + (!is_wrapped() || l != h+1); } - bool is_full() const { return l.is_zero() && h == uMaxInt(sz); } + bool is_full() const { return l == 0 && h == uMaxInt(sz); } bool is_wrapped() const { return l > h; } bool is_singleton() const { return l == h; } @@ -129,18 +131,18 @@ struct interval { /// return false if negation is empty bool negate(interval& result) const { if (!tight) { - result = interval(rational::zero(), uMaxInt(sz), true); + result = interval(0, uMaxInt(sz), true); return true; } if (is_full()) return false; - if (l.is_zero()) { - result = interval(h + rational::one(), uMaxInt(sz), sz); + if (l == 0) { + result = interval(h + 1, uMaxInt(sz), sz); } else if (uMaxInt(sz) == h) { - result = interval(rational::zero(), l - rational::one(), sz); + result = interval(0, l - 1, sz); } else { - result = interval(h + rational::one(), l - rational::one(), sz); + result = interval(h + 1, l - 1, sz); } return true; } @@ -171,47 +173,57 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { vector m_scopes; map m_bound; expr_list_map m_expr_vars; + expr_set m_bound_exprs; + + 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(); + return true; + } + return false; + } bool is_bound(expr *e, expr*& v, interval& b) const { - rational n; + uint64_t n; expr *lhs, *rhs; unsigned sz; if (m_bv.is_bv_ule(e, lhs, rhs)) { - if (m_bv.is_numeral(lhs, n, sz)) { // C ule x <=> x uge C + if (is_number(lhs, n, sz)) { // C ule x <=> x uge C if (m_bv.is_numeral(rhs)) return false; b = interval(n, uMaxInt(sz), sz, true); v = rhs; return true; } - if (m_bv.is_numeral(rhs, n, sz)) { // x ule C - b = interval(rational::zero(), n, sz, true); + if (is_number(rhs, n, sz)) { // x ule C + b = interval(0, n, sz, true); v = lhs; return true; } } else if (m_bv.is_bv_sle(e, lhs, rhs)) { - if (m_bv.is_numeral(lhs, n, sz)) { // C sle x <=> x sge C + if (is_number(lhs, n, sz)) { // C sle x <=> x sge C if (m_bv.is_numeral(rhs)) return false; - b = interval(n, rational::power_of_two(sz-1) - rational::one(), sz, true); + b = interval(n, (1ull << (sz-1)) - 1, sz, true); v = rhs; return true; } - if (m_bv.is_numeral(rhs, n, sz)) { // x sle C - b = interval(rational::power_of_two(sz-1), n, sz, true); + if (is_number(rhs, n, sz)) { // x sle C + b = interval(1ull << (sz-1), n, sz, true); v = lhs; return true; } } else if (m.is_eq(e, lhs, rhs)) { - if (m_bv.is_numeral(lhs, n, sz)) { + if (is_number(lhs, n, sz)) { if (m_bv.is_numeral(rhs)) return false; b = interval(n, n, sz, true); v = rhs; return true; } - if (m_bv.is_numeral(rhs, n, sz)) { + if (is_number(rhs, n, sz)) { b = interval(n, n, sz, true); v = lhs; return true; @@ -245,25 +257,29 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { } bool expr_has_bounds(expr* t) { + bool has_bounds = false; + if (m_bound_exprs.find(t, has_bounds)) + return has_bounds; + app* a = to_app(t); if ((m_bv.is_bv_ule(t) || m_bv.is_bv_sle(t) || m.is_eq(t)) && - (m_bv.is_numeral(a->get_arg(0)) || m_bv.is_numeral(a->get_arg(1)))) - return true; - - for (unsigned i = 0; i < a->get_num_args(); ++i) { - if (expr_has_bounds(a->get_arg(i))) - return true; + (m_bv.is_numeral(a->get_arg(0)) || m_bv.is_numeral(a->get_arg(1)))) { + has_bounds = true; } - return false; + + for (unsigned i = 0; !has_bounds && i < a->get_num_args(); ++i) { + has_bounds = expr_has_bounds(a->get_arg(i)); + } + + m_bound_exprs.insert(t, has_bounds); + return has_bounds; } public: - bv_bounds_simplifier(ast_manager& m, params_ref const& p) : m(m), m_params(p), m_bv(m) { updt_params(p); } - virtual void updt_params(params_ref const & p) { m_propagate_eq = p.get_bool("propagate_eq", false); } @@ -348,7 +364,8 @@ public: } else if (!b.intersect(ctx, intr)) { result = m.mk_false(); } else if (m_propagate_eq && intr.is_singleton()) { - result = m.mk_eq(t1, m_bv.mk_numeral(intr.l, m.get_sort(t1))); + result = m.mk_eq(t1, m_bv.mk_numeral(rational(intr.l, rational::ui64()), + m.get_sort(t1))); } } From c6c84dd59a0823921422c1b7b0e4f023eb91a5e3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 29 Feb 2016 17:04:26 -0800 Subject: [PATCH 38/57] update documentation help to be inline with fpLT. Issue #465 Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 9f3b0a1b9..3a8800f1d 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -8829,8 +8829,8 @@ def fpLT(a, b, ctx=None): >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpLT(x, y) x < y - >>> (x <= y).sexpr() - '(fp.leq x y)' + >>> (x < y).sexpr() + '(fp.lt x y)' """ return _mk_fp_bin_pred(Z3_mk_fpa_lt, a, b, ctx) From c9269c198382375fe923a3a52ca405578d403d91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andres=20N=C3=B6tzli?= Date: Mon, 29 Feb 2016 19:12:14 -0800 Subject: [PATCH 39/57] Fix documentation for floating-point comparisons --- src/api/python/z3.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 3a8800f1d..305733aa7 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -8824,7 +8824,7 @@ def _check_fp_args(a, b): _z3_assert(is_fp(a) or is_fp(b), "At least one of the arguments must be a Z3 floating-point expression") def fpLT(a, b, ctx=None): - """Create the Z3 floating-point expression `other <= self`. + """Create the Z3 floating-point expression `other < self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpLT(x, y) @@ -8846,7 +8846,7 @@ def fpLEQ(a, b, ctx=None): return _mk_fp_bin_pred(Z3_mk_fpa_leq, a, b, ctx) def fpGT(a, b, ctx=None): - """Create the Z3 floating-point expression `other <= self`. + """Create the Z3 floating-point expression `other > self`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpGT(x, y) @@ -8857,11 +8857,9 @@ def fpGT(a, b, ctx=None): return _mk_fp_bin_pred(Z3_mk_fpa_gt, a, b, ctx) def fpGEQ(a, b, ctx=None): - """Create the Z3 floating-point expression `other <= self`. + """Create the Z3 floating-point expression `other >= self`. >>> x, y = FPs('x y', FPSort(8, 24)) - >>> x + y - x + y >>> fpGEQ(x, y) x >= y >>> (x >= y).sexpr() @@ -8870,7 +8868,7 @@ def fpGEQ(a, b, ctx=None): return _mk_fp_bin_pred(Z3_mk_fpa_geq, a, b, ctx) def fpEQ(a, b, ctx=None): - """Create the Z3 floating-point expression `other <= self`. + """Create the Z3 floating-point expression `fpEQ(other, self)`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpEQ(x, y) @@ -8881,7 +8879,7 @@ def fpEQ(a, b, ctx=None): return _mk_fp_bin_pred(Z3_mk_fpa_eq, a, b, ctx) def fpNEQ(a, b, ctx=None): - """Create the Z3 floating-point expression `other <= self`. + """Create the Z3 floating-point expression `Not(fpEQ(other, self))`. >>> x, y = FPs('x y', FPSort(8, 24)) >>> fpNEQ(x, y) From 6cf76f2113b5ea8442156c3e4097f378f82446a5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 29 Feb 2016 20:23:20 -0800 Subject: [PATCH 40/57] remove references to _DEBUG use Z3DEBUG instead Signed-off-by: Nikolaj Bjorner --- src/math/automata/symbolic_automata_def.h | 23 ++++++++++++----------- src/muz/rel/dl_base.h | 4 ---- src/util/memory_manager.cpp | 2 +- src/util/memory_manager.h | 2 +- 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index db6172d49..213a4b2f9 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -75,23 +75,24 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim return 0; } - block final_block(fa->final_states()); - block non_final_block(fa->non_final_states()); - vector blocks; + vector pblocks; + unsigned_vector blocks; + pblocks.push_back(block(fa->final_states())); // 0 |-> final states + pblocks.push_back(block(fa->non_final_states()); // 1 |-> non-final states for (unsigned i = 0; i < fa->num_states(); ++i) { - if (fa->is_final_state(i)) { - blocks.push_back(final_block); + if (fa->is_final_state(i)) { + blocks.push_back(0); } else { - blocks.push_back(non_final_block); + blocks.push_back(1); } } vector W; if (final_block.size() > non_final_block.size()) { - W.push_back(non_final_block); + W.push_back(1); } else { - W.push_back(final_block); + W.push_back(0); } #if 0 @@ -111,7 +112,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim fa->get_moves_to(q, mvs); for (unsigned i = 0; i < mvs.size(); ++i) { unsigned src = mvs[i].src(); - if (blocks[src].size() > 1) { + if (pblocks[src].size() > 1) { T* t = mvs[i](); if (gamma.find(src, t1)) { t = m_ba.mk_or(t, t1); @@ -121,10 +122,10 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim } } } - hashtable relevant; + uint_set relevant; u_map::iterator end = gamma.end(); for (u_map::iterator it = gamma.begin(); it != end; ++it) { - relevant.insert(blocks[it->m_key]); + relevant.insert(it->m_key); } } diff --git a/src/muz/rel/dl_base.h b/src/muz/rel/dl_base.h index 781c8539d..1c7a81444 100644 --- a/src/muz/rel/dl_base.h +++ b/src/muz/rel/dl_base.h @@ -435,11 +435,7 @@ namespace datalog { void destroy() { SASSERT(this); this->~base_ancestor(); -#if _DEBUG - memory::deallocate(__FILE__, __LINE__, this); -#else memory::deallocate(this); -#endif } public: /** diff --git a/src/util/memory_manager.cpp b/src/util/memory_manager.cpp index 91d27ed27..76069ce44 100644 --- a/src/util/memory_manager.cpp +++ b/src/util/memory_manager.cpp @@ -198,7 +198,7 @@ void memory::display_i_max_usage(std::ostream & os) { << "\n"; } -#if _DEBUG +#if Z3DEBUG void memory::deallocate(char const * file, int line, void * p) { deallocate(p); TRACE_CODE(if (!g_finalizing) TRACE("memory", tout << "dealloc " << std::hex << p << std::dec << " " << file << ":" << line << "\n";);); diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index b6831faa7..aac61ea2a 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -60,7 +60,7 @@ public: static void deallocate(void* p); static ALLOC_ATTR void* allocate(size_t s); static ALLOC_ATTR void* reallocate(void *p, size_t s); -#if _DEBUG +#if Z3DEBUG static void deallocate(char const* file, int line, void* p); static ALLOC_ATTR void* allocate(char const* file, int line, char const* obj, size_t s); #endif From b90bc4e685826a7c0a13283b4a00707cb45e6e84 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 29 Feb 2016 21:15:44 -0800 Subject: [PATCH 41/57] fix build Signed-off-by: Nikolaj Bjorner --- src/math/automata/symbolic_automata_def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index 213a4b2f9..99ad9e9bc 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -78,7 +78,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim vector pblocks; unsigned_vector blocks; pblocks.push_back(block(fa->final_states())); // 0 |-> final states - pblocks.push_back(block(fa->non_final_states()); // 1 |-> non-final states +// pblocks.push_back(block(fa->non_final_states()); // 1 |-> non-final states for (unsigned i = 0; i < fa->num_states(); ++i) { if (fa->is_final_state(i)) { blocks.push_back(0); From 4a15d756d76a7bd24e3d9ae4c8b71a92f2d3ba86 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 29 Feb 2016 22:16:03 -0800 Subject: [PATCH 42/57] uint64_t -> uint64 for cross platform Signed-off-by: Nikolaj Bjorner --- src/math/automata/symbolic_automata.h | 2 + src/math/automata/symbolic_automata_def.h | 177 +++++++++++++++++++++- src/tactic/bv/bv_bounds_tactic.cpp | 10 +- 3 files changed, 178 insertions(+), 11 deletions(-) diff --git a/src/math/automata/symbolic_automata.h b/src/math/automata/symbolic_automata.h index d77e0548d..ddd938cac 100644 --- a/src/math/automata/symbolic_automata.h +++ b/src/math/automata/symbolic_automata.h @@ -96,6 +96,8 @@ public: automaton_t* remove_epsilons(automaton_t& a); automaton_t* mk_total(automaton_t& a); automaton_t* mk_minimize(automaton_t& a); + automaton_t* mk_minimize_total(automaton_t& a); + automaton_t* mk_difference(automaton_t& a, automaton_t& b); automaton_t* mk_product(automaton_t& a, automaton_t& b); }; diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index 99ad9e9bc..f37384b2c 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -74,7 +74,12 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim if (!fa) { return 0; } - + return mk_minimize_total(*fa.get()); +} + + +template +typename symbolic_automata::automaton_t* symbolic_automata::mk_minimize_total(automaton_t& a) { vector pblocks; unsigned_vector blocks; pblocks.push_back(block(fa->final_states())); // 0 |-> final states @@ -101,7 +106,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim u_map gamma; moves_t mvs; while (!W.empty()) { - block R(W.back()); + block R(pblocks[W.back()]); W.pop_back(); block Rcopy(R); gamma.reset(); @@ -123,13 +128,173 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim } } uint_set relevant; - u_map::iterator end = gamma.end(); - for (u_map::iterator it = gamma.begin(); it != end; ++it) { - relevant.insert(it->m_key); + u_map::iterator gend = gamma.end(); + for (u_map::iterator it = gamma.begin(); it != gend; ++it) { + relevant.insert(blocks[it->m_key]); + } + uint_set::iterator uit = relevant.begin(), uend = relevant.end(); + for (; uit != uend; ++uit) { + unsigned p0_index = *uit; + block& p0 = pblocks[p0_index]; + block p1; + for (u_map::iterator it = gamma.begin(); it != gend; ++it) { + if (p0.contains(*it)) p1.push_back(*it); + } + if (p1.size() < p0.size()) { + unsigned p1_index = pblocks.size(); + pblocks.push_back(p1); + for (uint_set::iterator it = p1.begin(), end = p1.end(); it != end; ++it) { + p0.remove(*it); + blocks[*it] = p1_index; + } + if (W.contains(p0_index)) { + W.push_back(p1_index); + } + else if (p0.size() <= p1.size()) { + W.push_back(p0_index); + } + else { + W.push_back(p1_index); + } + } + bool iterate = true; + while (iterate) { + iterate = false; + uint_set relevant; + for (u_map::iterator it = gamma.begin(); it != gend; ++it) { + if (pblocks[blocks[it->m_key]].size() > 1) { + relevant.insert(blocks[it->m_key]); + } + } + uint_set::iterator it = relevant.begin(), end = relevant.end(); + for (; it != end; ++it) { + block const& p = pblocks[*it]; + uint_set::iterator bi = p.begin(), be = p.end(); + + block p1; + p1.insert(*bi); + // psi = gamma[*bi]; // name of key or block? + ++bi; + for (; bi != be; ++bi) { + + } + } + } } - } #endif + +#if 0 + Func MkDiff = (x, y) => solver.MkAnd(x, solver.MkNot(y)); + + while (!W.IsEmpty) + { + //keep using Bcopy until no more changes occur + //effectively, this replaces the loop over characters + bool iterate = true; + //in each relevant block all states lead to B due to the initial splitting + + //only relevant blocks are potentially split + foreach (var P in relevant2) + { + var PE = P.GetEnumerator(); + PE.MoveNext(); + + var P1 = new Block(); + bool splitFound = false; + + var psi = Gamma[PE.Current]; + P1.Add(PE.Current); //C has at least 2 elements + + #region compute C1 as the new sub-block of C + while (PE.MoveNext()) + { + var q = PE.Current; + var phi = Gamma[q]; + if (splitFound) + { + var psi_and_phi = solver.MkAnd(psi, phi); + if (solver.IsSatisfiable(psi_and_phi)) + P1.Add(q); + } + else + { + var psi_min_phi = MkDiff(psi, phi); + if (solver.IsSatisfiable(psi_min_phi)) + { + psi = psi_min_phi; + splitFound = true; + } + else // [[psi]] is subset of [[phi]] + { + var phi_min_psi = MkDiff(phi, psi); + if (!solver.IsSatisfiable(phi_min_psi)) + P1.Add(q); //psi and phi are equivalent + else + { + //there is some a: q --a--> B and p --a--> compl(B) for all p in C1 + P1.Clear(); + P1.Add(q); + psi = phi_min_psi; + splitFound = true; + } + } + } + } + #endregion + + #region split P + if (P1.Count < P.Count) + { + iterate = (iterate || (P.Count > 2)); //otherwise C was split into singletons + foreach (var p in P1) + { + P.Remove(p); + Blocks[p] = P1; + } + + if (W.Contains(P)) + W.Push(P1); + else if (P.Count <= P1.Count) + W.Push(P); + else + W.Push(P1); + } + #endregion + } + } + } + + Dictionary, HashSet> condMap = new Dictionary, HashSet>(); + foreach (var move in GetMoves()) + { + int s = Blocks[move.SourceState].GetRepresentative(); + int t = Blocks[move.TargetState].GetRepresentative(); + var st = new Pair(s, t); + HashSet condSet; + if (!condMap.TryGetValue(st, out condSet)) + { + condSet = new HashSet(); + condSet.Add(move.Label); + condMap[st] = condSet; + } + else + condSet.Add(move.Label); + } + int newInitState = Blocks[fa.InitialState].GetRepresentative(); + var newMoves = new List>(); + var newFinals = new HashSet(); + foreach (var entry in condMap) + newMoves.Add(Move.Create(entry.Key.First, entry.Key.Second, solver.MkOr(entry.Value))); + foreach (var f in GetFinalStates()) + newFinals.Add(Blocks[f].GetRepresentative()); + + var res = Create(newInitState, newFinals, newMoves); + res.isDeterministic = true; + res.isEpsilonFree = true; + //res.EliminateDeadStates(); + return res; +#endif return 0; diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 15bfa69d2..2d266aed8 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -22,7 +22,7 @@ Author: #include "ast_pp.h" #include -static uint64_t uMaxInt(unsigned sz) { +static uint64 uMaxInt(unsigned sz) { SASSERT(sz <= 64); return ULLONG_MAX >> (64u - sz); } @@ -32,12 +32,12 @@ namespace { struct interval { // l < h: [l, h] // l > h: [0, h] U [l, UMAX_INT] - uint64_t l, h; + uint64 l, h; unsigned sz; bool tight; interval() {} - interval(uint64_t l, uint64_t h, unsigned sz, bool tight = false) : l(l), h(h), sz(sz), tight(tight) { + interval(uint64 l, uint64 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; @@ -175,7 +175,7 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { expr_list_map m_expr_vars; expr_set m_bound_exprs; - bool is_number(expr *e, uint64_t& n, unsigned& sz) const { + bool is_number(expr *e, uint64& n, unsigned& sz) const { rational r; if (m_bv.is_numeral(e, r, sz) && sz <= 64) { n = r.get_uint64(); @@ -185,7 +185,7 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { } bool is_bound(expr *e, expr*& v, interval& b) const { - uint64_t n; + uint64 n; expr *lhs, *rhs; unsigned sz; From 570bc3c9c188811486fa8e598bc02194c21a2bad Mon Sep 17 00:00:00 2001 From: Andres Notzli Date: Mon, 29 Feb 2016 23:41:33 -0800 Subject: [PATCH 43/57] Fix build Previous commits seem to have removed some variable declarations, so the build did not work. This patch reintroduces the variables. --- src/math/automata/symbolic_automata_def.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index f37384b2c..953bc83dd 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -79,9 +79,11 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim template -typename symbolic_automata::automaton_t* symbolic_automata::mk_minimize_total(automaton_t& a) { +typename symbolic_automata::automaton_t* symbolic_automata::mk_minimize_total(automaton_t& fa) { vector pblocks; unsigned_vector blocks; + block final_block(fa->final_states()); + block non_final_block(fa->non_final_states()); pblocks.push_back(block(fa->final_states())); // 0 |-> final states // pblocks.push_back(block(fa->non_final_states()); // 1 |-> non-final states for (unsigned i = 0; i < fa->num_states(); ++i) { From 830a99aab40abc04b0c6bd856620b429d37fcb69 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 1 Mar 2016 00:04:03 -0800 Subject: [PATCH 44/57] finish minimization Signed-off-by: Nikolaj Bjorner --- src/math/automata/symbolic_automata.h | 8 +- src/math/automata/symbolic_automata_def.h | 316 ++++++++++------------ 2 files changed, 151 insertions(+), 173 deletions(-) diff --git a/src/math/automata/symbolic_automata.h b/src/math/automata/symbolic_automata.h index ddd938cac..cd553eeef 100644 --- a/src/math/automata/symbolic_automata.h +++ b/src/math/automata/symbolic_automata.h @@ -79,16 +79,18 @@ class symbolic_automata { return m_rep; } - void add(unsigned i) { m_set.insert(i); } + void insert(unsigned i) { m_set.insert(i); } bool contains(unsigned i) const { return m_set.contains(i); } bool is_empty() const { return m_set.empty(); } unsigned size() const { return m_set.num_elems(); } void remove(unsigned i) { m_set.remove(i); m_rep_chosen = false; } void clear() { m_set.reset(); m_rep_chosen = false; } - uint_set::iterator begin() { return m_set.begin(); } - uint_set::iterator end() { return m_set.end(); } + uint_set::iterator begin() const { return m_set.begin(); } + uint_set::iterator end() const { return m_set.end(); } }; + void add_block(block const& p1, unsigned p0_index, unsigned_vector& blocks, vector& pblocks, unsigned_vector& W); + public: symbolic_automata(M& m, ba_t& ba): m(m), m_ba(ba) {} automaton_t* mk_determinstic(automaton_t& a); diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index f37384b2c..87e0f7b2d 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -35,7 +35,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_total moves_t mvs, new_mvs; for (unsigned i = 0; i < dead_state; ++i) { mvs.reset(); - a.get_moves(i, mvs, true); + a.get_moves_from(i, mvs, true); refs_t vs(m); for (unsigned j = 0; j < mvs.size(); ++j) { @@ -54,7 +54,8 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_total return a.clone(); } new_mvs.push_back(move_t(m, dead_state, dead_state, m_ba.mk_true())); - automaton_t::append_moves(0, a, new_mvs); + + // TBD private: automaton_t::append_moves(0, a, new_mvs); return alloc(automaton_t, m, a.init(), a.final_states(), new_mvs); } @@ -78,47 +79,65 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim } +template +void symbolic_automata::add_block(block const& p1, unsigned p0_index, unsigned_vector& blocks, vector& pblocks, unsigned_vector& W) { + block& p0 = pblocks[p0_index]; + if (p1.size() < p0.size()) { + unsigned p1_index = pblocks.size(); + pblocks.push_back(p1); + for (uint_set::iterator it = p1.begin(), end = p1.end(); it != end; ++it) { + p0.remove(*it); + blocks[*it] = p1_index; + } + if (W.contains(p0_index)) { + W.push_back(p1_index); + } + else if (p0.size() <= p1.size()) { + W.push_back(p0_index); + } + else { + W.push_back(p1_index); + } + } +} + template typename symbolic_automata::automaton_t* symbolic_automata::mk_minimize_total(automaton_t& a) { vector pblocks; unsigned_vector blocks; - pblocks.push_back(block(fa->final_states())); // 0 |-> final states -// pblocks.push_back(block(fa->non_final_states()); // 1 |-> non-final states - for (unsigned i = 0; i < fa->num_states(); ++i) { - if (fa->is_final_state(i)) { - blocks.push_back(0); - } - else { + unsigned_vector non_final; + for (unsigned i = 0; i < a.num_states(); ++i) { + if (!a.is_final_state(i)) { + non_final.push_back(i); blocks.push_back(1); } + else { + blocks.push_back(0); + } } - vector W; - if (final_block.size() > non_final_block.size()) { - W.push_back(1); - } - else { - W.push_back(0); - } - -#if 0 - + pblocks.push_back(block(a.final_states())); // 0 |-> final states + pblocks.push_back(block(non_final)); // 1 |-> non-final states + + unsigned_vector W; + W.push_back(pblocks[0].size() > pblocks[1].size() ? 1 : 0); + refs_t trail(m); u_map gamma; moves_t mvs; while (!W.empty()) { block R(pblocks[W.back()]); W.pop_back(); - block Rcopy(R); gamma.reset(); - uint_set::iterator it = Rcopy.begin(), end = Rcopy.end(); + uint_set::iterator it = R.begin(), end = R.end(); for (; it != end; ++it) { - unsigned q = *it; + unsigned dst = *it; mvs.reset(); - fa->get_moves_to(q, mvs); + a.get_moves_to(dst, mvs); for (unsigned i = 0; i < mvs.size(); ++i) { unsigned src = mvs[i].src(); if (pblocks[src].size() > 1) { - T* t = mvs[i](); + T* t = mvs[i].t(); + T* t1; if (gamma.find(src, t1)) { t = m_ba.mk_or(t, t1); trail.push_back(t); @@ -127,177 +146,131 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim } } } - uint_set relevant; + uint_set relevant1; u_map::iterator gend = gamma.end(); - for (u_map::iterator it = gamma.begin(); it != gend; ++it) { - relevant.insert(blocks[it->m_key]); - } - uint_set::iterator uit = relevant.begin(), uend = relevant.end(); - for (; uit != uend; ++uit) { - unsigned p0_index = *uit; - block& p0 = pblocks[p0_index]; + for (u_map::iterator git = gamma.begin(); git != gend; ++git) { + unsigned p0A_index = blocks[git->m_key]; + if (relevant1.contains(p0A_index)) { + continue; + } + relevant1.insert(p0A_index); + block& p0A = pblocks[p0A_index]; block p1; for (u_map::iterator it = gamma.begin(); it != gend; ++it) { - if (p0.contains(*it)) p1.push_back(*it); + if (p0A.contains(it->m_key)) p1.insert(it->m_key); } - if (p1.size() < p0.size()) { - unsigned p1_index = pblocks.size(); - pblocks.push_back(p1); - for (uint_set::iterator it = p1.begin(), end = p1.end(); it != end; ++it) { - p0.remove(*it); - blocks[*it] = p1_index; - } - if (W.contains(p0_index)) { - W.push_back(p1_index); - } - else if (p0.size() <= p1.size()) { - W.push_back(p0_index); - } - else { - W.push_back(p1_index); - } - } + + add_block(p1, p0A_index, blocks, pblocks, W); + bool iterate = true; while (iterate) { iterate = false; - uint_set relevant; + uint_set relevant2; for (u_map::iterator it = gamma.begin(); it != gend; ++it) { - if (pblocks[blocks[it->m_key]].size() > 1) { - relevant.insert(blocks[it->m_key]); + unsigned p0B_index = blocks[it->m_key]; + if (pblocks[p0B_index].size() <= 1 || relevant2.contains(p0B_index)) { + continue; } - } - uint_set::iterator it = relevant.begin(), end = relevant.end(); - for (; it != end; ++it) { - block const& p = pblocks[*it]; - uint_set::iterator bi = p.begin(), be = p.end(); + relevant2.insert(p0B_index); + block const& p0B = pblocks[p0B_index]; + uint_set::iterator bi = p0B.begin(), be = p0B.end(); block p1; p1.insert(*bi); - // psi = gamma[*bi]; // name of key or block? + bool split_found = false; + ref_t psi(gamma[*bi], m); ++bi; for (; bi != be; ++bi) { - + unsigned q = *bi; + ref_t phi(gamma[q], m); + if (split_found) { + ref_t phi_and_psi(m_ba.mk_and(phi, psi), m); + switch (m_ba.is_sat(phi_and_psi)) { + case l_true: + p1.insert(q); + break; + case l_undef: + return 0; + default: + break; + } + } + else { + ref_t psi_min_phi(m_ba.mk_and(psi, m_ba.mk_not(phi)), m); + lbool is_sat = m_ba.is_sat(psi_min_phi); + if (is_sat == l_undef) { + return 0; + } + if (is_sat == l_true) { + psi = psi_min_phi; + split_found = true; + continue; + } + // psi is a subset of phi + ref_t phi_min_psi(m_ba.mk_and(phi, m_ba.mk_not(psi)), m); + is_sat = m_ba.is_sat(phi_min_psi); + if (is_sat == l_undef) { + return 0; + } + else if (is_sat == l_false) { + p1.insert(q); // psi and phi are equivalent + } + else { + p1.clear(); + p1.insert(q); + psi = phi_min_psi; + split_found = true; + } + } } + if (p1.size() < p0B.size() && p0B.size() > 2) iterate = true; + add_block(p1, p0B_index, blocks, pblocks, W); } } } } -#endif -#if 0 - Func MkDiff = (x, y) => solver.MkAnd(x, solver.MkNot(y)); + unsigned new_init = pblocks[blocks[a.init()]].get_representative(); - while (!W.IsEmpty) - { - //keep using Bcopy until no more changes occur - //effectively, this replaces the loop over characters - bool iterate = true; - //in each relevant block all states lead to B due to the initial splitting + // set moves + map, default_eq > conds; + svector keys; + moves_t new_moves; - //only relevant blocks are potentially split - foreach (var P in relevant2) - { - var PE = P.GetEnumerator(); - PE.MoveNext(); - - var P1 = new Block(); - bool splitFound = false; - - var psi = Gamma[PE.Current]; - P1.Add(PE.Current); //C has at least 2 elements - - #region compute C1 as the new sub-block of C - while (PE.MoveNext()) - { - var q = PE.Current; - var phi = Gamma[q]; - if (splitFound) - { - var psi_and_phi = solver.MkAnd(psi, phi); - if (solver.IsSatisfiable(psi_and_phi)) - P1.Add(q); - } - else - { - var psi_min_phi = MkDiff(psi, phi); - if (solver.IsSatisfiable(psi_min_phi)) - { - psi = psi_min_phi; - splitFound = true; - } - else // [[psi]] is subset of [[phi]] - { - var phi_min_psi = MkDiff(phi, psi); - if (!solver.IsSatisfiable(phi_min_psi)) - P1.Add(q); //psi and phi are equivalent - else - { - //there is some a: q --a--> B and p --a--> compl(B) for all p in C1 - P1.Clear(); - P1.Add(q); - psi = phi_min_psi; - splitFound = true; - } - } - } - } - #endregion - - #region split P - if (P1.Count < P.Count) - { - iterate = (iterate || (P.Count > 2)); //otherwise C was split into singletons - foreach (var p in P1) - { - P.Remove(p); - Blocks[p] = P1; - } - - if (W.Contains(P)) - W.Push(P1); - else if (P.Count <= P1.Count) - W.Push(P); - else - W.Push(P1); - } - #endregion - } - } + for (unsigned i = 0; i < a.num_states(); ++i) { + unsigned src = pblocks[blocks[i]].get_representative(); + automaton_t::moves const& mvs = a.get_moves_from(i); + for (unsigned j = 0; j < mvs.size(); ++j) { + unsigned dst = pblocks[blocks[mvs[j].dst()]].get_representative(); + unsigned_pair st(src, dst); + T* t = 0; + if (conds.find(st, t)) { + t = m_ba.mk_or(t, mvs[j].t()); + trail.push_back(t); + conds.insert(st, t); } - - Dictionary, HashSet> condMap = new Dictionary, HashSet>(); - foreach (var move in GetMoves()) - { - int s = Blocks[move.SourceState].GetRepresentative(); - int t = Blocks[move.TargetState].GetRepresentative(); - var st = new Pair(s, t); - HashSet condSet; - if (!condMap.TryGetValue(st, out condSet)) - { - condSet = new HashSet(); - condSet.Add(move.Label); - condMap[st] = condSet; - } - else - condSet.Add(move.Label); + else { + conds.insert(st, mvs[j].t()); + keys.push_back(st); } - int newInitState = Blocks[fa.InitialState].GetRepresentative(); - var newMoves = new List>(); - var newFinals = new HashSet(); - foreach (var entry in condMap) - newMoves.Add(Move.Create(entry.Key.First, entry.Key.Second, solver.MkOr(entry.Value))); - foreach (var f in GetFinalStates()) - newFinals.Add(Blocks[f].GetRepresentative()); + } + } + for (unsigned i = 0; i < keys.size(); ++i) { + unsigned_pair st = keys[i]; + new_moves.push_back(move_t(m, st.first, st.second, conds[st])); + } + // set final states. + unsigned_vector new_final; + uint_set new_final_set; + for (unsigned i = 0; i < a.final_states().size(); ++i) { + unsigned f = pblocks[blocks[a.final_states()[i]]].get_representative(); + if (!new_final_set.contains(f)) { + new_final_set.insert(f); + new_final.push_back(f); + } + } - var res = Create(newInitState, newFinals, newMoves); - res.isDeterministic = true; - res.isEpsilonFree = true; - //res.EliminateDeadStates(); - return res; -#endif - - return 0; - + return alloc(automaton_t, m, new_init, new_final, new_moves); } template @@ -312,6 +285,9 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_produ if (a.is_final_state(a.init()) && b.is_final_state(b.init())) { final.push_back(0); } + if (false) { + mk_minimize(a); + } unsigned n = 1; moves_t mvsA, mvsB; while (!todo.empty()) { From 91d6b2cbbb969655d5ba55c5bc25f2b23f1907d2 Mon Sep 17 00:00:00 2001 From: Andres Notzli Date: Tue, 1 Mar 2016 00:21:10 -0800 Subject: [PATCH 45/57] [Z3py] Consistent behavior of eq and ne for FP Before, x == y and x != y were returning inconsistent expressions (i.e. `Not(x == y)` was not the same as `x != y`): >>> x = FP('x', Float32()) >>> y = FP('y', Float32()) >>> (x == y).sexpr() '(= x y)' >>> (x != y).sexpr() '(not (fp.eq x y))' `=` does not have the same semantics as `fp.eq` (e.g. `fp.eq` of +0.0 and -0.0 is true while it is false for `=`). This patch removes the __ne__ method from FPRef, so `x == y` and `x != y` use the inherited operations while fpEQ and fpNEQ can be used to refer to `fp.eq(..)`/`Not(fp.eq(..))`. --- src/api/python/z3.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 305733aa7..814198d22 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -8099,10 +8099,6 @@ class FPRef(ExprRef): def __gt__(self, other): return fpGT(self, other, self.ctx) - def __ne__(self, other): - return fpNEQ(self, other, self.ctx) - - def __add__(self, other): """Create the Z3 expression `self + other`. From 10ea36bfed4818320db66e68ab123459caaf201b Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 1 Mar 2016 10:00:58 +0000 Subject: [PATCH 46/57] fix build with gcc --- src/math/automata/symbolic_automata_def.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index 87e0f7b2d..a7b076a2c 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -147,8 +147,9 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim } } uint_set relevant1; - u_map::iterator gend = gamma.end(); - for (u_map::iterator git = gamma.begin(); git != gend; ++git) { + typedef typename u_map::iterator gamma_iterator; + gamma_iterator gend = gamma.end(); + for (gamma_iterator git = gamma.begin(); git != gend; ++git) { unsigned p0A_index = blocks[git->m_key]; if (relevant1.contains(p0A_index)) { continue; @@ -156,7 +157,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim relevant1.insert(p0A_index); block& p0A = pblocks[p0A_index]; block p1; - for (u_map::iterator it = gamma.begin(); it != gend; ++it) { + for (iterator it = gamma.begin(); it != gend; ++it) { if (p0A.contains(it->m_key)) p1.insert(it->m_key); } @@ -166,7 +167,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim while (iterate) { iterate = false; uint_set relevant2; - for (u_map::iterator it = gamma.begin(); it != gend; ++it) { + for (gamma_iterator it = gamma.begin(); it != gend; ++it) { unsigned p0B_index = blocks[it->m_key]; if (pblocks[p0B_index].size() <= 1 || relevant2.contains(p0B_index)) { continue; @@ -239,7 +240,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim for (unsigned i = 0; i < a.num_states(); ++i) { unsigned src = pblocks[blocks[i]].get_representative(); - automaton_t::moves const& mvs = a.get_moves_from(i); + typename automaton_t::moves const& mvs = a.get_moves_from(i); for (unsigned j = 0; j < mvs.size(); ++j) { unsigned dst = pblocks[blocks[mvs[j].dst()]].get_representative(); unsigned_pair st(src, dst); From 33431ef9220e02b311ae3bf6f8479037af8ae92e Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 1 Mar 2016 10:02:24 +0000 Subject: [PATCH 47/57] fix build with gcc --- src/math/automata/symbolic_automata_def.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index a7b076a2c..50c115d39 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -157,7 +157,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim relevant1.insert(p0A_index); block& p0A = pblocks[p0A_index]; block p1; - for (iterator it = gamma.begin(); it != gend; ++it) { + for (gamma_iterator it = gamma.begin(); it != gend; ++it) { if (p0A.contains(it->m_key)) p1.insert(it->m_key); } From 62e46aacd9819ac46aa5637fbb3272d3b2594b86 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 1 Mar 2016 11:31:08 +0000 Subject: [PATCH 48/57] bv_bounds: make may_simplify more precise to skip exprs with just 1 bound expr speedups up to 3x in selected benchmarks --- src/tactic/bv/bv_bounds_tactic.cpp | 63 ++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/tactic/bv/bv_bounds_tactic.cpp b/src/tactic/bv/bv_bounds_tactic.cpp index 2d266aed8..e67c2470b 100644 --- a/src/tactic/bv/bv_bounds_tactic.cpp +++ b/src/tactic/bv/bv_bounds_tactic.cpp @@ -164,7 +164,7 @@ struct undo_bound { class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { typedef obj_map map; typedef obj_map expr_set; - typedef obj_map expr_list_map; + typedef obj_map expr_cnt; ast_manager& m; params_ref m_params; @@ -172,8 +172,8 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { bv_util m_bv; vector m_scopes; map m_bound; - expr_list_map m_expr_vars; - expr_set m_bound_exprs; + svector m_expr_vars; + svector m_bound_exprs; bool is_number(expr *e, uint64& n, unsigned& sz) const { rational r; @@ -233,7 +233,9 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { } expr_set* get_expr_vars(expr* t) { - expr_set*& entry = m_expr_vars.insert_if_not_there2(t, 0)->get_data().m_value; + unsigned id = t->get_id(); + m_expr_vars.reserve(id + 1); + expr_set*& entry = m_expr_vars[id]; if (entry) return entry; @@ -256,23 +258,33 @@ class bv_bounds_simplifier : public ctx_simplify_tactic::simplifier { return set; } - bool expr_has_bounds(expr* t) { - bool has_bounds = false; - if (m_bound_exprs.find(t, has_bounds)) - return has_bounds; + expr_cnt* get_expr_bounds(expr* t) { + unsigned id = t->get_id(); + m_bound_exprs.reserve(id + 1); + expr_cnt*& entry = m_bound_exprs[id]; + if (entry) + return entry; + + expr_cnt* set = alloc(expr_cnt); + entry = set; + + if (!is_app(t)) + return set; + + interval b; + expr* e; + if (is_bound(t, e, b)) { + set->insert_if_not_there2(e, 0)->get_data().m_value++; + } app* a = to_app(t); - if ((m_bv.is_bv_ule(t) || m_bv.is_bv_sle(t) || m.is_eq(t)) && - (m_bv.is_numeral(a->get_arg(0)) || m_bv.is_numeral(a->get_arg(1)))) { - has_bounds = true; + for (unsigned i = 0; i < a->get_num_args(); ++i) { + expr_cnt* set_arg = get_expr_bounds(a->get_arg(i)); + for (expr_cnt::iterator I = set_arg->begin(), E = set_arg->end(); I != E; ++I) { + set->insert_if_not_there2(I->m_key, 0)->get_data().m_value += I->m_value; + } } - - for (unsigned i = 0; !has_bounds && i < a->get_num_args(); ++i) { - has_bounds = expr_has_bounds(a->get_arg(i)); - } - - m_bound_exprs.insert(t, has_bounds); - return has_bounds; + return set; } public: @@ -289,8 +301,11 @@ public: } virtual ~bv_bounds_simplifier() { - for (expr_list_map::iterator I = m_expr_vars.begin(), E = m_expr_vars.end(); I != E; ++I) { - dealloc(I->m_value); + for (unsigned i = 0, e = m_expr_vars.size(); i < e; ++i) { + dealloc(m_expr_vars[i]); + } + for (unsigned i = 0, e = m_bound_exprs.size(); i < e; ++i) { + dealloc(m_bound_exprs[i]); } } @@ -393,7 +408,13 @@ public: if (is_bound(t, t1, b)) { return b.is_full() || m_bound.contains(t1); } - return expr_has_bounds(t); + + expr_cnt* bounds = get_expr_bounds(t); + for (expr_cnt::iterator I = bounds->begin(), E = bounds->end(); I != E; ++I) { + if (I->m_value > 1 || m_bound.contains(I->m_key)) + return true; + } + return false; } virtual void pop(unsigned num_scopes) { From c171170bedc4d519594aeb2d8415905d0dd10414 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 1 Mar 2016 15:31:20 +0000 Subject: [PATCH 49/57] Fixed FP string input conversions. Fixes #464 --- src/api/api_numeral.cpp | 6 ++++-- src/util/mpf.cpp | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/api/api_numeral.cpp b/src/api/api_numeral.cpp index 770054870..491d9f597 100644 --- a/src/api/api_numeral.cpp +++ b/src/api/api_numeral.cpp @@ -68,8 +68,10 @@ extern "C" { (' ' == *m) || ('\n' == *m) || ('.' == *m) || ('e' == *m) || ('E' == *m) || - ('p' == *m && is_float) || - ('P' == *m && is_float))) { + (is_float && + ('p' == *m) || + ('P' == *m) || + ('+' == *m)))) { SET_ERROR_CODE(Z3_PARSER_ERROR); return 0; } diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 899b8635b..1d7ed1bb3 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -205,15 +205,23 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode // We expect [i].[f]P[e], where P means that the exponent is interpreted as 2^e instead of 10^e. std::string v(value); - size_t e_pos = v.find('p'); - if (e_pos == std::string::npos) e_pos = v.find('P'); std::string f, e; + bool sgn = false; + if (v.substr(0, 1) == "-") { + sgn = true; + v = v.substr(1); + } + else if (v.substr(0, 1) == "+") + v = v.substr(1); + + size_t e_pos = v.find('p'); + if (e_pos == std::string::npos) e_pos = v.find('P'); f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; - TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); + TRACE("mpf_dbg", tout << "sgn = " << sgn << " f = " << f << " e = " << e << std::endl;); scoped_mpq q(m_mpq_manager); m_mpq_manager.set(q, f.c_str()); @@ -222,6 +230,7 @@ void mpf_manager::set(mpf & o, unsigned ebits, unsigned sbits, mpf_rounding_mode m_mpz_manager.set(ex, e.c_str()); set(o, ebits, sbits, rm, ex, q); + o.sign = sgn; TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } From 908f09a9df876a4de3a3c45281f72000bb849004 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 1 Mar 2016 08:46:43 -0800 Subject: [PATCH 50/57] update logic Signed-off-by: Nikolaj Bjorner --- src/smt/params/smt_params.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index c31b2fb6d..3158ea9a1 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -38,6 +38,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_rlimit = p.rlimit(); m_max_conflicts = p.max_conflicts(); m_core_validate = p.core_validate(); + m_smtlib_logic = _p.get_symbol("logic", m_smtlib_logic); model_params mp(_p); m_model_compact = mp.compact(); if (_p.get_bool("arith.greatest_error_pivot", false)) From 7f51ecab373ffe889e61823c74fa670b3c710b19 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 1 Mar 2016 09:26:14 -0800 Subject: [PATCH 51/57] enable logic parameter update to configure SMTLIB logic Signed-off-by: Nikolaj Bjorner --- src/api/api_ast.cpp | 2 +- src/smt/params/smt_params.cpp | 2 +- src/smt/params/smt_params.h | 4 ++-- src/smt/smt_internalizer.cpp | 2 +- src/smt/tactic/smt_tactic.cpp | 5 +++++ 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index db10c8bb3..0203439ae 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -832,7 +832,7 @@ extern "C" { pp_params params; pp.set_simplify_implies(params.simplify_implies()); ast* a1 = to_ast(a); - pp.set_logic(mk_c(c)->fparams().m_smtlib_logic.c_str()); + pp.set_logic(mk_c(c)->fparams().m_logic.c_str()); if (!is_expr(a1)) { buffer << mk_pp(a1, mk_c(c)->m()); break; diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index 3158ea9a1..8dc0bb7d6 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -38,7 +38,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_rlimit = p.rlimit(); m_max_conflicts = p.max_conflicts(); m_core_validate = p.core_validate(); - m_smtlib_logic = _p.get_symbol("logic", m_smtlib_logic); + m_logic = _p.get_str("logic", m_logic.c_str()); model_params mp(_p); m_model_compact = mp.compact(); if (_p.get_bool("arith.greatest_error_pivot", false)) diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index 8127b7eef..a60f026bc 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -160,7 +160,7 @@ struct smt_params : public preprocessor_params, // // ----------------------------------- bool m_smtlib_dump_lemmas; - std::string m_smtlib_logic; + std::string m_logic; // ----------------------------------- // @@ -260,7 +260,7 @@ struct smt_params : public preprocessor_params, m_old_clause_relevancy(6), m_inv_clause_decay(1), m_smtlib_dump_lemmas(false), - m_smtlib_logic("AUFLIA"), + m_logic("AUFLIA"), m_profile_res_sub(false), m_display_bool_var2expr(false), m_display_ll_bool_var2expr(false), diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 78a6b85cb..5cad7547e 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1433,7 +1433,7 @@ namespace smt { literal_buffer tmp; neg_literals(num_lits, lits, tmp); SASSERT(tmp.size() == num_lits); - display_lemma_as_smt_problem(tmp.size(), tmp.c_ptr(), false_literal, m_fparams.m_smtlib_logic.c_str()); + display_lemma_as_smt_problem(tmp.size(), tmp.c_ptr(), false_literal, m_fparams.m_logic.c_str()); } mk_clause(num_lits, lits, js); } diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index f50f0afa5..afa4cf98f 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -123,6 +123,11 @@ public: TRACE("smt_tactic", tout << this << "\nupdt_params: " << p << "\n";); updt_params_core(p); fparams().updt_params(p); + symbol logic = p.get_sym(symbol("logic"), symbol::null); + if (logic != symbol::null) { + if (m_ctx) m_ctx->set_logic(logic); + m_logic = logic; + } SASSERT(p.get_bool("auto_config", fparams().m_auto_config) == fparams().m_auto_config); } From 4fe4db6657d806bc3444aca4c9de7d6fc127217a Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 1 Mar 2016 17:34:45 +0000 Subject: [PATCH 52/57] build fix for static libray on Windows --- scripts/mk_util.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index e32c9c3dd..a02a9b40f 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1261,13 +1261,16 @@ class DLLComponent(Component): out.write(' /DEF:%s.def' % os.path.join(self.to_src_dir, self.name)) out.write('\n') if self.static: - self.mk_static(out) - libfile = '%s$(LIB_EXT)' % self.dll_name + if IS_WINDOWS: + libfile = '%s-static$(LIB_EXT)' % self.dll_name + else: + libfile = '%s$(LIB_EXT)' % self.dll_name + self.mk_static(out, libfile) out.write('%s: %s %s\n\n' % (self.name, self.dll_file(), libfile)) else: out.write('%s: %s\n\n' % (self.name, self.dll_file())) - def mk_static(self, out): + def mk_static(self, out, libfile): # generate rule for lib objs = [] for cppfile in get_cpp_files(self.src_dir): @@ -1279,7 +1282,6 @@ class DLLComponent(Component): for cppfile in get_cpp_files(dep.src_dir): objfile = '%s$(OBJ_EXT)' % os.path.join(dep.build_dir, os.path.splitext(cppfile)[0]) objs.append(objfile) - libfile = '%s$(LIB_EXT)' % self.dll_name out.write('%s:' % libfile) for obj in objs: out.write(' ') From 0cb8193cdd2f2e5ae88fbc1565ddf00eaa323067 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 1 Mar 2016 17:42:33 +0000 Subject: [PATCH 53/57] logic fix --- src/api/api_numeral.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/api_numeral.cpp b/src/api/api_numeral.cpp index 491d9f597..40ebcf652 100644 --- a/src/api/api_numeral.cpp +++ b/src/api/api_numeral.cpp @@ -68,10 +68,10 @@ extern "C" { (' ' == *m) || ('\n' == *m) || ('.' == *m) || ('e' == *m) || ('E' == *m) || - (is_float && - ('p' == *m) || - ('P' == *m) || - ('+' == *m)))) { + (is_float && + (('p' == *m) || + ('P' == *m) || + ('+' == *m))))) { SET_ERROR_CODE(Z3_PARSER_ERROR); return 0; } From 67397bf71e9d48a0adbf3e9f47fe572c95c368ad Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 1 Mar 2016 09:48:24 -0800 Subject: [PATCH 54/57] enable logic parameter update to configure SMTLIB logic Signed-off-by: Nikolaj Bjorner --- src/api/api_ast.cpp | 4 ++-- src/ast/ast_smt_pp.h | 2 +- src/ast/proof_checker/proof_checker.cpp | 2 +- src/cmd_context/basic_cmds.cpp | 2 +- src/cmd_context/simplify_cmd.cpp | 2 +- src/opt/opt_solver.cpp | 4 ++-- src/opt/opt_solver.h | 2 +- src/smt/params/smt_params.cpp | 2 +- src/smt/params/smt_params.h | 4 ++-- src/smt/smt_context.h | 12 ++++++------ src/smt/smt_context_pp.cpp | 10 +++++----- src/smt/smt_internalizer.cpp | 2 +- src/smt/theory_arith_core.h | 4 ++-- src/smt/theory_dense_diff_logic_def.h | 2 +- src/smt/theory_diff_logic_def.h | 4 ++-- src/smt/theory_utvpi_def.h | 2 +- 16 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 0203439ae..e30da1aa1 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -832,7 +832,7 @@ extern "C" { pp_params params; pp.set_simplify_implies(params.simplify_implies()); ast* a1 = to_ast(a); - pp.set_logic(mk_c(c)->fparams().m_logic.c_str()); + pp.set_logic(mk_c(c)->fparams().m_logic); if (!is_expr(a1)) { buffer << mk_pp(a1, mk_c(c)->m()); break; @@ -880,7 +880,7 @@ extern "C" { std::ostringstream buffer; ast_smt_pp pp(mk_c(c)->m()); pp.set_benchmark_name(name); - pp.set_logic(logic); + pp.set_logic(logic?symbol(logic):symbol::null); pp.set_status(status); pp.add_attributes(attributes); pp_params params; diff --git a/src/ast/ast_smt_pp.h b/src/ast/ast_smt_pp.h index 59b1596fa..e88465828 100644 --- a/src/ast/ast_smt_pp.h +++ b/src/ast/ast_smt_pp.h @@ -67,7 +67,7 @@ public: void set_source_info(const char* si) { if (si) m_source_info = si; } void set_status(const char* s) { if (s) m_status = s; } void set_category(const char* c) { if (c) m_category = c; } - void set_logic(const char* l) { if (l) m_logic = l; } + void set_logic(symbol const& l) { m_logic = l; } void add_attributes(const char* s) { if (s) m_attributes += s; } void add_assumption(expr* n) { m_assumptions.push_back(n); } void add_assumption_star(expr* n) { m_assumptions_star.push_back(n); } diff --git a/src/ast/proof_checker/proof_checker.cpp b/src/ast/proof_checker/proof_checker.cpp index 16546db1e..45223cdb7 100644 --- a/src/ast/proof_checker/proof_checker.cpp +++ b/src/ast/proof_checker/proof_checker.cpp @@ -1280,7 +1280,7 @@ void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecede ast_smt_pp pp(m); pp.set_benchmark_name("lemma"); pp.set_status("unsat"); - pp.set_logic(m_logic.c_str()); + pp.set_logic(symbol(m_logic.c_str())); for (unsigned i = 0; i < num_antecedents; i++) pp.add_assumption(antecedents[i]); expr_ref n(m); diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index 957cbef4e..450bfbf5b 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -160,7 +160,7 @@ ATOMIC_CMD(get_proof_cmd, "get-proof", "retrieve proof", { ast_smt_pp pp(ctx.m()); cmd_is_declared isd(ctx); pp.set_is_declared(&isd); - pp.set_logic(ctx.get_logic().str().c_str()); + pp.set_logic(ctx.get_logic()); pp.display_smt2(ctx.regular_stream(), pr); ctx.regular_stream() << std::endl; }); diff --git a/src/cmd_context/simplify_cmd.cpp b/src/cmd_context/simplify_cmd.cpp index 9f0d67142..1c249ce1f 100644 --- a/src/cmd_context/simplify_cmd.cpp +++ b/src/cmd_context/simplify_cmd.cpp @@ -102,7 +102,7 @@ public: } if (!failed && m_params.get_bool("print_proofs", false)) { ast_smt_pp pp(ctx.m()); - pp.set_logic(ctx.get_logic().str().c_str()); + pp.set_logic(ctx.get_logic()); pp.display_expr_smt2(ctx.regular_stream(), pr.get()); ctx.regular_stream() << std::endl; } diff --git a/src/opt/opt_solver.cpp b/src/opt/opt_solver.cpp index 097cf5f3e..eff70e48e 100644 --- a/src/opt/opt_solver.cpp +++ b/src/opt/opt_solver.cpp @@ -162,7 +162,7 @@ namespace opt { std::stringstream file_name; file_name << "opt_solver" << ++m_dump_count << ".smt2"; std::ofstream buffer(file_name.str().c_str()); - to_smt2_benchmark(buffer, num_assumptions, assumptions, "opt_solver", ""); + to_smt2_benchmark(buffer, num_assumptions, assumptions, "opt_solver"); buffer.close(); IF_VERBOSE(1, verbose_stream() << "(created benchmark: " << file_name.str() << "..."; verbose_stream().flush();); @@ -400,7 +400,7 @@ namespace opt { unsigned num_assumptions, expr * const * assumptions, char const * name, - char const * logic, + symbol const& logic, char const * status, char const * attributes) { ast_smt_pp pp(m); diff --git a/src/opt/opt_solver.h b/src/opt/opt_solver.h index 128836089..3c58a4fec 100644 --- a/src/opt/opt_solver.h +++ b/src/opt/opt_solver.h @@ -135,7 +135,7 @@ namespace opt { void to_smt2_benchmark(std::ofstream & buffer, unsigned num_assumptions, expr * const * assumptions, char const * name = "benchmarks", - char const * logic = "", char const * status = "unknown", char const * attributes = ""); + symbol const& logic = symbol::null, char const * status = "unknown", char const * attributes = ""); private: lbool decrement_value(unsigned i, inf_eps& val); diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index 8dc0bb7d6..8222c3d60 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -38,7 +38,7 @@ void smt_params::updt_local_params(params_ref const & _p) { m_rlimit = p.rlimit(); m_max_conflicts = p.max_conflicts(); m_core_validate = p.core_validate(); - m_logic = _p.get_str("logic", m_logic.c_str()); + m_logic = _p.get_sym("logic", m_logic); model_params mp(_p); m_model_compact = mp.compact(); if (_p.get_bool("arith.greatest_error_pivot", false)) diff --git a/src/smt/params/smt_params.h b/src/smt/params/smt_params.h index a60f026bc..9c1eec649 100644 --- a/src/smt/params/smt_params.h +++ b/src/smt/params/smt_params.h @@ -160,7 +160,7 @@ struct smt_params : public preprocessor_params, // // ----------------------------------- bool m_smtlib_dump_lemmas; - std::string m_logic; + symbol m_logic; // ----------------------------------- // @@ -260,7 +260,7 @@ struct smt_params : public preprocessor_params, m_old_clause_relevancy(6), m_inv_clause_decay(1), m_smtlib_dump_lemmas(false), - m_logic("AUFLIA"), + m_logic(symbol::null), m_profile_res_sub(false), m_display_bool_var2expr(false), m_display_ll_bool_var2expr(false), diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index b918be2cf..8b2453e31 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -1218,18 +1218,18 @@ namespace smt { void display_hot_bool_vars(std::ostream & out) const; - void display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, literal consequent = false_literal, const char * logic = "AUFLIRA") const; + void display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, literal consequent = false_literal, symbol const& logic = symbol::null) const; - void display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent = false_literal, const char * logic = "AUFLIRA") const; + void display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent = false_literal, symbol const& logic = symbol::null) const; void display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, unsigned num_antecedent_eqs, enode_pair const * antecedent_eqs, - literal consequent = false_literal, const char * logic = "AUFLIRA") const; + literal consequent = false_literal, symbol const& logic = symbol::null) const; void display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, unsigned num_antecedent_eqs, enode_pair const * antecedent_eqs, - literal consequent = false_literal, const char * logic = "AUFLIRA") const; + literal consequent = false_literal, symbol const& logic = symbol::null) const; - void display_assignment_as_smtlib2(std::ostream& out, const char * logic = "AUFLIRA") const; + void display_assignment_as_smtlib2(std::ostream& out, symbol const& logic = symbol::null) const; void display_normalized_enodes(std::ostream & out) const; @@ -1367,7 +1367,7 @@ namespace smt { app * mk_eq_atom(expr * lhs, expr * rhs); - bool set_logic(symbol logic) { return m_setup.set_logic(logic); } + bool set_logic(symbol const& logic) { return m_setup.set_logic(logic); } void register_plugin(simplifier_plugin * s); diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 1f5566a03..a3079f52b 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -215,7 +215,7 @@ namespace smt { } } - void context::display_assignment_as_smtlib2(std::ostream& out, char const* logic) const { + void context::display_assignment_as_smtlib2(std::ostream& out, symbol const& logic) const { ast_smt_pp pp(m_manager); pp.set_benchmark_name("lemma"); pp.set_status("unknown"); @@ -421,7 +421,7 @@ namespace smt { st.display_internal(out); } - void context::display_lemma_as_smt_problem(std::ostream & out, unsigned num_antecedents, literal const * antecedents, literal consequent, const char * logic) const { + 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"); @@ -441,7 +441,7 @@ namespace smt { #define BUFFER_SZ 128 - void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent, const char * logic) const { + void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, literal consequent, symbol const& logic) const { char buffer[BUFFER_SZ]; #ifdef _WINDOWS sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt2", g_lemma_id); @@ -456,7 +456,7 @@ 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, const char * logic) const { + literal consequent, symbol const& logic) const { ast_smt_pp pp(m_manager); pp.set_benchmark_name("lemma"); pp.set_status("unsat"); @@ -480,7 +480,7 @@ namespace smt { void context::display_lemma_as_smt_problem(unsigned num_antecedents, literal const * antecedents, unsigned num_eq_antecedents, enode_pair const * eq_antecedents, - literal consequent, const char * logic) const { + literal consequent, symbol const& logic) const { char buffer[BUFFER_SZ]; #ifdef _WINDOWS sprintf_s(buffer, BUFFER_SZ, "lemma_%d.smt2", g_lemma_id); diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 5cad7547e..a420d85d9 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -1433,7 +1433,7 @@ namespace smt { literal_buffer tmp; neg_literals(num_lits, lits, tmp); SASSERT(tmp.size() == num_lits); - display_lemma_as_smt_problem(tmp.size(), tmp.c_ptr(), false_literal, m_fparams.m_logic.c_str()); + display_lemma_as_smt_problem(tmp.size(), tmp.c_ptr(), false_literal, m_fparams.m_logic); } mk_clause(num_lits, lits, js); } diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index dcd2cb316..301b5d14d 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -2872,7 +2872,7 @@ namespace smt { 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(), - ante.eqs().size(), ante.eqs().c_ptr(), l, 0); + ante.eqs().size(), ante.eqs().c_ptr(), l); } } @@ -2881,7 +2881,7 @@ namespace smt { context & ctx = get_context(); if (dump_lemmas()) { ctx.display_lemma_as_smt_problem(ante.lits().size(), ante.lits().c_ptr(), - ante.eqs().size(), ante.eqs().c_ptr(), l, 0); + ante.eqs().size(), ante.eqs().c_ptr(), l); } } diff --git a/src/smt/theory_dense_diff_logic_def.h b/src/smt/theory_dense_diff_logic_def.h index 28ee4b025..fd388b5bd 100644 --- a/src/smt/theory_dense_diff_logic_def.h +++ b/src/smt/theory_dense_diff_logic_def.h @@ -597,7 +597,7 @@ namespace smt { ctx.set_conflict(ctx.mk_justification(theory_conflict_justification(get_id(), r, antecedents.size(), antecedents.c_ptr()))); if (dump_lemmas()) { - ctx.display_lemma_as_smt_problem(antecedents.size(), antecedents.c_ptr(), false_literal, ""); + ctx.display_lemma_as_smt_problem(antecedents.size(), antecedents.c_ptr(), false_literal); } return; diff --git a/src/smt/theory_diff_logic_def.h b/src/smt/theory_diff_logic_def.h index f444fe88e..cca78e9c0 100644 --- a/src/smt/theory_diff_logic_def.h +++ b/src/smt/theory_diff_logic_def.h @@ -632,7 +632,7 @@ void theory_diff_logic::new_edge(dl_var src, dl_var dst, unsigned num_edges } ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); if (dump_lemmas()) { - char const * logic = m_is_lia ? "QF_LIA" : "QF_LRA"; + symbol logic(m_is_lia ? "QF_LIA" : "QF_LRA"); ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); } @@ -678,7 +678,7 @@ void theory_diff_logic::set_neg_cycle_conflict() { ); if (dump_lemmas()) { - char const * logic = m_is_lia ? "QF_LIA" : "QF_LRA"; + symbol logic(m_is_lia ? "QF_LIA" : "QF_LRA"); ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); } diff --git a/src/smt/theory_utvpi_def.h b/src/smt/theory_utvpi_def.h index 22b19b0b1..d5ed4e825 100644 --- a/src/smt/theory_utvpi_def.h +++ b/src/smt/theory_utvpi_def.h @@ -223,7 +223,7 @@ namespace smt { ); if (m_params.m_arith_dump_lemmas) { - char const * logic = m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"; + symbol logic(m_lra ? (m_lia?"QF_LIRA":"QF_LRA") : "QF_LIA"); ctx.display_lemma_as_smt_problem(lits.size(), lits.c_ptr(), false_literal, logic); } From 59e695f2be96e586ebb7c9c7560ec12c7436f970 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 1 Mar 2016 21:21:25 +0000 Subject: [PATCH 55/57] Bugfixes for FP numerals in Python Relates to #464, #470 --- src/api/python/z3.py | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 814198d22..531f2e8cd 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -48,6 +48,7 @@ from z3printer import * from fractions import Fraction import sys import io +import math if sys.version < '3': def _is_int(v): @@ -8409,11 +8410,24 @@ def FPSort(ebits, sbits, ctx=None): def _to_float_str(val, exp=0): if isinstance(val, float): - v = val.as_integer_ratio() - num = v[0] - den = v[1] - rvs = str(num) + '/' + str(den) - res = rvs + 'p' + _to_int_str(exp) + if math.isnan(val): + res = "NaN" + elif val == 0.0: + sone = math.copysign(1.0, val) + if sone < 0.0: + return "-0.0" + else: + return "+0.0" + elif val == float("+inf"): + res = "+oo" + elif val == float("-inf"): + res = "-oo" + else: + v = val.as_integer_ratio() + num = v[0] + den = v[1] + rvs = str(num) + '/' + str(den) + res = rvs + 'p' + _to_int_str(exp) elif isinstance(val, bool): if val: res = "1.0" @@ -8511,6 +8525,12 @@ def FPVal(sig, exp=None, fps=None, ctx=None): >>> v = FPVal(-2.25, FPSort(8, 24)) >>> v -1.125*(2**1) + >>> v = FPVal(-0.0, FPSort(8, 24)) + -0.0 + >>> v = FPVal(0.0, FPSort(8, 24)) + +0.0 + >>> v = FPVal(+0.0, FPSort(8, 24)) + +0.0 """ ctx = _get_ctx(ctx) if is_fp_sort(exp): @@ -8522,7 +8542,18 @@ def FPVal(sig, exp=None, fps=None, ctx=None): if exp == None: exp = 0 val = _to_float_str(sig) - return FPNumRef(Z3_mk_numeral(ctx.ref(), val, fps.ast), ctx) + if val == "NaN" or val == "nan": + return fpNaN(fps) + elif val == "-0.0": + return fpMinusZero(fps) + elif val == "0.0" or val == "+0.0": + return fpPlusZero(fps) + elif val == "+oo" or val == "+inf" or val == "+Inf": + return fpPlusInfinity(fps) + elif val == "-oo" or val == "-inf" or val == "-Inf": + return fpMinusInfinity(fps) + else: + return FPNumRef(Z3_mk_numeral(ctx.ref(), val, fps.ast), ctx) def FP(name, fpsort, ctx=None): """Return a floating-point constant named `name`. From 18b9cd19482c08759b868c9574eb48bb3c55a0b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andres=20N=C3=B6tzli?= Date: Tue, 1 Mar 2016 18:56:20 -0800 Subject: [PATCH 56/57] [Z3py] Fix documentation in FPSortRef --- src/api/python/z3.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 531f2e8cd..3a4276dfd 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -7964,7 +7964,7 @@ class FPSortRef(SortRef): return int(Z3_fpa_get_ebits(self.ctx_ref(), self.ast)) def sbits(self): - """Retrieves the number of bits reserved for the exponent in the FloatingPoint sort `self`. + """Retrieves the number of bits reserved for the significand in the FloatingPoint sort `self`. >>> b = FPSort(8, 24) >>> b.sbits() 24 @@ -7972,8 +7972,7 @@ class FPSortRef(SortRef): return int(Z3_fpa_get_sbits(self.ctx_ref(), self.ast)) def cast(self, val): - """Try to cast `val` as a Floating-point expression - + """Try to cast `val` as a floating-point expression. >>> b = FPSort(8, 24) >>> b.cast(1.0) 1 From a25336a899f3910f0c2a9cf386854ea055dfd673 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 1 Mar 2016 22:31:44 -0800 Subject: [PATCH 57/57] fix test build, working on rec-functions and automata complementation Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 13 +- src/cmd_context/cmd_context.h | 1 + src/math/automata/symbolic_automata.h | 3 + src/math/automata/symbolic_automata_def.h | 140 ++++++++++++++++++++-- src/smt/smt_model_checker.cpp | 121 +++++++++++++------ src/smt/smt_model_checker.h | 6 +- src/smt/smt_quantifier.cpp | 12 +- src/test/expr_rand.cpp | 4 +- 8 files changed, 241 insertions(+), 59 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index eb18c5262..cdc1df6be 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -326,6 +326,7 @@ cmd_context::cmd_context(bool main_ctx, ast_manager * m, symbol const & l): m_manager(m), m_own_manager(m == 0), m_manager_initialized(false), + m_rec_fun_declared(false), m_pmanager(0), m_sexpr_manager(0), m_regular("stdout", std::cout), @@ -830,14 +831,18 @@ void cmd_context::insert(symbol const & s, object_ref * r) { } void cmd_context::insert_rec_fun(func_decl* f, expr_ref_vector const& binding, svector const& ids, expr* e) { - expr_ref eq(m()), lhs(m()); + expr_ref eq(m()); + app_ref lhs(m()); lhs = m().mk_app(f, binding.size(), binding.c_ptr()); eq = m().mk_eq(lhs, e); if (!ids.empty()) { - eq = m().mk_forall(ids.size(), f->get_domain(), ids.c_ptr(), eq); + expr* pat = m().mk_pattern(lhs); + eq = m().mk_forall(ids.size(), f->get_domain(), ids.c_ptr(), eq, 0, symbol(":rec-fun"), symbol::null, 1, &pat); + } + if (!ids.empty() && !m_rec_fun_declared) { + warning_msg("recursive functions are currently only partially supported: they are translated into recursive equations without special handling"); + m_rec_fun_declared = true; } - warning_msg("recursive functions are currently only partially supported: they are translated into recursive equations without special handling"); - // TBD: basic implementation asserts axiom. Life-time of recursive equation follows scopes (unlikely to be what SMT-LIB 2.5 wants). assert_expr(eq); } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 833977a1c..865999042 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -165,6 +165,7 @@ protected: ast_manager * m_manager; bool m_own_manager; bool m_manager_initialized; + bool m_rec_fun_declared; pdecl_manager * m_pmanager; sexpr_manager * m_sexpr_manager; check_logic m_check_logic; diff --git a/src/math/automata/symbolic_automata.h b/src/math/automata/symbolic_automata.h index cd553eeef..cc3a0f5a8 100644 --- a/src/math/automata/symbolic_automata.h +++ b/src/math/automata/symbolic_automata.h @@ -34,6 +34,9 @@ class symbolic_automata { typedef vector moves_t; typedef obj_ref ref_t; typedef ref_vector refs_t; + typedef std::pair unsigned_pair; + template class u2_map : public map, default_eq > {}; + M& m; ba_t& m_ba; diff --git a/src/math/automata/symbolic_automata_def.h b/src/math/automata/symbolic_automata_def.h index 50c115d39..2df5275c2 100644 --- a/src/math/automata/symbolic_automata_def.h +++ b/src/math/automata/symbolic_automata_def.h @@ -25,7 +25,6 @@ Revision History: #include "symbolic_automata.h" #include "hashtable.h" -typedef std::pair unsigned_pair; @@ -234,7 +233,7 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim unsigned new_init = pblocks[blocks[a.init()]].get_representative(); // set moves - map, default_eq > conds; + u2_map conds; svector keys; moves_t new_moves; @@ -276,25 +275,22 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_minim template typename symbolic_automata::automaton_t* symbolic_automata::mk_product(automaton_t& a, automaton_t& b) { - map, default_eq > state_ids; + u2_map pair2id; unsigned_pair init_pair(a.init(), b.init()); svector todo; todo.push_back(init_pair); - state_ids.insert(init_pair, 0); + pair2id.insert(init_pair, 0); moves_t mvs; unsigned_vector final; if (a.is_final_state(a.init()) && b.is_final_state(b.init())) { final.push_back(0); } - if (false) { - mk_minimize(a); - } unsigned n = 1; moves_t mvsA, mvsB; while (!todo.empty()) { unsigned_pair curr_pair = todo.back(); todo.pop_back(); - unsigned src = state_ids[curr_pair]; + unsigned src = pair2id[curr_pair]; mvsA.reset(); mvsB.reset(); a.get_moves_from(curr_pair.first, mvsA, true); b.get_moves_from(curr_pair.second, mvsB, true); @@ -310,9 +306,9 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_produ } unsigned_pair tgt_pair(mvsA[i].dst(), mvsB[j].dst()); unsigned tgt; - if (!state_ids.find(tgt_pair, tgt)) { + if (!pair2id.find(tgt_pair, tgt)) { tgt = n++; - state_ids.insert(tgt_pair, tgt); + pair2id.insert(tgt_pair, tgt); todo.push_back(tgt_pair); if (a.is_final_state(tgt_pair.first) && b.is_final_state(tgt_pair.second)) { final.push_back(tgt); @@ -366,6 +362,130 @@ typename symbolic_automata::automaton_t* symbolic_automata::mk_produ } } +#if 0 +template +unsigned symbolic_automata::get_product_state_id(u2_map& pair2id, unsigned_pair const& p, unsigned& id) { + unsigned result = 0; + if (!pair2id.find(p, result)) { + result = id++; + pair2id.insert(p, result); + } + return result; +} +#endif +template +typename symbolic_automata::automaton_t* symbolic_automata::mk_difference(automaton_t& a, automaton_t& b) { +#if 0 + map bs2id; // set of b-states to unique id + vector id2bs; // unique id to set of b-states + u2_map pair2id; // pair of states to new state id + unsigned sink_state = UINT_MAX; + uint_set bset; + moves_t new_moves; // moves in the resulting automaton + unsigned_vector new_final_states; // new final states + unsigned p_state_id = 0; // next state identifier + bs2id.insert(uint_set(), sink_state); // the sink state has no b-states + bset.insert(b.init()); // the initial state has a single initial b state + bs2id.insert(bset, 0); // the index to the initial b state is 0 + id2bs.push_back(bset); + if (!b.is_final_state(b.init()) && a.is_final_state(a.init())) { + new_final_states.push_back(p_state_id); + } + + svector todo; + unsigned_pair state(a.init(), 0); + todo.push_back(state); + pair2id.insert(state, p_state_id++); + + // or just make todo a vector whose indices coincide with state_id. + while (!todo.empty()) { + state = todo.back(); + unsigned state_id = pair2id[state]; + todo.pop_back(); + mvsA.reset(); + a.get_moves_from(state.first, mvsA, true); + if (state.second == sink_state) { + for (unsigned i = 0; i < mvsA.size(); ++i) { + unsigned_pair dst(mvsA[i].dst(), sink_state); + bool is_new = !pair2id.contains(dst); + unsigned dst_id = get_product_state_id(pair2id, dst, p_state_id); + new_moves.push_back(move_t(m, state_id, dst_id, mvsA[i].t())); + if (is_new && a.is_final_state(mvsA[i].dst())) { + new_final_states.push_back(dst_id); + todo.push_back(dst); + } + } + } + else { + get_moves_from(b, id2bs[state.second], mvsB); + generate_min_terms(mvsB, min_terms); + for (unsigned j = 0; j < min_terms.size(); ++j) { + for (unsigned i = 0; i < mvsA.size(); ++i) { + ref_t cond(m_ba.mk_and(mvsA[i].t(), min_terms[j].second), m); + switch (m_ba.is_sat(cond)) { + case l_false: + break; + case l_true: + ab_combinations.push_back(ab_comb(i, min_terms[j].first, cond)); + break; + case l_undef: + return 0; + } + } + } + + for (unsigned i = 0; i < ab_combinations.size(); ++i) { + move_t const& mvA = mvsA[ab_combinations[i].A]; + bset.reset(); + bool is_final = a.is_final_state(mvA.dst()); + for (unsigned j = 0; j < mvsB.size(); ++j) { + if (ab_combinations[i].B[j]) { + bset.insert(mvsB[j].dst()); + is_final &= !b.is_final_state(mvsB[j].dst()); + } + } + unsigned new_b; + if (bset.empty()) { + new_b = sink_state; + } + else if (!bs2id.find(bset, new_b)) { + new_b = id2bs.size(); + id2bs.push_back(bset); + bs2id.insert(bset, new_b); + } + unsigned_pair dst(mvA.dst(), new_b); + bool is_new = !pair2id.contains(dst); + dst_id = get_product_state_id(pair2id, dst, p_state_id); + move_t new_move(m, state_id, dst_id, ab_combinations[i].cond); + new_moves.push_back(new_move); + if (is_new) { + if (is_final) { + new_final_states.push_back(dst_id); + } + todo.push_back(dst); + } + } + } + } + + + if (new_final_states.empty()) { + return alloc(automaton_t, m); + } + + automaton_t* result = alloc(automaton_t, m, 0, new_final_states, new_moves); + +#if 0 + result->isEpsilonFree = true; + if (A.IsDeterministic) + result->isDeterministic = true; + result->EliminateDeadStates(); +#endif + return result; + +#endif + return 0; +} #endif diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index b294bf4c9..d67b6a3eb 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -31,7 +31,7 @@ Revision History: namespace smt { model_checker::model_checker(ast_manager & m, qi_params const & p, model_finder & mf): - m_manager(m), + m(m), m_params(p), m_qm(0), m_context(0), @@ -93,9 +93,9 @@ namespace smt { obj_hashtable::iterator end = universe.end(); for (; it != end; ++it) { expr * e = *it; - eqs.push_back(m_manager.mk_eq(sk, e)); + eqs.push_back(m.mk_eq(sk, e)); } - m_aux_context->assert_expr(m_manager.mk_or(eqs.size(), eqs.c_ptr())); + m_aux_context->assert_expr(m.mk_or(eqs.size(), eqs.c_ptr())); } #define PP_DEPTH 8 @@ -106,16 +106,16 @@ namespace smt { The variables are replaced by skolem constants. These constants are stored in sks. */ void model_checker::assert_neg_q_m(quantifier * q, expr_ref_vector & sks) { - expr_ref tmp(m_manager); + expr_ref tmp(m); m_curr_model->eval(q->get_expr(), tmp, true); - TRACE("model_checker", tout << "q after applying interpretation:\n" << mk_ismt2_pp(tmp, m_manager) << "\n";); + TRACE("model_checker", tout << "q after applying interpretation:\n" << mk_ismt2_pp(tmp, m) << "\n";); ptr_buffer subst_args; unsigned num_decls = q->get_num_decls(); subst_args.resize(num_decls, 0); sks.resize(num_decls, 0); for (unsigned i = 0; i < num_decls; i++) { sort * s = q->get_decl_sort(num_decls - i - 1); - expr * sk = m_manager.mk_fresh_const(0, s); + expr * sk = m.mk_fresh_const(0, s); sks[num_decls - i - 1] = sk; subst_args[num_decls - i - 1] = sk; if (m_curr_model->is_finite(s)) { @@ -123,12 +123,12 @@ namespace smt { } } - expr_ref sk_body(m_manager); - var_subst s(m_manager); + expr_ref sk_body(m); + var_subst s(m); s(tmp, subst_args.size(), subst_args.c_ptr(), sk_body); - expr_ref r(m_manager); - r = m_manager.mk_not(sk_body); - TRACE("model_checker", tout << "mk_neg_q_m:\n" << mk_ismt2_pp(r, m_manager) << "\n";); + expr_ref r(m); + r = m.mk_not(sk_body); + TRACE("model_checker", tout << "mk_neg_q_m:\n" << mk_ismt2_pp(r, m) << "\n";); m_aux_context->assert_expr(r); } @@ -138,13 +138,13 @@ namespace smt { unsigned num_decls = q->get_num_decls(); // Remark: sks were created for the flat version of q. SASSERT(sks.size() >= num_decls); - expr_ref_buffer bindings(m_manager); + expr_ref_vector bindings(m); bindings.resize(num_decls); unsigned max_generation = 0; for (unsigned i = 0; i < num_decls; i++) { expr * sk = sks.get(num_decls - i - 1); func_decl * sk_d = to_app(sk)->get_decl(); - expr_ref sk_value(m_manager); + expr_ref sk_value(m); sk_value = cex->get_const_interp(sk_d); if (sk_value == 0) { sk_value = cex->get_some_value(sk_d->get_range()); @@ -155,7 +155,7 @@ namespace smt { unsigned sk_term_gen; expr * sk_term = m_model_finder.get_inv(q, i, sk_value, sk_term_gen); if (sk_term != 0) { - SASSERT(!m_manager.is_model_value(sk_term)); + SASSERT(!m.is_model_value(sk_term)); if (sk_term_gen > max_generation) max_generation = sk_term_gen; sk_value = sk_term; @@ -177,27 +177,30 @@ namespace smt { TRACE("model_checker", tout << q->get_qid() << " found (use_inv: " << use_inv << ") new instance: "; for (unsigned i = 0; i < num_decls; i++) { - tout << mk_ismt2_pp(bindings[i], m_manager) << " "; + tout << mk_ismt2_pp(bindings[i].get(), m) << " "; } tout << "\n";); - for (unsigned i = 0; i < num_decls; i++) + add_instance(q, bindings, max_generation); + return true; + } + + void model_checker::add_instance(quantifier* q, expr_ref_vector const& bindings, unsigned max_generation) { + for (unsigned i = 0; i < bindings.size(); i++) m_new_instances_bindings.push_back(bindings[i]); void * mem = m_new_instances_region.allocate(instance::get_obj_size(q->get_num_decls())); instance * new_inst = new (mem) instance(q, bindings.c_ptr(), max_generation); m_new_instances.push_back(new_inst); - - return true; } void model_checker::operator()(expr *n) { - if (m_manager.is_model_value(n)) { + if (m.is_model_value(n)) { throw is_model_value(); } } bool model_checker::contains_model_value(expr* n) { - if (m_manager.is_model_value(n)) { + if (m.is_model_value(n)) { return true; } if (is_app(n) && to_app(n)->get_num_args() == 0) { @@ -217,22 +220,22 @@ namespace smt { bool model_checker::add_blocking_clause(model * cex, expr_ref_vector & sks) { SASSERT(cex != 0); unsigned num_sks = sks.size(); - expr_ref_buffer diseqs(m_manager); + expr_ref_buffer diseqs(m); for (unsigned i = 0; i < num_sks; i++) { expr * sk = sks.get(i); func_decl * sk_d = to_app(sk)->get_decl(); - expr_ref sk_value(m_manager); + expr_ref sk_value(m); sk_value = cex->get_const_interp(sk_d); if (sk_value == 0) { sk_value = cex->get_some_value(sk_d->get_range()); if (sk_value == 0) return false; // get_some_value failed... aborting add_blocking_clause } - diseqs.push_back(m_manager.mk_not(m_manager.mk_eq(sk, sk_value))); + diseqs.push_back(m.mk_not(m.mk_eq(sk, sk_value))); } - expr_ref blocking_clause(m_manager); - blocking_clause = m_manager.mk_or(diseqs.size(), diseqs.c_ptr()); - TRACE("model_checker", tout << "blocking clause:\n" << mk_ismt2_pp(blocking_clause, m_manager) << "\n";); + expr_ref blocking_clause(m); + blocking_clause = m.mk_or(diseqs.size(), diseqs.c_ptr()); + TRACE("model_checker", tout << "blocking clause:\n" << mk_ismt2_pp(blocking_clause, m) << "\n";); m_aux_context->assert_expr(blocking_clause); return true; } @@ -245,15 +248,15 @@ namespace smt { m_aux_context->push(); quantifier * flat_q = get_flat_quantifier(q); - TRACE("model_checker", tout << "model checking:\n" << mk_ismt2_pp(q->get_expr(), m_manager) << "\n" << - mk_ismt2_pp(flat_q->get_expr(), m_manager) << "\n";); - expr_ref_vector sks(m_manager); + TRACE("model_checker", tout << "model checking:\n" << mk_ismt2_pp(q->get_expr(), m) << "\n" << + mk_ismt2_pp(flat_q->get_expr(), m) << "\n";); + expr_ref_vector sks(m); assert_neg_q_m(flat_q, sks); TRACE("model_checker", tout << "skolems:\n"; for (unsigned i = 0; i < sks.size(); i++) { expr * sk = sks.get(i); - tout << mk_ismt2_pp(sk, m_manager) << " " << mk_pp(m_manager.get_sort(sk), m_manager) << "\n"; + tout << mk_ismt2_pp(sk, m) << " " << mk_pp(m.get_sort(sk), m) << "\n"; }); lbool r = m_aux_context->check(); @@ -301,6 +304,43 @@ namespace smt { return false; } + bool model_checker::check_rec_fun(quantifier* q) { + TRACE("model_checker", tout << mk_pp(q, m) << "\n";); + SASSERT(q->get_num_patterns() == 1); + expr* fn = to_app(q->get_pattern(0))->get_arg(0); + SASSERT(is_app(fn)); + func_decl* f = to_app(fn)->get_decl(); + enode_vector::const_iterator it = m_context->begin_enodes_of(f); + enode_vector::const_iterator end = m_context->end_enodes_of(f); + + bool is_undef = false; + expr_ref_vector args(m); + unsigned num_decls = q->get_num_decls(); + args.resize(num_decls, 0); + var_subst sub(m); + expr_ref tmp(m), result(m); + for (; it != end; ++it) { + if (m_context->is_relevant(*it)) { + app* e = (*it)->get_owner(); + for (unsigned i = 0; i < e->get_num_args(); ++i) { + args[num_decls - i - 1] = e->get_arg(i); + } + sub(q->get_expr(), num_decls, args.c_ptr(), tmp); + m_curr_model->eval(tmp, result, true); + if (m.is_true(result)) { + continue; + } + if (m.is_false(result)) { + add_instance(q, args, 0); + return false; + } + TRACE("model_checker", tout << tmp << "evaluates to undetermined " << result << "\n";); + is_undef = true; + } + } + return !is_undef; + } + void model_checker::init_aux_context() { if (!m_fparams) { m_fparams = alloc(smt_params, m_context->get_fparams()); @@ -347,7 +387,7 @@ namespace smt { quantifier * q = *it; if(!m_qm->mbqi_enabled(q)) continue; TRACE("model_checker", - tout << "Check: " << mk_pp(q, m_manager) << "\n"; + tout << "Check: " << mk_pp(q, m) << "\n"; tout << m_context->get_assignment(q) << "\n";); if (m_context->is_relevant(q) && m_context->get_assignment(q) == l_true) { @@ -355,7 +395,12 @@ namespace smt { verbose_stream() << "(smt.mbqi :checking " << q->get_qid() << ")\n"; } found_relevant = true; - if (!check(q)) { + if (q->get_qid() == symbol(":rec-fun")) { + if (!check_rec_fun(q)) { + num_failures++; + } + } + else if (!check(q)) { if (m_params.m_mbqi_trace || get_verbosity_level() >= 5) { verbose_stream() << "(smt.mbqi :failed " << q->get_qid() << ")\n"; } @@ -425,22 +470,22 @@ namespace smt { for (unsigned i = 0; i < num_decls; i++) { expr * b = inst->m_bindings[i]; if (!m_context->e_internalized(b)) { - TRACE("model_checker_bug_detail", tout << "internalizing b:\n" << mk_pp(b, m_manager) << "\n";); + TRACE("model_checker_bug_detail", tout << "internalizing b:\n" << mk_pp(b, m) << "\n";); m_context->internalize(b, false, gen); } bindings.push_back(m_context->get_enode(b)); } - TRACE("model_checker_bug_detail", tout << "instantiating... q:\n" << mk_pp(q, m_manager) << "\n"; + TRACE("model_checker_bug_detail", tout << "instantiating... q:\n" << mk_pp(q, m) << "\n"; tout << "inconsistent: " << m_context->inconsistent() << "\n"; tout << "bindings:\n"; for (unsigned i = 0; i < num_decls; i++) { expr * b = inst->m_bindings[i]; - tout << mk_pp(b, m_manager) << "\n"; + tout << mk_pp(b, m) << "\n"; }); TRACE("model_checker_instance", - expr_ref inst_expr(m_manager); - instantiate(m_manager, q, inst->m_bindings, inst_expr); - tout << "(assert " << mk_ismt2_pp(inst_expr, m_manager) << ")\n";); + 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); TRACE("model_checker_bug_detail", tout << "after instantiating, inconsistent: " << m_context->inconsistent() << "\n";); } diff --git a/src/smt/smt_model_checker.h b/src/smt/smt_model_checker.h index 2b8f1aa4d..b94ddb6bb 100644 --- a/src/smt/smt_model_checker.h +++ b/src/smt/smt_model_checker.h @@ -37,7 +37,7 @@ namespace smt { class quantifier_manager; class model_checker { - ast_manager & m_manager; + ast_manager & m; // _manager; qi_params const & m_params; // copy of smt_params for auxiliary context. // the idea is to use a different configuration for the aux context (e.g., disable relevancy) @@ -59,7 +59,8 @@ namespace smt { void assert_neg_q_m(quantifier * q, expr_ref_vector & sks); bool add_blocking_clause(model * cex, expr_ref_vector & sks); bool check(quantifier * q); - + bool check_rec_fun(quantifier* q); + struct instance { quantifier * m_q; unsigned m_generation; @@ -82,6 +83,7 @@ namespace smt { struct is_model_value {}; expr_mark m_visited; bool contains_model_value(expr* e); + void add_instance(quantifier* q, expr_ref_vector const& bindings, unsigned max_generation); public: model_checker(ast_manager & m, qi_params const & p, model_finder & mf); diff --git a/src/smt/smt_quantifier.cpp b/src/smt/smt_quantifier.cpp index e06480bc5..0501714ee 100644 --- a/src/smt/smt_quantifier.cpp +++ b/src/smt/smt_quantifier.cpp @@ -40,6 +40,7 @@ namespace smt { ptr_vector m_quantifiers; scoped_ptr m_plugin; unsigned m_num_instances; + symbol m_rec_fun; imp(quantifier_manager & wrapper, context & ctx, smt_params & p, quantifier_manager_plugin * plugin): m_wrapper(wrapper), @@ -47,7 +48,8 @@ namespace smt { m_params(p), m_qi_queue(m_wrapper, ctx, p), m_qstat_gen(ctx.get_manager(), ctx.get_region()), - m_plugin(plugin) { + m_plugin(plugin), + m_rec_fun(":rec-fun") { m_num_instances = 0; m_qi_queue.setup(); } @@ -184,6 +186,10 @@ namespace smt { m_qi_queue.instantiate(); } + bool check_quantifier(quantifier* q) { + return m_context.is_relevant(q) && m_context.get_assignment(q) == l_true; // TBD: && q->get_qid() != m_rec_fun; + } + bool quick_check_quantifiers() { if (m_params.m_qi_quick_checker == MC_NO) return true; @@ -195,7 +201,7 @@ namespace smt { ptr_vector::const_iterator it = m_quantifiers.begin(); ptr_vector::const_iterator end = m_quantifiers.end(); for (; it != end; ++it) - if (m_context.is_relevant(*it) && m_context.get_assignment(*it) == l_true && mc.instantiate_unsat(*it)) + if (check_quantifier(*it) && mc.instantiate_unsat(*it)) result = false; if (m_params.m_qi_quick_checker == MC_UNSAT || !result) { m_qi_queue.instantiate(); @@ -206,7 +212,7 @@ namespace smt { IF_VERBOSE(10, verbose_stream() << "quick checking quantifiers (not sat)...\n";); it = m_quantifiers.begin(); for (; it != end; ++it) - if (m_context.is_relevant(*it) && m_context.get_assignment(*it) == l_true && mc.instantiate_not_sat(*it)) + if (check_quantifier(*it) && mc.instantiate_not_sat(*it)) result = false; m_qi_queue.instantiate(); return result; diff --git a/src/test/expr_rand.cpp b/src/test/expr_rand.cpp index 972c6e242..96d21a44c 100644 --- a/src/test/expr_rand.cpp +++ b/src/test/expr_rand.cpp @@ -38,7 +38,7 @@ void tst_expr_arith(unsigned num_files) { er.get_next(m.mk_bool_sort(), e); ast_smt_pp pp(m); - pp.set_logic("QF_AUFLIA"); + pp.set_logic(symbol("QF_AUFLIA")); std::ostringstream buffer; buffer << "random_arith_" << i << ".smt"; std::cout << buffer.str() << "\n"; @@ -81,7 +81,7 @@ void tst_expr_rand(unsigned num_files) { er.get_next(m.mk_bool_sort(), e); ast_smt_pp pp(m); - pp.set_logic("QF_AUFBV"); + pp.set_logic(symbol("QF_AUFBV")); std::ostringstream buffer; buffer << "random_bv_" << i << ".smt"; std::cout << buffer.str() << "\n";